1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * Plotting engines similar to ps (PostScript, Gerber, svg)
22  *
23  * @file plotters_pslike.h
24  */
25 
26 #pragma once
27 
28 #include "plotter.h"
29 
30 
31 /**
32  * The PSLIKE_PLOTTER class is an intermediate class to handle common routines for engines
33  * working more or less with the postscript imaging model.
34  */
35 class PSLIKE_PLOTTER : public PLOTTER
36 {
37 public:
PSLIKE_PLOTTER()38     PSLIKE_PLOTTER() :
39             plotScaleAdjX( 1 ),
40             plotScaleAdjY( 1 ),
41             m_textMode( PLOT_TEXT_MODE::PHANTOM )
42     {
43     }
44 
45     /**
46      * PS and PDF fully implement native text (for the Latin-1 subset)
47      */
SetTextMode(PLOT_TEXT_MODE mode)48     virtual void SetTextMode( PLOT_TEXT_MODE mode ) override
49     {
50         if( mode != PLOT_TEXT_MODE::DEFAULT )
51             m_textMode = mode;
52     }
53 
54     /**
55      * Set the 'fine' scaling for the postscript engine
56      */
SetScaleAdjust(double scaleX,double scaleY)57     void SetScaleAdjust( double scaleX, double scaleY )
58     {
59         plotScaleAdjX = scaleX;
60         plotScaleAdjY = scaleY;
61     }
62 
63     // Pad routines are handled with lower level primitives
64     virtual void FlashPadCircle( const wxPoint& aPadPos, int aDiameter,
65                                  OUTLINE_MODE aTraceMode, void* aData ) override;
66     virtual void FlashPadOval( const wxPoint& aPadPos, const wxSize& aSize, double aPadOrient,
67                                OUTLINE_MODE aTraceMode, void* aData ) override;
68     virtual void FlashPadRect( const wxPoint& aPadPos, const wxSize& aSize, double aPadOrient,
69                                OUTLINE_MODE aTraceMode, void* aData ) override;
70     virtual void FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
71                                     int aCornerRadius, double aOrient,
72                                     OUTLINE_MODE aTraceMode, void* aData ) override;
73     virtual void FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, double aOrient,
74                                  SHAPE_POLY_SET* aPolygons,
75                                  OUTLINE_MODE aTraceMode, void* aData ) override;
76     virtual void FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners,
77                                  double aPadOrient, OUTLINE_MODE aTraceMode, void* aData ) override;
78     virtual void FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter, int aCornerCount,
79                                       double aOrient, OUTLINE_MODE aTraceMode,
80                                       void* aData ) override;
81 
82     /**
83      * The SetColor implementation is split with the subclasses:
84      * The PSLIKE computes the rgb values, the subclass emits the
85      * operator to actually do it
86      */
87     virtual void SetColor( const COLOR4D& color ) override;
88 
89 protected:
90     /**
91      * This is the core for postscript/PDF text alignment.
92      *
93      * It computes the transformation matrix to generate a user space
94      * system aligned with the text. Even the PS uses the concat
95      * operator to simplify PDF generation (concat is everything PDF
96      * has to modify the CTM. Lots of parameters, both in and out.
97      */
98     void computeTextParameters( const wxPoint&           aPos,
99                                 const wxString&          aText,
100                                 int                      aOrient,
101                                 const wxSize&            aSize,
102                                 bool                     aMirror,
103                                 enum EDA_TEXT_HJUSTIFY_T aH_justify,
104                                 enum EDA_TEXT_VJUSTIFY_T aV_justify,
105                                 int                      aWidth,
106                                 bool                     aItalic,
107                                 bool                     aBold,
108                                 double                   *wideningFactor,
109                                 double                   *ctm_a,
110                                 double                   *ctm_b,
111                                 double                   *ctm_c,
112                                 double                   *ctm_d,
113                                 double                   *ctm_e,
114                                 double                   *ctm_f,
115                                 double                   *heightFactor );
116 
117     /**
118      * Computes the x coordinates for the overlining in a string of text.
119      * Fills the passed vector with couples of (start, stop) values to be
120      * used in the text coordinate system (use computeTextParameters to
121      * obtain the parameters to establish such a system)
122      */
123     void postscriptOverlinePositions( const wxString& aText, int aXSize, bool aItalic, bool aBold,
124                                       std::vector<int> *pos_pairs );
125 
126     /// convert a wxString unicode string to a char string compatible with the accepted
127     /// string plotter format (convert special chars and non ascii7 chars)
128     virtual std::string encodeStringForPlotter( const wxString& aUnicode );
129 
130     /// Virtual primitive for emitting the setrgbcolor operator
131     virtual void emitSetRGBColor( double r, double g, double b ) = 0;
132 
133     /// Height of the postscript font (from the AFM)
134     static const double postscriptTextAscent; // = 0.718;
135 
136     /**
137      * Sister function for the GraphicTextWidth in drawtxt.cpp
138      * Does the same processing (i.e. calculates a text string width) but
139      * using postscript metrics for the Helvetica font (optionally used for
140      * PS and PDF plotting
141      */
142     int returnPostscriptTextWidth( const wxString& aText, int aXSize, bool aItalic, bool aBold );
143 
144     /// Fine user scale adjust ( = 1.0 if no correction)
145     double plotScaleAdjX, plotScaleAdjY;
146 
147     /// How to draw text
148     PLOT_TEXT_MODE m_textMode;
149 };
150 
151 
152 class PS_PLOTTER : public PSLIKE_PLOTTER
153 {
154 public:
PS_PLOTTER()155     PS_PLOTTER()
156     {
157         // The phantom plot in postscript is an hack and reportedly
158         // crashes Adobe's own postscript interpreter!
159         m_textMode = PLOT_TEXT_MODE::STROKE;
160     }
161 
GetDefaultFileExtension()162     static wxString GetDefaultFileExtension()
163     {
164         return wxString( wxT( "ps" ) );
165     }
166 
GetPlotterType()167     virtual PLOT_FORMAT GetPlotterType() const override
168     {
169         return PLOT_FORMAT::POST;
170     }
171 
172     /**
173      * The code within this function (and the CloseFilePS function)
174      * creates postscript files whose contents comply with Adobe's
175      * Document Structuring Convention, as documented by assorted
176      * details described within the following URLs:
177      *
178      * http://en.wikipedia.org/wiki/Document_Structuring_Conventions
179      * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf
180      *
181      *
182      * BBox is the boundary box (position and size of the "client rectangle"
183      * for drawings (page - margins) in mils (0.001 inch)
184      */
185     virtual bool StartPlot() override;
186     virtual bool EndPlot() override;
187 
188     /**
189      * Set the current line width (in IUs) for the next plot.
190      */
191     virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override;
192 
193     /**
194      * PostScript supports dashed lines.
195      */
196     virtual void SetDash( PLOT_DASH_TYPE dashed ) override;
197 
198     virtual void SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
199                               double aScale, bool aMirror ) override;
200     virtual void Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill,
201                        int width = USE_DEFAULT_LINE_WIDTH ) override;
202     virtual void Circle( const wxPoint& pos, int diametre, FILL_T fill,
203                          int width = USE_DEFAULT_LINE_WIDTH ) override;
204     virtual void Arc( const wxPoint& centre, double StAngle, double EndAngle, int rayon,
205                       FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override;
206 
207     virtual void PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill,
208                            int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override;
209 
210     /**
211      * PostScript-likes at the moment are the only plot engines supporting bitmaps.
212      */
213     virtual void PlotImage( const wxImage& aImage, const wxPoint& aPos,
214                             double aScaleFactor ) override;
215 
216     virtual void PenTo( const wxPoint& pos, char plume ) override;
217     virtual void Text( const wxPoint&              aPos,
218                        const COLOR4D&              aColor,
219                        const wxString&             aText,
220                        double                      aOrient,
221                        const wxSize&               aSize,
222                        enum EDA_TEXT_HJUSTIFY_T    aH_justify,
223                        enum EDA_TEXT_VJUSTIFY_T    aV_justify,
224                        int                         aWidth,
225                        bool                        aItalic,
226                        bool                        aBold,
227                        bool                        aMultilineAllowed = false,
228                        void* aData = nullptr ) override;
229 protected:
230     virtual void emitSetRGBColor( double r, double g, double b ) override;
231 };
232 
233 
234 class PDF_PLOTTER : public PSLIKE_PLOTTER
235 {
236 public:
PDF_PLOTTER()237     PDF_PLOTTER() :
238             pageTreeHandle( 0 ),
239             fontResDictHandle( 0 ),
240             pageStreamHandle( 0 ),
241             streamLengthHandle( 0 ),
242             workFile( nullptr )
243     {
244     }
245 
GetPlotterType()246     virtual PLOT_FORMAT GetPlotterType() const override
247     {
248         return PLOT_FORMAT::PDF;
249     }
250 
GetDefaultFileExtension()251     static wxString GetDefaultFileExtension()
252     {
253         return wxString( wxT( "pdf" ) );
254     }
255 
256     /**
257      * Open or create the plot file aFullFilename.
258      *
259      * The base class open the file in text mode, so we should have this
260      * function overlaid for PDF files, which are binary files.
261      *
262      * @param aFullFilename is the full file name of the file to create.
263      * @return true if success, false if the file cannot be created/opened.
264      */
265     virtual bool OpenFile( const wxString& aFullFilename ) override;
266 
267     /**
268      * The PDF engine supports multiple pages; the first one is opened 'for free' the following
269      * are to be closed and reopened. Between each page parameters can be set.
270      */
271     virtual bool StartPlot() override;
272     virtual bool EndPlot() override;
273 
274     /**
275      * Start a new page in the PDF document.
276      */
277     virtual void StartPage();
278 
279     /**
280      * Close the current page in the PDF document (and emit its compressed stream).
281      */
282     virtual void ClosePage();
283 
284     /**
285      * Pen width setting for PDF.
286      *
287      * Since the specs *explicitly* says that a 0 width is a bad thing to use (since it
288      * results in 1 pixel traces), we convert such requests to the minimal width (like 1)
289      * Note pen width = 0 is used in plot polygons to plot filled polygons with no outline
290      * thickness.  Use in this case pen width = 1 does not actually change the polygon.
291      */
292     virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override;
293 
294     /**
295      * PDF supports dashed lines
296      */
297     virtual void SetDash( PLOT_DASH_TYPE dashed ) override;
298 
299     /**
300      * PDF can have multiple pages, so SetPageSettings can be called
301      * with the outputFile open (but not inside a page stream!)
302      */
303     virtual void SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
304                               double aScale, bool aMirror ) override;
305 
306     /**
307      * Rectangles in PDF. Supported by the native operator.
308      */
309     virtual void Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill,
310                        int width = USE_DEFAULT_LINE_WIDTH ) override;
311 
312     /**
313      * Circle drawing for PDF. They're approximated by curves, but fill is supported
314      */
315     virtual void Circle( const wxPoint& pos, int diametre, FILL_T fill,
316                          int width = USE_DEFAULT_LINE_WIDTH ) override;
317 
318     /**
319      * The PDF engine can't directly plot arcs, it uses the base emulation.
320      * So no filled arcs (not a great loss... )
321      */
322     virtual void Arc( const wxPoint& centre, double StAngle, double EndAngle, int rayon,
323                       FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override;
324 
325     /**
326      * Polygon plotting for PDF. Everything is supported
327      */
328     virtual void PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill,
329                            int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override;
330 
331     virtual void PenTo( const wxPoint& pos, char plume ) override;
332 
333     virtual void Text( const wxPoint&              aPos,
334                        const COLOR4D&              aColor,
335                        const wxString&             aText,
336                        double                      aOrient,
337                        const wxSize&               aSize,
338                        enum EDA_TEXT_HJUSTIFY_T    aH_justify,
339                        enum EDA_TEXT_VJUSTIFY_T    aV_justify,
340                        int                         aWidth,
341                        bool                        aItalic,
342                        bool                        aBold,
343                        bool                        aMultilineAllowed = false,
344                        void* aData = nullptr ) override;
345     /**
346      * PDF images are handles as inline, not XObject streams...
347      */
348     virtual void PlotImage( const wxImage& aImage, const wxPoint& aPos,
349                             double aScaleFactor ) override;
350 
351 
352 protected:
353     /// convert a wxString unicode string to a char string compatible with the accepted
354     /// string PDF format (convert special chars and non ascii7 chars)
355     std::string encodeStringForPlotter( const wxString& aUnicode ) override;
356 
357     /**
358      * PDF supports colors fully. It actually has distinct fill and pen colors,
359      * but we set both at the same time.
360      *
361      * XXX Keeping them divided could result in a minor optimization in
362      * Eeschema filled shapes, but would propagate to all the other plot
363      * engines. Also arcs are filled as pies but only the arc is stroked so
364      * it would be difficult to handle anyway.
365      */
366     virtual void emitSetRGBColor( double r, double g, double b ) override;
367 
368     /**
369      * Allocate a new handle in the table of the PDF object. The
370      * handle must be completed using startPdfObject. It's an in-RAM operation
371      * only, no output is done.
372      */
373     int allocPdfObject();
374 
375     /**
376      * Open a new PDF object and returns the handle if the parameter is -1.
377      * Otherwise fill in the xref entry for the passed object
378      */
379     int startPdfObject(int handle = -1);
380 
381     /**
382      * Close the current PDF object
383      */
384     void closePdfObject();
385 
386     /**
387      * Starts a PDF stream (for the page). Returns the object handle opened
388      * Pass -1 (default) for a fresh object. Especially from PDF 1.5 streams
389      * can contain a lot of things, but for the moment we only handle page
390      * content.
391      */
392     int startPdfStream(int handle = -1);
393 
394     /**
395      * Finish the current PDF stream (writes the deferred length, too)
396      */
397     void closePdfStream();
398 
399     int pageTreeHandle;      /// Handle to the root of the page tree object
400     int fontResDictHandle;   /// Font resource dictionary
401     std::vector<int> pageHandles;/// Handles to the page objects
402     int pageStreamHandle;    /// Handle of the page content object
403     int streamLengthHandle;      /// Handle to the deferred stream length
404     wxString workFilename;
405     FILE* workFile;              /// Temporary file to construct the stream before zipping
406     std::vector<long> xrefTable; /// The PDF xref offset table
407 };
408 
409 
410 class SVG_PLOTTER : public PSLIKE_PLOTTER
411 {
412 public:
413     SVG_PLOTTER();
414 
GetDefaultFileExtension()415     static wxString GetDefaultFileExtension()
416     {
417         return wxString( wxT( "svg" ) );
418     }
419 
GetPlotterType()420     virtual PLOT_FORMAT GetPlotterType() const override
421     {
422         return PLOT_FORMAT::SVG;
423     }
424 
425     virtual void SetColor( const COLOR4D& color ) override;
426 
427     /**
428      * Create SVG file header.
429      */
430     virtual bool StartPlot() override;
431     virtual bool EndPlot() override;
432 
433     /**
434      * Set the current line width (in IUs) for the next plot.
435      */
436     virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override;
437 
438     /**
439      * SVG supports dashed lines.
440      */
441     virtual void SetDash( PLOT_DASH_TYPE dashed ) override;
442 
443     virtual void SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
444                               double aScale, bool aMirror ) override;
445     virtual void Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill,
446                        int width = USE_DEFAULT_LINE_WIDTH ) override;
447     virtual void Circle( const wxPoint& pos, int diametre, FILL_T fill,
448                          int width = USE_DEFAULT_LINE_WIDTH ) override;
449     virtual void Arc( const wxPoint& centre, double StAngle, double EndAngle, int rayon,
450                       FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override;
451 
452     virtual void BezierCurve( const wxPoint& aStart, const wxPoint& aControl1,
453                               const wxPoint& aControl2, const wxPoint& aEnd,
454                               int aTolerance,
455                               int aLineThickness = USE_DEFAULT_LINE_WIDTH ) override;
456 
457     virtual void PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill,
458                            int aWidth = USE_DEFAULT_LINE_WIDTH, void * aData = nullptr ) override;
459 
460     /**
461      * PostScript-likes at the moment are the only plot engines supporting bitmaps.
462      */
463     virtual void PlotImage( const wxImage& aImage, const wxPoint& aPos,
464                             double aScaleFactor ) override;
465 
466     virtual void PenTo( const wxPoint& pos, char plume ) override;
467 
468     /**
469      * Select SVG step size (number of digits needed for 1 mm or 1 inch )
470      *
471      * Should be called only after SetViewport() is called
472      *
473      * @param aResolution = number of digits in mantissa of coordinate
474      *                      use a value from 3-6
475      *                      do not use value > 6 to avoid overflow in PCBNEW
476      *                      do not use value > 4 to avoid overflow for other parts
477      * @param aUseInches = true to use inches, false to use mm (default)
478      */
479     virtual void SetSvgCoordinatesFormat( unsigned aResolution, bool aUseInches = false ) override;
480 
481     /**
482      * Calling this function allows one to define the beginning of a group
483      * of drawing items (used in SVG format to separate components)
484      * @param aData should be a string for the SVG ID tag
485      */
486     virtual void StartBlock( void* aData ) override;
487 
488     /**
489      * Calling this function allows one to define the end of a group of drawing
490      * items the group is started by StartBlock()
491      * @param aData should be null
492      */
493     virtual void EndBlock( void* aData ) override;
494 
495     virtual void Text( const wxPoint&              aPos,
496                        const COLOR4D&              aColor,
497                        const wxString&             aText,
498                        double                      aOrient,
499                        const wxSize&               aSize,
500                        enum EDA_TEXT_HJUSTIFY_T    aH_justify,
501                        enum EDA_TEXT_VJUSTIFY_T    aV_justify,
502                        int                         aWidth,
503                        bool                        aItalic,
504                        bool                        aBold,
505                        bool                        aMultilineAllowed = false,
506                        void* aData = nullptr ) override;
507 
508 protected:
509     /**
510      * Initialize m_pen_rgb_color from reduced values r, g ,b
511      * ( reduced values are 0.0 to 1.0 )
512      */
513     virtual void emitSetRGBColor( double r, double g, double b ) override;
514 
515     /**
516      * Output the string which define pen and brush color, shape, transparency
517      *
518      * @param aIsGroup If false, do not form a new group for the style.
519      * @param aExtraStyle If given, the string will be added into the style string before closing
520      */
521     void setSVGPlotStyle( bool aIsGroup = true, const std::string& aExtraStyle = {} );
522 
523     /**
524      * Prepare parameters for setSVGPlotStyle()
525      */
526     void setFillMode( FILL_T fill );
527 
528     FILL_T         m_fillMode;          // true if the current contour
529                                         // rect, arc, circle, polygon must be filled
530     long           m_pen_rgb_color;     // current rgb color value: each color has
531                                         // a value 0 ... 255, and the 3 colors are
532                                         // grouped in a 3x8 bits value
533                                         // (written in hex to svg files)
534     long           m_brush_rgb_color;   // same as m_pen_rgb_color, used to fill
535                                         // some contours.
536     bool           m_graphics_changed;  // true if a pen/brush parameter is modified
537                                         // color, pen size, fill mode ...
538                                         // the new SVG stype must be output on file
539     PLOT_DASH_TYPE m_dashed;            // plot line style
540     bool           m_useInch;           // is 0 if the step size is 10**-n*mm
541                                         // is 1 if the step size is 10**-n*inch
542                                         // Where n is given from m_precision
543     unsigned       m_precision;         // How fine the step size is
544                                         // Use 3-6 (3 means um precision, 6 nm precision) in PcbNew
545                                         // 3-4 in other modules (avoid values >4 to avoid overflow)
546                                         // see also comment for m_useInch.
547 };
548