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