KiCad PCB EDA Suite
SVG_plotter.cpp
Go to the documentation of this file.
1 
6 /*
7  * This program source code file is part of KiCad, a free EDA CAD application.
8  *
9  * Copyright (C) 2020 Jean-Pierre Charras, jp.charras at wanadoo.fr
10  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
30 /* Some info on basic items SVG format, used here:
31  * The root element of all SVG files is the <svg> element.
32  *
33  * The <g> element is used to group SVG shapes together.
34  * Once grouped you can transform the whole group of shapes as if it was a single shape.
35  * This is an advantage compared to a nested <svg> element
36  * which cannot be the target of transformation by itself.
37  *
38  * The <rect> element represents a rectangle.
39  * Using this element you can draw rectangles of various width, height,
40  * with different stroke (outline) and fill colors, with sharp or rounded corners etc.
41  *
42  * <svg xmlns="http://www.w3.org/2000/svg"
43  * xmlns:xlink="http://www.w3.org/1999/xlink">
44  *
45  * <rect x="10" y="10" height="100" width="100"
46  * style="stroke:#006600; fill: #00cc00"/>
47  *
48  * </svg>
49  *
50  * The <circle> element is used to draw circles.
51  * <circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/>
52  *
53  * The <ellipse> element is used to draw ellipses.
54  * An ellipse is a circle that does not have equal height and width.
55  * Its radius in the x and y directions are different, in other words.
56  * <ellipse cx="40" cy="40" rx="30" ry="15"
57  * style="stroke:#006600; fill:#00cc00"/>
58  *
59  * The <line> element is used to draw lines.
60  *
61  * <line x1="0" y1="10" x2="0" y2="100" style="stroke:#006600;"/>
62  * <line x1="10" y1="10" x2="100" y2="100" style="stroke:#006600;"/>
63  *
64  * The <polyline> element is used to draw multiple connected lines
65  * Here is a simple example:
66  *
67  * <polyline points="0,0 30,0 15,30" style="stroke:#006600;"/>
68  *
69  * The <polygon> element is used to draw with multiple (3 or more) sides / edges.
70  * Here is a simple example:
71  *
72  * <polygon points="0,0 50,0 25,50" style="stroke:#660000; fill:#cc3333;"/>
73  *
74  * The <path> element is used to draw advanced shapes combined from lines and arcs,
75  * with or without fill.
76  * It is probably the most advanced and versatile SVG shape of them all.
77  * It is probably also the hardest element to master.
78  * <path d="M50,50
79  * A30,30 0 0,1 35,20
80  * L100,100
81  * M110,110
82  * L100,0"
83  * style="stroke:#660000; fill:none;"/>
84  *
85  * Draw an elliptic arc: it is one of basic path command:
86  * <path d="M(startx,starty) A(radiusx,radiusy)
87  * rotation-axe-x
88  * flag_arc_large,flag_sweep endx,endy">
89  * flag_arc_large: 0 = small arc > 180 deg, 1 = large arc > 180 deg
90  * flag_sweep : 0 = CCW, 1 = CW
91  * The center of ellipse is automatically calculated.
92  */
93 
94 #include <base64.h>
95 #include <eda_rect.h>
96 #include <eda_shape.h>
97 #include <string_utils.h>
98 #include <macros.h>
99 #include <trigo.h>
100 
101 #include <cstdint>
102 #include <wx/mstream.h>
103 
105 
106 
112 static wxString XmlEsc( const wxString& aStr, bool isAttribute = false )
113 {
114  wxString escaped;
115 
116  escaped.reserve( aStr.length() );
117 
118  for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it )
119  {
120  const wxChar c = *it;
121 
122  switch( c )
123  {
124  case wxS( '<' ):
125  escaped.append( wxS( "&lt;" ) );
126  break;
127  case wxS( '>' ):
128  escaped.append( wxS( "&gt;" ) );
129  break;
130  case wxS( '&' ):
131  escaped.append( wxS( "&amp;" ) );
132  break;
133  case wxS( '\r' ):
134  escaped.append( wxS( "&#xD;" ) );
135  break;
136  default:
137  if( isAttribute )
138  {
139  switch( c )
140  {
141  case wxS( '"' ):
142  escaped.append( wxS( "&quot;" ) );
143  break;
144  case wxS( '\t' ):
145  escaped.append( wxS( "&#x9;" ) );
146  break;
147  case wxS( '\n' ):
148  escaped.append( wxS( "&#xA;" ));
149  break;
150  default:
151  escaped.append(c);
152  }
153  }
154  else
155  escaped.append(c);
156  }
157  }
158 
159  return escaped;
160 }
161 
162 
164 {
165  m_graphics_changed = true;
167  m_fillMode = FILL_T::NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR
168  m_pen_rgb_color = 0; // current color value (black)
169  m_brush_rgb_color = 0; // current color value (black)
171  m_useInch = true; // decimils is the default
172  m_precision = 4; // because there where used before it was changeable
173 }
174 
175 
176 void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
177  double aScale, bool aMirror )
178 {
179  m_plotMirror = aMirror;
180  m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed
181  m_plotOffset = aOffset;
182  m_plotScale = aScale;
183  m_IUsPerDecimil = aIusPerDecimil;
184 
185  /* Compute the paper size in IUs */
187  m_paperSize.x *= 10.0 * aIusPerDecimil;
188  m_paperSize.y *= 10.0 * aIusPerDecimil;
189 
190  // set iuPerDeviceUnit, in 0.1mils ( 2.54um )
191  // this was used before the format was changeable, so we set is as default
192  SetSvgCoordinatesFormat( 4, true );
193 }
194 
195 
196 void SVG_PLOTTER::SetSvgCoordinatesFormat( unsigned aResolution, bool aUseInches )
197 {
198  m_useInch = aUseInches;
199  m_precision = aResolution;
200 
201  // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
202  double iusPerMM = m_IUsPerDecimil / 2.54 * 1000;
203  m_iuPerDeviceUnit = pow( 10.0, m_precision ) / ( iusPerMM );
204 
205  if( m_useInch )
206  m_iuPerDeviceUnit /= 25.4; // convert to inch
207 }
208 
209 
211 {
213 
214  if( m_graphics_changed )
215  setSVGPlotStyle();
216 }
217 
218 
220 {
221  if( m_fillMode != fill )
222  {
223  m_graphics_changed = true;
224  m_fillMode = fill;
225  }
226 }
227 
228 
229 void SVG_PLOTTER::setSVGPlotStyle( bool aIsGroup, const std::string& aExtraStyle )
230 {
231  if( aIsGroup )
232  fputs( "</g>\n<g ", m_outputFile );
233 
234  // output the background fill color
235  fprintf( m_outputFile, "style=\"fill:#%6.6lX; ", m_brush_rgb_color );
236 
237  switch( m_fillMode )
238  {
239  case FILL_T::NO_FILL: fputs( "fill-opacity:0.0; ", m_outputFile ); break;
240  case FILL_T::FILLED_SHAPE: fputs( "fill-opacity:1.0; ", m_outputFile ); break;
241  case FILL_T::FILLED_WITH_BG_BODYCOLOR: fputs( "fill-opacity:0.6; ", m_outputFile ); break;
243  UNIMPLEMENTED_FOR( "FILLED_WITH_COLOR" );
244  break;
245  }
246 
247  double pen_w = userToDeviceSize( GetCurrentLineWidth() );
248 
249  if( pen_w < 0.0 ) // Ensure pen width validity
250  pen_w = 0.0;
251 
252  fprintf( m_outputFile, "\nstroke:#%6.6lX; stroke-width:%f; stroke-opacity:1; \n",
253  m_pen_rgb_color, pen_w );
254  fputs( "stroke-linecap:round; stroke-linejoin:round;", m_outputFile );
255 
256  //set any extra attributes for non-solid lines
257  switch( m_dashed )
258  {
260  fprintf( m_outputFile, "stroke-dasharray:%f,%f;", GetDashMarkLenIU(), GetDashGapLenIU() );
261  break;
262  case PLOT_DASH_TYPE::DOT:
263  fprintf( m_outputFile, "stroke-dasharray:%f,%f;", GetDotMarkLenIU(), GetDashGapLenIU() );
264  break;
266  fprintf( m_outputFile, "stroke-dasharray:%f,%f,%f,%f;",
269  break;
272  default:
273  //do nothing
274  break;
275  }
276 
277  if( aExtraStyle.length() )
278  {
279  fputs( aExtraStyle.c_str(), m_outputFile );
280  }
281 
282  fputs( "\"", m_outputFile );
283 
284  if( aIsGroup )
285  {
286  fputs( ">", m_outputFile );
287  m_graphics_changed = false;
288  }
289 
290  fputs( "\n", m_outputFile );
291 }
292 
293 
294 void SVG_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
295 {
296  if( aWidth == DO_NOT_SET_LINE_WIDTH )
297  return;
298  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
300  else if( aWidth == 0 )
301  aWidth = 1;
302 
303  wxASSERT_MSG( aWidth > 0, "Plotter called to set negative pen width" );
304 
305  if( aWidth != m_currentPenWidth )
306  {
307  m_graphics_changed = true;
308  m_currentPenWidth = aWidth;
309  }
310 
311  if( m_graphics_changed )
312  setSVGPlotStyle();
313 }
314 
315 
316 void SVG_PLOTTER::StartBlock( void* aData )
317 {
318  std::string* idstr = reinterpret_cast<std::string*>( aData );
319 
320  fputs( "<g ", m_outputFile );
321 
322  if( idstr )
323  fprintf( m_outputFile, "id=\"%s\"", idstr->c_str() );
324 
325  fprintf( m_outputFile, ">\n" );
326 }
327 
328 
329 void SVG_PLOTTER::EndBlock( void* aData )
330 {
331  fprintf( m_outputFile, "</g>\n" );
332 
333  m_graphics_changed = true;
334 }
335 
336 
337 void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b )
338 {
339  int red = (int) ( 255.0 * r );
340  int green = (int) ( 255.0 * g );
341  int blue = (int) ( 255.0 * b );
342  long rgb_color = (red << 16) | (green << 8) | blue;
343 
344  if( m_pen_rgb_color != rgb_color )
345  {
346  m_graphics_changed = true;
347  m_pen_rgb_color = rgb_color;
348 
349  // Currently, use the same color for brush and pen (i.e. to draw and fill a contour).
350  m_brush_rgb_color = rgb_color;
351  }
352 }
353 
354 
356 {
357  if( m_dashed != dashed )
358  {
359  m_graphics_changed = true;
360  m_dashed = dashed;
361  }
362 
363  if( m_graphics_changed )
364  setSVGPlotStyle();
365 }
366 
367 
368 void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
369 {
370  EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) );
371  rect.Normalize();
372  DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() );
373  DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() );
374  DSIZE size_dev = end_dev - org_dev;
375 
376  // Ensure size of rect in device coordinates is > 0
377  // I don't know if this is a SVG issue or a Inkscape issue, but
378  // Inkscape has problems with negative or null values for width and/or height, so avoid them
379  DBOX rect_dev( org_dev, size_dev);
380  rect_dev.Normalize();
381 
382  setFillMode( fill );
383  SetCurrentLineWidth( width );
384 
385  // Rectangles having a 0 size value for height or width are just not drawn on Inkscape,
386  // so use a line when happens.
387  if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line
388  {
389  fprintf( m_outputFile,
390  "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\n",
391  rect_dev.GetPosition().x, rect_dev.GetPosition().y,
392  rect_dev.GetEnd().x, rect_dev.GetEnd().y );
393  }
394  else
395  {
396  fprintf( m_outputFile,
397  "<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" rx=\"%f\" />\n",
398  rect_dev.GetPosition().x, rect_dev.GetPosition().y,
399  rect_dev.GetSize().x, rect_dev.GetSize().y,
400  0.0 /* radius of rounded corners */ );
401  }
402 }
403 
404 
405 void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width )
406 {
407  DPOINT pos_dev = userToDeviceCoordinates( pos );
408  double radius = userToDeviceSize( diametre / 2.0 );
409 
410  setFillMode( fill );
411  SetCurrentLineWidth( width );
412 
413  // If diameter is less than width, switch to filled mode
414  if( fill == FILL_T::NO_FILL && diametre < width )
415  {
417  SetCurrentLineWidth( 0 );
418 
419  radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
420  }
421 
422  fprintf( m_outputFile,
423  "<circle cx=\"%f\" cy=\"%f\" r=\"%f\" /> \n",
424  pos_dev.x, pos_dev.y, radius );
425 }
426 
427 
428 void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
429  FILL_T fill, int width )
430 {
431  /* Draws an arc of a circle, centered on (xc,yc), with starting point
432  * (x1, y1) and ending at (x2, y2). The current pen is used for the outline
433  * and the current brush for filling the shape.
434  *
435  * The arc is drawn in an anticlockwise direction from the start point to
436  * the end point
437  */
438 
439  if( radius <= 0 )
440  {
441  Circle( centre, width, FILL_T::FILLED_SHAPE, 0 );
442  return;
443  }
444 
445  if( StAngle > EndAngle )
446  std::swap( StAngle, EndAngle );
447 
448  // Calculate start point.
449  DPOINT centre_dev = userToDeviceCoordinates( centre );
450  double radius_dev = userToDeviceSize( radius );
451 
452  if( !m_yaxisReversed ) // Should be never the case
453  {
454  double tmp = StAngle;
455  StAngle = -EndAngle;
456  EndAngle = -tmp;
457  }
458 
459  if( m_plotMirror )
460  {
462  {
463  StAngle = 1800.0 -StAngle;
464  EndAngle = 1800.0 -EndAngle;
465  std::swap( StAngle, EndAngle );
466  }
467  else
468  {
469  StAngle = -StAngle;
470  EndAngle = -EndAngle;
471  }
472  }
473 
474  DPOINT start;
475  start.x = radius_dev;
476  RotatePoint( &start.x, &start.y, StAngle );
477  DPOINT end;
478  end.x = radius_dev;
479  RotatePoint( &end.x, &end.y, EndAngle );
480  start += centre_dev;
481  end += centre_dev;
482 
483  double theta1 = DECIDEG2RAD( StAngle );
484 
485  if( theta1 < 0 )
486  theta1 = theta1 + M_PI * 2;
487 
488  double theta2 = DECIDEG2RAD( EndAngle );
489 
490  if( theta2 < 0 )
491  theta2 = theta2 + M_PI * 2;
492 
493  if( theta2 < theta1 )
494  theta2 = theta2 + M_PI * 2;
495 
496  int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees
497 
498  if( fabs( theta2 - theta1 ) > M_PI )
499  flg_arc = 1;
500 
501  int flg_sweep = 0; // flag for sweep always 0
502 
503  // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves)
504  // params are start point, radius1, radius2, X axe rotation,
505  // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg),
506  // sweep arc ( 0 = CCW, 1 = CW),
507  // end point
508  if( fill != FILL_T::NO_FILL )
509  {
510  // Filled arcs (in Eeschema) consist of the pie wedge and a stroke only on the arc
511  // This needs to be drawn in two steps.
512  setFillMode( fill );
513  SetCurrentLineWidth( 0 );
514 
515  fprintf( m_outputFile, "<path d=\"M%f %f A%f %f 0.0 %d %d %f %f L %f %f Z\" />\n",
516  start.x, start.y, radius_dev, radius_dev,
517  flg_arc, flg_sweep,
518  end.x, end.y, centre_dev.x, centre_dev.y );
519  }
520 
522  SetCurrentLineWidth( width );
523  fprintf( m_outputFile, "<path d=\"M%f %f A%f %f 0.0 %d %d %f %f\" />\n",
524  start.x, start.y, radius_dev, radius_dev,
525  flg_arc, flg_sweep,
526  end.x, end.y );
527 }
528 
529 
530 void SVG_PLOTTER::BezierCurve( const wxPoint& aStart, const wxPoint& aControl1,
531  const wxPoint& aControl2, const wxPoint& aEnd,
532  int aTolerance, int aLineThickness )
533 {
534 #if 1
536  SetCurrentLineWidth( aLineThickness );
537 
538  DPOINT start = userToDeviceCoordinates( aStart );
539  DPOINT ctrl1 = userToDeviceCoordinates( aControl1 );
540  DPOINT ctrl2 = userToDeviceCoordinates( aControl2 );
541  DPOINT end = userToDeviceCoordinates( aEnd );
542 
543  // Generate a cubic curve: start point and 3 other control points.
544  fprintf( m_outputFile, "<path d=\"M%f,%f C%f,%f %f,%f %f,%f\" />\n",
545  start.x, start.y, ctrl1.x, ctrl1.y,
546  ctrl2.x, ctrl2.y, end.x, end.y );
547 #else
548  PLOTTER::BezierCurve( aStart, aControl1, aControl2, aEnd, aTolerance, aLineThickness );
549 #endif
550 }
551 
552 
553 void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, FILL_T aFill,
554  int aWidth, void* aData )
555 {
556  if( aCornerList.size() <= 1 )
557  return;
558 
559  setFillMode( aFill );
560  SetCurrentLineWidth( aWidth );
561  fprintf( m_outputFile, "<path ");
562 
563  switch( aFill )
564  {
565  case FILL_T::NO_FILL:
566  setSVGPlotStyle( false, "fill:none" );
567  break;
568 
571  setSVGPlotStyle( false, "fill-rule:evenodd;" );
572  break;
573 
575  wxFAIL_MSG( "FILLED_WITH_COLOR not implemented" );
576  break;
577  }
578 
579  DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
580  fprintf( m_outputFile, "d=\"M %f,%f\n", pos.x, pos.y );
581 
582  for( unsigned ii = 1; ii < aCornerList.size() - 1; ii++ )
583  {
584  pos = userToDeviceCoordinates( aCornerList[ii] );
585  fprintf( m_outputFile, "%f,%f\n", pos.x, pos.y );
586  }
587 
588  // If the corner list ends where it begins, then close the poly
589  if( aCornerList.front() == aCornerList.back() )
590  {
591  fprintf( m_outputFile, "Z\" /> \n" );
592  }
593  else
594  {
595  pos = userToDeviceCoordinates( aCornerList.back() );
596  fprintf( m_outputFile, "%f,%f\n\" /> \n", pos.x, pos.y );
597  }
598 }
599 
600 
601 void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos, double aScaleFactor )
602 {
603  wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() );
604 
605  // Requested size (in IUs)
606  DPOINT drawsize( aScaleFactor * pix_size.x, aScaleFactor * pix_size.y );
607 
608  // calculate the bitmap start position
609  wxPoint start( aPos.x - drawsize.x / 2, aPos.y - drawsize.y / 2);
610 
611  // Rectangles having a 0 size value for height or width are just not drawn on Inkscape,
612  // so use a line when happens.
613  if( drawsize.x == 0.0 || drawsize.y == 0.0 ) // Draw a line
614  {
615  PLOTTER::PlotImage( aImage, aPos, aScaleFactor );
616  }
617  else
618  {
619  wxMemoryOutputStream img_stream;
620 
621  aImage.SaveFile( img_stream, wxBITMAP_TYPE_PNG );
622  size_t input_len = img_stream.GetOutputStreamBuffer()->GetBufferSize();
623  std::vector<uint8_t> buffer( input_len );
624  std::vector<uint8_t> encoded;
625 
626  img_stream.CopyTo( buffer.data(), buffer.size() );
627  base64::encode( buffer, encoded );
628 
629  fprintf( m_outputFile,
630  "<image x=\"%f\" y=\"%f\" xlink:href=\"data:image/png;base64,",
631  userToDeviceSize( start.x ), userToDeviceSize( start.y ) );
632 
633  for( size_t i = 0; i < encoded.size(); i++ )
634  {
635  fprintf( m_outputFile, "%c", static_cast<char>( encoded[i] ) );
636 
637  if( ( i % 64 ) == 63 )
638  fprintf( m_outputFile, "\n" );
639  }
640 
641  fprintf( m_outputFile, "\"\npreserveAspectRatio=\"none\" width=\"%f\" height=\"%f\" />",
642  userToDeviceSize( drawsize.x ), userToDeviceSize( drawsize.y ) );
643  }
644 }
645 
646 
647 void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume )
648 {
649  if( plume == 'Z' )
650  {
651  if( m_penState != 'Z' )
652  {
653  fputs( "\" />\n", m_outputFile );
654  m_penState = 'Z';
655  m_penLastpos.x = -1;
656  m_penLastpos.y = -1;
657  }
658 
659  return;
660  }
661 
662  if( m_penState == 'Z' ) // here plume = 'D' or 'U'
663  {
664  DPOINT pos_dev = userToDeviceCoordinates( pos );
665 
666  // Ensure we do not use a fill mode when moving the pen,
667  // in SVG mode (i;e. we are plotting only basic lines, not a filled area
668  if( m_fillMode != FILL_T::NO_FILL )
669  {
671  setSVGPlotStyle();
672  }
673 
674  fprintf( m_outputFile, "<path d=\"M%d %d\n", (int) pos_dev.x, (int) pos_dev.y );
675  }
676  else if( m_penState != plume || pos != m_penLastpos )
677  {
678  DPOINT pos_dev = userToDeviceCoordinates( pos );
679  fprintf( m_outputFile, "L%d %d\n", (int) pos_dev.x, (int) pos_dev.y );
680  }
681 
682  m_penState = plume;
683  m_penLastpos = pos;
684 }
685 
686 
688 {
689  wxASSERT( m_outputFile );
690  wxString msg;
691 
692  static const char* header[] =
693  {
694  "<?xml version=\"1.0\" standalone=\"no\"?>\n",
695  " <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n",
696  " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n",
697  "<svg\n"
698  " xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
699  " xmlns=\"http://www.w3.org/2000/svg\"\n",
700  " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n",
701  " version=\"1.1\"\n",
702  nullptr
703  };
704 
705  // Write header.
706  for( int ii = 0; header[ii] != nullptr; ii++ )
707  {
708  fputs( header[ii], m_outputFile );
709  }
710 
711  // Write viewport pos and size
712  wxPoint origin; // TODO set to actual value
713  fprintf( m_outputFile, " width=\"%fcm\" height=\"%fcm\" viewBox=\"%d %d %d %d\">\n",
714  (double) m_paperSize.x / m_IUsPerDecimil * 2.54 / 10000,
715  (double) m_paperSize.y / m_IUsPerDecimil * 2.54 / 10000, origin.x, origin.y,
716  (int) ( m_paperSize.x * m_iuPerDeviceUnit ),
717  (int) ( m_paperSize.y * m_iuPerDeviceUnit) );
718 
719  // Write title
720  char date_buf[250];
721  time_t ltime = time( nullptr );
722  strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", localtime( &ltime ) );
723 
724  fprintf( m_outputFile,
725  "<title>SVG Picture created as %s date %s </title>\n",
726  TO_UTF8( XmlEsc( wxFileName( m_filename ).GetFullName() ) ), date_buf );
727 
728  // End of header
729  fprintf( m_outputFile, " <desc>Picture generated by %s </desc>\n",
730  TO_UTF8( XmlEsc( m_creator ) ) );
731 
732  // output the pen and brush color (RVB values in hex) and opacity
733  double opacity = 1.0; // 0.0 (transparent to 1.0 (solid)
734  fprintf( m_outputFile,
735  "<g style=\"fill:#%6.6lX; fill-opacity:%f;stroke:#%6.6lX; stroke-opacity:%f;\n",
736  m_brush_rgb_color, opacity, m_pen_rgb_color, opacity );
737 
738  // output the pen cap and line joint
739  fputs( "stroke-linecap:round; stroke-linejoin:round;\"\n", m_outputFile );
740  fputs( " transform=\"translate(0 0) scale(1 1)\">\n", m_outputFile );
741  return true;
742 }
743 
744 
746 {
747  fputs( "</g> \n</svg>\n", m_outputFile );
748  fclose( m_outputFile );
749  m_outputFile = nullptr;
750 
751  return true;
752 }
753 
754 
755 void SVG_PLOTTER::Text( const wxPoint& aPos,
756  const COLOR4D& aColor,
757  const wxString& aText,
758  double aOrient,
759  const wxSize& aSize,
760  enum EDA_TEXT_HJUSTIFY_T aH_justify,
761  enum EDA_TEXT_VJUSTIFY_T aV_justify,
762  int aWidth,
763  bool aItalic,
764  bool aBold,
765  bool aMultilineAllowed,
766  void* aData )
767 {
769  SetColor( aColor );
770  SetCurrentLineWidth( aWidth );
771 
772  wxPoint text_pos = aPos;
773  const char *hjust = "start";
774 
775  switch( aH_justify )
776  {
778  hjust = "middle";
779  break;
780 
782  hjust = "end";
783  break;
784 
786  hjust = "start";
787  break;
788  }
789 
790  switch( aV_justify )
791  {
793  text_pos.y += aSize.y / 2;
794  break;
795 
797  text_pos.y += aSize.y;
798  break;
799 
801  break;
802  }
803 
804  wxSize text_size;
805 
806  // aSize.x or aSize.y is < 0 for mirrored texts.
807  // The actual text size value is the absolute value
808  text_size.x = std::abs( GraphicTextWidth( aText, aSize, aItalic, aWidth ) );
809  text_size.y = std::abs( aSize.x * 4/3 ); // Hershey font height to em size conversion
810  DPOINT anchor_pos_dev = userToDeviceCoordinates( aPos );
811  DPOINT text_pos_dev = userToDeviceCoordinates( text_pos );
812  DPOINT sz_dev = userToDeviceSize( text_size );
813 
814  if( aOrient != 0 ) {
815  fprintf( m_outputFile,
816  "<g transform=\"rotate(%f %f %f)\">\n",
817  - aOrient * 0.1, anchor_pos_dev.x, anchor_pos_dev.y );
818  }
819 
820  fprintf( m_outputFile,
821  "<text x=\"%f\" y=\"%f\"\n", text_pos_dev.x, text_pos_dev.y );
822 
824  if( aSize.x < 0 )
825  fprintf( m_outputFile, "transform=\"scale(-1 1) translate(%f 0)\"\n", -2 * text_pos_dev.x );
826 
827  fprintf( m_outputFile,
828  "textLength=\"%f\" font-size=\"%f\" lengthAdjust=\"spacingAndGlyphs\"\n"
829  "text-anchor=\"%s\" opacity=\"0\">%s</text>\n",
830  sz_dev.x, sz_dev.y,
831  hjust, TO_UTF8( XmlEsc( aText ) ) );
832 
833  if( aOrient != 0 )
834  fputs( "</g>\n", m_outputFile );
835 
836  fprintf( m_outputFile,
837  "<g class=\"stroked-text\"><desc>%s</desc>\n",
838  TO_UTF8( XmlEsc( aText ) ) );
839  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
840  aWidth, aItalic, aBold, aMultilineAllowed );
841  fputs( "</g>", m_outputFile );
842 }
double GetDotMarkLenIU() const
Definition: plotter.cpp:139
unsigned m_precision
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:82
virtual void SetDash(PLOT_DASH_TYPE dashed) override
SVG supports dashed lines.
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor) override
PostScript-likes at the moment are the only plot engines supporting bitmaps.
virtual void SetSvgCoordinatesFormat(unsigned aResolution, bool aUseInches=false) override
Select SVG step size (number of digits needed for 1 mm or 1 inch )
virtual void SetViewport(const wxPoint &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
const Vec GetEnd() const
Definition: box2.h:178
wxString m_filename
Definition: plotter.h:600
void setFillMode(FILL_T fill)
Prepare parameters for setSVGPlotStyle()
int color
Definition: DXF_plotter.cpp:57
virtual void PenTo(const wxPoint &pos, char plume) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
FILE * m_outputFile
Output file.
Definition: plotter.h:590
double m_iuPerDeviceUnit
Definition: plotter.h:581
void setSVGPlotStyle(bool aIsGroup=true, const std::string &aExtraStyle={})
Output the string which define pen and brush color, shape, transparency.
virtual void BezierCurve(const wxPoint &aStart, const wxPoint &aControl1, const wxPoint &aControl2, const wxPoint &aEnd, int aTolerance, int aLineThickness=USE_DEFAULT_LINE_WIDTH)
Generic fallback: Cubic Bezier curve rendered as a polyline In KiCad the bezier curves have 4 control...
Definition: plotter.cpp:211
int GraphicTextWidth(const wxString &aText, const wxSize &aSize, bool aItalic, bool aBold)
The full X size is GraphicTextWidth + the thickness of graphic lines.
Definition: gr_text.cpp:93
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
double m_IUsPerDecimil
Definition: plotter.h:579
virtual void SetColor(const COLOR4D &color) override
The SetColor implementation is split with the subclasses: The PSLIKE computes the rgb values,...
static wxString XmlEsc(const wxString &aStr, bool isAttribute=false)
Translates '<' to "<", '>' to ">" and so on, according to the spec: http://www.w3....
FILL_T
Definition: eda_shape.h:53
PLOT_DASH_TYPE m_dashed
This file contains miscellaneous commonly used macros and functions.
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
void encode(const std::vector< uint8_t > &aInput, std::vector< uint8_t > &aOutput)
Definition: base64.cpp:76
double GetDashGapLenIU() const
Definition: plotter.cpp:151
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:573
virtual int GetCurrentLineWidth() const
Definition: plotter.h:168
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
virtual bool EndPlot() override
bool m_yaxisReversed
Definition: plotter.h:587
const wxPoint GetEnd() const
Definition: eda_rect.h:103
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:126
virtual void SetColor(const COLOR4D &color) override
The SetColor implementation is split with the subclasses: The PSLIKE computes the rgb values,...
Definition: PS_plotter.cpp:62
bool m_plotMirror
Definition: plotter.h:584
int m_currentPenWidth
Definition: plotter.h:595
virtual void SetTextMode(PLOT_TEXT_MODE mode) override
PS and PDF fully implement native text (for the Latin-1 subset)
const wxPoint GetOrigin() const
Definition: eda_rect.h:101
virtual void Arc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
Generic fallback: arc rendered as a polyline.
BOX2< Vec > & Normalize()
Ensure that the height ant width are positive.
Definition: box2.h:112
wxPoint m_penLastpos
Definition: plotter.h:597
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
virtual bool StartPlot() override
Create SVG file header.
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:607
const wxSize & GetSizeMils() const
Definition: page_info.h:135
virtual DPOINT userToDeviceSize(const wxSize &size)
Modify size according to the plotter scale factors (wxSize version, returns a DPOINT).
Definition: plotter.cpp:123
E_SERIE r
Definition: eserie.cpp:41
virtual void Circle(const wxPoint &pos, int diametre, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
Plotting engines similar to ps (PostScript, Gerber, svg)
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:89
const Vec & GetPosition() const
Definition: box2.h:177
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:92
void Normalize()
Ensures that the height ant width are positive.
Definition: eda_rect.cpp:35
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor)
Only PostScript plotters can plot bitmaps.
Definition: plotter.cpp:239
wxPoint m_plotOffset
Definition: plotter.h:583
virtual void SetCurrentLineWidth(int width, void *aData=nullptr) override
Set the current line width (in IUs) for the next plot.
PAGE_INFO m_pageInfo
Definition: plotter.h:602
double GetDashMarkLenIU() const
Definition: plotter.cpp:145
bool m_graphics_changed
long m_brush_rgb_color
virtual void EndBlock(void *aData) override
Calling this function allows one to define the end of a group of drawing items the group is started b...
Handle the component boundary box.
Definition: eda_rect.h:42
double DECIDEG2RAD(double deg)
Definition: trigo.h:233
virtual void emitSetRGBColor(double r, double g, double b) override
Initialize m_pen_rgb_color from reduced values r, g ,b ( reduced values are 0.0 to 1....
char m_penState
Definition: plotter.h:596
int GetDefaultPenWidth() const
virtual void 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=false, void *aData=nullptr) override
Draw text with the plotter.
virtual void 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=false, void *aData=nullptr)
Draw text with the plotter.
Definition: gr_text.cpp:219
virtual void StartBlock(void *aData) override
Calling this function allows one to define the beginning of a group of drawing items (used in SVG for...
const Vec & GetSize() const
Definition: box2.h:172
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:125
wxSize m_paperSize
Definition: plotter.h:603
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr) override
Draw a polygon ( filled or not ).
virtual void BezierCurve(const wxPoint &aStart, const wxPoint &aControl1, const wxPoint &aControl2, const wxPoint &aEnd, int aTolerance, int aLineThickness=USE_DEFAULT_LINE_WIDTH) override
Generic fallback: Cubic Bezier curve rendered as a polyline In KiCad the bezier curves have 4 control...
bool m_mirrorIsHorizontal
Definition: plotter.h:586
wxString m_creator
Definition: plotter.h:599
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103