diff --git a/include/wx/dcsvg.h b/include/wx/dcsvg.h index 2a64f8882c..939b0dd36e 100644 --- a/include/wx/dcsvg.h +++ b/include/wx/dcsvg.h @@ -11,16 +11,25 @@ #ifndef _WX_DCSVG_H_ #define _WX_DCSVG_H_ +#if wxUSE_SVG + #include "wx/string.h" #include "wx/filename.h" #include "wx/dc.h" - -#if wxUSE_SVG - #include "wx/scopedptr.h" #define wxSVGVersion wxT("v0101") +enum wxSVGShapeRenderingMode +{ + wxSVG_SHAPE_RENDERING_AUTO = 0, + wxSVG_SHAPE_RENDERING_OPTIMIZE_SPEED, + wxSVG_SHAPE_RENDERING_CRISP_EDGES, + wxSVG_SHAPE_RENDERING_GEOMETRIC_PRECISION, + + wxSVG_SHAPE_RENDERING_OPTIMISE_SPEED = wxSVG_SHAPE_RENDERING_OPTIMIZE_SPEED +}; + class WXDLLIMPEXP_FWD_BASE wxFileOutputStream; class WXDLLIMPEXP_FWD_CORE wxSVGFileDC; @@ -76,9 +85,9 @@ public: class WXDLLIMPEXP_CORE wxSVGFileDCImpl : public wxDCImpl { public: - wxSVGFileDCImpl(wxSVGFileDC *owner, const wxString &filename, + wxSVGFileDCImpl(wxSVGFileDC* owner, const wxString& filename, int width = 320, int height = 240, double dpi = 72.0, - const wxString &title = wxString()); + const wxString& title = wxString()); virtual ~wxSVGFileDCImpl(); @@ -101,7 +110,7 @@ public: virtual wxCoord GetCharWidth() const wxOVERRIDE; #if wxUSE_PALETTE - virtual void SetPalette(const wxPalette& WXUNUSED(palette)) wxOVERRIDE + virtual void SetPalette(const wxPalette& WXUNUSED(palette)) wxOVERRIDE { wxFAIL_MSG(wxT("wxSVGFILEDC::SetPalette not implemented")); } @@ -136,7 +145,7 @@ public: m_graphics_changed = true; } - virtual void SetBackground(const wxBrush &brush) wxOVERRIDE; + virtual void SetBackground(const wxBrush& brush) wxOVERRIDE; virtual void SetBackgroundMode(int mode) wxOVERRIDE; virtual void SetBrush(const wxBrush& brush) wxOVERRIDE; virtual void SetFont(const wxFont& font) wxOVERRIDE; @@ -146,81 +155,102 @@ public: void SetBitmapHandler(wxSVGBitmapHandler* handler); + void SetShapeRenderingMode(wxSVGShapeRenderingMode renderingMode); + private: - virtual bool DoGetPixel(wxCoord, wxCoord, wxColour *) const wxOVERRIDE + virtual bool DoGetPixel(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), + wxColour* WXUNUSED(col)) const wxOVERRIDE { wxFAIL_MSG(wxT("wxSVGFILEDC::DoGetPixel Call not implemented")); return true; } - virtual bool DoBlit(wxCoord, wxCoord, wxCoord, wxCoord, wxDC *, - wxCoord, wxCoord, wxRasterOperationMode = wxCOPY, - bool = 0, int = -1, int = -1) wxOVERRIDE; + virtual bool DoBlit(wxCoord xdest, wxCoord ydest, + wxCoord width, wxCoord height, + wxDC* source, + wxCoord xsrc, wxCoord ysrc, + wxRasterOperationMode rop, + bool useMask = false, + wxCoord xsrcMask = wxDefaultCoord, + wxCoord ysrcMask = wxDefaultCoord) wxOVERRIDE; - virtual void DoCrossHair(wxCoord, wxCoord) wxOVERRIDE + virtual void DoCrossHair(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y)) wxOVERRIDE { wxFAIL_MSG(wxT("wxSVGFILEDC::CrossHair Call not implemented")); } - virtual void DoDrawArc(wxCoord, wxCoord, wxCoord, wxCoord, wxCoord, wxCoord) wxOVERRIDE; + virtual void DoDrawArc(wxCoord x1, wxCoord y1, + wxCoord x2, wxCoord y2, + wxCoord xc, wxCoord yc) wxOVERRIDE; - virtual void DoDrawBitmap(const wxBitmap &, wxCoord, wxCoord, bool = false) wxOVERRIDE; + virtual void DoDrawBitmap(const wxBitmap& bmp, wxCoord x, wxCoord y, + bool useMask = false) wxOVERRIDE; - virtual void DoDrawCheckMark(wxCoord x, wxCoord y, wxCoord w, wxCoord h) wxOVERRIDE; - - virtual void DoDrawEllipse(wxCoord x, wxCoord y, wxCoord w, wxCoord h) wxOVERRIDE; + virtual void DoDrawEllipse(wxCoord x, wxCoord y, + wxCoord width, wxCoord height) wxOVERRIDE; virtual void DoDrawEllipticArc(wxCoord x, wxCoord y, wxCoord w, wxCoord h, double sa, double ea) wxOVERRIDE; - virtual void DoDrawIcon(const wxIcon &, wxCoord, wxCoord) wxOVERRIDE; + virtual void DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y) wxOVERRIDE; virtual void DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2) wxOVERRIDE; virtual void DoDrawLines(int n, const wxPoint points[], - wxCoord xoffset = 0, wxCoord yoffset = 0) wxOVERRIDE; + wxCoord xoffset, wxCoord yoffset) wxOVERRIDE; - virtual void DoDrawPoint(wxCoord, wxCoord) wxOVERRIDE; + virtual void DoDrawPoint(wxCoord x, wxCoord y) wxOVERRIDE; virtual void DoDrawPolygon(int n, const wxPoint points[], wxCoord xoffset, wxCoord yoffset, - wxPolygonFillMode fillStyle) wxOVERRIDE; + wxPolygonFillMode fillStyle = wxODDEVEN_RULE) wxOVERRIDE; virtual void DoDrawPolyPolygon(int n, const int count[], const wxPoint points[], wxCoord xoffset, wxCoord yoffset, wxPolygonFillMode fillStyle) wxOVERRIDE; - virtual void DoDrawRectangle(wxCoord x, wxCoord y, wxCoord w, wxCoord h) wxOVERRIDE; + virtual void DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height) wxOVERRIDE; virtual void DoDrawRotatedText(const wxString& text, wxCoord x, wxCoord y, double angle) wxOVERRIDE; virtual void DoDrawRoundedRectangle(wxCoord x, wxCoord y, - wxCoord w, wxCoord h, - double radius = 20) wxOVERRIDE; + wxCoord width, wxCoord height, + double radius) wxOVERRIDE; virtual void DoDrawText(const wxString& text, wxCoord x, wxCoord y) wxOVERRIDE; virtual bool DoFloodFill(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxColour& WXUNUSED(col), - wxFloodFillStyle WXUNUSED(style) = wxFLOOD_SURFACE) wxOVERRIDE + wxFloodFillStyle WXUNUSED(style)) wxOVERRIDE { wxFAIL_MSG(wxT("wxSVGFILEDC::DoFloodFill Call not implemented")); return false; } - virtual void DoGetSize(int * x, int *y) const wxOVERRIDE + virtual void DoGradientFillLinear(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + wxDirection nDirection) wxOVERRIDE; + + virtual void DoGradientFillConcentric(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + const wxPoint& circleCenter) wxOVERRIDE; + + virtual void DoGetSize(int* width, int* height) const wxOVERRIDE { - if ( x ) - *x = m_width; - if ( y ) - *y = m_height; + if ( width ) + *width = m_width; + if ( height ) + *height = m_height; } - virtual void DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, - wxCoord *descent = NULL, - wxCoord *externalLeading = NULL, - const wxFont *font = NULL) const wxOVERRIDE; + virtual void DoGetTextExtent(const wxString& string, + wxCoord* x, wxCoord* y, + wxCoord* descent = NULL, + wxCoord* externalLeading = NULL, + const wxFont* theFont = NULL) const wxOVERRIDE; virtual void DoSetDeviceClippingRegion(const wxRegion& region) wxOVERRIDE { @@ -228,16 +258,17 @@ private: region.GetBox().width, region.GetBox().height); } - virtual void DoSetClippingRegion(int x, int y, int width, int height) wxOVERRIDE; + virtual void DoSetClippingRegion(wxCoord x, wxCoord y, + wxCoord w, wxCoord h) wxOVERRIDE; - virtual void DoGetSizeMM(int *width, int *height) const wxOVERRIDE; + virtual void DoGetSizeMM(int* width, int* height) const wxOVERRIDE; virtual wxSize GetPPI() const wxOVERRIDE; - void Init(const wxString &filename, int width, int height, - double dpi, const wxString &title); + void Init(const wxString& filename, int width, int height, + double dpi, const wxString& title); - void write(const wxString &s); + void write(const wxString& s); private: // If m_graphics_changed is true, close the current element and start a @@ -255,6 +286,7 @@ private: double m_dpi; wxScopedPtr m_outfile; wxScopedPtr m_bmp_handler; // class to handle bitmaps + wxSVGShapeRenderingMode m_renderingMode; // The clipping nesting level is incremented by every call to // SetClippingRegion() and reset when DestroyClippingRegion() is called. @@ -264,7 +296,11 @@ private: // incremented in each SetClippingRegion() call. size_t m_clipUniqueId; + // Unique ID for every gradient. + size_t m_gradientUniqueId; + wxDECLARE_ABSTRACT_CLASS(wxSVGFileDCImpl); + wxDECLARE_NO_COPY_CLASS(wxSVGFileDCImpl); }; @@ -284,6 +320,16 @@ public: // Use a custom bitmap handler: takes ownership of the handler. void SetBitmapHandler(wxSVGBitmapHandler* handler); + + void SetShapeRenderingMode(wxSVGShapeRenderingMode renderingMode); + +private: + wxSVGFileDC() + : wxDC(new wxSVGFileDCImpl(this, wxString())) + { + } + + wxDECLARE_DYNAMIC_CLASS(wxSVGFileDC); }; #endif // wxUSE_SVG diff --git a/interface/wx/dcsvg.h b/interface/wx/dcsvg.h index f4d55e22a1..bc6355139d 100644 --- a/interface/wx/dcsvg.h +++ b/interface/wx/dcsvg.h @@ -5,6 +5,23 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +/** + SVG shape rendering mode. + + These options represent the values defined in the SVG specification: + https://svgwg.org/svg2-draft/painting.html#ShapeRenderingProperty +*/ +enum wxSVGShapeRenderingMode +{ + wxSVG_SHAPE_RENDERING_AUTO = 0, + wxSVG_SHAPE_RENDERING_OPTIMIZE_SPEED, + wxSVG_SHAPE_RENDERING_CRISP_EDGES, + wxSVG_SHAPE_RENDERING_GEOMETRIC_PRECISION, + + wxSVG_SHAPE_RENDERING_OPTIMISE_SPEED = wxSVG_SHAPE_RENDERING_OPTIMIZE_SPEED +}; + + /** @class wxSVGFileDC @@ -74,6 +91,17 @@ public: */ void SetBitmapHandler(wxSVGBitmapHandler* handler); + /** + Set the shape rendering mode of the generated SVG. + All subsequent drawing calls will have this rendering mode set in the + SVG file. + + The default mode is wxSVG_SHAPE_RENDERING_AUTO. + + @since 3.1.3 + */ + void SetShapeRenderingMode(wxSVGShapeRenderingMode renderingMode); + /** Sets the clipping region for this device context to the intersection of the given region described by the parameters of this method and the previously diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 6229cd863f..c70b16ae1c 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -48,16 +48,23 @@ namespace // in this code originally). inline wxString NumStr(double f) { + // Handle this case specially to avoid generating "-0.00" in the output. + if ( f == 0 ) + { + return wxS("0.00"); + } + return wxString::FromCDouble(f, 2); } // Return the colour representation as HTML-like "#rrggbb" string and also // returns its alpha as opacity number in 0..1 range. -wxString Col2SVG(wxColour c, float *opacity) +wxString Col2SVG(wxColour c, float* opacity = NULL) { if ( c.Alpha() != wxALPHA_OPAQUE ) { - *opacity = c.Alpha() / 255.0f; + if ( opacity ) + *opacity = c.Alpha() / 255.0f; // Remove the alpha before using GetAsString(wxC2S_HTML_SYNTAX) as it // doesn't support colours with alpha channel. @@ -65,16 +72,17 @@ wxString Col2SVG(wxColour c, float *opacity) } else // No alpha. { - *opacity = 1.; + if ( opacity ) + *opacity = 1.0f; } return c.GetAsString(wxC2S_HTML_SYNTAX); } -wxString wxPenString(wxColour c, int style = wxPENSTYLE_SOLID) +wxString GetPenStroke(const wxColour& c, int style = wxPENSTYLE_SOLID) { float opacity; - wxString s = wxS("stroke:") + Col2SVG(c, &opacity) + wxS("; "); + wxString s = wxS("stroke:") + Col2SVG(c, &opacity) + wxS(";"); switch ( style ) { @@ -84,45 +92,47 @@ wxString wxPenString(wxColour c, int style = wxPENSTYLE_SOLID) case wxPENSTYLE_LONG_DASH: case wxPENSTYLE_DOT_DASH: case wxPENSTYLE_USER_DASH: - s += wxString::Format(wxS("stroke-opacity:%s; "), NumStr(opacity)); + s += wxString::Format(wxS(" stroke-opacity:%s;"), NumStr(opacity)); break; case wxPENSTYLE_TRANSPARENT: - s += wxS("stroke-opacity:0.0; "); + s += wxS(" stroke-opacity:0.0;"); break; default: wxASSERT_MSG(false, wxS("wxSVGFileDC::Requested Pen Style not available")); + break; } return s; } -wxString wxBrushString(wxColour c, int style = wxBRUSHSTYLE_SOLID) +wxString GetBrushFill(const wxColour& c, int style = wxBRUSHSTYLE_SOLID) { float opacity; - wxString s = wxS("fill:") + Col2SVG(c, &opacity) + wxS("; "); + wxString s = wxS("fill:") + Col2SVG(c, &opacity) + wxS(";"); switch ( style ) { case wxBRUSHSTYLE_SOLID: + case wxBRUSHSTYLE_BDIAGONAL_HATCH: case wxBRUSHSTYLE_FDIAGONAL_HATCH: case wxBRUSHSTYLE_CROSSDIAG_HATCH: case wxBRUSHSTYLE_CROSS_HATCH: case wxBRUSHSTYLE_VERTICAL_HATCH: case wxBRUSHSTYLE_HORIZONTAL_HATCH: - s += wxString::Format(wxS("fill-opacity:%s; "), NumStr(opacity)); + s += wxString::Format(wxS(" fill-opacity:%s;"), NumStr(opacity)); break; case wxBRUSHSTYLE_TRANSPARENT: - s += wxS("fill-opacity:0.0; "); + s += wxS(" fill-opacity:0.0;"); break; default: wxASSERT_MSG(false, wxS("wxSVGFileDC::Requested Brush Style not available")); + break; } return s; } -static -wxString wxGetPenPattern(wxPen& pen) +wxString GetPenPattern(const wxPen& pen) { wxString s; @@ -139,21 +149,21 @@ wxString wxGetPenPattern(wxPen& pen) switch (pen.GetStyle()) { case wxPENSTYLE_DOT: - s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\" "), w * 2, w * 5); + s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\""), w * 2, w * 5); break; case wxPENSTYLE_SHORT_DASH: - s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\" "), w * 10, w * 8); + s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\""), w * 10, w * 8); break; case wxPENSTYLE_LONG_DASH: - s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\" "), w * 15, w * 8); + s = wxString::Format(wxS("stroke-dasharray=\"%f,%f\""), w * 15, w * 8); break; case wxPENSTYLE_DOT_DASH: - s = wxString::Format(wxS("stroke-dasharray=\"%f,%f,%f,%f\" "), w * 8, w * 8, w * 2, w * 8); + s = wxString::Format(wxS("stroke-dasharray=\"%f,%f,%f,%f\""), w * 8, w * 8, w * 2, w * 8); break; case wxPENSTYLE_USER_DASH: { s = wxS("stroke-dasharray=\""); - wxDash *dashes; + wxDash* dashes; int count = pen.GetDashes(&dashes); if ((dashes != NULL) && (count > 0)) { @@ -164,7 +174,7 @@ wxString wxGetPenPattern(wxPen& pen) s << ","; } } - s += wxS("\" "); + s += wxS("\""); break; } case wxPENSTYLE_STIPPLE_MASK_OPAQUE: @@ -187,12 +197,52 @@ wxString wxGetPenPattern(wxPen& pen) return s; } -wxString wxGetBrushStyleName(wxBrush& brush) +wxString GetPenStyle(const wxPen& pen) +{ + wxString penStyle; + + penStyle += wxString::Format(wxS("stroke-width:%d;"), pen.GetWidth()); + + switch (pen.GetCap()) + { + case wxCAP_PROJECTING: + penStyle += wxS(" stroke-linecap:square;"); + break; + case wxCAP_BUTT: + penStyle += wxS(" stroke-linecap:butt;"); + break; + case wxCAP_ROUND: + default: + penStyle += wxS(" stroke-linecap:round;"); + break; + } + + switch (pen.GetJoin()) + { + case wxJOIN_BEVEL: + penStyle += wxS(" stroke-linejoin:bevel;"); + break; + case wxJOIN_MITER: + penStyle += wxS(" stroke-linejoin:miter;"); + break; + case wxJOIN_ROUND: + default: + penStyle += wxS(" stroke-linejoin:round;"); + break; + } + + return penStyle; +} + +wxString GetBrushStyleName(const wxBrush& brush) { wxString brushStyle; switch (brush.GetStyle()) { + case wxBRUSHSTYLE_BDIAGONAL_HATCH: + brushStyle = wxS("BdiagonalHatch"); + break; case wxBRUSHSTYLE_FDIAGONAL_HATCH: brushStyle = wxS("FdiagonalHatch"); break; @@ -211,7 +261,6 @@ wxString wxGetBrushStyleName(wxBrush& brush) case wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE: case wxBRUSHSTYLE_STIPPLE_MASK: case wxBRUSHSTYLE_STIPPLE: - case wxBRUSHSTYLE_BDIAGONAL_HATCH: wxASSERT_MSG(false, wxS("wxSVGFileDC::Requested Brush Fill not available")); break; case wxBRUSHSTYLE_SOLID: @@ -224,56 +273,85 @@ wxString wxGetBrushStyleName(wxBrush& brush) return brushStyle; } -wxString wxGetBrushFill(wxBrush& brush) +wxString GetBrushPattern(const wxBrush& brush) { wxString s; - wxString brushStyle = wxGetBrushStyleName(brush); + wxString brushStyle = GetBrushStyleName(brush); - if (!brushStyle.IsEmpty()) - s = wxS(" fill=\"url(#") + brushStyle + brush.GetColour().GetAsString(wxC2S_HTML_SYNTAX).substr(1) + wxS(")\""); + if (!brushStyle.empty()) + s = wxS("fill=\"url(#") + brushStyle + Col2SVG(brush.GetColour()).substr(1) + wxS(")\""); return s; } -wxString wxCreateBrushFill(wxBrush& brush) +wxString GetRenderMode(const wxSVGShapeRenderingMode style) +{ + wxString mode; + switch (style) + { + case wxSVG_SHAPE_RENDERING_OPTIMIZE_SPEED: + mode = wxS("optimizeSpeed"); + break; + case wxSVG_SHAPE_RENDERING_CRISP_EDGES: + mode = wxS("crispEdges"); + break; + case wxSVG_SHAPE_RENDERING_GEOMETRIC_PRECISION: + mode = wxS("geometricPrecision"); + break; + case wxSVG_SHAPE_RENDERING_AUTO: + mode = wxS("auto"); + break; + } + + wxString s = wxString::Format(wxS("shape-rendering=\"%s\""), mode); + return s; +} + +wxString CreateBrushFill(const wxBrush& brush, wxSVGShapeRenderingMode mode) { wxString s; - wxString patternName = wxGetBrushStyleName(brush); + wxString patternName = GetBrushStyleName(brush); - if (!patternName.IsEmpty()) + if (!patternName.empty()) { - patternName += brush.GetColour().GetAsString(wxC2S_HTML_SYNTAX).substr(1); - s = wxS("\n"); - s += wxS(" \n\n"); + wxString brushColourStr = Col2SVG(brush.GetColour()); + + s += wxString::Format(wxS(" \n"), + patternName, brushColourStr.substr(1)); + s += wxString::Format(wxS(" \n"), + brushColourStr, pattern, GetRenderMode(mode)); + s += wxS(" \n"); } return s; } -void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) +void SetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) { const double scale = sDC.GetContentScaleFactor(); if ( scale > 1 ) @@ -337,7 +415,7 @@ wxSVGBitmapEmbedHandler::ProcessBitmap(const wxBitmap& bmp, // write to the SVG file const wxCharBuffer buf = s.utf8_str(); - stream.Write(buf, strlen((const char *)buf)); + stream.Write(buf, strlen((const char*)buf)); return stream.IsOk(); } @@ -362,7 +440,7 @@ wxSVGBitmapFileHandler::ProcessBitmap(const wxBitmap& bmp, { sPNG.SetFullName(wxString::Format("%s%simage%d.png", sPNG.GetName(), - sPNG.GetName().IsEmpty() ? "" : "_", + sPNG.GetName().empty() ? "" : "_", sub_images++)); } while ( sPNG.FileExists() ); @@ -387,29 +465,36 @@ wxSVGBitmapFileHandler::ProcessBitmap(const wxBitmap& bmp, // wxSVGFileDC (specialisations) // ---------------------------------------------------------- +wxIMPLEMENT_DYNAMIC_CLASS(wxSVGFileDC, wxDC); + void wxSVGFileDC::SetBitmapHandler(wxSVGBitmapHandler* handler) { ((wxSVGFileDCImpl*)GetImpl())->SetBitmapHandler(handler); } +void wxSVGFileDC::SetShapeRenderingMode(wxSVGShapeRenderingMode renderingMode) +{ + ((wxSVGFileDCImpl*)GetImpl())->SetShapeRenderingMode(renderingMode); +} + // ---------------------------------------------------------- // wxSVGFileDCImpl // ---------------------------------------------------------- -wxIMPLEMENT_ABSTRACT_CLASS(wxSVGFileDCImpl, wxDC); +wxIMPLEMENT_ABSTRACT_CLASS(wxSVGFileDCImpl, wxDCImpl); -wxSVGFileDCImpl::wxSVGFileDCImpl(wxSVGFileDC *owner, const wxString &filename, - int width, int height, double dpi, const wxString &title) +wxSVGFileDCImpl::wxSVGFileDCImpl(wxSVGFileDC* owner, const wxString& filename, + int width, int height, double dpi, const wxString& title) : wxDCImpl(owner) { Init(filename, width, height, dpi, title); } -void wxSVGFileDCImpl::Init(const wxString &filename, int Width, int Height, - double dpi, const wxString &title) +void wxSVGFileDCImpl::Init(const wxString& filename, int width, int height, + double dpi, const wxString& title) { - m_width = Width; - m_height = Height; + m_width = width; + m_height = height; m_dpi = dpi; @@ -418,6 +503,8 @@ void wxSVGFileDCImpl::Init(const wxString &filename, int Width, int Height, m_clipUniqueId = 0; m_clipNestingLevel = 0; + m_gradientUniqueId = 0; + m_mm_to_pix_x = dpi / 25.4; m_mm_to_pix_y = dpi / 25.4; @@ -432,16 +519,22 @@ void wxSVGFileDCImpl::Init(const wxString &filename, int Width, int Height, m_filename = filename; m_graphics_changed = true; + m_renderingMode = wxSVG_SHAPE_RENDERING_AUTO; + ////////////////////code here m_bmp_handler.reset(); - m_outfile.reset(new wxFileOutputStream(m_filename)); + + if ( m_filename.empty() ) + m_outfile.reset(); + else + m_outfile.reset(new wxFileOutputStream(m_filename)); wxString s; s += wxS("\n"); s += wxS("\n\n"); s += wxS("\n"), NumStr(float(Width) / dpi * 2.54), NumStr(float(Height) / dpi * 2.54), Width, Height); + s += wxString::Format(wxS(" width=\"%scm\" height=\"%scm\" viewBox=\"0 0 %d %d\">\n"), NumStr(float(m_width) / dpi * 2.54), NumStr(float(m_height) / dpi * 2.54), m_width, m_height); s += wxString::Format(wxS("%s\n"), title); s += wxString(wxS("Picture generated by wxSVG ")) + wxSVGVersion + wxS("\n\n"); s += wxS("\n"); @@ -460,7 +553,7 @@ wxSVGFileDCImpl::~wxSVGFileDCImpl() write(s); } -void wxSVGFileDCImpl::DoGetSizeMM(int *width, int *height) const +void wxSVGFileDCImpl::DoGetSizeMM(int* width, int* height) const { if (width) *width = wxRound( (double)m_width / GetMMToPXx() ); @@ -490,7 +583,8 @@ void wxSVGFileDCImpl::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2) NewGraphicsIfNeeded(); wxString s; - s = wxString::Format(wxS(" \n"), wxGetPenPattern(m_pen), x1, y1, x2, y2); + s = wxString::Format(wxS(" \n"), + x1, y1, x2, y2, GetRenderMode(m_renderingMode), GetPenPattern(m_pen)); write(s); @@ -505,8 +599,9 @@ void wxSVGFileDCImpl::DoDrawLines(int n, const wxPoint points[], wxCoord xoffset NewGraphicsIfNeeded(); wxString s; - s = wxS(" \n"); + s += wxString::Format(wxS("\" style=\"fill:none\" %s %s/>\n"), + GetRenderMode(m_renderingMode), GetPenPattern(m_pen)); write(s); } } -void wxSVGFileDCImpl::DoDrawPoint(wxCoord x1, wxCoord y1) +void wxSVGFileDCImpl::DoDrawPoint(wxCoord x, wxCoord y) { - wxString s; NewGraphicsIfNeeded(); - s = wxS("\n"); + + wxString s; + + s = wxS(" \n "); write(s); - DoDrawLine(x1, y1, x1, y1); - s = wxS("\n"); + + DoDrawLine(x, y, x, y); + + s = wxS(" \n"); write(s); } -void wxSVGFileDCImpl::DoDrawCheckMark(wxCoord x1, wxCoord y1, wxCoord width, wxCoord height) +void wxSVGFileDCImpl::DoDrawText(const wxString& text, wxCoord x, wxCoord y) { - wxDCImpl::DoDrawCheckMark(x1, y1, width, height); -} - -void wxSVGFileDCImpl::DoDrawText(const wxString& text, wxCoord x1, wxCoord y1) -{ - DoDrawRotatedText(text, x1, y1, 0.0); + DoDrawRotatedText(text, x, y, 0.0); } void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoord y, double angle) @@ -586,7 +681,7 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor textDecoration += wxS(" underline"); if (m_font.GetStrikethrough()) textDecoration += wxS(" line-through"); - if (textDecoration.IsEmpty()) + if (textDecoration.empty()) textDecoration = wxS(" none"); wxString style = wxS("style=\""); @@ -596,8 +691,8 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor style += wxString::Format(wxS("font-size:%spt; "), NumStr(m_font.GetFractionalPointSize())); style += wxString::Format(wxS("text-decoration:%s; "), textDecoration); style += wxString::Format(wxS("%s %s stroke-width:0; "), - wxBrushString(m_textForegroundColour), - wxPenString(m_textForegroundColour)); + GetBrushFill(m_textForegroundColour), + GetPenStroke(m_textForegroundColour)); style += wxS("white-space: pre;"); style += wxS("\""); @@ -609,42 +704,43 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor const wxArrayString lines = wxSplit(sText, '\n', '\0'); for (size_t lineNum = 0; lineNum < lines.size(); lineNum++) { - const int xRect = x + wxRound(lineNum * dx); - const int yRect = y + wxRound(lineNum * dy); + const double xRect = x + lineNum * dx; + const double yRect = y + lineNum * dy; // convert x,y to SVG text x,y (the coordinates of the text baseline) wxCoord ww, hh, desc; wxString const& line = lines[lineNum]; DoGetTextExtent(line, &ww, &hh, &desc); - const int xText = xRect + (hh - desc) * sin(rad); - const int yText = yRect + (hh - desc) * cos(rad); + const double xText = xRect + (hh - desc) * sin(rad); + const double yText = yRect + (hh - desc) * cos(rad); if (m_backgroundMode == wxBRUSHSTYLE_SOLID) { // draw text background const wxString rectStyle = wxString::Format( wxS("style=\"%s %s stroke-width:1;\""), - wxBrushString(m_textBackgroundColour), - wxPenString(m_textBackgroundColour)); + GetBrushFill(m_textBackgroundColour), + GetPenStroke(m_textBackgroundColour)); const wxString rectTransform = wxString::Format( - wxS("transform=\"rotate(%s %d %d)\""), - NumStr(-angle), xRect, yRect); + wxS("transform=\"rotate(%s %s %s)\""), + NumStr(-angle), NumStr(xRect), NumStr(yRect)); s = wxString::Format( - wxS(" \n"), - xRect, yRect, ww, hh, rectStyle, rectTransform); + wxS(" \n"), + NumStr(xRect), NumStr(yRect), ww, hh, + GetRenderMode(m_renderingMode), rectStyle, rectTransform); write(s); } const wxString transform = wxString::Format( - wxS("transform=\"rotate(%s %d %d)\""), - NumStr(-angle), xText, yText); + wxS("transform=\"rotate(%s %s %s)\""), + NumStr(-angle), NumStr(xText), NumStr(yText)); s = wxString::Format( - wxS(" %s\n"), - xText, yText, ww, style, transform, + wxS(" %s\n"), + NumStr(xText), NumStr(yText), ww, style, transform, #if wxUSE_MARKUP wxMarkupParser::Quote(line) #else @@ -658,7 +754,7 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor void wxSVGFileDCImpl::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height) { - DoDrawRoundedRectangle(x, y, width, height, 0); + DoDrawRoundedRectangle(x, y, width, height, 0.0); } void wxSVGFileDCImpl::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius) @@ -666,10 +762,10 @@ void wxSVGFileDCImpl::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width NewGraphicsIfNeeded(); wxString s; - s = wxString::Format(wxS(" \n"), + x, y, width, height, NumStr(radius), + GetRenderMode(m_renderingMode), GetPenPattern(m_pen), GetBrushPattern(m_brush)); - s += wxS("/>\n"); write(s); CalcBoundingBox(x, y); @@ -681,22 +777,21 @@ void wxSVGFileDCImpl::DoDrawPolygon(int n, const wxPoint points[], wxPolygonFillMode fillStyle) { NewGraphicsIfNeeded(); + wxString s; - s = wxS(" \n"); + + s += wxString::Format(wxS("\" %s %s %s style=\"fill-rule:%s;\"/>\n"), + GetRenderMode(m_renderingMode), GetPenPattern(m_pen), GetBrushPattern(m_brush), + fillStyle == wxODDEVEN_RULE ? wxS("evenodd") : wxS("nonzero")); + write(s); } @@ -746,12 +841,13 @@ void wxSVGFileDCImpl::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord { NewGraphicsIfNeeded(); - int rh = height / 2; - int rw = width / 2; + const double rh = height / 2.0; + const double rw = width / 2.0; wxString s; - s = wxString::Format(wxS(" \n"); write(s); @@ -803,8 +899,8 @@ void wxSVGFileDCImpl::DoDrawArc(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, if (x1 == x2 && y1 == y2) { // drawing full circle fails with default arc. Draw two half arcs instead. - s = wxString::Format(wxS(" \n"); + s += wxString::Format(wxS("\" %s %s/>\n"), + GetRenderMode(m_renderingMode), GetPenPattern(m_pen)); write(s); } @@ -883,15 +979,15 @@ void wxSVGFileDCImpl::DoDrawEllipticArc(wxCoord x, wxCoord y, wxCoord w, wxCoord { // Drawing full circle fails with default arc. Draw two half arcs instead. fArc = 1; - arcPath = wxString::Format(wxS(" \n"); + arcFill += wxString::Format(wxS(" L%s %s z\" %s %s/>\n"), + NumStr(xc), NumStr(yc), + GetRenderMode(m_renderingMode), GetPenPattern(m_pen)); write(arcFill); } wxDCBrushChanger setTransp(*GetOwner(), *wxTRANSPARENT_BRUSH); NewGraphicsIfNeeded(); - wxString arcLine = arcPath + wxS("\"/>\n"); + wxString arcLine = wxString::Format(wxS("%s\" %s %s/>\n"), + arcPath, GetRenderMode(m_renderingMode), GetPenPattern(m_pen)); write(arcLine); } -void wxSVGFileDCImpl::DoSetClippingRegion(int x, int y, int width, int height) +void wxSVGFileDCImpl::DoGradientFillLinear(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + wxDirection nDirection) +{ + NewGraphicsIfNeeded(); + + float initOpacity; + float destOpacity; + wxString initCol = Col2SVG(initialColour, &initOpacity); + wxString destCol = Col2SVG(destColour, &destOpacity); + + const int x1 = ((nDirection & wxLEFT) > 0) ? 100 : 0; + const int y1 = ((nDirection & wxUP) > 0) ? 100 : 0; + const int x2 = ((nDirection & wxRIGHT) > 0) ? 100 : 0; + const int y2 = ((nDirection & wxDOWN) > 0) ? 100 : 0; + + wxString s; + s += wxS(" \n"); + s += wxString::Format(wxS(" \n"), + m_gradientUniqueId, x1, y1, x2, y2); + s += wxString::Format(wxS(" \n"), + initCol, NumStr(initOpacity)); + s += wxString::Format(wxS(" \n"), + destCol, NumStr(destOpacity)); + s += wxS(" \n"); + s += wxS(" \n"); + + s += wxString::Format(wxS(" \n"), + rect.x, rect.y, rect.width, rect.height, m_gradientUniqueId, + GetRenderMode(m_renderingMode), GetPenPattern(m_pen), GetBrushPattern(m_brush)); + + m_gradientUniqueId++; + + write(s); + + CalcBoundingBox(rect.x, rect.y); + CalcBoundingBox(rect.x + rect.width, rect.y + rect.height); +} + +void wxSVGFileDCImpl::DoGradientFillConcentric(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + const wxPoint& circleCenter) +{ + NewGraphicsIfNeeded(); + + float initOpacity; + float destOpacity; + wxString initCol = Col2SVG(initialColour, &initOpacity); + wxString destCol = Col2SVG(destColour, &destOpacity); + + const double cx = circleCenter.x * 100.0 / rect.GetWidth(); + const double cy = circleCenter.y * 100.0 / rect.GetHeight(); + const double fx = cx; + const double fd = cy; + + wxString s; + s += wxS(" \n"); + s += wxString::Format(wxS(" \n"), + m_gradientUniqueId, NumStr(cx), NumStr(cy), NumStr(fx), NumStr(fd)); + s += wxString::Format(wxS(" \n"), + initCol, NumStr(initOpacity)); + s += wxString::Format(wxS(" \n"), + destCol, NumStr(destOpacity)); + s += wxS(" \n"); + s += wxS(" \n"); + + s += wxString::Format(wxS(" \n"), + rect.x, rect.y, rect.width, rect.height, m_gradientUniqueId, + GetRenderMode(m_renderingMode), GetPenPattern(m_pen), GetBrushPattern(m_brush)); + + m_gradientUniqueId++; + + write(s); + + CalcBoundingBox(rect.x, rect.y); + CalcBoundingBox(rect.x + rect.width, rect.y + rect.height); +} + +void wxSVGFileDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord width, wxCoord height) { wxString svg; @@ -975,18 +1153,23 @@ void wxSVGFileDCImpl::DestroyClippingRegion() wxDCImpl::DestroyClippingRegion(); } -void wxSVGFileDCImpl::DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, wxCoord *descent, wxCoord *externalLeading, const wxFont *font) const +void wxSVGFileDCImpl::DoGetTextExtent(const wxString& string, + wxCoord* x, + wxCoord* y, + wxCoord* descent, + wxCoord* externalLeading, + const wxFont* theFont) const { wxScreenDC sDC; - wxSetScaledScreenDCFont(sDC, font ? *font : m_font); + SetScaledScreenDCFont(sDC, theFont ? *theFont : m_font); - sDC.GetTextExtent(string, w, h, descent, externalLeading); + sDC.GetTextExtent(string, x, y, descent, externalLeading); } wxCoord wxSVGFileDCImpl::GetCharHeight() const { wxScreenDC sDC; - wxSetScaledScreenDCFont(sDC, m_font); + SetScaledScreenDCFont(sDC, m_font); return sDC.GetCharHeight(); @@ -995,7 +1178,7 @@ wxCoord wxSVGFileDCImpl::GetCharHeight() const wxCoord wxSVGFileDCImpl::GetCharWidth() const { wxScreenDC sDC; - wxSetScaledScreenDCFont(sDC, m_font); + SetScaledScreenDCFont(sDC, m_font); return sDC.GetCharWidth(); } @@ -1005,7 +1188,7 @@ wxCoord wxSVGFileDCImpl::GetCharWidth() const // wxSVGFileDCImpl - set functions // ---------------------------------------------------------- -void wxSVGFileDCImpl::SetBackground(const wxBrush &brush) +void wxSVGFileDCImpl::SetBackground(const wxBrush& brush) { m_backgroundBrush = brush; } @@ -1020,14 +1203,24 @@ void wxSVGFileDCImpl::SetBitmapHandler(wxSVGBitmapHandler* handler) m_bmp_handler.reset(handler); } +void wxSVGFileDCImpl::SetShapeRenderingMode(wxSVGShapeRenderingMode renderingMode) +{ + m_renderingMode = renderingMode; +} + void wxSVGFileDCImpl::SetBrush(const wxBrush& brush) { m_brush = brush; m_graphics_changed = true; - wxString pattern = wxCreateBrushFill(m_brush); - write(pattern); + wxString pattern = CreateBrushFill(m_brush, m_renderingMode); + if ( !pattern.empty() ) + { + NewGraphicsIfNeeded(); + + write(pattern); + } } void wxSVGFileDCImpl::SetPen(const wxPen& pen) @@ -1051,45 +1244,17 @@ void wxSVGFileDCImpl::NewGraphicsIfNeeded() void wxSVGFileDCImpl::DoStartNewGraphics() { - wxString s, sBrush, sPenCap, sPenJoin, sPenStyle, sLast; + wxString s; - sBrush = wxS("\n"), + GetPenStyle(m_pen), + GetBrushFill(m_brush.GetColour(), m_brush.GetStyle()), + GetPenStroke(m_pen.GetColour(), m_pen.GetStyle()), + NumStr((m_deviceOriginX - m_logicalOriginX) * m_signX), + NumStr((m_deviceOriginY - m_logicalOriginY) * m_signY), + NumStr(m_scaleX * m_signX), + NumStr(m_scaleY * m_signY)); - switch ( m_pen.GetCap() ) - { - case wxCAP_PROJECTING: - sPenCap = wxS("stroke-linecap:square; "); - break; - case wxCAP_BUTT: - sPenCap = wxS("stroke-linecap:butt; "); - break; - case wxCAP_ROUND: - default: - sPenCap = wxS("stroke-linecap:round; "); - } - - switch (m_pen.GetJoin()) - { - case wxJOIN_BEVEL: - sPenJoin = wxS("stroke-linejoin:bevel; "); - break; - case wxJOIN_MITER: - sPenJoin = wxS("stroke-linejoin:miter; "); - break; - case wxJOIN_ROUND: - default: - sPenJoin = wxS("stroke-linejoin:round; "); - } - - sLast = wxString::Format(wxS("stroke-width:%d\" transform=\"translate(%s %s) scale(%s %s)\">"), - m_pen.GetWidth(), - NumStr((m_deviceOriginX - m_logicalOriginX)* m_signX), - NumStr((m_deviceOriginY - m_logicalOriginY)* m_signY), - NumStr(m_scaleX * m_signX), - NumStr(m_scaleY * m_signY)); - - s = sBrush + sPenCap + sPenJoin + sPenStyle + sLast + wxS("\n"); write(s); } @@ -1098,13 +1263,15 @@ void wxSVGFileDCImpl::SetFont(const wxFont& font) m_font = font; } -// export a bitmap as a raster image in png -bool wxSVGFileDCImpl::DoBlit(wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, - wxDC* source, wxCoord xsrc, wxCoord ysrc, - wxRasterOperationMode logicalFunc /*= wxCOPY*/, bool useMask /*= false*/, - wxCoord /*xsrcMask = -1*/, wxCoord /*ysrcMask = -1*/) +bool wxSVGFileDCImpl::DoBlit(wxCoord xdest, wxCoord ydest, + wxCoord width, wxCoord height, + wxDC* source, + wxCoord xsrc, wxCoord ysrc, + wxRasterOperationMode rop, + bool useMask, + wxCoord WXUNUSED(xsrcMask), wxCoord WXUNUSED(ysrcMask)) { - if (logicalFunc != wxCOPY) + if (rop != wxCOPY) { wxASSERT_MSG(false, wxS("wxSVGFileDC::DoBlit Call requested nonCopy mode; this is not possible")); return false; @@ -1123,17 +1290,18 @@ bool wxSVGFileDCImpl::DoBlit(wxCoord xdest, wxCoord ydest, wxCoord width, wxCoor return false; } -void wxSVGFileDCImpl::DoDrawIcon(const class wxIcon & myIcon, wxCoord x, wxCoord y) +void wxSVGFileDCImpl::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y) { - wxBitmap myBitmap(myIcon.GetWidth(), myIcon.GetHeight()); + wxBitmap myBitmap(icon.GetWidth(), icon.GetHeight()); wxMemoryDC memDC; memDC.SelectObject(myBitmap); - memDC.DrawIcon(myIcon, 0, 0); + memDC.DrawIcon(icon, 0, 0); memDC.SelectObject(wxNullBitmap); DoDrawBitmap(myBitmap, x, y); } -void wxSVGFileDCImpl::DoDrawBitmap(const class wxBitmap & bmp, wxCoord x, wxCoord y, bool WXUNUSED(bTransparent) /*=0*/) +void wxSVGFileDCImpl::DoDrawBitmap(const wxBitmap& bmp, wxCoord x, wxCoord y, + bool WXUNUSED(useMask)) { NewGraphicsIfNeeded(); @@ -1141,16 +1309,22 @@ void wxSVGFileDCImpl::DoDrawBitmap(const class wxBitmap & bmp, wxCoord x, wxCoor if ( !m_bmp_handler ) m_bmp_handler.reset(new wxSVGBitmapFileHandler(m_filename)); - m_bmp_handler->ProcessBitmap(bmp, x, y, *m_outfile); -} - -void wxSVGFileDCImpl::write(const wxString &s) -{ - m_OK = m_outfile->IsOk(); + m_OK = m_outfile && m_outfile->IsOk(); if (!m_OK) return; + + m_bmp_handler->ProcessBitmap(bmp, x, y, *m_outfile); + m_OK = m_outfile->IsOk(); +} + +void wxSVGFileDCImpl::write(const wxString& s) +{ + m_OK = m_outfile && m_outfile->IsOk(); + if (!m_OK) + return; + const wxCharBuffer buf = s.utf8_str(); - m_outfile->Write(buf, strlen((const char *)buf)); + m_outfile->Write(buf, strlen((const char*)buf)); m_OK = m_outfile->IsOk(); }