/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /** * Plotting engines similar to ps (PostScript, Gerber, svg) * * @file plotters_pslike.h */ #pragma once #include "plotter.h" /** * The PSLIKE_PLOTTER class is an intermediate class to handle common routines for engines * working more or less with the postscript imaging model. */ class PSLIKE_PLOTTER : public PLOTTER { public: PSLIKE_PLOTTER() : plotScaleAdjX( 1 ), plotScaleAdjY( 1 ), m_textMode( PLOT_TEXT_MODE::PHANTOM ) { } /** * PS and PDF fully implement native text (for the Latin-1 subset) */ virtual void SetTextMode( PLOT_TEXT_MODE mode ) override { if( mode != PLOT_TEXT_MODE::DEFAULT ) m_textMode = mode; } /** * Set the 'fine' scaling for the postscript engine */ void SetScaleAdjust( double scaleX, double scaleY ) { plotScaleAdjX = scaleX; plotScaleAdjY = scaleY; } // Pad routines are handled with lower level primitives virtual void FlashPadCircle( const VECTOR2I& aPadPos, int aDiameter, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashPadOval( const VECTOR2I& aPadPos, const VECTOR2I& aSize, const EDA_ANGLE& aPadOrient, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashPadRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize, const EDA_ANGLE& aPadOrient, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize, int aCornerRadius, const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners, const EDA_ANGLE& aPadOrient, OUTLINE_MODE aTraceMode, void* aData ) override; virtual void FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData ) override; /** * The SetColor implementation is split with the subclasses: * The PSLIKE computes the rgb values, the subclass emits the * operator to actually do it */ virtual void SetColor( const COLOR4D& color ) override; protected: /** * This is the core for postscript/PDF text alignment. * * It computes the transformation matrix to generate a user space * system aligned with the text. Even the PS uses the concat * operator to simplify PDF generation (concat is everything PDF * has to modify the CTM. Lots of parameters, both in and out. */ void computeTextParameters( const VECTOR2I& aPos, const wxString& aText, const EDA_ANGLE& aOrient, const VECTOR2I& aSize, bool aMirror, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, double *wideningFactor, double *ctm_a, double *ctm_b, double *ctm_c, double *ctm_d, double *ctm_e, double *ctm_f, double *heightFactor ); /** * Computes the x coordinates for the overlining in a string of text. * Fills the passed vector with couples of (start, stop) values to be * used in the text coordinate system (use computeTextParameters to * obtain the parameters to establish such a system) */ void postscriptOverlinePositions( const wxString& aText, int aXSize, bool aItalic, bool aBold, std::vector *pos_pairs ); /// convert a wxString unicode string to a char string compatible with the accepted /// string plotter format (convert special chars and non ascii7 chars) virtual std::string encodeStringForPlotter( const wxString& aUnicode ); /// Virtual primitive for emitting the setrgbcolor operator virtual void emitSetRGBColor( double r, double g, double b ) = 0; /// Height of the postscript font (from the AFM) static const double postscriptTextAscent; // = 0.718; /** * Sister function for the GraphicTextWidth in drawtxt.cpp * Does the same processing (i.e. calculates a text string width) but * using postscript metrics for the Helvetica font (optionally used for * PS and PDF plotting */ int returnPostscriptTextWidth( const wxString& aText, int aXSize, bool aItalic, bool aBold ); /// Fine user scale adjust ( = 1.0 if no correction) double plotScaleAdjX, plotScaleAdjY; /// How to draw text PLOT_TEXT_MODE m_textMode; }; class PS_PLOTTER : public PSLIKE_PLOTTER { public: PS_PLOTTER() { // The phantom plot in postscript is an hack and reportedly // crashes Adobe's own postscript interpreter! m_textMode = PLOT_TEXT_MODE::STROKE; } static wxString GetDefaultFileExtension() { return wxString( wxT( "ps" ) ); } virtual PLOT_FORMAT GetPlotterType() const override { return PLOT_FORMAT::POST; } /** * The code within this function (and the CloseFilePS function) * creates postscript files whose contents comply with Adobe's * Document Structuring Convention, as documented by assorted * details described within the following URLs: * * http://en.wikipedia.org/wiki/Document_Structuring_Conventions * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf * * * BBox is the boundary box (position and size of the "client rectangle" * for drawings (page - margins) in mils (0.001 inch) */ virtual bool StartPlot() override; virtual bool EndPlot() override; /** * Set the current line width (in IUs) for the next plot. */ virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override; /** * PostScript supports dashed lines. */ virtual void SetDash( PLOT_DASH_TYPE dashed ) override; virtual void SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil, double aScale, bool aMirror ) override; virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; virtual void Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR2I& aEnd, FILL_T aFill, int aWidth, int aMaxError ) override; virtual void PlotPoly( const std::vector& aCornerList, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override; /** * PostScript-likes at the moment are the only plot engines supporting bitmaps. */ virtual void PlotImage( const wxImage& aImage, const VECTOR2I& aPos, double aScaleFactor ) override; virtual void PenTo( const VECTOR2I& pos, char plume ) override; virtual void Text( const VECTOR2I& aPos, const COLOR4D& aColor, const wxString& aText, const EDA_ANGLE& aOrient, const VECTOR2I& aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed = false, KIFONT::FONT* aFont = nullptr, void* aData = nullptr ) override; protected: virtual void emitSetRGBColor( double r, double g, double b ) override; }; class PDF_PLOTTER : public PSLIKE_PLOTTER { public: PDF_PLOTTER() : pageTreeHandle( 0 ), fontResDictHandle( 0 ), pageStreamHandle( 0 ), streamLengthHandle( 0 ), workFile( nullptr ) { } virtual PLOT_FORMAT GetPlotterType() const override { return PLOT_FORMAT::PDF; } static wxString GetDefaultFileExtension() { return wxString( wxT( "pdf" ) ); } /** * Open or create the plot file aFullFilename. * * The base class open the file in text mode, so we should have this * function overlaid for PDF files, which are binary files. * * @param aFullFilename is the full file name of the file to create. * @return true if success, false if the file cannot be created/opened. */ virtual bool OpenFile( const wxString& aFullFilename ) override; /** * The PDF engine supports multiple pages; the first one is opened 'for free' the following * are to be closed and reopened. Between each page parameters can be set. */ virtual bool StartPlot() override; virtual bool EndPlot() override; /** * Start a new page in the PDF document. */ virtual void StartPage(); /** * Close the current page in the PDF document (and emit its compressed stream). */ virtual void ClosePage(); /** * Pen width setting for PDF. * * Since the specs *explicitly* says that a 0 width is a bad thing to use (since it * results in 1 pixel traces), we convert such requests to the minimal width (like 1) * Note pen width = 0 is used in plot polygons to plot filled polygons with no outline * thickness. Use in this case pen width = 1 does not actually change the polygon. */ virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override; /** * PDF supports dashed lines */ virtual void SetDash( PLOT_DASH_TYPE dashed ) override; /** * PDF can have multiple pages, so SetPageSettings can be called * with the outputFile open (but not inside a page stream!) */ virtual void SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil, double aScale, bool aMirror ) override; /** * Rectangles in PDF. Supported by the native operator. */ virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; /** * Circle drawing for PDF. They're approximated by curves, but fill is supported */ virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; /** * The PDF engine can't directly plot arcs so we use polygonization. */ virtual void Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR2I& aEnd, FILL_T aFill, int aWidth, int aMaxError ) override; virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; /** * Polygon plotting for PDF. Everything is supported */ virtual void PlotPoly( const std::vector& aCornerList, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override; virtual void PenTo( const VECTOR2I& pos, char plume ) override; virtual void Text( const VECTOR2I& aPos, const COLOR4D& aColor, const wxString& aText, const EDA_ANGLE& aOrient, const VECTOR2I& aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed = false, KIFONT::FONT* aFont = nullptr, void* aData = nullptr ) override; /** * PDF images are handles as inline, not XObject streams... */ virtual void PlotImage( const wxImage& aImage, const VECTOR2I& aPos, double aScaleFactor ) override; protected: /// convert a wxString unicode string to a char string compatible with the accepted /// string PDF format (convert special chars and non ascii7 chars) std::string encodeStringForPlotter( const wxString& aUnicode ) override; /** * PDF supports colors fully. It actually has distinct fill and pen colors, * but we set both at the same time. * * XXX Keeping them divided could result in a minor optimization in * Eeschema filled shapes, but would propagate to all the other plot * engines. Also arcs are filled as pies but only the arc is stroked so * it would be difficult to handle anyway. */ virtual void emitSetRGBColor( double r, double g, double b ) override; /** * Allocate a new handle in the table of the PDF object. The * handle must be completed using startPdfObject. It's an in-RAM operation * only, no output is done. */ int allocPdfObject(); /** * Open a new PDF object and returns the handle if the parameter is -1. * Otherwise fill in the xref entry for the passed object */ int startPdfObject(int handle = -1); /** * Close the current PDF object */ void closePdfObject(); /** * Starts a PDF stream (for the page). Returns the object handle opened * Pass -1 (default) for a fresh object. Especially from PDF 1.5 streams * can contain a lot of things, but for the moment we only handle page * content. */ int startPdfStream(int handle = -1); /** * Finish the current PDF stream (writes the deferred length, too) */ void closePdfStream(); int pageTreeHandle; /// Handle to the root of the page tree object int fontResDictHandle; /// Font resource dictionary std::vector pageHandles;/// Handles to the page objects int pageStreamHandle; /// Handle of the page content object int streamLengthHandle; /// Handle to the deferred stream length wxString workFilename; FILE* workFile; /// Temporary file to construct the stream before zipping std::vector xrefTable; /// The PDF xref offset table }; class SVG_PLOTTER : public PSLIKE_PLOTTER { public: SVG_PLOTTER(); static wxString GetDefaultFileExtension() { return wxString( wxT( "svg" ) ); } virtual PLOT_FORMAT GetPlotterType() const override { return PLOT_FORMAT::SVG; } virtual void SetColor( const COLOR4D& color ) override; /** * Create SVG file header. */ virtual bool StartPlot() override; virtual bool EndPlot() override; /** * Set the current line width (in IUs) for the next plot. */ virtual void SetCurrentLineWidth( int width, void* aData = nullptr ) override; /** * SVG supports dashed lines. */ virtual void SetDash( PLOT_DASH_TYPE dashed ) override; virtual void SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil, double aScale, bool aMirror ) override; virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width = USE_DEFAULT_LINE_WIDTH ) override; virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; virtual void BezierCurve( const VECTOR2I& aStart, const VECTOR2I& aControl1, const VECTOR2I& aControl2, const VECTOR2I& aEnd, int aTolerance, int aLineThickness = USE_DEFAULT_LINE_WIDTH ) override; virtual void PlotPoly( const std::vector& aCornerList, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH, void * aData = nullptr ) override; /** * PostScript-likes at the moment are the only plot engines supporting bitmaps. */ virtual void PlotImage( const wxImage& aImage, const VECTOR2I& aPos, double aScaleFactor ) override; virtual void PenTo( const VECTOR2I& pos, char plume ) override; /** * Select SVG coordinate precision (number of digits needed for 1 mm ) * (SVG plotter uses always metric unit) * Should be called only after SetViewport() is called * * @param aPrecision = number of digits in mantissa of coordinate * use a value from 3-6 * do not use value > 6 to avoid overflow in PCBNEW * do not use value > 4 to avoid overflow for other parts */ virtual void SetSvgCoordinatesFormat( unsigned aPrecision ) override; /** * Calling this function allows one to define the beginning of a group * of drawing items (used in SVG format to separate components) * @param aData should be a string for the SVG ID tag */ virtual void StartBlock( void* aData ) override; /** * Calling this function allows one to define the end of a group of drawing * items the group is started by StartBlock() * @param aData should be null */ virtual void EndBlock( void* aData ) override; virtual void Text( const VECTOR2I& aPos, const COLOR4D& aColor, const wxString& aText, const EDA_ANGLE& aOrient, const VECTOR2I& aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed = false, KIFONT::FONT* aFont = nullptr, void* aData = nullptr ) override; protected: /** * Initialize m_pen_rgb_color from reduced values r, g ,b * ( reduced values are 0.0 to 1.0 ) */ virtual void emitSetRGBColor( double r, double g, double b ) override; /** * Output the string which define pen and brush color, shape, transparency * * @param aIsGroup If false, do not form a new group for the style. * @param aExtraStyle If given, the string will be added into the style string before closing */ void setSVGPlotStyle( bool aIsGroup = true, const std::string& aExtraStyle = {} ); /** * Prepare parameters for setSVGPlotStyle() */ void setFillMode( FILL_T fill ); FILL_T m_fillMode; // true if the current contour // rect, arc, circle, polygon must be filled long m_pen_rgb_color; // current rgb color value: each color has // a value 0 ... 255, and the 3 colors are // grouped in a 3x8 bits value // (written in hex to svg files) long m_brush_rgb_color; // same as m_pen_rgb_color, used to fill // some contours. bool m_graphics_changed; // true if a pen/brush parameter is modified // color, pen size, fill mode ... // the new SVG stype must be output on file PLOT_DASH_TYPE m_dashed; // plot line style bool m_useInch; // is 0 if the step size is 10**-n*mm // is 1 if the step size is 10**-n*inch // Where n is given from m_precision unsigned m_precision; // How fine the step size is // Use 3-6 (3 means um precision, 6 nm precision) in PcbNew // 3-4 in other modules (avoid values >4 to avoid overflow) // see also comment for m_useInch. };