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_base_frame.h>
96 #include <eda_rect.h>
97 #include <kicad_string.h>
98 #include <macros.h>
99 #include <trigo.h>
100 
101 #include <cstdint>
102 #include <wx/mstream.h>
103 #include <render_settings.h>
104 
105 #include "plotters_pslike.h"
106 
107 
113 static wxString XmlEsc( const wxString& aStr, bool isAttribute = false )
114 {
115  wxString escaped;
116 
117  escaped.reserve( aStr.length() );
118 
119  for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it )
120  {
121  const wxChar c = *it;
122 
123  switch( c )
124  {
125  case wxS( '<' ):
126  escaped.append( wxS( "&lt;" ) );
127  break;
128  case wxS( '>' ):
129  escaped.append( wxS( "&gt;" ) );
130  break;
131  case wxS( '&' ):
132  escaped.append( wxS( "&amp;" ) );
133  break;
134  case wxS( '\r' ):
135  escaped.append( wxS( "&#xD;" ) );
136  break;
137  default:
138  if( isAttribute )
139  {
140  switch( c )
141  {
142  case wxS( '"' ):
143  escaped.append( wxS( "&quot;" ) );
144  break;
145  case wxS( '\t' ):
146  escaped.append( wxS( "&#x9;" ) );
147  break;
148  case wxS( '\n' ):
149  escaped.append( wxS( "&#xA;" ));
150  break;
151  default:
152  escaped.append(c);
153  }
154  }
155  else
156  escaped.append(c);
157  }
158  }
159 
160  return escaped;
161 }
162 
163 
165 {
166  m_graphics_changed = true;
168  m_fillMode = FILL_TYPE::NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR
169  m_pen_rgb_color = 0; // current color value (black)
170  m_brush_rgb_color = 0; // current color value (black)
172  m_useInch = true; // decimils is the default
173  m_precision = 4; // because there where used before it was changeable
174 }
175 
176 
177 void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
178  double aScale, bool aMirror )
179 {
180  m_plotMirror = aMirror;
181  m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed
182  m_plotOffset = aOffset;
183  m_plotScale = aScale;
184  m_IUsPerDecimil = aIusPerDecimil;
185 
186  /* Compute the paper size in IUs */
188  m_paperSize.x *= 10.0 * aIusPerDecimil;
189  m_paperSize.y *= 10.0 * aIusPerDecimil;
190 
191  // set iuPerDeviceUnit, in 0.1mils ( 2.54um )
192  // this was used before the format was changeable, so we set is as default
193  SetSvgCoordinatesFormat( 4, true );
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_TYPE::NO_FILL:
240  fputs( "fill-opacity:0.0; ", m_outputFile );
241  break;
242 
244  fputs( "fill-opacity:1.0; ", m_outputFile );
245  break;
246 
248  fputs( "fill-opacity:0.6; ", m_outputFile );
249  break;
250 
252  wxFAIL_MSG( "FILLED_WITH_COLOR not implemented" );
253  break;
254  }
255 
256  double pen_w = userToDeviceSize( GetCurrentLineWidth() );
257 
258  if( pen_w < 0.0 ) // Ensure pen width validity
259  pen_w = 0.0;
260 
261  fprintf( m_outputFile, "\nstroke:#%6.6lX; stroke-width:%f; stroke-opacity:1; \n",
262  m_pen_rgb_color, pen_w );
263  fputs( "stroke-linecap:round; stroke-linejoin:round;", m_outputFile );
264 
265  //set any extra attributes for non-solid lines
266  switch( m_dashed )
267  {
269  fprintf( m_outputFile, "stroke-dasharray:%f,%f;", GetDashMarkLenIU(), GetDashGapLenIU() );
270  break;
271  case PLOT_DASH_TYPE::DOT:
272  fprintf( m_outputFile, "stroke-dasharray:%f,%f;", GetDotMarkLenIU(), GetDashGapLenIU() );
273  break;
275  fprintf( m_outputFile, "stroke-dasharray:%f,%f,%f,%f;",
278  break;
281  default:
282  //do nothing
283  break;
284  }
285 
286  if( aExtraStyle.length() )
287  {
288  fputs( aExtraStyle.c_str(), m_outputFile );
289  }
290 
291  fputs( "\"", m_outputFile );
292 
293  if( aIsGroup )
294  {
295  fputs( ">", m_outputFile );
296  m_graphics_changed = false;
297  }
298 
299  fputs( "\n", m_outputFile );
300 }
301 
302 
303 void SVG_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
304 {
305  if( aWidth == DO_NOT_SET_LINE_WIDTH )
306  return;
307  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
309  else if( aWidth == 0 )
310  aWidth = 1;
311 
312  wxASSERT_MSG( aWidth > 0, "Plotter called to set negative pen width" );
313 
314  if( aWidth != m_currentPenWidth )
315  {
316  m_graphics_changed = true;
317  m_currentPenWidth = aWidth;
318  }
319 
320  if( m_graphics_changed )
321  setSVGPlotStyle();
322 }
323 
324 
325 void SVG_PLOTTER::StartBlock( void* aData )
326 {
327  std::string* idstr = reinterpret_cast<std::string*>( aData );
328 
329  fputs( "<g ", m_outputFile );
330 
331  if( idstr )
332  fprintf( m_outputFile, "id=\"%s\"", idstr->c_str() );
333 
334  fprintf( m_outputFile, ">\n" );
335 }
336 
337 
338 void SVG_PLOTTER::EndBlock( void* aData )
339 {
340  fprintf( m_outputFile, "</g>\n" );
341 
342  m_graphics_changed = true;
343 }
344 
345 
346 void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b )
347 {
348  int red = (int) ( 255.0 * r );
349  int green = (int) ( 255.0 * g );
350  int blue = (int) ( 255.0 * b );
351  long rgb_color = (red << 16) | (green << 8) | blue;
352 
353  if( m_pen_rgb_color != rgb_color )
354  {
355  m_graphics_changed = true;
356  m_pen_rgb_color = rgb_color;
357 
358  // Currently, use the same color for brush and pen
359  // (i.e. to draw and fill a contour)
360  m_brush_rgb_color = rgb_color;
361  }
362 }
363 
364 
366 {
367  if( m_dashed != dashed )
368  {
369  m_graphics_changed = true;
370  m_dashed = dashed;
371  }
372 
373  if( m_graphics_changed )
374  setSVGPlotStyle();
375 }
376 
377 
378 void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
379 {
380  EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) );
381  rect.Normalize();
382  DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() );
383  DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() );
384  DSIZE size_dev = end_dev - org_dev;
385  // Ensure size of rect in device coordinates is > 0
386  // I don't know if this is a SVG issue or a Inkscape issue, but
387  // Inkscape has problems with negative or null values for width and/or height, so avoid them
388  DBOX rect_dev( org_dev, size_dev);
389  rect_dev.Normalize();
390 
391  setFillMode( fill );
392  SetCurrentLineWidth( width );
393 
394  // Rectangles having a 0 size value for height or width are just not drawn on Inkscape,
395  // so use a line when happens.
396  if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line
397  {
398  fprintf( m_outputFile,
399  "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\n",
400  rect_dev.GetPosition().x, rect_dev.GetPosition().y,
401  rect_dev.GetEnd().x, rect_dev.GetEnd().y );
402  }
403  else
404  {
405  fprintf( m_outputFile,
406  "<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" rx=\"%f\" />\n",
407  rect_dev.GetPosition().x, rect_dev.GetPosition().y,
408  rect_dev.GetSize().x, rect_dev.GetSize().y,
409  0.0 /* radius of rounded corners */ );
410  }
411 }
412 
413 
414 void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_TYPE fill, int width )
415 {
416  DPOINT pos_dev = userToDeviceCoordinates( pos );
417  double radius = userToDeviceSize( diametre / 2.0 );
418 
419  setFillMode( fill );
420  SetCurrentLineWidth( width );
421 
422  // If diameter is less than width, switch to filled mode
423  if( fill == FILL_TYPE::NO_FILL && diametre < width )
424  {
426  SetCurrentLineWidth( 0 );
427 
428  radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
429  }
430 
431  fprintf( m_outputFile,
432  "<circle cx=\"%f\" cy=\"%f\" r=\"%f\" /> \n",
433  pos_dev.x, pos_dev.y, radius );
434 }
435 
436 
437 void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
438  FILL_TYPE fill, int width )
439 {
440  /* Draws an arc of a circle, centered on (xc,yc), with starting point
441  * (x1, y1) and ending at (x2, y2). The current pen is used for the outline
442  * and the current brush for filling the shape.
443  *
444  * The arc is drawn in an anticlockwise direction from the start point to
445  * the end point
446  */
447 
448  if( radius <= 0 )
449  {
450  Circle( centre, width, FILL_TYPE::FILLED_SHAPE, 0 );
451  return;
452  }
453 
454  if( StAngle > EndAngle )
455  std::swap( StAngle, EndAngle );
456 
457  // Calculate start point.
458  DPOINT centre_dev = userToDeviceCoordinates( centre );
459  double radius_dev = userToDeviceSize( radius );
460 
461  if( !m_yaxisReversed ) // Should be never the case
462  {
463  double tmp = StAngle;
464  StAngle = -EndAngle;
465  EndAngle = -tmp;
466  }
467 
468  if( m_plotMirror )
469  {
471  {
472  StAngle = 1800.0 -StAngle;
473  EndAngle = 1800.0 -EndAngle;
474  std::swap( StAngle, EndAngle );
475  }
476  else
477  {
478  StAngle = -StAngle;
479  EndAngle = -EndAngle;
480  }
481  }
482 
483  DPOINT start;
484  start.x = radius_dev;
485  RotatePoint( &start.x, &start.y, StAngle );
486  DPOINT end;
487  end.x = radius_dev;
488  RotatePoint( &end.x, &end.y, EndAngle );
489  start += centre_dev;
490  end += centre_dev;
491 
492  double theta1 = DECIDEG2RAD( StAngle );
493 
494  if( theta1 < 0 )
495  theta1 = theta1 + M_PI * 2;
496 
497  double theta2 = DECIDEG2RAD( EndAngle );
498 
499  if( theta2 < 0 )
500  theta2 = theta2 + M_PI * 2;
501 
502  if( theta2 < theta1 )
503  theta2 = theta2 + M_PI * 2;
504 
505  int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees
506 
507  if( fabs( theta2 - theta1 ) > M_PI )
508  flg_arc = 1;
509 
510  int flg_sweep = 0; // flag for sweep always 0
511 
512  // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves)
513  // params are start point, radius1, radius2, X axe rotation,
514  // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg),
515  // sweep arc ( 0 = CCW, 1 = CW),
516  // end point
517  if( fill != FILL_TYPE::NO_FILL )
518  {
519  // Filled arcs (in Eeschema) consist of the pie wedge and a stroke only on the arc
520  // This needs to be drawn in two steps.
521  setFillMode( fill );
522  SetCurrentLineWidth( 0 );
523 
524  fprintf( m_outputFile, "<path d=\"M%f %f A%f %f 0.0 %d %d %f %f L %f %f Z\" />\n",
525  start.x, start.y, radius_dev, radius_dev,
526  flg_arc, flg_sweep,
527  end.x, end.y, centre_dev.x, centre_dev.y );
528  }
529 
531  SetCurrentLineWidth( width );
532  fprintf( m_outputFile, "<path d=\"M%f %f A%f %f 0.0 %d %d %f %f\" />\n",
533  start.x, start.y, radius_dev, radius_dev,
534  flg_arc, flg_sweep,
535  end.x, end.y );
536 }
537 
538 
539 void SVG_PLOTTER::BezierCurve( const wxPoint& aStart, const wxPoint& aControl1,
540  const wxPoint& aControl2, const wxPoint& aEnd,
541  int aTolerance, int aLineThickness )
542 {
543 #if 1
545  SetCurrentLineWidth( aLineThickness );
546 
547  DPOINT start = userToDeviceCoordinates( aStart );
548  DPOINT ctrl1 = userToDeviceCoordinates( aControl1 );
549  DPOINT ctrl2 = userToDeviceCoordinates( aControl2 );
550  DPOINT end = userToDeviceCoordinates( aEnd );
551 
552  // Generate a cubic curve: start point and 3 other control points.
553  fprintf( m_outputFile, "<path d=\"M%f,%f C%f,%f %f,%f %f,%f\" />\n",
554  start.x, start.y, ctrl1.x, ctrl1.y,
555  ctrl2.x, ctrl2.y, end.x, end.y );
556 #else
557  PLOTTER::BezierCurve( aStart, aControl1,aControl2, aEnd,aTolerance, aLineThickness );
558 #endif
559 }
560 
561 
562 void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, FILL_TYPE aFill,
563  int aWidth, void* aData )
564 {
565  if( aCornerList.size() <= 1 )
566  return;
567 
568  setFillMode( aFill );
569  SetCurrentLineWidth( aWidth );
570  fprintf( m_outputFile, "<path ");
571 
572  switch( aFill )
573  {
574  case FILL_TYPE::NO_FILL:
575  setSVGPlotStyle( false, "fill:none" );
576  break;
577 
580  setSVGPlotStyle( false, "fill-rule:evenodd;" );
581  break;
582 
584  wxFAIL_MSG( "FILLED_WITH_COLOR not implemented" );
585  break;
586  }
587 
588  DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
589  fprintf( m_outputFile, "d=\"M %f,%f\n", pos.x, pos.y );
590 
591  for( unsigned ii = 1; ii < aCornerList.size() - 1; ii++ )
592  {
593  pos = userToDeviceCoordinates( aCornerList[ii] );
594  fprintf( m_outputFile, "%f,%f\n", pos.x, pos.y );
595  }
596 
597  // If the corner list ends where it begins, then close the poly
598  if( aCornerList.front() == aCornerList.back() )
599  {
600  fprintf( m_outputFile, "Z\" /> \n" );
601  }
602  else
603  {
604  pos = userToDeviceCoordinates( aCornerList.back() );
605  fprintf( m_outputFile, "%f,%f\n\" /> \n", pos.x, pos.y );
606  }
607 }
608 
609 
610 void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos, double aScaleFactor )
611 {
612  wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() );
613 
614  // Requested size (in IUs)
615  DPOINT drawsize( aScaleFactor * pix_size.x, aScaleFactor * pix_size.y );
616 
617  // calculate the bitmap start position
618  wxPoint start( aPos.x - drawsize.x / 2, aPos.y - drawsize.y / 2);
619 
620  // Rectangles having a 0 size value for height or width are just not drawn on Inkscape,
621  // so use a line when happens.
622  if( drawsize.x == 0.0 || drawsize.y == 0.0 ) // Draw a line
623  {
624  PLOTTER::PlotImage( aImage, aPos, aScaleFactor );
625  }
626  else
627  {
628  wxMemoryOutputStream img_stream;
629 
630  aImage.SaveFile( img_stream, wxBITMAP_TYPE_PNG );
631  size_t input_len = img_stream.GetOutputStreamBuffer()->GetBufferSize();
632  std::vector<uint8_t> buffer( input_len );
633  std::vector<uint8_t> encoded;
634 
635  img_stream.CopyTo( buffer.data(), buffer.size() );
636  base64::encode( buffer, encoded );
637 
638  fprintf( m_outputFile,
639  "<image x=\"%f\" y=\"%f\" xlink:href=\"data:image/png;base64,",
640  userToDeviceSize( start.x ), userToDeviceSize( start.y ) );
641 
642  for( size_t i = 0; i < encoded.size(); i++ )
643  {
644  fprintf( m_outputFile, "%c", static_cast<char>( encoded[i] ) );
645 
646  if( ( i % 64 ) == 63 )
647  fprintf( m_outputFile, "\n" );
648  }
649 
650  fprintf( m_outputFile, "\"\npreserveAspectRatio=\"none\" width=\"%f\" height=\"%f\" />",
651  userToDeviceSize( drawsize.x ), userToDeviceSize( drawsize.y ) );
652  }
653 }
654 
655 
656 void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume )
657 {
658  if( plume == 'Z' )
659  {
660  if( m_penState != 'Z' )
661  {
662  fputs( "\" />\n", m_outputFile );
663  m_penState = 'Z';
664  m_penLastpos.x = -1;
665  m_penLastpos.y = -1;
666  }
667 
668  return;
669  }
670 
671  if( m_penState == 'Z' ) // here plume = 'D' or 'U'
672  {
673  DPOINT pos_dev = userToDeviceCoordinates( pos );
674 
675  // Ensure we do not use a fill mode when moving the pen,
676  // in SVG mode (i;e. we are plotting only basic lines, not a filled area
678  {
680  setSVGPlotStyle();
681  }
682 
683  fprintf( m_outputFile, "<path d=\"M%d %d\n", (int) pos_dev.x, (int) pos_dev.y );
684  }
685  else if( m_penState != plume || pos != m_penLastpos )
686  {
687  DPOINT pos_dev = userToDeviceCoordinates( pos );
688  fprintf( m_outputFile, "L%d %d\n", (int) pos_dev.x, (int) pos_dev.y );
689  }
690 
691  m_penState = plume;
692  m_penLastpos = pos;
693 }
694 
695 
697 {
698  wxASSERT( m_outputFile );
699  wxString msg;
700 
701  static const char* header[] =
702  {
703  "<?xml version=\"1.0\" standalone=\"no\"?>\n",
704  " <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n",
705  " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n",
706  "<svg\n"
707  " xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
708  " xmlns=\"http://www.w3.org/2000/svg\"\n",
709  " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n",
710  " version=\"1.1\"\n",
711  nullptr
712  };
713 
714  // Write header.
715  for( int ii = 0; header[ii] != nullptr; ii++ )
716  {
717  fputs( header[ii], m_outputFile );
718  }
719 
720  // Write viewport pos and size
721  wxPoint origin; // TODO set to actual value
722  fprintf( m_outputFile, " width=\"%fcm\" height=\"%fcm\" viewBox=\"%d %d %d %d\">\n",
723  (double) m_paperSize.x / m_IUsPerDecimil * 2.54 / 10000,
724  (double) m_paperSize.y / m_IUsPerDecimil * 2.54 / 10000, origin.x, origin.y,
725  (int) ( m_paperSize.x * m_iuPerDeviceUnit ),
726  (int) ( m_paperSize.y * m_iuPerDeviceUnit) );
727 
728  // Write title
729  char date_buf[250];
730  time_t ltime = time( nullptr );
731  strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", localtime( &ltime ) );
732 
733  fprintf( m_outputFile,
734  "<title>SVG Picture created as %s date %s </title>\n",
735  TO_UTF8( XmlEsc( wxFileName( m_filename ).GetFullName() ) ), date_buf );
736 
737  // End of header
738  fprintf( m_outputFile, " <desc>Picture generated by %s </desc>\n",
739  TO_UTF8( XmlEsc( m_creator ) ) );
740 
741  // output the pen and brush color (RVB values in hex) and opacity
742  double opacity = 1.0; // 0.0 (transparent to 1.0 (solid)
743  fprintf( m_outputFile,
744  "<g style=\"fill:#%6.6lX; fill-opacity:%f;stroke:#%6.6lX; stroke-opacity:%f;\n",
745  m_brush_rgb_color, opacity, m_pen_rgb_color, opacity );
746 
747  // output the pen cap and line joint
748  fputs( "stroke-linecap:round; stroke-linejoin:round;\"\n", m_outputFile );
749  fputs( " transform=\"translate(0 0) scale(1 1)\">\n", m_outputFile );
750  return true;
751 }
752 
753 
755 {
756  fputs( "</g> \n</svg>\n", m_outputFile );
757  fclose( m_outputFile );
758  m_outputFile = nullptr;
759 
760  return true;
761 }
762 
763 
764 void SVG_PLOTTER::Text( const wxPoint& aPos,
765  const COLOR4D& aColor,
766  const wxString& aText,
767  double aOrient,
768  const wxSize& aSize,
769  enum EDA_TEXT_HJUSTIFY_T aH_justify,
770  enum EDA_TEXT_VJUSTIFY_T aV_justify,
771  int aWidth,
772  bool aItalic,
773  bool aBold,
774  bool aMultilineAllowed,
775  void* aData )
776 {
778  SetColor( aColor );
779  SetCurrentLineWidth( aWidth );
780 
781  wxPoint text_pos = aPos;
782  const char *hjust = "start";
783 
784  switch( aH_justify )
785  {
787  hjust = "middle";
788  break;
789 
791  hjust = "end";
792  break;
793 
795  hjust = "start";
796  break;
797  }
798 
799  switch( aV_justify )
800  {
802  text_pos.y += aSize.y / 2;
803  break;
804 
806  text_pos.y += aSize.y;
807  break;
808 
810  break;
811  }
812 
813  wxSize text_size;
814  // aSize.x or aSize.y is < 0 for mirrored texts.
815  // The actual text size value is the absolute value
816  text_size.x = std::abs( GraphicTextWidth( aText, aSize, aItalic, aWidth ) );
817  text_size.y = std::abs( aSize.x * 4/3 ); // Hershey font height to em size conversion
818  DPOINT anchor_pos_dev = userToDeviceCoordinates( aPos );
819  DPOINT text_pos_dev = userToDeviceCoordinates( text_pos );
820  DPOINT sz_dev = userToDeviceSize( text_size );
821 
822  if( aOrient != 0 ) {
823  fprintf( m_outputFile,
824  "<g transform=\"rotate(%f %f %f)\">\n",
825  - aOrient * 0.1, anchor_pos_dev.x, anchor_pos_dev.y );
826  }
827 
828  fprintf( m_outputFile,
829  "<text x=\"%f\" y=\"%f\"\n", text_pos_dev.x, text_pos_dev.y );
830 
832  if( aSize.x < 0 )
833  fprintf( m_outputFile, "transform=\"scale(-1 1) translate(%f 0)\"\n", -2 * text_pos_dev.x );
834 
835  fprintf( m_outputFile,
836  "textLength=\"%f\" font-size=\"%f\" lengthAdjust=\"spacingAndGlyphs\"\n"
837  "text-anchor=\"%s\" opacity=\"0\">%s</text>\n",
838  sz_dev.x, sz_dev.y,
839  hjust, TO_UTF8( XmlEsc( aText ) ) );
840 
841  if( aOrient != 0 )
842  fputs( "</g>\n", m_outputFile );
843 
844  fprintf( m_outputFile,
845  "<g class=\"stroked-text\"><desc>%s</desc>\n",
846  TO_UTF8( XmlEsc( aText ) ) );
847  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
848  aWidth, aItalic, aBold, aMultilineAllowed );
849  fputs( "</g>", m_outputFile );
850 }
double GetDotMarkLenIU() const
Definition: plotter.cpp:140
unsigned m_precision
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
virtual void Arc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
Generic fallback: arc rendered as a polyline.
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 )
FILL_TYPE
The set of fill types used in plotting or drawing enclosed areas.
Definition: fill_type.h:28
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
int color
Definition: DXF_plotter.cpp:60
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:94
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
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,...
virtual void Circle(const wxPoint &pos, int diametre, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
static wxString XmlEsc(const wxString &aStr, bool isAttribute=false)
Translates '<' to "<", '>' to ">" and so on, according to the spec: http://www.w3....
PLOT_DASH_TYPE m_dashed
This file contains miscellaneous commonly used macros and functions.
void encode(const std::vector< uint8_t > &aInput, std::vector< uint8_t > &aOutput)
Definition: base64.cpp:76
double GetDashGapLenIU() const
Definition: plotter.cpp:152
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:64
bool m_plotMirror
Definition: plotter.h:584
int m_currentPenWidth
Definition: plotter.h:595
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_TYPE aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr) override
Draw a polygon ( filled or not ).
FILL_TYPE m_fillMode
virtual void SetTextMode(PLOT_TEXT_MODE mode) override
PS and PDF fully implement native text (for the Latin-1 subset)
void setFillMode(FILL_TYPE fill)
Prepare parameters for setSVGPlotStyle()
const wxPoint GetOrigin() const
Definition: eda_rect.h:101
BOX2< Vec > & Normalize()
Ensure that the height ant width are positive.
Definition: box2.h:112
wxPoint m_penLastpos
Definition: plotter.h:597
Base window classes and related definitions.
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:124
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:68
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:93
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:146
bool m_graphics_changed
long m_brush_rgb_color
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
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:235
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:217
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 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