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.

510 lines
16 KiB

  1. /**
  2. * @file common_plotPS_functions.cpp
  3. * @brief Kicad: Common plot SVG functions
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
  9. * Copyright (C) 1992-2012 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 <wxstruct.h>
  94. #include <base_struct.h>
  95. #include <common.h>
  96. #include <plot_common.h>
  97. #include <macros.h>
  98. #include <kicad_string.h>
  99. SVG_PLOTTER::SVG_PLOTTER()
  100. {
  101. m_graphics_changed = true;
  102. SetTextMode( PLOTTEXTMODE_STROKE );
  103. m_fillMode = NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR
  104. m_pen_rgb_color = 0; // current color value (black)
  105. m_brush_rgb_color = 0; // current color value (black)
  106. }
  107. void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  108. double aScale, bool aMirror )
  109. {
  110. wxASSERT( !outputFile );
  111. plotMirror = not aMirror; // unlike other plotters, SVG has Y axis reversed
  112. plotOffset = aOffset;
  113. plotScale = aScale;
  114. m_IUsPerDecimil = aIusPerDecimil;
  115. iuPerDeviceUnit = 1.0 / aIusPerDecimil;
  116. /* Compute the paper size in IUs */
  117. paperSize = pageInfo.GetSizeMils();
  118. paperSize.x *= 10.0 * aIusPerDecimil;
  119. paperSize.y *= 10.0 * aIusPerDecimil;
  120. SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default
  121. }
  122. void SVG_PLOTTER::SetColor( EDA_COLOR_T color )
  123. {
  124. PSLIKE_PLOTTER::SetColor( color );
  125. }
  126. void SVG_PLOTTER::setFillMode( FILL_T fill )
  127. {
  128. if( m_fillMode != fill )
  129. {
  130. m_graphics_changed = true;
  131. m_fillMode = fill;
  132. }
  133. }
  134. void SVG_PLOTTER::setSVGPlotStyle()
  135. {
  136. fputs( "</g>\n<g style=\"", outputFile );
  137. fputs( "fill:#", outputFile );
  138. // output the background fill color
  139. fprintf( outputFile, "%6.6lX; ", m_brush_rgb_color );
  140. switch( m_fillMode )
  141. {
  142. case NO_FILL:
  143. fputs( "fill-opacity:0.0; ", outputFile );
  144. break;
  145. case FILLED_SHAPE:
  146. fputs( "fill-opacity:1.0; ", outputFile );
  147. break;
  148. case FILLED_WITH_BG_BODYCOLOR:
  149. fputs( "fill-opacity:0.6; ", outputFile );
  150. break;
  151. }
  152. double pen_w = userToDeviceSize( GetCurrentLineWidth() );
  153. fprintf( outputFile, "\nstroke:#%6.6lX; stroke-width:%g; stroke-opacity:1; \n",
  154. m_pen_rgb_color, pen_w );
  155. fputs( "stroke-linecap:round; stroke-linejoin:round;\">\n", outputFile );
  156. m_graphics_changed = false;
  157. }
  158. /* Set the current line width (in IUs) for the next plot
  159. */
  160. void SVG_PLOTTER::SetCurrentLineWidth( int width )
  161. {
  162. int pen_width;
  163. if( width >= 0 )
  164. pen_width = width;
  165. else
  166. pen_width = defaultPenWidth;
  167. if( pen_width != currentPenWidth )
  168. {
  169. m_graphics_changed = true;
  170. currentPenWidth = pen_width;
  171. }
  172. if( m_graphics_changed )
  173. setSVGPlotStyle();
  174. }
  175. /* initialize m_red, m_green, m_blue ( 0 ... 255)
  176. * from reduced values r, g ,b ( 0.0 to 1.0 )
  177. */
  178. void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b )
  179. {
  180. int red = (int) ( 255.0 * r );
  181. int green = (int) ( 255.0 * g );
  182. int blue = (int) ( 255.0 * b );
  183. long rgb_color = (red << 16) | (green << 8) | blue;
  184. if( m_pen_rgb_color != rgb_color )
  185. {
  186. m_graphics_changed = true;
  187. m_pen_rgb_color = rgb_color;
  188. // Currently, use the same color for brush and pen
  189. // (i.e. to draw and fill a contour)
  190. m_brush_rgb_color = rgb_color;
  191. }
  192. }
  193. /**
  194. * SVG supports dashed lines
  195. */
  196. void SVG_PLOTTER::SetDash( bool dashed )
  197. {
  198. }
  199. void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
  200. {
  201. EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) );
  202. rect.Normalize();
  203. DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() );
  204. DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() );
  205. DSIZE size_dev = end_dev - org_dev;
  206. // Ensure size of rect in device coordinates is > 0
  207. // Inkscape has problems with negative values for width and/or height
  208. DBOX rect_dev( org_dev, size_dev);
  209. rect_dev.Normalize();
  210. setFillMode( fill );
  211. SetCurrentLineWidth( width );
  212. fprintf( outputFile,
  213. "<rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" />\n",
  214. rect_dev.GetPosition().x, rect_dev.GetPosition().y,
  215. rect_dev.GetSize().x, rect_dev.GetSize().y,
  216. 0.0 // radius of rounded corners
  217. );
  218. }
  219. void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width )
  220. {
  221. DPOINT pos_dev = userToDeviceCoordinates( pos );
  222. double radius = userToDeviceSize( diametre / 2.0 );
  223. setFillMode( fill );
  224. SetCurrentLineWidth( width );
  225. fprintf( outputFile,
  226. "<circle cx=\"%g\" cy=\"%g\" r=\"%g\" /> \n",
  227. pos_dev.x, pos_dev.y, radius );
  228. }
  229. void SVG_PLOTTER::Arc( const wxPoint& centre, int StAngle, int EndAngle, int radius,
  230. FILL_T fill, int width )
  231. {
  232. /* Draws an arc of a circle, centred on (xc,yc), with starting point
  233. * (x1, y1) and ending at (x2, y2). The current pen is used for the outline
  234. * and the current brush for filling the shape.
  235. *
  236. * The arc is drawn in an anticlockwise direction from the start point to
  237. * the end point
  238. */
  239. if( radius <= 0 )
  240. return;
  241. if( StAngle > EndAngle )
  242. EXCHG( StAngle, EndAngle );
  243. setFillMode( fill );
  244. SetCurrentLineWidth( width );
  245. // Calculate start point.
  246. DPOINT centre_dev = userToDeviceCoordinates( centre );
  247. double radius_dev = userToDeviceSize( radius );
  248. if( !plotMirror )
  249. {
  250. int tmp = StAngle;
  251. StAngle = -EndAngle;
  252. EndAngle = -tmp;
  253. }
  254. DPOINT start;
  255. start.x = radius_dev;
  256. RotatePoint( &start.x, &start.y, StAngle );
  257. DPOINT end;
  258. end.x = radius_dev;
  259. RotatePoint( &end.x, &end.y, EndAngle );
  260. start += centre_dev;
  261. end += centre_dev;
  262. double theta1 = StAngle * M_PI / 1800.0;
  263. if( theta1 < 0 )
  264. theta1 = theta1 + M_PI * 2;
  265. double theta2 = EndAngle * M_PI / 1800.0;
  266. if( theta2 < 0 )
  267. theta2 = theta2 + M_PI * 2;
  268. if( theta2 < theta1 )
  269. theta2 = theta2 + M_PI * 2;
  270. int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees
  271. if( fabs( theta2 - theta1 ) > M_PI )
  272. flg_arc = 1;
  273. int flg_sweep = 0; // flag for sweep always 0
  274. // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves)
  275. // params are start point, radius1, radius2, X axe rotation,
  276. // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg),
  277. // sweep arc ( 0 = CCW, 1 = CW),
  278. // end point
  279. fprintf( outputFile, "<path d=\"M%g %g A%g %g 0.0 %d %d %g %g \" />\n",
  280. start.x, start.y, radius_dev, radius_dev,
  281. flg_arc, flg_sweep,
  282. end.x, end.y );
  283. }
  284. void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
  285. FILL_T aFill, int aWidth )
  286. {
  287. if( aCornerList.size() <= 1 )
  288. return;
  289. setFillMode( aFill );
  290. SetCurrentLineWidth( aWidth );
  291. switch( aFill )
  292. {
  293. case NO_FILL:
  294. fprintf( outputFile, "<polyline fill=\"none;\"\n" );
  295. break;
  296. case FILLED_WITH_BG_BODYCOLOR:
  297. case FILLED_SHAPE:
  298. fprintf( outputFile, "<polyline style=\"fill-rule:evenodd;\"\n" );
  299. break;
  300. }
  301. DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
  302. fprintf( outputFile, "points=\"%d,%d\n", (int) pos.x, (int) pos.y );
  303. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  304. {
  305. pos = userToDeviceCoordinates( aCornerList[ii] );
  306. fprintf( outputFile, "%d,%d\n", (int) pos.x, (int) pos.y );
  307. }
  308. // Close/(fill) the path
  309. fprintf( outputFile, "\" /> \n" );
  310. }
  311. /**
  312. * Postscript-likes at the moment are the only plot engines supporting bitmaps...
  313. */
  314. void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos,
  315. double aScaleFactor )
  316. {
  317. // in svg file we must insert a link to a png image file to plot an image
  318. // the image itself is not included in the svg file.
  319. // So we prefer skip the image, and just draw a rectangle,
  320. // like other plotters which do not support images
  321. PLOTTER::PlotImage( aImage, aPos, aScaleFactor );
  322. }
  323. void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume )
  324. {
  325. if( plume == 'Z' )
  326. {
  327. if( penState != 'Z' )
  328. {
  329. fputs( "\" />\n", outputFile );
  330. penState = 'Z';
  331. penLastpos.x = -1;
  332. penLastpos.y = -1;
  333. }
  334. return;
  335. }
  336. if( penState == 'Z' ) // here plume = 'D' or 'U'
  337. {
  338. DPOINT pos_dev = userToDeviceCoordinates( pos );
  339. fprintf( outputFile, "<path d=\"M%d %d\n",
  340. (int) pos_dev.x, (int) pos_dev.y );
  341. }
  342. else if( penState != plume || pos != penLastpos )
  343. {
  344. DPOINT pos_dev = userToDeviceCoordinates( pos );
  345. fprintf( outputFile, "L%d %d\n",
  346. (int) pos_dev.x, (int) pos_dev.y );
  347. }
  348. penState = plume;
  349. penLastpos = pos;
  350. }
  351. /**
  352. * The code within this function
  353. * creates SVG files header
  354. */
  355. bool SVG_PLOTTER::StartPlot()
  356. {
  357. wxASSERT( outputFile );
  358. wxString msg;
  359. static const char* header[] =
  360. {
  361. "<?xml version=\"1.0\" standalone=\"no\"?>\n",
  362. " <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n",
  363. " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n",
  364. "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" \n",
  365. NULL
  366. };
  367. // Write header.
  368. for( int ii = 0; header[ii] != NULL; ii++ )
  369. {
  370. fputs( header[ii], outputFile );
  371. }
  372. // Write viewport pos and size
  373. wxPoint origin; // TODO set to actual value
  374. fprintf( outputFile,
  375. " width=\"%gcm\" height=\"%gcm\" viewBox=\"%d %d %d %d \">\n",
  376. (double) paperSize.x / m_IUsPerDecimil * 2.54 / 10000,
  377. (double) paperSize.y / m_IUsPerDecimil * 2.54 / 10000,
  378. origin.x, origin.y,
  379. (int) ( paperSize.x / m_IUsPerDecimil ),
  380. (int) ( paperSize.y / m_IUsPerDecimil) );
  381. // Write title
  382. char date_buf[250];
  383. time_t ltime = time( NULL );
  384. strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S",
  385. localtime( &ltime ) );
  386. fprintf( outputFile,
  387. "<title>SVG Picture created as %s date %s </title>\n",
  388. TO_UTF8( wxFileName( filename ).GetFullName() ), date_buf );
  389. // End of header
  390. fprintf( outputFile, " <desc>Picture generated by %s </desc>\n",
  391. TO_UTF8( creator ) );
  392. // output the pen and brush color (RVB values in hex) and opacity
  393. double opacity = 1.0; // 0.0 (transparent to 1.0 (solid)
  394. fprintf( outputFile,
  395. "<g style=\"fill:#%6.6lX; fill-opacity:%g;stroke:#%6.6lX; stroke-opacity:%g;\n",
  396. m_brush_rgb_color, opacity, m_pen_rgb_color, opacity );
  397. // output the pen cap and line joint
  398. fputs( "stroke-linecap:round; stroke-linejoin:round; \"\n", outputFile );
  399. fputs( " transform=\"translate(0 0) scale(1 1)\">\n", outputFile );
  400. return true;
  401. }
  402. bool SVG_PLOTTER::EndPlot()
  403. {
  404. fputs( "</g> \n</svg>\n", outputFile );
  405. fclose( outputFile );
  406. outputFile = NULL;
  407. return true;
  408. }
  409. void SVG_PLOTTER::Text( const wxPoint& aPos,
  410. enum EDA_COLOR_T aColor,
  411. const wxString& aText,
  412. int aOrient,
  413. const wxSize& aSize,
  414. enum EDA_TEXT_HJUSTIFY_T aH_justify,
  415. enum EDA_TEXT_VJUSTIFY_T aV_justify,
  416. int aWidth,
  417. bool aItalic,
  418. bool aBold )
  419. {
  420. setFillMode( NO_FILL );
  421. SetColor( aColor );
  422. SetCurrentLineWidth( aWidth );
  423. // TODO: see if the postscript native text code can be used in SVG plotter
  424. PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
  425. aWidth, aItalic, aBold );
  426. }