1 /**
2  * @file DXF_plotter.cpp
3  * @brief Kicad: specialized plotter for DXF files format
4  */
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23  * or you may search the http://www.gnu.org website for the version 2 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
26  */
27 
28 #include <plotters/plotter_dxf.h>
29 #include <macros.h>
30 #include <string_utils.h>
31 #include <convert_basic_shapes_to_polygon.h>
32 #include <trigo.h>
33 
34 /**
35  * Oblique angle for DXF native text
36  * (I don't remember if 15 degrees is the ISO value... it looks nice anyway)
37  */
38 static const double DXF_OBLIQUE_ANGLE = 15;
39 
40 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
41 
42    - The primary colors (1 - 9)
43    - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
44    - Greys (251 - 255)
45 
46    There is *no* black... the white does it on paper, usually, and
47    anyway it depends on the plotter configuration, since DXF colors
48    are meant to be logical only (they represent *both* line color and
49    width); later version with plot styles only complicate the matter!
50 
51    As usual, brown and magenta/purple are difficult to place since
52    they are actually variations of other colors.
53  */
54 static const struct
55 {
56     const char *name;
57     int color;
58 } dxf_layer[NBCOLORS] =
59 {
60     { "BLACK",      7 },    // In DXF, color 7 is *both* white and black!
61     { "GRAY1",      251 },
62     { "GRAY2",      8 },
63     { "GRAY3",      9 },
64     { "WHITE",      7 },
65     { "LYELLOW",    51 },
66     { "BLUE1",      178 },
67     { "GREEN1",     98 },
68     { "CYAN1",      138 },
69     { "RED1",       18 },
70     { "MAGENTA1",   228 },
71     { "BROWN1",     58 },
72     { "BLUE2",      5 },
73     { "GREEN2",     3 },
74     { "CYAN2",      4 },
75     { "RED2",       1 },
76     { "MAGENTA2",   6 },
77     { "BROWN2",     54 },
78     { "BLUE3",      171 },
79     { "GREEN3",     91 },
80     { "CYAN3",      131 },
81     { "RED3",       11 },
82     { "MAGENTA3",   221 },
83     { "YELLOW3",    2 },
84     { "BLUE4",      5 },
85     { "GREEN4",     3 },
86     { "CYAN4",      4 },
87     { "RED4",       1 },
88     { "MAGENTA4",   6 },
89     { "YELLOW4",    2 }
90 };
91 
92 
getDXFLineType(PLOT_DASH_TYPE aType)93 static const char* getDXFLineType( PLOT_DASH_TYPE aType )
94 {
95     switch( aType )
96     {
97     case PLOT_DASH_TYPE::DEFAULT:
98     case PLOT_DASH_TYPE::SOLID:
99         return "CONTINUOUS";
100     case PLOT_DASH_TYPE::DASH:
101         return "DASHED";
102     case PLOT_DASH_TYPE::DOT:
103         return "DOTTED";
104     case PLOT_DASH_TYPE::DASHDOT:
105         return "DASHDOT";
106     default:
107         wxFAIL_MSG( "Unhandled PLOT_DASH_TYPE" );
108         return "CONTINUOUS";
109     }
110 }
111 
112 
113 // A helper function to create a color name acceptable in DXF files
114 // DXF files do not use a RGB definition
getDXFColorName(const COLOR4D & aColor)115 static wxString getDXFColorName( const COLOR4D& aColor )
116 {
117     EDA_COLOR_T color = COLOR4D::FindNearestLegacyColor( int( aColor.r * 255 ),
118                                                          int( aColor.g * 255 ),
119                                                          int( aColor.b * 255 ) );
120     wxString cname( dxf_layer[color].name );
121     return cname;
122 }
123 
124 
SetUnits(DXF_UNITS aUnit)125 void DXF_PLOTTER::SetUnits( DXF_UNITS aUnit )
126 {
127     m_plotUnits = aUnit;
128 
129     switch( aUnit )
130     {
131     case DXF_UNITS::MILLIMETERS:
132         m_unitScalingFactor = 0.00254;
133         m_measurementDirective = 1;
134         break;
135 
136     case DXF_UNITS::INCHES:
137     default:
138         m_unitScalingFactor = 0.0001;
139         m_measurementDirective = 0;
140     }
141 }
142 
143 
SetViewport(const wxPoint & aOffset,double aIusPerDecimil,double aScale,bool aMirror)144 void DXF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
145                                double aScale, bool aMirror )
146 {
147     m_plotOffset  = aOffset;
148     m_plotScale   = aScale;
149 
150     /* DXF paper is 'virtual' so there is no need of a paper size.
151        Also this way we can handle the aux origin which can be useful
152        (for example when aligning to a mechanical drawing) */
153     m_paperSize.x = 0;
154     m_paperSize.y = 0;
155 
156     /* Like paper size DXF units are abstract too. Anyway there is a
157      * system variable (MEASUREMENT) which will be set to 0 to indicate
158      * english units */
159     m_IUsPerDecimil = aIusPerDecimil;
160     m_iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
161     m_iuPerDeviceUnit *= GetUnitScaling();    // Get the scaling factor for the current units
162 
163     m_plotMirror = false;                     // No mirroring on DXF
164     m_currentColor = COLOR4D::BLACK;
165 }
166 
167 
StartPlot()168 bool DXF_PLOTTER::StartPlot()
169 {
170     wxASSERT( m_outputFile );
171 
172     // DXF HEADER - Boilerplate
173     // Defines the minimum for drawing i.e. the angle system and the
174     // 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
175     fprintf( m_outputFile,
176             "  0\n"
177             "SECTION\n"
178             "  2\n"
179             "HEADER\n"
180             "  9\n"
181             "$ANGBASE\n"
182             "  50\n"
183             "0.0\n"
184             "  9\n"
185             "$ANGDIR\n"
186             "  70\n"
187             "1\n"
188             "  9\n"
189             "$MEASUREMENT\n"
190             "  70\n"
191             "%u\n"
192             "  0\n"
193             "ENDSEC\n"
194             "  0\n"
195             "SECTION\n"
196             "  2\n"
197             "TABLES\n"
198             "  0\n"
199             "TABLE\n"
200             "  2\n"
201             "LTYPE\n"
202             "  70\n"
203             "4\n"
204             "  0\n"
205             "LTYPE\n"
206             "  5\n"
207             "40F\n"
208             "  2\n"
209             "CONTINUOUS\n"
210             "  70\n"
211             "0\n"
212             "  3\n"
213             "Solid line\n"
214             "  72\n"
215             "65\n"
216             "  73\n"
217             "0\n"
218             "  40\n"
219             "0.0\n"
220             "  0\n"
221             "LTYPE\n"
222             "  5\n"
223             "410\n"
224             "  2\n"
225             "DASHDOT\n"
226             " 70\n"
227             "0\n"
228             "  3\n"
229             "Dash Dot ____ _ ____ _\n"
230             " 72\n"
231             "65\n"
232             " 73\n"
233             "4\n"
234             " 40\n"
235             "2.0\n"
236             " 49\n"
237             "1.25\n"
238             " 49\n"
239             "-0.25\n"
240             " 49\n"
241             "0.25\n"
242             " 49\n"
243             "-0.25\n"
244             "  0\n"
245             "LTYPE\n"
246             "  5\n"
247             "411\n"
248             "  2\n"
249             "DASHED\n"
250             " 70\n"
251             "0\n"
252             "  3\n"
253             "Dashed __ __ __ __ __\n"
254             " 72\n"
255             "65\n"
256             " 73\n"
257             "2\n"
258             " 40\n"
259             "0.75\n"
260             " 49\n"
261             "0.5\n"
262             " 49\n"
263             "-0.25\n"
264             "  0\n"
265             "LTYPE\n"
266             "  5\n"
267             "43B\n"
268             "  2\n"
269             "DOTTED\n"
270             " 70\n"
271             "0\n"
272             "  3\n"
273             "Dotted .  .  .  .\n"
274             " 72\n"
275             "65\n"
276             " 73\n"
277             "2\n"
278             " 40\n"
279             "0.2\n"
280             " 49\n"
281             "0.0\n"
282             " 49\n"
283             "-0.2\n"
284             "  0\n"
285             "ENDTAB\n",
286              GetMeasurementDirective() );
287 
288     // Text styles table
289     // Defines 4 text styles, one for each bold/italic combination
290     fputs( "  0\n"
291            "TABLE\n"
292            "  2\n"
293            "STYLE\n"
294            "  70\n"
295            "4\n", m_outputFile );
296 
297     static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"};
298     for(int i = 0; i < 4; i++ )
299     {
300         fprintf( m_outputFile,
301                  "  0\n"
302                  "STYLE\n"
303                  "  2\n"
304                  "%s\n"         // Style name
305                  "  70\n"
306                  "0\n"          // Standard flags
307                  "  40\n"
308                  "0\n"          // Non-fixed height text
309                  "  41\n"
310                  "1\n"          // Width factor (base)
311                  "  42\n"
312                  "1\n"          // Last height (mandatory)
313                  "  50\n"
314                  "%g\n"         // Oblique angle
315                  "  71\n"
316                  "0\n"          // Generation flags (default)
317                  "  3\n"
318                  // The standard ISO font (when kicad is build with it
319                  // the dxf text in acad matches *perfectly*)
320                  "isocp.shx\n", // Font name (when not bigfont)
321                  // Apply a 15 degree angle to italic text
322                  style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
323     }
324 
325     EDA_COLOR_T numLayers = NBCOLORS;
326 
327     // If printing in monochrome, only output the black layer
328     if( !GetColorMode() )
329         numLayers = static_cast<EDA_COLOR_T>( 1 );
330 
331     // Layer table - one layer per color
332     fprintf( m_outputFile,
333              "  0\n"
334              "ENDTAB\n"
335              "  0\n"
336              "TABLE\n"
337              "  2\n"
338              "LAYER\n"
339              "  70\n"
340              "%d\n", numLayers );
341 
342     /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
343 
344        - The primary colors (1 - 9)
345        - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
346        - Greys (251 - 255)
347      */
348 
349     wxASSERT( numLayers <= NBCOLORS );
350 
351     for( EDA_COLOR_T i = BLACK; i < numLayers; i = static_cast<EDA_COLOR_T>( int( i ) + 1 )  )
352     {
353         fprintf( m_outputFile,
354                  "  0\n"
355                  "LAYER\n"
356                  "  2\n"
357                  "%s\n"         // Layer name
358                  "  70\n"
359                  "0\n"          // Standard flags
360                  "  62\n"
361                  "%d\n"         // Color number
362                  "  6\n"
363                  "CONTINUOUS\n",// Linetype name
364                  dxf_layer[i].name, dxf_layer[i].color );
365     }
366 
367     // End of layer table, begin entities
368     fputs( "  0\n"
369            "ENDTAB\n"
370            "  0\n"
371            "ENDSEC\n"
372            "  0\n"
373            "SECTION\n"
374            "  2\n"
375            "ENTITIES\n", m_outputFile );
376 
377     return true;
378 }
379 
380 
EndPlot()381 bool DXF_PLOTTER::EndPlot()
382 {
383     wxASSERT( m_outputFile );
384 
385     // DXF FOOTER
386     fputs( "  0\n"
387            "ENDSEC\n"
388            "  0\n"
389            "EOF\n", m_outputFile );
390     fclose( m_outputFile );
391     m_outputFile = nullptr;
392 
393     return true;
394 }
395 
396 
SetColor(const COLOR4D & color)397 void DXF_PLOTTER::SetColor( const COLOR4D& color )
398 {
399     if( ( m_colorMode )
400        || ( color == COLOR4D::BLACK )
401        || ( color == COLOR4D::WHITE ) )
402     {
403         m_currentColor = color;
404     }
405     else
406     {
407         m_currentColor = COLOR4D::BLACK;
408     }
409 }
410 
411 
Rect(const wxPoint & p1,const wxPoint & p2,FILL_T fill,int width)412 void DXF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
413 {
414     wxASSERT( m_outputFile );
415     MoveTo( p1 );
416     LineTo( wxPoint( p1.x, p2.y ) );
417     LineTo( wxPoint( p2.x, p2.y ) );
418     LineTo( wxPoint( p2.x, p1.y ) );
419     FinishTo( wxPoint( p1.x, p1.y ) );
420 }
421 
422 
Circle(const wxPoint & centre,int diameter,FILL_T fill,int width)423 void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int width )
424 {
425     wxASSERT( m_outputFile );
426     double radius = userToDeviceSize( diameter / 2 );
427     DPOINT centre_dev = userToDeviceCoordinates( centre );
428 
429     if( radius > 0 )
430     {
431         wxString cname = getDXFColorName( m_currentColor );
432 
433         if( fill == FILL_T::NO_FILL )
434         {
435             fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n",
436                      TO_UTF8( cname ),
437                      centre_dev.x, centre_dev.y, radius );
438         }
439         else if( fill == FILL_T::FILLED_SHAPE )
440         {
441             double r = radius*0.5;
442             fprintf( m_outputFile, "0\nPOLYLINE\n" );
443             fprintf( m_outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname ) );
444             fprintf( m_outputFile, "40\n%g\n41\n%g\n", radius, radius);
445             fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
446             fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
447                      centre_dev.x-r, centre_dev.y );
448             fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
449             fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
450                      centre_dev.x+r, centre_dev.y );
451             fprintf( m_outputFile, "0\nSEQEND\n");
452         }
453     }
454 }
455 
456 
PlotPoly(const std::vector<wxPoint> & aCornerList,FILL_T aFill,int aWidth,void * aData)457 void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, FILL_T aFill, int aWidth,
458                             void* aData )
459 {
460     if( aCornerList.size() <= 1 )
461         return;
462 
463     unsigned last = aCornerList.size() - 1;
464 
465     // Plot outlines with lines (thickness = 0) to define the polygon
466     if( aWidth <= 0  )
467     {
468         MoveTo( aCornerList[0] );
469 
470         for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
471             LineTo( aCornerList[ii] );
472 
473         // Close polygon if 'fill' requested
474         if( aFill != FILL_T::NO_FILL )
475         {
476             if( aCornerList[last] != aCornerList[0] )
477                 LineTo( aCornerList[0] );
478         }
479 
480         PenFinish();
481 
482         return;
483     }
484 
485     // if the polygon outline has thickness, and is not filled
486     // (i.e. is a polyline) plot outlines with thick segments
487     if( aWidth > 0 && aFill == FILL_T::NO_FILL )
488     {
489         MoveTo( aCornerList[0] );
490 
491         for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
492             ThickSegment( aCornerList[ii-1], aCornerList[ii], aWidth, FILLED, nullptr );
493 
494         return;
495     }
496 
497     // The polygon outline has thickness, and is filled
498     // Build and plot the polygon which contains the initial
499     // polygon and its thick outline
500     SHAPE_POLY_SET  bufferOutline;
501     SHAPE_POLY_SET  bufferPolybase;
502 
503     bufferPolybase.NewOutline();
504 
505     // enter outline as polygon:
506     for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
507     {
508         TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
509                                 aWidth, GetPlotterArcHighDef(), ERROR_INSIDE );
510     }
511 
512     // enter the initial polygon:
513     for( unsigned ii = 0; ii < aCornerList.size(); ii++ )
514     {
515         bufferPolybase.Append( aCornerList[ii] );
516     }
517 
518     // Merge polygons to build the polygon which contains the initial
519     // polygon and its thick outline
520 
521     // create the outline which contains thick outline:
522     bufferPolybase.BooleanAdd( bufferOutline, SHAPE_POLY_SET::PM_FAST );
523     bufferPolybase.Fracture( SHAPE_POLY_SET::PM_FAST );
524 
525     if( bufferPolybase.OutlineCount() < 1 )      // should not happen
526         return;
527 
528     const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
529 
530     if( path.PointCount() < 2 )           // should not happen
531         return;
532 
533     // Now, output the final polygon to DXF file:
534     last = path.PointCount() - 1;
535     VECTOR2I point = path.CPoint( 0 );
536 
537     wxPoint startPoint( point.x, point.y );
538     MoveTo( startPoint );
539 
540     for( int ii = 1; ii < path.PointCount(); ii++ )
541     {
542         point = path.CPoint( ii );
543         LineTo( wxPoint( point.x, point.y ) );
544     }
545 
546     // Close polygon, if needed
547     point = path.CPoint( last );
548     wxPoint endPoint( point.x, point.y );
549 
550     if( endPoint != startPoint )
551         LineTo( startPoint );
552 
553     PenFinish();
554 }
555 
556 
PenTo(const wxPoint & pos,char plume)557 void DXF_PLOTTER::PenTo( const wxPoint& pos, char plume )
558 {
559     wxASSERT( m_outputFile );
560 
561     if( plume == 'Z' )
562     {
563         return;
564     }
565 
566     DPOINT pos_dev = userToDeviceCoordinates( pos );
567     DPOINT pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );
568 
569     if( m_penLastpos != pos && plume == 'D' )
570     {
571         wxASSERT( m_currentLineType >= PLOT_DASH_TYPE::FIRST_TYPE
572                   && m_currentLineType <= PLOT_DASH_TYPE::LAST_TYPE );
573         // DXF LINE
574         wxString    cname = getDXFColorName( m_currentColor );
575         const char* lname = getDXFLineType( static_cast<PLOT_DASH_TYPE>( m_currentLineType ) );
576         fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n",
577                  TO_UTF8( cname ), lname,
578                  pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y );
579     }
580 
581     m_penLastpos = pos;
582 }
583 
584 
SetDash(PLOT_DASH_TYPE aDashed)585 void DXF_PLOTTER::SetDash( PLOT_DASH_TYPE aDashed )
586 {
587     wxASSERT( aDashed >= PLOT_DASH_TYPE::FIRST_TYPE && aDashed <= PLOT_DASH_TYPE::LAST_TYPE );
588     m_currentLineType = aDashed;
589 }
590 
591 
ThickSegment(const wxPoint & aStart,const wxPoint & aEnd,int aWidth,OUTLINE_MODE aPlotMode,void * aData)592 void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
593                                 OUTLINE_MODE aPlotMode, void* aData )
594 {
595     if( aPlotMode == SKETCH )
596     {
597         std::vector<wxPoint> cornerList;
598         SHAPE_POLY_SET outlineBuffer;
599         TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(),
600                                 ERROR_INSIDE );
601         const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
602 
603         cornerList.reserve( path.PointCount() );
604 
605         for( int jj = 0; jj < path.PointCount(); jj++ )
606             cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
607 
608         // Ensure the polygon is closed
609         if( cornerList[0] != cornerList[cornerList.size() - 1] )
610             cornerList.push_back( cornerList[0] );
611 
612         PlotPoly( cornerList, FILL_T::NO_FILL );
613     }
614     else
615     {
616         MoveTo( aStart );
617         FinishTo( aEnd );
618     }
619 }
620 
621 
Arc(const wxPoint & centre,double StAngle,double EndAngle,int radius,FILL_T fill,int width)622 void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
623                        FILL_T fill, int width )
624 {
625     wxASSERT( m_outputFile );
626 
627     if( radius <= 0 )
628         return;
629 
630     // In DXF, arcs are drawn CCW.
631     // In Kicad, arcs are CW or CCW
632     // If StAngle > EndAngle, it is CW. So transform it to CCW
633     if( StAngle > EndAngle )
634     {
635         std::swap( StAngle, EndAngle );
636     }
637 
638     DPOINT centre_dev = userToDeviceCoordinates( centre );
639     double radius_dev = userToDeviceSize( radius );
640 
641     // Emit a DXF ARC entity
642     wxString cname = getDXFColorName( m_currentColor );
643     fprintf( m_outputFile,
644              "0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n",
645              TO_UTF8( cname ),
646              centre_dev.x, centre_dev.y, radius_dev,
647              StAngle / 10.0, EndAngle / 10.0 );
648 }
649 
650 
FlashPadOval(const wxPoint & pos,const wxSize & aSize,double orient,OUTLINE_MODE trace_mode,void * aData)651 void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
652                                 OUTLINE_MODE trace_mode, void* aData )
653 {
654     wxASSERT( m_outputFile );
655     wxSize size( aSize );
656 
657     /* The chip is reduced to an oval tablet with size.y > size.x
658      * (Oval vertical orientation 0) */
659     if( size.x > size.y )
660     {
661         std::swap( size.x, size.y );
662         orient = AddAngles( orient, 900 );
663     }
664 
665     sketchOval( pos, size, orient, -1 );
666 }
667 
668 
FlashPadCircle(const wxPoint & pos,int diametre,OUTLINE_MODE trace_mode,void * aData)669 void DXF_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre,
670                                   OUTLINE_MODE trace_mode, void* aData )
671 {
672     wxASSERT( m_outputFile );
673     Circle( pos, diametre, FILL_T::NO_FILL );
674 }
675 
676 
FlashPadRect(const wxPoint & pos,const wxSize & padsize,double orient,OUTLINE_MODE trace_mode,void * aData)677 void DXF_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize,
678                                 double orient, OUTLINE_MODE trace_mode, void* aData )
679 {
680     wxASSERT( m_outputFile );
681     wxSize size;
682     int    ox, oy, fx, fy;
683 
684     size.x = padsize.x / 2;
685     size.y = padsize.y / 2;
686 
687     if( size.x < 0 )
688         size.x = 0;
689 
690     if( size.y < 0 )
691         size.y = 0;
692 
693     // If a dimension is zero, the trace is reduced to 1 line
694     if( size.x == 0 )
695     {
696         ox = pos.x;
697         oy = pos.y - size.y;
698         RotatePoint( &ox, &oy, pos.x, pos.y, orient );
699         fx = pos.x;
700         fy = pos.y + size.y;
701         RotatePoint( &fx, &fy, pos.x, pos.y, orient );
702         MoveTo( wxPoint( ox, oy ) );
703         FinishTo( wxPoint( fx, fy ) );
704         return;
705     }
706 
707     if( size.y == 0 )
708     {
709         ox = pos.x - size.x;
710         oy = pos.y;
711         RotatePoint( &ox, &oy, pos.x, pos.y, orient );
712         fx = pos.x + size.x;
713         fy = pos.y;
714         RotatePoint( &fx, &fy, pos.x, pos.y, orient );
715         MoveTo( wxPoint( ox, oy ) );
716         FinishTo( wxPoint( fx, fy ) );
717         return;
718     }
719 
720     ox = pos.x - size.x;
721     oy = pos.y - size.y;
722     RotatePoint( &ox, &oy, pos.x, pos.y, orient );
723     MoveTo( wxPoint( ox, oy ) );
724 
725     fx = pos.x - size.x;
726     fy = pos.y + size.y;
727     RotatePoint( &fx, &fy, pos.x, pos.y, orient );
728     LineTo( wxPoint( fx, fy ) );
729 
730     fx = pos.x + size.x;
731     fy = pos.y + size.y;
732     RotatePoint( &fx, &fy, pos.x, pos.y, orient );
733     LineTo( wxPoint( fx, fy ) );
734 
735     fx = pos.x + size.x;
736     fy = pos.y - size.y;
737     RotatePoint( &fx, &fy, pos.x, pos.y, orient );
738     LineTo( wxPoint( fx, fy ) );
739 
740     FinishTo( wxPoint( ox, oy ) );
741 }
742 
743 
FlashPadRoundRect(const wxPoint & aPadPos,const wxSize & aSize,int aCornerRadius,double aOrient,OUTLINE_MODE aTraceMode,void * aData)744 void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
745                                      int aCornerRadius, double aOrient,
746                                      OUTLINE_MODE aTraceMode, void* aData )
747 {
748     SHAPE_POLY_SET outline;
749     TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
750                                           0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
751 
752     // TransformRoundRectToPolygon creates only one convex polygon
753     SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
754 
755     MoveTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
756 
757     for( int ii = 1; ii < poly.PointCount(); ++ii )
758         LineTo( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
759 
760     FinishTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
761 }
762 
FlashPadCustom(const wxPoint & aPadPos,const wxSize & aSize,double aOrient,SHAPE_POLY_SET * aPolygons,OUTLINE_MODE aTraceMode,void * aData)763 void DXF_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
764                                   double aOrient, SHAPE_POLY_SET* aPolygons,
765                                   OUTLINE_MODE aTraceMode, void* aData )
766 {
767     for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
768     {
769         SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
770 
771         MoveTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
772 
773         for( int ii = 1; ii < poly.PointCount(); ++ii )
774             LineTo( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
775 
776         FinishTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
777     }
778 }
779 
780 
FlashPadTrapez(const wxPoint & aPadPos,const wxPoint * aCorners,double aPadOrient,OUTLINE_MODE aTrace_Mode,void * aData)781 void DXF_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners,
782                                   double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
783 {
784     wxASSERT( m_outputFile );
785     wxPoint coord[4];       /* coord actual corners of a trapezoidal trace */
786 
787     for( int ii = 0; ii < 4; ii++ )
788     {
789         coord[ii] = aCorners[ii];
790         RotatePoint( &coord[ii], aPadOrient );
791         coord[ii] += aPadPos;
792     }
793 
794     // Plot edge:
795     MoveTo( coord[0] );
796     LineTo( coord[1] );
797     LineTo( coord[2] );
798     LineTo( coord[3] );
799     FinishTo( coord[0] );
800 }
801 
802 
FlashRegularPolygon(const wxPoint & aShapePos,int aRadius,int aCornerCount,double aOrient,OUTLINE_MODE aTraceMode,void * aData)803 void DXF_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, int aRadius, int aCornerCount,
804                                        double aOrient, OUTLINE_MODE aTraceMode, void* aData )
805 {
806     // Do nothing
807     wxASSERT( 0 );
808 }
809 
810 
811 /**
812  * Check if a given string contains non-ASCII characters.
813  *
814  * @fixme The performance of this code is really poor, but in this case it can be
815  *        acceptable because the plot operation is not called very often.
816  *
817  * @param string String to check.
818  * @return true if it contains some non-ASCII character, false if all characters are
819  *         inside ASCII range (<=255).
820  */
containsNonAsciiChars(const wxString & string)821 bool containsNonAsciiChars( const wxString& string )
822 {
823     for( unsigned i = 0; i < string.length(); i++ )
824     {
825         wchar_t ch = string[i];
826         if( ch > 255 )
827             return true;
828     }
829     return false;
830 }
831 
832 
Text(const wxPoint & aPos,const COLOR4D & aColor,const wxString & aText,double aOrient,const wxSize & aSize,enum EDA_TEXT_HJUSTIFY_T aH_justify,enum EDA_TEXT_VJUSTIFY_T aV_justify,int aWidth,bool aItalic,bool aBold,bool aMultilineAllowed,void * aData)833 void DXF_PLOTTER::Text( const wxPoint&              aPos,
834                         const COLOR4D&              aColor,
835                         const wxString&             aText,
836                         double                      aOrient,
837                         const wxSize&               aSize,
838                         enum EDA_TEXT_HJUSTIFY_T    aH_justify,
839                         enum EDA_TEXT_VJUSTIFY_T    aV_justify,
840                         int                         aWidth,
841                         bool                        aItalic,
842                         bool                        aBold,
843                         bool                        aMultilineAllowed,
844                         void*                       aData )
845 {
846     // Fix me: see how to use DXF text mode for multiline texts
847     if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
848         aMultilineAllowed = false;  // the text has only one line.
849 
850     bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
851 
852     if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
853     {
854         // output text as graphics.
855         // Perhaps multiline texts could be handled as DXF text entity
856         // but I do not want spend time about this (JPC)
857         PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
858                        aWidth, aItalic, aBold, aMultilineAllowed );
859     }
860     else
861     {
862         /* Emit text as a text entity. This loses formatting and shape but it's
863            more useful as a CAD object */
864         DPOINT origin_dev = userToDeviceCoordinates( aPos );
865         SetColor( aColor );
866         wxString cname = getDXFColorName( m_currentColor );
867         DPOINT size_dev = userToDeviceSize( aSize );
868         int h_code = 0, v_code = 0;
869 
870         switch( aH_justify )
871         {
872         case GR_TEXT_HJUSTIFY_LEFT:
873             h_code = 0;
874             break;
875         case GR_TEXT_HJUSTIFY_CENTER:
876             h_code = 1;
877             break;
878         case GR_TEXT_HJUSTIFY_RIGHT:
879             h_code = 2;
880             break;
881         }
882 
883         switch( aV_justify )
884         {
885         case GR_TEXT_VJUSTIFY_TOP:
886             v_code = 3;
887             break;
888         case GR_TEXT_VJUSTIFY_CENTER:
889             v_code = 2;
890             break;
891         case GR_TEXT_VJUSTIFY_BOTTOM:
892             v_code = 1;
893             break;
894         }
895 
896         // Position, size, rotation and alignment
897         // The two alignment point usages is somewhat idiot (see the DXF ref)
898         // Anyway since we don't use the fit/aligned options, they're the same
899         fprintf( m_outputFile,
900                 "  0\n"
901                 "TEXT\n"
902                 "  7\n"
903                 "%s\n"          // Text style
904                 "  8\n"
905                 "%s\n"          // Layer name
906                 "  10\n"
907                 "%g\n"          // First point X
908                 "  11\n"
909                 "%g\n"          // Second point X
910                 "  20\n"
911                 "%g\n"          // First point Y
912                 "  21\n"
913                 "%g\n"          // Second point Y
914                 "  40\n"
915                 "%g\n"          // Text height
916                 "  41\n"
917                 "%g\n"          // Width factor
918                 "  50\n"
919                 "%g\n"          // Rotation
920                 "  51\n"
921                 "%g\n"          // Oblique angle
922                 "  71\n"
923                 "%d\n"          // Mirror flags
924                 "  72\n"
925                 "%d\n"          // H alignment
926                 "  73\n"
927                 "%d\n",         // V alignment
928                 aBold ? (aItalic ? "KICADBI" : "KICADB")
929                       : (aItalic ? "KICADI" : "KICAD"),
930                  TO_UTF8( cname ),
931                  origin_dev.x, origin_dev.x,
932                  origin_dev.y, origin_dev.y,
933                  size_dev.y, fabs( size_dev.x / size_dev.y ),
934                  aOrient / 10.0,
935                  aItalic ? DXF_OBLIQUE_ANGLE : 0,
936                  size_dev.x < 0 ? 2 : 0, // X mirror flag
937                 h_code, v_code );
938 
939         /* There are two issue in emitting the text:
940            - Our overline character (~) must be converted to the appropriate
941            control sequence %%O or %%o
942            - Text encoding in DXF is more or less unspecified since depends on
943            the DXF declared version, the acad version reading it *and* some
944            system variables to be put in the header handled only by newer acads
945            Also before R15 unicode simply is not supported (you need to use
946            bigfonts which are a massive PITA). Common denominator solution:
947            use Latin1 (and however someone could choke on it, anyway). Sorry
948            for the extended latin people. If somewant want to try fixing this
949            recent version seems to use UTF-8 (and not UCS2 like the rest of
950            Windows)
951 
952            XXX Actually there is a *third* issue: older DXF formats are limited
953            to 255 bytes records (it was later raised to 2048); since I'm lazy
954            and text so long is not probable I just don't implement this rule.
955            If someone is interested in fixing this, you have to emit the first
956            partial lines with group code 3 (max 250 bytes each) and then finish
957            with a group code 1 (less than 250 bytes). The DXF refs explains it
958            in no more details...
959          */
960 
961         int braceNesting = 0;
962         int overbarDepth = -1;
963 
964         fputs( "  1\n", m_outputFile );
965 
966         for( unsigned int i = 0; i < aText.length(); i++ )
967         {
968             /* Here I do a bad thing: writing the output one byte at a time!
969                but today I'm lazy and I have no idea on how to coerce a Unicode
970                wxString to spit out latin1 encoded text ...
971 
972                At least stdio is *supposed* to do output buffering, so there is
973                hope is not too slow */
974             wchar_t ch = aText[i];
975 
976             if( ch > 255 )
977             {
978                 // I can't encode this...
979                 putc( '?', m_outputFile );
980             }
981             else
982             {
983                 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
984                 {
985                     fputs( "%%o", m_outputFile );
986                     overbarDepth = braceNesting;
987 
988                     // Skip the '{'
989                     i++;
990                     continue;
991                 }
992                 else if( aText[i] == '{' )
993                 {
994                     braceNesting++;
995                 }
996                 else if( aText[i] == '}' )
997                 {
998                     if( braceNesting > 0 )
999                         braceNesting--;
1000 
1001                     if( braceNesting == overbarDepth )
1002                     {
1003                         fputs( "%%O", m_outputFile );
1004                         overbarDepth = -1;
1005                         continue;
1006                     }
1007                 }
1008 
1009                 putc( ch, m_outputFile );
1010             }
1011         }
1012 
1013         putc( '\n', m_outputFile );
1014     }
1015 }
1016 
1017