1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/svg.cpp
4 // Author: Chris Elliott
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
21 #include "wx/dcmemory.h"
22 #include "wx/dcscreen.h"
28 #include "wx/wfstream.h"
29 #include "wx/filename.h"
31 // ----------------------------------------------------------
33 // ----------------------------------------------------------
38 inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
40 // This function returns a string representation of a floating point number in
41 // C locale (i.e. always using "." for the decimal separator) and with the
42 // fixed precision (which is 2 for some unknown reason but this is what it was
43 // in this code originally).
44 inline wxString
NumStr(double f
)
46 return wxString::FromCDouble(f
, 2);
49 wxString
wxPenString(wxColour c
, int style
= wxPENSTYLE_SOLID
)
51 wxString s
= wxT("stroke:") + c
.GetAsString(wxC2S_HTML_SYNTAX
) + wxT("; ");
52 // Use the color's alpha value (if not opaque) for the opacity.
53 // Note that a transparent pen will override the alpha value.
54 if (c
.Alpha() != wxALPHA_OPAQUE
&& style
!= wxPENSTYLE_TRANSPARENT
)
56 s
+= wxString::Format(wxT("stroke-opacity:%s; "), NumStr(c
.Alpha()/255.));
62 case wxPENSTYLE_SOLID
:
63 s
+= wxT("stroke-opacity:1.0; ");
65 case wxPENSTYLE_TRANSPARENT
:
66 s
+= wxT("stroke-opacity:0.0; ");
69 wxASSERT_MSG(false, wxT("wxSVGFileDC::Requested Pen Style not available"));
75 wxString
wxBrushString(wxColour c
, int style
= wxBRUSHSTYLE_SOLID
)
77 wxString s
= wxT("fill:") + c
.GetAsString(wxC2S_HTML_SYNTAX
) + wxT("; ");
78 // Use the color's alpha value (if not opaque) for the opacity.
79 // Note that a transparent brush will override the alpha value.
80 if (c
.Alpha() != wxALPHA_OPAQUE
&& style
!= wxBRUSHSTYLE_TRANSPARENT
)
82 s
+= wxString::Format(wxT("fill-opacity:%s; "), NumStr(c
.Alpha()/255.));
88 case wxBRUSHSTYLE_SOLID
:
89 s
+= wxT("fill-opacity:1.0; ");
91 case wxBRUSHSTYLE_TRANSPARENT
:
92 s
+= wxT("fill-opacity:0.0; ");
95 wxASSERT_MSG(false, wxT("wxSVGFileDC::Requested Brush Style not available"));
101 } // anonymous namespace
103 // ----------------------------------------------------------
105 // ----------------------------------------------------------
107 IMPLEMENT_ABSTRACT_CLASS(wxSVGFileDCImpl
, wxDC
)
109 wxSVGFileDCImpl::wxSVGFileDCImpl( wxSVGFileDC
*owner
, const wxString
&filename
,
110 int width
, int height
, double dpi
) :
113 Init( filename
, width
, height
, dpi
);
116 void wxSVGFileDCImpl::Init (const wxString
&filename
, int Width
, int Height
, double dpi
)
125 m_mm_to_pix_x
= dpi
/25.4;
126 m_mm_to_pix_y
= dpi
/25.4;
128 m_backgroundBrush
= *wxTRANSPARENT_BRUSH
;
129 m_textForegroundColour
= *wxBLACK
;
130 m_textBackgroundColour
= *wxWHITE
;
131 m_colour
= wxColourDisplay();
133 m_pen
= *wxBLACK_PEN
;
134 m_font
= *wxNORMAL_FONT
;
135 m_brush
= *wxWHITE_BRUSH
;
137 m_graphics_changed
= true;
139 ////////////////////code here
141 m_outfile
= new wxFileOutputStream(filename
);
142 m_OK
= m_outfile
->Ok ();
145 m_filename
= filename
;
148 s
= wxT("<?xml version=\"1.0\" standalone=\"no\"?>") + wxString(wxT("\n"));
150 s
= wxT("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" ") + wxString(wxT("\n"));
152 s
= wxT("\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"> ") + wxString(wxT("\n"));
154 s
= wxT("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" ") + wxString(wxT("\n"));
156 s
.Printf( wxT(" width=\"%scm\" height=\"%scm\" viewBox=\"0 0 %d %d \"> \n"), NumStr(float(Width
)/dpi
*2.54), NumStr(float(Height
)/dpi
*2.54), Width
, Height
);
158 s
= wxT("<title>SVG Picture created as ") + wxFileName(filename
).GetFullName() + wxT(" </title>") + wxT("\n");
160 s
= wxString (wxT("<desc>Picture generated by wxSVG ")) + wxSVGVersion
+ wxT(" </desc>")+ wxT("\n");
162 s
= wxT("<g style=\"fill:black; stroke:black; stroke-width:1\">") + wxString(wxT("\n"));
167 wxSVGFileDCImpl::~wxSVGFileDCImpl()
169 wxString s
= wxT("</g> \n</svg> \n");
174 void wxSVGFileDCImpl::DoGetSizeMM( int *width
, int *height
) const
177 *width
= wxRound( (double)m_width
/ m_mm_to_pix_x
);
180 *height
= wxRound( (double)m_height
/ m_mm_to_pix_y
);
183 wxSize
wxSVGFileDCImpl::GetPPI() const
185 return wxSize( wxRound(m_dpi
), wxRound(m_dpi
) );
188 void wxSVGFileDCImpl::DoDrawLine (wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
190 if (m_graphics_changed
) NewGraphics ();
192 s
.Printf ( wxT("<path d=\"M%d %d L%d %d\" /> \n"), x1
,y1
,x2
,y2
);
197 CalcBoundingBox(x1
, y1
);
198 CalcBoundingBox(x2
, y2
);
202 void wxSVGFileDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
204 for ( int i
= 1; i
< n
; i
++ )
206 DoDrawLine ( points
[i
-1].x
+ xoffset
, points
[i
-1].y
+ yoffset
,
207 points
[ i
].x
+ xoffset
, points
[ i
].y
+ yoffset
);
211 void wxSVGFileDCImpl::DoDrawPoint (wxCoord x1
, wxCoord y1
)
214 if (m_graphics_changed
) NewGraphics ();
215 s
= wxT("<g style = \"stroke-linecap:round;\" > ") + wxString(wxT("\n"));
217 DoDrawLine ( x1
,y1
,x1
,y1
);
222 void wxSVGFileDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
, wxCoord width
, wxCoord height
)
224 wxDCImpl::DoDrawCheckMark (x1
,y1
,width
,height
);
227 void wxSVGFileDCImpl::DoDrawText(const wxString
& text
, wxCoord x1
, wxCoord y1
)
229 DoDrawRotatedText(text
, x1
,y1
,0.0);
232 void wxSVGFileDCImpl::DoDrawRotatedText(const wxString
& sText
, wxCoord x
, wxCoord y
, double angle
)
234 //known bug; if the font is drawn in a scaled DC, it will not behave exactly as wxMSW
235 if (m_graphics_changed
) NewGraphics ();
238 // calculate bounding box
240 DoGetTextExtent(sText
, &w
, &h
, &desc
);
242 double rad
= DegToRad(angle
);
244 // wxT("upper left") and wxT("upper right")
245 CalcBoundingBox(x
, y
);
246 CalcBoundingBox((wxCoord
)(x
+ w
*cos(rad
)), (wxCoord
)(y
- h
*sin(rad
)));
248 // wxT("bottom left") and wxT("bottom right")
249 x
+= (wxCoord
)(h
*sin(rad
));
250 y
+= (wxCoord
)(h
*cos(rad
));
251 CalcBoundingBox(x
, y
);
252 CalcBoundingBox((wxCoord
)(x
+ h
*sin(rad
)), (wxCoord
)(y
+ h
*cos(rad
)));
254 if (m_backgroundMode
== wxBRUSHSTYLE_SOLID
)
256 // draw background first
257 // just like DoDrawRectangle except we pass the text color to it and set the border to a 1 pixel wide text background
259 sTmp
.Printf ( wxT(" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "), x
,y
+desc
-h
, w
, h
);
260 s
= sTmp
+ wxT("style=\"") + wxBrushString(m_textBackgroundColour
);
261 s
+= wxT("stroke-width:1; ") + wxPenString(m_textBackgroundColour
);
262 sTmp
.Printf ( wxT("\" transform=\"rotate( %s %d %d ) \" />"), NumStr(-angle
), x
,y
);
263 s
+= sTmp
+ wxT("\n");
266 //now do the text itself
267 s
.Printf (wxT(" <text x=\"%d\" y=\"%d\" "),x
,y
);
269 sTmp
= m_font
.GetFaceName ();
270 if (sTmp
.Len () > 0) s
+= wxT("style=\"font-family:") + sTmp
+ wxT("; ");
271 else s
+= wxT("style=\" ");
273 wxString fontweights
[3] = { wxT("normal"), wxT("lighter"), wxT("bold") };
274 s
+= wxT("font-weight:") + fontweights
[m_font
.GetWeight() - wxNORMAL
] + wxT("; ");
276 wxString fontstyles
[5] = { wxT("normal"), wxT("style error"), wxT("style error"), wxT("italic"), wxT("oblique") };
277 s
+= wxT("font-style:") + fontstyles
[m_font
.GetStyle() - wxNORMAL
] + wxT("; ");
279 sTmp
.Printf (wxT("font-size:%dpt; "), m_font
.GetPointSize () );
281 //text will be solid, unless alpha value isn't opaque in the foreground colour
282 s
+= wxBrushString(m_textForegroundColour
) + wxPenString(m_textForegroundColour
);
283 sTmp
.Printf ( wxT("stroke-width:0;\" transform=\"rotate( %s %d %d ) \" >"), NumStr(-angle
), x
,y
);
284 s
+= sTmp
+ sText
+ wxT("</text> ") + wxT("\n");
291 void wxSVGFileDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
293 DoDrawRoundedRectangle(x
, y
, width
, height
, 0);
296 void wxSVGFileDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
299 if (m_graphics_changed
) NewGraphics ();
302 s
.Printf ( wxT(" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" rx=\"%s\" "),
303 x
, y
, width
, height
, NumStr(radius
) );
308 CalcBoundingBox(x
, y
);
309 CalcBoundingBox(x
+ width
, y
+ height
);
312 void wxSVGFileDCImpl::DoDrawPolygon(int n
, wxPoint points
[],
313 wxCoord xoffset
, wxCoord yoffset
,
314 wxPolygonFillMode fillStyle
)
316 if (m_graphics_changed
) NewGraphics ();
318 s
= wxT("<polygon style=\"");
319 if ( fillStyle
== wxODDEVEN_RULE
)
320 s
+= wxT("fill-rule:evenodd; ");
322 s
+= wxT("fill-rule:nonzero; ");
324 s
+= wxT("\" \npoints=\"");
326 for (int i
= 0; i
< n
; i
++)
328 sTmp
.Printf ( wxT("%d,%d"), points
[i
].x
+xoffset
, points
[i
].y
+yoffset
);
329 s
+= sTmp
+ wxT("\n");
330 CalcBoundingBox ( points
[i
].x
+xoffset
, points
[i
].y
+yoffset
);
332 s
+= wxT("\" /> \n");
336 void wxSVGFileDCImpl::DoDrawEllipse (wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
339 if (m_graphics_changed
) NewGraphics ();
345 s
.Printf ( wxT("<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" "), x
+rw
,y
+rh
, rw
, rh
);
350 CalcBoundingBox(x
, y
);
351 CalcBoundingBox(x
+ width
, y
+ height
);
354 void wxSVGFileDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord xc
, wxCoord yc
)
356 /* Draws an arc of a circle, centred on (xc, yc), with starting point
357 (x1, y1) and ending at (x2, y2). The current pen is used for the outline
358 and the current brush for filling the shape.
360 The arc is drawn in an anticlockwise direction from the start point to
363 Might be better described as Pie drawing */
365 if (m_graphics_changed
) NewGraphics ();
368 // we need the radius of the circle which has two estimates
369 double r1
= sqrt ( double( (x1
-xc
)*(x1
-xc
) ) + double( (y1
-yc
)*(y1
-yc
) ) );
370 double r2
= sqrt ( double( (x2
-xc
)*(x2
-xc
) ) + double( (y2
-yc
)*(y2
-yc
) ) );
372 wxASSERT_MSG( (fabs ( r2
-r1
) <= 3), wxT("wxSVGFileDC::DoDrawArc Error in getting radii of circle"));
373 if ( fabs ( r2
-r1
) > 3 ) //pixels
375 s
= wxT("<!--- wxSVGFileDC::DoDrawArc Error in getting radii of circle --> \n");
379 double theta1
= atan2((double)(yc
-y1
),(double)(x1
-xc
));
380 if ( theta1
< 0 ) theta1
= theta1
+ M_PI
* 2;
381 double theta2
= atan2((double)(yc
-y2
), (double)(x2
-xc
));
382 if ( theta2
< 0 ) theta2
= theta2
+ M_PI
* 2;
383 if ( theta2
< theta1
) theta2
= theta2
+ M_PI
*2;
385 int fArc
; // flag for large or small arc 0 means less than 180 degrees
386 if ( fabs(theta2
- theta1
) > M_PI
) fArc
= 1; else fArc
= 0;
388 int fSweep
= 0; // flag for sweep always 0
390 s
.Printf ( wxT("<path d=\"M%d %d A%s %s 0.0 %d %d %d %d L%d %d z "),
391 x1
,y1
, NumStr(r1
), NumStr(r2
), fArc
, fSweep
, x2
, y2
, xc
, yc
);
393 // the z means close the path and fill
394 s
+= wxT(" \" /> \n");
403 void wxSVGFileDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
406 Draws an arc of an ellipse. The current pen is used for drawing the arc
407 and the current brush is used for drawing the pie. This function is
408 currently only available for X window and PostScript device contexts.
410 x and y specify the x and y coordinates of the upper-left corner of the
411 rectangle that contains the ellipse.
413 width and height specify the width and height of the rectangle that
414 contains the ellipse.
416 start and end specify the start and end of the arc relative to the
417 three-o'clock position from the center of the rectangle. Angles are
418 specified in degrees (360 is a complete circle). Positive values mean
419 counter-clockwise motion. If start is equal to end, a complete ellipse
422 //known bug: SVG draws with the current pen along the radii, but this does not happen in wxMSW
424 if (m_graphics_changed
) NewGraphics ();
434 double xs
, ys
, xe
, ye
;
435 xs
= xc
+ rx
* cos (DegToRad(sa
));
436 xe
= xc
+ rx
* cos (DegToRad(ea
));
437 ys
= yc
- ry
* sin (DegToRad(sa
));
438 ye
= yc
- ry
* sin (DegToRad(ea
));
440 ///now same as circle arc...
442 double theta1
= atan2(ys
-yc
, xs
-xc
);
443 double theta2
= atan2(ye
-yc
, xe
-xc
);
445 int fArc
; // flag for large or small arc 0 means less than 180 degrees
446 if ( (theta2
- theta1
) > 0 ) fArc
= 1; else fArc
= 0;
449 if ( fabs(theta2
- theta1
) > M_PI
) fSweep
= 1; else fSweep
= 0;
451 s
.Printf ( wxT("<path d=\"M%d %d A%d %d 0.0 %d %d %d %d L %d %d z "),
452 int(xs
), int(ys
), int(rx
), int(ry
),
453 fArc
, fSweep
, int(xe
), int(ye
), int(xc
), int(yc
) );
455 s
+= wxT(" \" /> \n");
463 void wxSVGFileDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*w
, wxCoord
*h
, wxCoord
*descent
, wxCoord
*externalLeading
, const wxFont
*font
) const
468 sDC
.SetFont (m_font
);
469 if ( font
!= NULL
) sDC
.SetFont ( *font
);
470 sDC
.GetTextExtent(string
, w
, h
, descent
, externalLeading
);
473 wxCoord
wxSVGFileDCImpl::GetCharHeight() const
477 sDC
.SetFont (m_font
);
479 return ( sDC
.GetCharHeight() );
483 wxCoord
wxSVGFileDCImpl::GetCharWidth() const
486 sDC
.SetFont (m_font
);
488 return ( sDC
.GetCharWidth() );
493 // ----------------------------------------------------------
494 // wxSVGFileDCImpl - set functions
495 // ----------------------------------------------------------
497 void wxSVGFileDCImpl::SetBackground( const wxBrush
&brush
)
500 m_backgroundBrush
= brush
;
505 void wxSVGFileDCImpl::SetBackgroundMode( int mode
)
507 m_backgroundMode
= mode
;
512 void wxSVGFileDCImpl::SetBrush(const wxBrush
& brush
)
517 m_graphics_changed
= true;
521 void wxSVGFileDCImpl::SetPen(const wxPen
& pen
)
523 // width, color, ends, joins : currently implemented
524 // dashes, stipple : not implemented
527 m_graphics_changed
= true;
530 void wxSVGFileDCImpl::NewGraphics ()
532 wxString s
, sBrush
, sPenCap
, sPenJoin
, sPenStyle
, sLast
, sWarn
;
534 sBrush
= wxT("</g>\n<g style=\"") + wxBrushString ( m_brush
.GetColour (), m_brush
.GetStyle () )
535 + wxPenString(m_pen
.GetColour(), m_pen
.GetStyle());
537 switch ( m_pen
.GetCap () )
539 case wxCAP_PROJECTING
:
540 sPenCap
= wxT("stroke-linecap:square; ");
543 sPenCap
= wxT("stroke-linecap:butt; ");
547 sPenCap
= wxT("stroke-linecap:round; ");
549 switch ( m_pen
.GetJoin () )
552 sPenJoin
= wxT("stroke-linejoin:bevel; ");
555 sPenJoin
= wxT("stroke-linejoin:miter; ");
559 sPenJoin
= wxT("stroke-linejoin:round; ");
562 sLast
.Printf( wxT("stroke-width:%d\" \n transform=\"translate(%s %s) scale(%s %s)\">"),
563 m_pen
.GetWidth(), NumStr(m_logicalOriginX
), NumStr(m_logicalOriginY
), NumStr(m_scaleX
), NumStr(m_scaleY
) );
565 s
= sBrush
+ sPenCap
+ sPenJoin
+ sPenStyle
+ sLast
+ wxT("\n") + sWarn
;
567 m_graphics_changed
= false;
571 void wxSVGFileDCImpl::SetFont(const wxFont
& font
)
577 // export a bitmap as a raster image in png
578 bool wxSVGFileDCImpl::DoBlit(wxCoord xdest
, wxCoord ydest
, wxCoord width
, wxCoord height
,
579 wxDC
* source
, wxCoord xsrc
, wxCoord ysrc
,
580 wxRasterOperationMode logicalFunc
/*= wxCOPY*/, bool useMask
/*= false*/,
581 wxCoord
/*xsrcMask = -1*/, wxCoord
/*ysrcMask = -1*/)
583 if (logicalFunc
!= wxCOPY
)
585 wxASSERT_MSG(false, wxT("wxSVGFileDC::DoBlit Call requested nonCopy mode; this is not possible"));
588 if (useMask
!= false)
590 wxASSERT_MSG(false, wxT("wxSVGFileDC::DoBlit Call requested false mask; this is not possible"));
593 wxBitmap
myBitmap (width
, height
);
595 memDC
.SelectObject( myBitmap
);
596 memDC
.Blit(0, 0, width
, height
, source
, xsrc
, ysrc
);
597 memDC
.SelectObject( wxNullBitmap
);
598 DoDrawBitmap(myBitmap
, xdest
, ydest
);
602 void wxSVGFileDCImpl::DoDrawIcon(const class wxIcon
& myIcon
, wxCoord x
, wxCoord y
)
604 wxBitmap
myBitmap (myIcon
.GetWidth(), myIcon
.GetHeight() );
606 memDC
.SelectObject( myBitmap
);
607 memDC
.DrawIcon(myIcon
,0,0);
608 memDC
.SelectObject( wxNullBitmap
);
609 DoDrawBitmap(myBitmap
, x
, y
);
613 void wxSVGFileDCImpl::DoDrawBitmap(const class wxBitmap
& bmp
, wxCoord x
, wxCoord y
, bool WXUNUSED(bTransparent
) /*=0*/ )
615 if (m_graphics_changed
) NewGraphics ();
617 wxString sTmp
, s
, sPNG
;
618 if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG
) == NULL
)
619 wxImage::AddHandler(new wxPNGHandler
);
621 // create suitable file name
622 sTmp
.Printf ( wxT("_image%d.png"), m_sub_images
);
623 sPNG
= m_filename
.BeforeLast(wxT('.')) + sTmp
;
624 while (wxFile::Exists(sPNG
) )
627 sTmp
.Printf ( wxT("_image%d.png"), m_sub_images
);
628 sPNG
= m_filename
.BeforeLast(wxT('.')) + sTmp
;
631 //create copy of bitmap (wxGTK doesn't like saving a constant bitmap)
632 wxBitmap myBitmap
= bmp
;
634 bool bPNG_OK
= myBitmap
.SaveFile(sPNG
,wxBITMAP_TYPE_PNG
);
636 // reference the bitmap from the SVG doc
637 // only use filename & ext
638 sPNG
= sPNG
.AfterLast(wxFileName::GetPathSeparator());
640 // reference the bitmap from the SVG doc
641 int w
= myBitmap
.GetWidth();
642 int h
= myBitmap
.GetHeight();
643 sTmp
.Printf ( wxT(" <image x=\"%d\" y=\"%d\" width=\"%dpx\" height=\"%dpx\" "), x
,y
,w
,h
);
645 sTmp
.Printf ( wxT(" xlink:href=\"%s\"> \n"), sPNG
.c_str() );
646 s
+= sTmp
+ wxT("<title>Image from wxSVG</title> </image>") + wxT("\n");
652 m_OK
= m_outfile
->Ok () && bPNG_OK
;
657 void wxSVGFileDCImpl::write(const wxString
&s
)
659 const wxCharBuffer buf
= s
.utf8_str();
660 m_outfile
->Write(buf
, strlen((const char *)buf
));
661 m_OK
= m_outfile
->Ok();