You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

679 lines
20 KiB

  1. /**
  2. * @file SVG_plotter.cpp
  3. * @brief Kicad: specialized plotter for SVG files format
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  9. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. /* Some info on basic items SVG format, used here:
  29. * The root element of all SVG files is the <svg> element.
  30. *
  31. * The <g> element is used to group SVG shapes together.
  32. * Once grouped you can transform the whole group of shapes as if it was a single shape.
  33. * This is an advantage compared to a nested <svg> element
  34. * which cannot be the target of transformation by itself.
  35. *
  36. * The <rect> element represents a rectangle.
  37. * Using this element you can draw rectangles of various width, height,
  38. * with different stroke (outline) and fill colors, with sharp or rounded corners etc.
  39. *
  40. * <svg xmlns="http://www.w3.org/2000/svg"
  41. * xmlns:xlink="http://www.w3.org/1999/xlink">
  42. *
  43. * <rect x="10" y="10" height="100" width="100"
  44. * style="stroke:#006600; fill: #00cc00"/>
  45. *
  46. * </svg>
  47. *
  48. * The <circle> element is used to draw circles.
  49. * <circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/>
  50. *
  51. * The <ellipse> element is used to draw ellipses.
  52. * An ellipse is a circle that does not have equal height and width.
  53. * Its radius in the x and y directions are different, in other words.
  54. * <ellipse cx="40" cy="40" rx="30" ry="15"
  55. * style="stroke:#006600; fill:#00cc00"/>
  56. *
  57. * The <line> element is used to draw lines.
  58. *
  59. * <line x1="0" y1="10" x2="0" y2="100" style="stroke:#006600;"/>
  60. * <line x1="10" y1="10" x2="100" y2="100" style="stroke:#006600;"/>
  61. *
  62. * The <polyline> element is used to draw multiple connected lines
  63. * Here is a simple example:
  64. *
  65. * <polyline points="0,0 30,0 15,30" style="stroke:#006600;"/>
  66. *
  67. * The <polygon> element is used to draw with multiple (3 or more) sides / edges.
  68. * Here is a simple example:
  69. *
  70. * <polygon points="0,0 50,0 25,50" style="stroke:#660000; fill:#cc3333;"/>
  71. *
  72. * The <path> element is used to draw advanced shapes combined from lines and arcs,
  73. * with or without fill.
  74. * It is probably the most advanced and versatile SVG shape of them all.
  75. * It is probably also the hardest element to master.
  76. * <path d="M50,50
  77. * A30,30 0 0,1 35,20
  78. * L100,100
  79. * M110,110
  80. * L100,0"
  81. * style="stroke:#660000; fill:none;"/>
  82. *
  83. * Draw an elliptic arc: it is one of basic path command:
  84. * <path d="M(startx,starty) A(radiusx,radiusy)
  85. * rotation-axe-x
  86. * flag_arc_large,flag_sweep endx,endy">
  87. * flag_arc_large: 0 = small arc > 180 deg, 1 = large arc > 180 deg
  88. * flag_sweep : 0 = CCW, 1 = CW
  89. * The center of ellipse is automatically calculated.
  90. */
  91. #include <fctsys.h>
  92. #include <trigo.h>
  93. #include <eda_base_frame.h>
  94. #include <eda_rect.h>
  95. #include <base_struct.h>
  96. #include <common.h>
  97. #include <plotter.h>
  98. #include <macros.h>
  99. #include <kicad_string.h>
  100. /**
  101. * Function XmlEsc
  102. * translates '<' to "&lt;", '>' to "&gt;" and so on, according to the spec:
  103. * http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
  104. * May be moved to a library if needed generally, but not expecting that.
  105. */
  106. static wxString XmlEsc( const wxString& aStr, bool isAttribute = false )
  107. {
  108. wxString escaped;
  109. escaped.reserve( aStr.length() );
  110. for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it )
  111. {
  112. const wxChar c = *it;
  113. switch( c )
  114. {
  115. case wxS( '<' ):
  116. escaped.append( wxS( "&lt;" ) );
  117. break;
  118. case wxS( '>' ):
  119. escaped.append( wxS( "&gt;" ) );
  120. break;
  121. case wxS( '&' ):
  122. escaped.append( wxS( "&amp;" ) );
  123. break;
  124. case wxS( '\r' ):
  125. escaped.append( wxS( "&#xD;" ) );
  126. break;
  127. default:
  128. if( isAttribute )
  129. {
  130. switch( c )
  131. {
  132. case wxS( '"' ):
  133. escaped.append( wxS( "&quot;" ) );
  134. break;
  135. case wxS( '\t' ):
  136. escaped.append( wxS( "&#x9;" ) );
  137. break;
  138. case wxS( '\n' ):
  139. escaped.append( wxS( "&#xA;" ));
  140. break;
  141. default:
  142. escaped.append(c);
  143. }
  144. }
  145. else
  146. escaped.append(c);
  147. }
  148. }
  149. return escaped;
  150. }
  151. SVG_PLOTTER::SVG_PLOTTER()
  152. {
  153. m_graphics_changed = true;
  154. SetTextMode( PLOTTEXTMODE_STROKE );
  155. m_fillMode = NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR
  156. m_pen_rgb_color = 0; // current color value (black)
  157. m_brush_rgb_color = 0; // current color value (black)
  158. m_dashed = false;
  159. }
  160. void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  161. double aScale, bool aMirror )
  162. {
  163. m_plotMirror = aMirror;
  164. m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed
  165. plotOffset = aOffset;
  166. plotScale = aScale;
  167. m_IUsPerDecimil = aIusPerDecimil;
  168. iuPerDeviceUnit = 1.0 / aIusPerDecimil;
  169. /* Compute the paper size in IUs */
  170. paperSize = pageInfo.GetSizeMils();
  171. paperSize.x *= 10.0 * aIusPerDecimil;
  172. paperSize.y *= 10.0 * aIusPerDecimil;
  173. SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default
  174. }
  175. void SVG_PLOTTER::SetColor( COLOR4D color )
  176. {
  177. PSLIKE_PLOTTER::SetColor( color );
  178. if( m_graphics_changed )
  179. setSVGPlotStyle();
  180. }
  181. void SVG_PLOTTER::setFillMode( FILL_T fill )
  182. {
  183. if( m_fillMode != fill )
  184. {
  185. m_graphics_changed = true;
  186. m_fillMode = fill;
  187. }
  188. }
  189. void SVG_PLOTTER::setSVGPlotStyle( bool aIsGroup, const std::string& aExtraStyle )
  190. {
  191. if( aIsGroup )
  192. fputs( "</g>\n<g ", outputFile );
  193. // output the background fill color
  194. fprintf( outputFile, "style=\"fill:#%6.6lX; ", m_brush_rgb_color );
  195. switch( m_fillMode )
  196. {
  197. case NO_FILL:
  198. fputs( "fill-opacity:0.0; ", outputFile );
  199. break;
  200. case FILLED_SHAPE:
  201. fputs( "fill-opacity:1.0; ", outputFile );
  202. break;
  203. case FILLED_WITH_BG_BODYCOLOR:
  204. fputs( "fill-opacity:0.6; ", outputFile );
  205. break;
  206. }
  207. double pen_w = userToDeviceSize( GetCurrentLineWidth() );
  208. fprintf( outputFile, "\nstroke:#%6.6lX; stroke-width:%g; stroke-opacity:1; \n",
  209. m_pen_rgb_color, pen_w );
  210. fputs( "stroke-linecap:round; stroke-linejoin:round;", outputFile );
  211. switch( m_dashed )
  212. {
  213. case PLOTDASHTYPE_DASH:
  214. fprintf( outputFile, "stroke-dasharray:%g,%g;",
  215. GetDashMarkLenIU(), GetDashGapLenIU() );
  216. break;
  217. case PLOTDASHTYPE_DOT:
  218. fprintf( outputFile, "stroke-dasharray:%g,%g;",
  219. GetDotMarkLenIU(), GetDashGapLenIU() );
  220. break;
  221. case PLOTDASHTYPE_DASHDOT:
  222. fprintf( outputFile, "stroke-dasharray:%g,%g,%g,%g;",
  223. GetDashMarkLenIU(), GetDashGapLenIU(), GetDotMarkLenIU(), GetDashGapLenIU() );
  224. break;
  225. }
  226. if( aExtraStyle.length() )
  227. {
  228. fputs( aExtraStyle.c_str(), outputFile );
  229. }
  230. fputs( "\"", outputFile );
  231. if( aIsGroup )
  232. {
  233. fputs( ">", outputFile );
  234. m_graphics_changed = false;
  235. }
  236. fputs( "\n", outputFile );
  237. }
  238. /* Set the current line width (in IUs) for the next plot
  239. */
  240. void SVG_PLOTTER::SetCurrentLineWidth( int width, void* aData )
  241. {
  242. int pen_width;
  243. if( width >= 0 )
  244. pen_width = width;
  245. else
  246. pen_width = defaultPenWidth;
  247. if( pen_width != currentPenWidth )
  248. {
  249. m_graphics_changed = true;
  250. currentPenWidth = pen_width;
  251. }
  252. if( m_graphics_changed )
  253. setSVGPlotStyle();
  254. }
  255. void SVG_PLOTTER::StartBlock( void* aData )
  256. {
  257. std::string* idstr = reinterpret_cast<std::string*>( aData );
  258. fputs( "<g ", outputFile );
  259. if( idstr )
  260. fprintf( outputFile, "id=\"%s\"", idstr->c_str() );
  261. fprintf( outputFile, ">\n" );
  262. }
  263. void SVG_PLOTTER::EndBlock( void* aData )
  264. {
  265. fprintf( outputFile, "</g>\n" );
  266. }
  267. /* initialize m_red, m_green, m_blue ( 0 ... 255)
  268. * from reduced values r, g ,b ( 0.0 to 1.0 )
  269. */
  270. void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b )
  271. {
  272. int red = (int) ( 255.0 * r );
  273. int green = (int) ( 255.0 * g );
  274. int blue = (int) ( 255.0 * b );
  275. long rgb_color = (red << 16) | (green << 8) | blue;
  276. if( m_pen_rgb_color != rgb_color )
  277. {
  278. m_graphics_changed = true;
  279. m_pen_rgb_color = rgb_color;
  280. // Currently, use the same color for brush and pen
  281. // (i.e. to draw and fill a contour)
  282. m_brush_rgb_color = rgb_color;
  283. }
  284. }
  285. /**
  286. * SVG supports dashed lines
  287. */
  288. void SVG_PLOTTER::SetDash( int dashed )
  289. {
  290. if( m_dashed != dashed )
  291. {
  292. m_graphics_changed = true;
  293. m_dashed = dashed;
  294. }
  295. if( m_graphics_changed )
  296. setSVGPlotStyle();
  297. }
  298. void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
  299. {
  300. EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) );
  301. rect.Normalize();
  302. DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() );
  303. DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() );
  304. DSIZE size_dev = end_dev - org_dev;
  305. // Ensure size of rect in device coordinates is > 0
  306. // I don't know if this is a SVG issue or a Inkscape issue, but
  307. // Inkscape has problems with negative or null values for width and/or height, so avoid them
  308. DBOX rect_dev( org_dev, size_dev);
  309. rect_dev.Normalize();
  310. setFillMode( fill );
  311. SetCurrentLineWidth( width );
  312. // Rectangles having a 0 size value for height or width are just not drawn on Inscape,
  313. // so use a line when happens.
  314. if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line
  315. fprintf( outputFile,
  316. "<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\" />\n",
  317. rect_dev.GetPosition().x, rect_dev.GetPosition().y,
  318. rect_dev.GetEnd().x, rect_dev.GetEnd().y
  319. );
  320. else
  321. fprintf( outputFile,
  322. "<rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" />\n",
  323. rect_dev.GetPosition().x, rect_dev.GetPosition().y,
  324. rect_dev.GetSize().x, rect_dev.GetSize().y,
  325. 0.0 // radius of rounded corners
  326. );
  327. }
  328. void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width )
  329. {
  330. DPOINT pos_dev = userToDeviceCoordinates( pos );
  331. double radius = userToDeviceSize( diametre / 2.0 );
  332. setFillMode( fill );
  333. SetCurrentLineWidth( width );
  334. // If diameter is less than width, switch to filled mode
  335. if( fill == NO_FILL && diametre < width )
  336. {
  337. setFillMode( FILLED_SHAPE );
  338. SetCurrentLineWidth( 0 );
  339. radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
  340. }
  341. fprintf( outputFile,
  342. "<circle cx=\"%g\" cy=\"%g\" r=\"%g\" /> \n",
  343. pos_dev.x, pos_dev.y, radius );
  344. }
  345. void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
  346. FILL_T fill, int width )
  347. {
  348. /* Draws an arc of a circle, centred on (xc,yc), with starting point
  349. * (x1, y1) and ending at (x2, y2). The current pen is used for the outline
  350. * and the current brush for filling the shape.
  351. *
  352. * The arc is drawn in an anticlockwise direction from the start point to
  353. * the end point
  354. */
  355. if( radius <= 0 )
  356. {
  357. Circle( centre, width, FILLED_SHAPE, 0 );
  358. return;
  359. }
  360. if( StAngle > EndAngle )
  361. std::swap( StAngle, EndAngle );
  362. setFillMode( fill );
  363. SetCurrentLineWidth( width );
  364. // Calculate start point.
  365. DPOINT centre_dev = userToDeviceCoordinates( centre );
  366. double radius_dev = userToDeviceSize( radius );
  367. if( !m_yaxisReversed ) // Should be never the case
  368. {
  369. double tmp = StAngle;
  370. StAngle = -EndAngle;
  371. EndAngle = -tmp;
  372. }
  373. if( m_plotMirror )
  374. {
  375. if( m_mirrorIsHorizontal )
  376. {
  377. StAngle = 1800.0 -StAngle;
  378. EndAngle = 1800.0 -EndAngle;
  379. std::swap( StAngle, EndAngle );
  380. }
  381. else
  382. {
  383. StAngle = -StAngle;
  384. EndAngle = -EndAngle;
  385. }
  386. }
  387. DPOINT start;
  388. start.x = radius_dev;
  389. RotatePoint( &start.x, &start.y, StAngle );
  390. DPOINT end;
  391. end.x = radius_dev;
  392. RotatePoint( &end.x, &end.y, EndAngle );
  393. start += centre_dev;
  394. end += centre_dev;
  395. double theta1 = DECIDEG2RAD( StAngle );
  396. if( theta1 < 0 )
  397. theta1 = theta1 + M_PI * 2;
  398. double theta2 = DECIDEG2RAD( EndAngle );
  399. if( theta2 < 0 )
  400. theta2 = theta2 + M_PI * 2;
  401. if( theta2 < theta1 )
  402. theta2 = theta2 + M_PI * 2;
  403. int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees
  404. if( fabs( theta2 - theta1 ) > M_PI )
  405. flg_arc = 1;
  406. int flg_sweep = 0; // flag for sweep always 0
  407. // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves)
  408. // params are start point, radius1, radius2, X axe rotation,
  409. // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg),
  410. // sweep arc ( 0 = CCW, 1 = CW),
  411. // end point
  412. fprintf( outputFile, "<path d=\"M%g %g A%g %g 0.0 %d %d %g %g \" />\n",
  413. start.x, start.y, radius_dev, radius_dev,
  414. flg_arc, flg_sweep,
  415. end.x, end.y );
  416. }
  417. void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
  418. FILL_T aFill, int aWidth, void * aData )
  419. {
  420. if( aCornerList.size() <= 1 )
  421. return;
  422. setFillMode( aFill );
  423. SetCurrentLineWidth( aWidth );
  424. fprintf( outputFile, "<path ");
  425. switch( aFill )
  426. {
  427. case NO_FILL:
  428. setSVGPlotStyle( false, "fill:none" );
  429. break;
  430. case FILLED_WITH_BG_BODYCOLOR:
  431. case FILLED_SHAPE:
  432. setSVGPlotStyle( false, "fill-rule:evenodd;" );
  433. break;
  434. }
  435. DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
  436. fprintf( outputFile, "d=\"M %d,%d\n", (int) pos.x, (int) pos.y );
  437. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  438. {
  439. pos = userToDeviceCoordinates( aCornerList[ii] );
  440. fprintf( outputFile, "%d,%d\n", (int) pos.x, (int) pos.y );
  441. }
  442. fprintf( outputFile, "Z\" /> \n" );
  443. }
  444. /**
  445. * Postscript-likes at the moment are the only plot engines supporting bitmaps...
  446. */
  447. void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos,
  448. double aScaleFactor )
  449. {
  450. // in svg file we must insert a link to a png image file to plot an image
  451. // the image itself is not included in the svg file.
  452. // So we prefer skip the image, and just draw a rectangle,
  453. // like other plotters which do not support images
  454. PLOTTER::PlotImage( aImage, aPos, aScaleFactor );
  455. }
  456. void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume )
  457. {
  458. if( plume == 'Z' )
  459. {
  460. if( penState != 'Z' )
  461. {
  462. fputs( "\" />\n", outputFile );
  463. penState = 'Z';
  464. penLastpos.x = -1;
  465. penLastpos.y = -1;
  466. }
  467. return;
  468. }
  469. if( penState == 'Z' ) // here plume = 'D' or 'U'
  470. {
  471. DPOINT pos_dev = userToDeviceCoordinates( pos );
  472. // Ensure we do not use a fill mode when moving tne pen,
  473. // in SVG mode (i;e. we are plotting only basic lines, not a filled area
  474. if( m_fillMode != NO_FILL )
  475. {
  476. setFillMode( NO_FILL );
  477. setSVGPlotStyle();
  478. }
  479. fprintf( outputFile, "<path d=\"M%d %d\n",
  480. (int) pos_dev.x, (int) pos_dev.y );
  481. }
  482. else if( penState != plume || pos != penLastpos )
  483. {
  484. DPOINT pos_dev = userToDeviceCoordinates( pos );
  485. fprintf( outputFile, "L%d %d\n",
  486. (int) pos_dev.x, (int) pos_dev.y );
  487. }
  488. penState = plume;
  489. penLastpos = pos;
  490. }
  491. /**
  492. * The code within this function
  493. * creates SVG files header
  494. */
  495. bool SVG_PLOTTER::StartPlot()
  496. {
  497. wxASSERT( outputFile );
  498. wxString msg;
  499. static const char* header[] =
  500. {
  501. "<?xml version=\"1.0\" standalone=\"no\"?>\n",
  502. " <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n",
  503. " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n",
  504. "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" \n",
  505. NULL
  506. };
  507. // Write header.
  508. for( int ii = 0; header[ii] != NULL; ii++ )
  509. {
  510. fputs( header[ii], outputFile );
  511. }
  512. // Write viewport pos and size
  513. wxPoint origin; // TODO set to actual value
  514. fprintf( outputFile,
  515. " width=\"%gcm\" height=\"%gcm\" viewBox=\"%d %d %d %d \">\n",
  516. (double) paperSize.x / m_IUsPerDecimil * 2.54 / 10000,
  517. (double) paperSize.y / m_IUsPerDecimil * 2.54 / 10000,
  518. origin.x, origin.y,
  519. (int) ( paperSize.x / m_IUsPerDecimil ),
  520. (int) ( paperSize.y / m_IUsPerDecimil) );
  521. // Write title
  522. char date_buf[250];
  523. time_t ltime = time( NULL );
  524. strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S",
  525. localtime( &ltime ) );
  526. fprintf( outputFile,
  527. "<title>SVG Picture created as %s date %s </title>\n",
  528. TO_UTF8( XmlEsc( wxFileName( filename ).GetFullName() ) ), date_buf );
  529. // End of header
  530. fprintf( outputFile, " <desc>Picture generated by %s </desc>\n",
  531. TO_UTF8( XmlEsc( creator ) ) );
  532. // output the pen and brush color (RVB values in hex) and opacity
  533. double opacity = 1.0; // 0.0 (transparent to 1.0 (solid)
  534. fprintf( outputFile,
  535. "<g style=\"fill:#%6.6lX; fill-opacity:%g;stroke:#%6.6lX; stroke-opacity:%g;\n",
  536. m_brush_rgb_color, opacity, m_pen_rgb_color, opacity );
  537. // output the pen cap and line joint
  538. fputs( "stroke-linecap:round; stroke-linejoin:round; \"\n", outputFile );
  539. fputs( " transform=\"translate(0 0) scale(1 1)\">\n", outputFile );
  540. return true;
  541. }
  542. bool SVG_PLOTTER::EndPlot()
  543. {
  544. fputs( "</g> \n</svg>\n", outputFile );
  545. fclose( outputFile );
  546. outputFile = NULL;
  547. return true;
  548. }
  549. void SVG_PLOTTER::Text( const wxPoint& aPos,
  550. const COLOR4D aColor,
  551. const wxString& aText,
  552. double aOrient,
  553. const wxSize& aSize,
  554. enum EDA_TEXT_HJUSTIFY_T aH_justify,
  555. enum EDA_TEXT_VJUSTIFY_T aV_justify,
  556. int aWidth,
  557. bool aItalic,
  558. bool aBold,
  559. bool aMultilineAllowed,
  560. void* aData )
  561. {
  562. setFillMode( NO_FILL );
  563. SetColor( aColor );
  564. SetCurrentLineWidth( aWidth );
  565. // TODO: see if the postscript native text code can be used in SVG plotter
  566. PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
  567. aWidth, aItalic, aBold, aMultilineAllowed );
  568. }