kicad/common/dcsvg.cpp

1600 lines
44 KiB
C++
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////
// Name: svg.cpp
// Purpose: SVG plot
// Author: Chris Elliott
// Modified by: JP Charras (dec 2006)
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <dcsvg.h>
#include <wx/filename.h>
#include <wx/image.h>
#include <macros.h>
2012-04-19 20:07:36 +00:00
#include <common.h>
#if wxCHECK_VERSION( 2, 9, 0 )
// We could do nothing, because wxWidgets 3 supports the SVG format
// (previously, it was a contribution library, not included in wxWidgets)
// However arcs are drawn as pies, and we must change it.
// Unfortunately most of functions are private, and we cannot derive
// our KicadSVGFileDCImpl from wxSVGFileDCImpl
// and just override the 2 incorrect functions
// Just wxWidget dcsvg is copied here and 2 functions are modified:
// KicadSVGFileDCImpl::DoDrawArc() and KicadSVGFileDCImpl::DoDrawEllipticArc()
namespace
{
inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
// This function returns a string representation of a floating point number in
// C locale (i.e. always using "." for the decimal separator) and with the
// fixed precision (which is 2 for some unknown reason but this is what it was
// in this code originally).
inline wxString NumStr(double f)
{
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)
{
if ( c.Alpha() != wxALPHA_OPAQUE )
{
*opacity = c.Alpha()/255.;
// Remove the alpha before using GetAsString(wxC2S_HTML_SYNTAX) as it
// doesn't support colours with alpha channel.
c = wxColour(c.GetRGB());
}
else // No alpha.
{
*opacity = 1.;
}
return c.GetAsString(wxC2S_HTML_SYNTAX);
}
wxString wxPenString(wxColour c, int style = wxPENSTYLE_SOLID)
{
float opacity;
wxString s = wxT("stroke:") + Col2SVG(c, &opacity) + wxT("; ");
switch ( style )
{
case wxPENSTYLE_SOLID:
s += wxString::Format(wxT("stroke-opacity:%s; "), NumStr(opacity));
break;
case wxPENSTYLE_TRANSPARENT:
s += wxT("stroke-opacity:0.0; ");
break;
default :
wxASSERT_MSG(false, wxT("wxSVGFileDC::Requested Pen Style not available"));
}
return s;
}
wxString wxBrushString(wxColour c, int style = wxBRUSHSTYLE_SOLID)
{
float opacity;
wxString s = wxT("fill:") + Col2SVG(c, &opacity) + wxT("; ");
switch ( style )
{
case wxBRUSHSTYLE_SOLID:
s += wxString::Format(wxT("fill-opacity:%s; "), NumStr(opacity));
break;
case wxBRUSHSTYLE_TRANSPARENT:
s += wxT("fill-opacity:0.0; ");
break;
default :
wxASSERT_MSG(false, wxT("wxSVGFileDC::Requested Brush Style not available"));
}
return s;
}
} // anonymous namespace
// ----------------------------------------------------------
// KicadSVGFileDCImpl
// ----------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(KicadSVGFileDCImpl, wxDC)
KicadSVGFileDCImpl::KicadSVGFileDCImpl( KicadSVGFileDC *owner, const wxString &aFilename,
wxPoint aOrigin, wxSize aSize, double aDpi ) :
wxDCImpl( owner )
{
Init( aFilename, aOrigin, aSize, aDpi );
}
void KicadSVGFileDCImpl::Init ( const wxString &aFilename,
wxPoint aOrigin, wxSize aSize, double aDpi)
{
m_width = aSize.x;
m_height = aSize.y;
m_dpi = aDpi;
m_OK = true;
m_mm_to_pix_x = m_dpi/25.4;
m_mm_to_pix_y = m_dpi/25.4;
m_backgroundBrush = *wxTRANSPARENT_BRUSH;
m_textForegroundColour = *wxBLACK;
m_textBackgroundColour = *wxWHITE;
m_colour = wxColourDisplay();
m_pen = *wxBLACK_PEN;
m_font = *wxNORMAL_FONT;
m_brush = *wxWHITE_BRUSH;
m_graphics_changed = true;
////////////////////code here
m_outfile = new wxFileOutputStream(aFilename);
m_OK = m_outfile->IsOk();
if (m_OK)
{
m_filename = aFilename;
m_sub_images = 0;
wxString s;
s = wxT("<?xml version=\"1.0\" standalone=\"no\"?>") + wxString(wxT("\n"));
write(s);
s = wxT("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" ") + wxString(wxT("\n"));
write(s);
s = wxT("\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"> ") + wxString(wxT("\n"));
write(s);
s = wxT("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" ") + wxString(wxT("\n"));
write(s);
s.Printf( wxT(" width=\"%scm\" height=\"%scm\" viewBox=\"%d %d %d %d \"> \n"),
NumStr(float(m_width)/m_dpi*2.54), NumStr(float(m_height)/m_dpi*2.54),
aOrigin.x, aOrigin.y, m_width, m_height );
write(s);
s = wxT("<title>SVG Picture created as ") + wxFileName(m_filename).GetFullName() + wxT(" </title>") + wxT("\n");
write(s);
s = wxString (wxT("<desc>Picture generated by wxSVG ")) + wxSVGVersion + wxT(" </desc>")+ wxT("\n");
write(s);
s = wxT("<g style=\"fill:black; stroke:black; stroke-width:1\">") + wxString(wxT("\n"));
write(s);
}
}
KicadSVGFileDCImpl::~KicadSVGFileDCImpl()
{
wxString s = wxT("</g> \n</svg> \n");
write(s);
delete m_outfile;
}
void KicadSVGFileDCImpl::DoGetSizeMM( int *width, int *height ) const
{
if (width)
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
*width = KiROUND( (double)m_width / m_mm_to_pix_x );
if (height)
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
*height = KiROUND( (double)m_height / m_mm_to_pix_y );
}
wxSize KicadSVGFileDCImpl::GetPPI() const
{
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
return wxSize( KiROUND(m_dpi), KiROUND(m_dpi) );
}
void KicadSVGFileDCImpl::DoDrawLine (wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
{
if (m_graphics_changed) NewGraphics();
wxString s;
s.Printf ( wxT("<path d=\"M%d %d L%d %d\" /> \n"), x1,y1,x2,y2 );
if (m_OK)
{
write(s);
}
CalcBoundingBox(x1, y1);
CalcBoundingBox(x2, y2);
}
void KicadSVGFileDCImpl::DoDrawLines(int n, wxPoint points[], wxCoord xoffset , wxCoord yoffset )
{
for ( int i = 1; i < n; i++ )
{
DoDrawLine ( points [i-1].x + xoffset, points [i-1].y + yoffset,
points [ i ].x + xoffset, points [ i ].y + yoffset );
}
}
void KicadSVGFileDCImpl::DoDrawPoint (wxCoord x1, wxCoord y1)
{
wxString s;
if (m_graphics_changed) NewGraphics();
s = wxT("<g style = \"stroke-linecap:round;\" > ") + wxString(wxT("\n"));
write(s);
DoDrawLine ( x1,y1,x1,y1 );
s = wxT("</g>");
write(s);
}
void KicadSVGFileDCImpl::DoDrawCheckMark(wxCoord x1, wxCoord y1, wxCoord width, wxCoord height)
{
wxDCImpl::DoDrawCheckMark (x1,y1,width,height);
}
void KicadSVGFileDCImpl::DoDrawText(const wxString& text, wxCoord x1, wxCoord y1)
{
DoDrawRotatedText(text, x1,y1,0.0);
}
void KicadSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoord y, double angle)
{
//known bug; if the font is drawn in a scaled DC, it will not behave exactly as wxMSW
if (m_graphics_changed) NewGraphics();
wxString s, sTmp;
// calculate bounding box
wxCoord w, h, desc;
DoGetTextExtent(sText, &w, &h, &desc);
double rad = DegToRad(angle);
// wxT("upper left") and wxT("upper right")
CalcBoundingBox(x, y);
CalcBoundingBox((wxCoord)(x + w*cos(rad)), (wxCoord)(y - h*sin(rad)));
// wxT("bottom left") and wxT("bottom right")
x += (wxCoord)(h*sin(rad));
y += (wxCoord)(h*cos(rad));
CalcBoundingBox(x, y);
CalcBoundingBox((wxCoord)(x + h*sin(rad)), (wxCoord)(y + h*cos(rad)));
if (m_backgroundMode == wxBRUSHSTYLE_SOLID)
{
// draw background first
// just like DoDrawRectangle except we pass the text color to it and set the border to a 1 pixel wide text background
sTmp.Printf ( wxT(" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "), x,y+desc-h, w, h );
s = sTmp + wxT("style=\"") + wxBrushString(m_textBackgroundColour);
s += wxT("stroke-width:1; ") + wxPenString(m_textBackgroundColour);
sTmp.Printf ( wxT("\" transform=\"rotate( %s %d %d ) \" />"), NumStr(-angle), x,y );
s += sTmp + wxT("\n");
write(s);
}
//now do the text itself
s.Printf (wxT(" <text x=\"%d\" y=\"%d\" "),x,y );
sTmp = m_font.GetFaceName();
if (sTmp.Len() > 0) s += wxT("style=\"font-family:") + sTmp + wxT("; ");
else s += wxT("style=\" ");
wxString fontweights [3] = { wxT("normal"), wxT("lighter"), wxT("bold") };
s += wxT("font-weight:") + fontweights[m_font.GetWeight() - wxNORMAL] + wxT("; ");
wxString fontstyles [5] = { wxT("normal"), wxT("style error"), wxT("style error"), wxT("italic"), wxT("oblique") };
s += wxT("font-style:") + fontstyles[m_font.GetStyle() - wxNORMAL] + wxT("; ");
sTmp.Printf (wxT("font-size:%dpt; "), m_font.GetPointSize() );
s += sTmp;
//text will be solid, unless alpha value isn't opaque in the foreground colour
s += wxBrushString(m_textForegroundColour) + wxPenString(m_textForegroundColour);
sTmp.Printf ( wxT("stroke-width:0;\" transform=\"rotate( %s %d %d ) \" >"), NumStr(-angle), x,y );
s += sTmp + sText + wxT("</text> ") + wxT("\n");
if (m_OK)
{
write(s);
}
}
void KicadSVGFileDCImpl::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
{
DoDrawRoundedRectangle(x, y, width, height, 0);
}
void KicadSVGFileDCImpl::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius )
{
if (m_graphics_changed) NewGraphics();
wxString s;
s.Printf ( wxT(" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" rx=\"%s\" "),
x, y, width, height, NumStr(radius) );
s += wxT(" /> \n");
write(s);
CalcBoundingBox(x, y);
CalcBoundingBox(x + width, y + height);
}
void KicadSVGFileDCImpl::DoDrawPolygon(int n, wxPoint points[],
wxCoord xoffset, wxCoord yoffset,
wxPolygonFillMode fillStyle)
{
if (m_graphics_changed) NewGraphics();
wxString s, sTmp;
s = wxT("<polygon style=\"");
if ( fillStyle == wxODDEVEN_RULE )
s += wxT("fill-rule:evenodd; ");
else
s += wxT("fill-rule:nonzero; ");
s += wxT("\" \npoints=\"");
for (int i = 0; i < n; i++)
{
sTmp.Printf ( wxT("%d,%d"), points [i].x+xoffset, points[i].y+yoffset );
s += sTmp + wxT("\n");
CalcBoundingBox ( points [i].x+xoffset, points[i].y+yoffset);
}
s += wxT("\" /> \n");
write(s);
}
void KicadSVGFileDCImpl::DoDrawEllipse (wxCoord x, wxCoord y, wxCoord width, wxCoord height)
{
if (m_graphics_changed) NewGraphics();
int rh = height /2;
int rw = width /2;
wxString s;
s.Printf ( wxT("<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" "), x+rw,y+rh, rw, rh );
s += wxT(" /> \n");
write(s);
CalcBoundingBox(x, y);
CalcBoundingBox(x + width, y + height);
}
void KicadSVGFileDCImpl::DoDrawArc(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc)
{
/* Draws an arc of a circle, centred on (xc, yc), with starting point
(x1, y1) and ending at (x2, y2). The current pen is used for the outline
and the current brush for filling the shape.
The arc is drawn in an anticlockwise direction from the start point to
the end point
*/
if (m_graphics_changed) NewGraphics();
wxString s;
// we need the radius of the circle which has two estimates
double r1 = sqrt ( double( (x1-xc)*(x1-xc) ) + double( (y1-yc)*(y1-yc) ) );
double r2 = sqrt ( double( (x2-xc)*(x2-xc) ) + double( (y2-yc)*(y2-yc) ) );
wxASSERT_MSG( (fabs ( r2-r1 ) <= 3), wxT("wxSVGFileDC::DoDrawArc Error in getting radii of circle"));
if ( fabs ( r2-r1 ) > 3 ) //pixels
{
s = wxT("<!--- wxSVGFileDC::DoDrawArc Error in getting radii of circle --> \n");
write(s);
}
double theta1 = atan2((double)(yc-y1),(double)(x1-xc));
if ( theta1 < 0 ) theta1 = theta1 + M_PI * 2;
double theta2 = atan2((double)(yc-y2), (double)(x2-xc));
if ( theta2 < 0 ) theta2 = theta2 + M_PI * 2;
if ( theta2 < theta1 ) theta2 = theta2 + M_PI *2;
int fArc; // flag for large or small arc 0 means less than 180 degrees
if ( fabs(theta2 - theta1) > M_PI ) fArc = 1; else fArc = 0;
int fSweep = 0; // flag for sweep always 0
// Draw a pie:
// the z means close the path and fill
// s.Printf ( wxT("<path d=\"M%d %d A%s %s 0.0 %d %d %d %d L%d %d z "),
// x1,y1, NumStr(r1), NumStr(r2), fArc, fSweep, x2, y2, xc, yc );
// Draw a single arc:
s.Printf( wxT("<path d=\"M%d %d A%s %s 0.0 %d %d %d %d" ),
x1,y1, NumStr(r1), NumStr(r2), fArc, fSweep, x2, y2 );
s += wxT(" \" /> \n");
if (m_OK)
{
write(s);
}
}
void KicadSVGFileDCImpl::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
{
/*
Draws an arc of an ellipse. The current pen is used for drawing the arc
and the current brush is used for drawing the pie. This function is
currently only available for X window and PostScript device contexts.
x and y specify the x and y coordinates of the upper-left corner of the
rectangle that contains the ellipse.
width and height specify the width and height of the rectangle that
contains the ellipse.
start and end specify the start and end of the arc relative to the
three-o'clock position from the center of the rectangle. Angles are
specified in degrees (360 is a complete circle). Positive values mean
counter-clockwise motion. If start is equal to end, a complete ellipse
will be drawn. */
//known bug: SVG draws with the current pen along the radii, but this does not happen in wxMSW
if (m_graphics_changed) NewGraphics();
wxString s;
//radius
double rx = w / 2;
double ry = h / 2;
// center
double xc = x + rx;
double yc = y + ry;
double xs, ys, xe, ye;
xs = xc + rx * cos (DegToRad(sa));
xe = xc + rx * cos (DegToRad(ea));
ys = yc - ry * sin (DegToRad(sa));
ye = yc - ry * sin (DegToRad(ea));
///now same as circle arc...
double theta1 = atan2(ys-yc, xs-xc);
double theta2 = atan2(ye-yc, xe-xc);
int fArc; // flag for large or small arc 0 means less than 180 degrees
if ( (theta2 - theta1) > 0 ) fArc = 1; else fArc = 0;
int fSweep;
if ( fabs(theta2 - theta1) > M_PI) fSweep = 1; else fSweep = 0;
// Draw a pie:
// s.Printf ( wxT("<path d=\"M%d %d A%d %d 0.0 %d %d %d %d L %d %d z "),
// int(xs), int(ys), int(rx), int(ry),
// fArc, fSweep, int(xe), int(ye), int(xc), int(yc) );
// Draw an arc:
s.Printf ( wxT("<path d=\"M%d %d A%d %d 0.0 %d %d %d %d"),
int(xs), int(ys), int(rx), int(ry),
fArc, fSweep, int(xe), int(ye) );
s += wxT(" \" /> \n");
if (m_OK)
{
write(s);
}
}
void KicadSVGFileDCImpl::DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, wxCoord *descent , wxCoord *externalLeading , const wxFont *font) const
{
wxScreenDC sDC;
sDC.SetFont (m_font);
if ( font != NULL ) sDC.SetFont ( *font );
sDC.GetTextExtent(string, w, h, descent, externalLeading );
}
wxCoord KicadSVGFileDCImpl::GetCharHeight() const
{
wxScreenDC sDC;
sDC.SetFont (m_font);
return sDC.GetCharHeight();
}
wxCoord KicadSVGFileDCImpl::GetCharWidth() const
{
wxScreenDC sDC;
sDC.SetFont (m_font);
return sDC.GetCharWidth();
}
// ----------------------------------------------------------
// wxSVGFileDCImpl - set functions
// ----------------------------------------------------------
void KicadSVGFileDCImpl::SetBackground( const wxBrush &brush )
{
m_backgroundBrush = brush;
}
void KicadSVGFileDCImpl::SetBackgroundMode( int mode )
{
m_backgroundMode = mode;
}
void KicadSVGFileDCImpl::SetBrush(const wxBrush& brush)
{
m_brush = brush;
m_graphics_changed = true;
}
void KicadSVGFileDCImpl::SetPen(const wxPen& pen)
{
// width, color, ends, joins : currently implemented
// dashes, stipple : not implemented
m_pen = pen;
m_graphics_changed = true;
}
void KicadSVGFileDCImpl::NewGraphics()
{
wxString s, sBrush, sPenCap, sPenJoin, sPenStyle, sLast, sWarn;
sBrush = wxT("</g>\n<g style=\"") + wxBrushString ( m_brush.GetColour(), m_brush.GetStyle() )
+ wxPenString(m_pen.GetColour(), m_pen.GetStyle());
switch ( m_pen.GetCap() )
{
case wxCAP_PROJECTING :
sPenCap = wxT("stroke-linecap:square; ");
break;
case wxCAP_BUTT :
sPenCap = wxT("stroke-linecap:butt; ");
break;
case wxCAP_ROUND :
default :
sPenCap = wxT("stroke-linecap:round; ");
}
switch ( m_pen.GetJoin() )
{
case wxJOIN_BEVEL :
sPenJoin = wxT("stroke-linejoin:bevel; ");
break;
case wxJOIN_MITER :
sPenJoin = wxT("stroke-linejoin:miter; ");
break;
case wxJOIN_ROUND :
default :
sPenJoin = wxT("stroke-linejoin:round; ");
}
sLast.Printf( wxT("stroke-width:%d\" \n transform=\"translate(%s %s) scale(%s %s)\">"),
m_pen.GetWidth(), NumStr(m_logicalOriginX), NumStr(m_logicalOriginY), NumStr(m_scaleX), NumStr(m_scaleY) );
s = sBrush + sPenCap + sPenJoin + sPenStyle + sLast + wxT("\n") + sWarn;
write(s);
m_graphics_changed = false;
}
void KicadSVGFileDCImpl::SetFont(const wxFont& font)
{
m_font = font;
}
// export a bitmap as a raster image in png
bool KicadSVGFileDCImpl::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*/)
{
if (logicalFunc != wxCOPY)
{
wxASSERT_MSG(false, wxT("wxSVGFileDC::DoBlit Call requested nonCopy mode; this is not possible"));
return false;
}
if (useMask != false)
{
wxASSERT_MSG(false, wxT("wxSVGFileDC::DoBlit Call requested false mask; this is not possible"));
return false;
}
wxBitmap myBitmap (width, height);
wxMemoryDC memDC;
memDC.SelectObject( myBitmap );
memDC.Blit(0, 0, width, height, source, xsrc, ysrc);
memDC.SelectObject( wxNullBitmap );
DoDrawBitmap(myBitmap, xdest, ydest);
return false;
}
void KicadSVGFileDCImpl::DoDrawIcon(const class wxIcon & myIcon, wxCoord x, wxCoord y)
{
wxBitmap myBitmap (myIcon.GetWidth(), myIcon.GetHeight() );
wxMemoryDC memDC;
memDC.SelectObject( myBitmap );
memDC.DrawIcon(myIcon,0,0);
memDC.SelectObject( wxNullBitmap );
DoDrawBitmap(myBitmap, x, y);
}
void KicadSVGFileDCImpl::DoDrawBitmap(const class wxBitmap & bmp, wxCoord x, wxCoord y , bool WXUNUSED(bTransparent) /*=0*/ )
{
if (m_graphics_changed) NewGraphics();
wxString sTmp, s, sPNG;
if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL )
wxImage::AddHandler(new wxPNGHandler);
// create suitable file name
sTmp.Printf ( wxT("_image%d.png"), m_sub_images);
sPNG = m_filename.BeforeLast(wxT('.')) + sTmp;
while (wxFile::Exists(sPNG) )
{
m_sub_images ++;
sTmp.Printf ( wxT("_image%d.png"), m_sub_images);
sPNG = m_filename.BeforeLast(wxT('.')) + sTmp;
}
//create copy of bitmap (wxGTK doesn't like saving a constant bitmap)
wxBitmap myBitmap = bmp;
//save it
bool bPNG_OK = myBitmap.SaveFile(sPNG,wxBITMAP_TYPE_PNG);
// reference the bitmap from the SVG doc
// only use filename & ext
sPNG = sPNG.AfterLast(wxFileName::GetPathSeparator());
// reference the bitmap from the SVG doc
int w = myBitmap.GetWidth();
int h = myBitmap.GetHeight();
sTmp.Printf ( wxT(" <image x=\"%d\" y=\"%d\" width=\"%dpx\" height=\"%dpx\" "), x,y,w,h );
s += sTmp;
sTmp.Printf ( wxT(" xlink:href=\"%s\"> \n"), sPNG.c_str() );
s += sTmp + wxT("<title>Image from wxSVG</title> </image>") + wxT("\n");
if (m_OK && bPNG_OK)
{
write(s);
}
m_OK = m_outfile->IsOk() && bPNG_OK;
}
void KicadSVGFileDCImpl::write(const wxString &s)
{
const wxCharBuffer buf = s.utf8_str();
m_outfile->Write(buf, strlen((const char *)buf));
m_OK = m_outfile->IsOk();
}
#else
#define newline wxString( wxT( "\n" ) )
#define space wxString( wxT( " " ) )
#define semicolon wxString( wxT( ";" ) )
#ifdef __BORLANDC__
#pragma warn -rch
#pragma warn -ccc
#endif
/* some define not included in early wxWidget versions: */
#ifndef twips2mm
#define twips2mm 0.0176388888889
#endif
#ifndef pt2mm
#define pt2mm 0.352777777778
#endif
static inline double DegToRad( double deg )
{
return (deg * M_PI) / 180.0;
2010-10-04 12:58:07 +00:00
}
wxString wxColStr( wxColour c )
{
unsigned char r, g, b;
r = c.Red();
g = c.Green();
b = c.Blue();
// possible Unicode bug here
wxString s = wxDecToHex( r ) + wxDecToHex( g ) + wxDecToHex( b );
return s;
}
wxString wxBrushString( wxColour c, int style )
{
wxString s = wxT( "fill:#" ) + wxColStr( c ) + semicolon + space;
switch( style )
{
case wxSOLID:
s = s + wxT( "fill-opacity:1.0; " );
break;
case wxTRANSPARENT:
s = s + wxT( "fill-opacity:0.0; " );
break;
default:
wxASSERT_MSG( false, wxT( "wxSVGFileDC::Requested Brush Style not available" ) );
}
s = s + newline;
return s;
}
/***********************************************************************/
void wxSVGFileDC::Init( const wxString& aFilename,
wxPoint aOrigin, wxSize aSize, double aDpi )
/***********************************************************************/
/* set up things first wxDCBase does all this?
*/
{
m_width = aSize.x;
m_height = aSize.y;
m_clipping = false;
m_OK = true;
m_mm_to_pix_x = aDpi / 25.4;
m_mm_to_pix_y = aDpi / 25.4;
m_signX = m_signY = 1;
m_userScaleX = m_userScaleY =
m_deviceOriginX = m_deviceOriginY = 0;
m_OriginX = m_OriginY = 0;
m_logicalOriginX = m_logicalOriginY = 0;
m_logicalScaleX = m_logicalScaleY = 0;
m_scaleX = m_scaleY = 1.0;
m_logicalFunction = wxCOPY;
m_backgroundMode = wxTRANSPARENT;
m_mappingMode = wxMM_TEXT;
m_backgroundBrush = *wxTRANSPARENT_BRUSH;
m_textForegroundColour = *wxBLACK;
m_textBackgroundColour = *wxWHITE;
m_colour = wxColourDisplay();
m_pen = *wxBLACK_PEN;
m_font = *wxNORMAL_FONT;
m_brush = *wxWHITE_BRUSH;
m_graphics_changed = true;
////////////////////code here
m_outfile = new wxFileOutputStream( aFilename );
m_OK = m_outfile->Ok();
if( m_OK )
{
m_filename = aFilename;
m_sub_images = 0;
wxString s;
s = wxT( "<?xml version=\"1.0\" standalone=\"no\"?>" ); s = s + newline;
write( s );
s = wxT( "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" " ) + newline;
write( s );
s = wxT( "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> " ) + newline;
write( s );
s.Printf( wxT( "<svg\n" ) );
write( s );
s.Printf( wxT( " xmlns=\"http://www.w3.org/2000/svg\"\n" ) );
write( s );
s.Printf( wxT( " version=\"1.1\"\n" ) );
write( s );
s.Printf( wxT( " width=\"%gin\" height=\"%gin\" viewBox=\"%d %d %d %d \"\n" ),
double (m_width) / aDpi, double (m_height) / aDpi,
aOrigin.x, aOrigin.y, aSize.x, aSize.y );
write( s );
s.Printf( wxT( ">\n" ) );
write( s );
s = wxT( " <title>SVG Picture created as " ) + wxFileNameFromPath( aFilename ) +
wxT( " </title>" ) + newline;
write( s );
s = wxString( wxT( " <desc>Picture generated by wxSVG " ) ) + wxSVGVersion + wxT(
" </desc>" ) + newline;
write( s );
s = wxT( " <g style=\"fill:black; stroke:black; stroke-width:1\">" ) + newline;
write( s );
}
}
// constructor
wxSVGFileDC::wxSVGFileDC( const wxString &aFilename,
wxPoint aOrigin, wxSize aSize, double aDpi )
{
Init( aFilename, aOrigin, aSize, aDpi);
2010-10-04 12:58:07 +00:00
}
wxSVGFileDC::~wxSVGFileDC()
{
wxString s = wxT( "</g> \n</svg> \n" );
write( s );
delete m_outfile;
}
//////////////////////////////////////////////////////////////////////////////////////////
void wxSVGFileDC::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 )
{
if( m_graphics_changed )
NewGraphics();
wxString s;
s.Printf( wxT( "<path d=\"M%d %d L%d %d\" /> \n" ), x1, y1, x2, y2 );
if( m_OK )
{
write( s );
}
CalcBoundingBox( x1, y1 );
CalcBoundingBox( x2, y2 );
return;
2010-10-04 12:58:07 +00:00
}
void wxSVGFileDC::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset )
{
for( int i = 1; i < n; i++ )
{
DoDrawLine( points[i - 1].x + xoffset, points[i - 1].y + yoffset,
points[ i ].x + xoffset, points[ i ].y + yoffset );
}
}
void wxSVGFileDC::DoDrawPoint( wxCoord x1, wxCoord y1 )
{
wxString s;
if( m_graphics_changed )
NewGraphics();
s = wxT( "<g style = \"stroke-linecap:round;\" > " ) + newline;
write( s );
DrawLine( x1, y1, x1, y1 );
s = wxT( "</g>" );
write( s );
}
void wxSVGFileDC::DoDrawCheckMark( wxCoord x1, wxCoord y1, wxCoord width, wxCoord height )
{
wxDCBase::DoDrawCheckMark( x1, y1, width, height );
}
void wxSVGFileDC::DoDrawText( const wxString& text, wxCoord x1, wxCoord y1 )
{
DoDrawRotatedText( text, x1, y1, 0.0 );
}
void wxSVGFileDC::DoDrawRotatedText( const wxString& sText, wxCoord x, wxCoord y, double angle )
{
//known bug; if the font is drawn in a scaled DC, it will not behave exactly as wxMSW
if( m_graphics_changed )
NewGraphics();
wxString s, sTmp;
// calculate bounding box
wxCoord w, h, desc;
DoGetTextExtent( sText, &w, &h, &desc );
double rad = DegToRad( angle );
// wxT("upper left") and wxT("upper right")
CalcBoundingBox( x, y );
CalcBoundingBox( (wxCoord) ( x + w * cos( rad ) ), (wxCoord) ( y - h * sin( rad ) ) );
// wxT("bottom left") and wxT("bottom right")
x += (wxCoord) ( h * sin( rad ) );
y += (wxCoord) ( h * cos( rad ) );
CalcBoundingBox( x, y );
CalcBoundingBox( (wxCoord) ( x + h * sin( rad ) ), (wxCoord) ( y + h * cos( rad ) ) );
if( m_backgroundMode == wxSOLID )
{
// draw background first
// just like DoDrawRectangle except we pass the text color to it and set the border to a 1 pixel wide text background
sTmp.Printf( wxT(
" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" " ), x, y + desc -
h, w, h );
s = sTmp + wxT( "style=\"fill:#" ) + wxColStr( m_textBackgroundColour ) + wxT( "; " );
s = s + wxT( "stroke-width:1; stroke:#" ) + wxColStr( m_textBackgroundColour ) + wxT( "; " );
sTmp.Printf( wxT( "\" transform=\"rotate( %.2g %d %d ) \">" ), -angle, x, y );
s = s + sTmp + newline;
write( s );
}
//now do the text itself
s.Printf( wxT( " <text x=\"%d\" y=\"%d\" " ), x, y );
sTmp = m_font.GetFaceName();
if( sTmp.Len() > 0 )
s = s + wxT( "style=\"font-family:" ) + sTmp + wxT( "; " );
else
s = s + wxT( "style=\" " );
wxString fontweights[3] = { wxT( "normal" ), wxT( "lighter" ), wxT( "bold" ) };
s = s + wxT( "font-weight:" ) + fontweights[m_font.GetWeight() - wxNORMAL] + semicolon + space;
wxString fontstyles[5] = {
wxT( "normal" ), wxT( "style error" ), wxT( "style error" ), wxT(
"italic" ), wxT( "oblique" )
};
s = s + wxT( "font-style:" ) + fontstyles[m_font.GetStyle() - wxNORMAL] + semicolon + space;
sTmp.Printf( wxT( "font-size:%dpt; fill:#" ), m_font.GetPointSize() );
s = s + sTmp;
s = s + wxColStr( m_textForegroundColour ) + wxT( "; stroke:#" ) + wxColStr(
m_textForegroundColour ) + wxT( "; " );
sTmp.Printf( wxT( "stroke-width:0;\" transform=\"rotate( %.2g %d %d ) \" >" ), -angle, x, y );
s = s + sTmp + sText + wxT( "</text> " ) + newline;
if( m_OK )
{
write( s );
}
}
void wxSVGFileDC::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
{
DoDrawRoundedRectangle( x, y, width, height, 0 );
}
void wxSVGFileDC::DoDrawRoundedRectangle( wxCoord x,
wxCoord y,
wxCoord width,
wxCoord height,
double radius )
{
if( m_graphics_changed )
NewGraphics();
wxString s;
s.Printf( wxT( " <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" rx=\"%.2g\" " ),
x, y, width, height, radius );
s = s + wxT( " /> " ) + newline;
write( s );
CalcBoundingBox( x, y );
CalcBoundingBox( x + width, y + height );
}
void wxSVGFileDC::DoDrawPolygon( int n,
wxPoint points[],
wxCoord xoffset,
wxCoord yoffset,
int fillStyle )
{
if( m_graphics_changed )
NewGraphics();
wxString s, sTmp;
s = wxT( "<polygon style=\"" );
if( fillStyle == wxODDEVEN_RULE )
s = s + wxT( "fill-rule:evenodd; " );
else
s = s + wxT( "fill-rule:nonzero; " );
s = s + wxT( "\" \npoints=\"" );
for( int i = 0; i < n; i++ )
{
sTmp.Printf( wxT( "%d,%d" ), points[i].x + xoffset, points[i].y + yoffset );
s = s + sTmp + newline;
CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset );
}
s = s + wxT( "\" /> " );
s = s + newline;
write( s );
}
void wxSVGFileDC::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
{
if( m_graphics_changed )
NewGraphics();
int rh = height / 2;
int rw = width / 2;
wxString s;
s.Printf( wxT( "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" " ), x + rw, y + rh, rw, rh );
s = s + wxT( " /> " ) + newline;
write( s );
CalcBoundingBox( x, y );
CalcBoundingBox( x + width, y + height );
}
void wxSVGFileDC::DoDrawArc( wxCoord x1,
wxCoord y1,
wxCoord x2,
wxCoord y2,
wxCoord xc,
wxCoord yc )
{
/* Draws an arc of a circle, centred on (xc, yc), with starting point
* (x1, y1) and ending at (x2, y2). The current pen is used for the outline
* and the current brush for filling the shape.
*
* The arc is drawn in an anticlockwise direction from the start point to
* the end point. */
if( m_graphics_changed )
NewGraphics();
wxString s;
// we need the radius of the circle which has two estimates
double r1 = sqrt( double ( (x1 - xc) * (x1 - xc) ) + double ( (y1 - yc) * (y1 - yc) ) );
double r2 = sqrt( double ( (x2 - xc) * (x2 - xc) ) + double ( (y2 - yc) * (y2 - yc) ) );
wxASSERT_MSG( (fabs( r2 - r1 ) <= 3),
wxT( "wxSVGFileDC::DoDrawArc Error in getting radii of circle" ) );
if( fabs( r2 - r1 ) > 3 ) //pixels
{
s = wxT( "<!--- wxSVGFileDC::DoDrawArc Error in getting radii of circle --> \n" );
write( s );
}
double theta1 = atan2( (double) (yc - y1), (double) (x1 - xc) );
if( theta1 < 0 )
theta1 = theta1 + M_PI * 2;
double theta2 = atan2( (double) (yc - y2), (double) (x2 - xc) );
if( theta2 < 0 )
theta2 = theta2 + M_PI * 2;
if( theta2 < theta1 )
theta2 = theta2 + M_PI * 2;
int fArc; // flag for large or small arc 0 means less than 180 degrees
if( fabs( theta2 - theta1 ) > M_PI )
fArc = 1;else
fArc = 0;
int fSweep;
if( (theta2 - theta1) > 0 )
fSweep = 0;else
fSweep = 1;
float Axis_rotation = 0.0;
// Draw arc as pie:
// s.Printf ( wxT("<path d=\"M%d %d A%.2g %.2g 0.0 %d %d %d %d L%d %d z "),
// the z means close the path and fill (usefull to draw a pie)
// x1, y1, r1, r2, fArc, fSweep, x2, y2, xc ,yc );
// Draw a single arc:
s.Printf( wxT( "<path d=\"M%d %d A%.2g %.2g %g %d %d %d %d" ),
x1, y1, r1, r2,
Axis_rotation,
fArc, fSweep, x2, y2 );
s = s + wxT( " \" /> " ) + newline;
if( m_OK )
{
write( s );
}
}
void wxSVGFileDC::DoDrawEllipticArc( wxCoord x,
wxCoord y,
wxCoord w,
wxCoord h,
double sa,
double ea )
{
/*
* Draws an arc of an ellipse. The current pen is used for drawing the arc
* and the current brush is used for drawing the pie. This function is
* currently only available for X window and PostScript device contexts.
*
* x and y specify the x and y coordinates of the upper-left corner of the
* rectangle that contains the ellipse.
*
* width and height specify the width and height of the rectangle that
* contains the ellipse.
*
* start and end specify the start and end of the arc relative to the
* three-o'clock position from the center of the rectangle. Angles are
* specified in degrees (360 is a complete circle). Positive values mean
* counter-clockwise motion. If start is equal to end, a complete ellipse
* will be drawn. */
//known bug: SVG draws with the current pen along the radii, but this does not happen in wxMSW
if( m_graphics_changed )
NewGraphics();
wxString s;
//radius
double rx = w / 2;
double ry = h / 2;
// center
double xc = x + rx;
double yc = y + ry;
double xs, ys, xe, ye;
xs = xc + rx* cos( DegToRad (sa) );
xe = xc + rx* cos( DegToRad (ea) );
ys = yc - ry* sin( DegToRad (sa) );
ye = yc - ry* sin( DegToRad (ea) );
///now same as circle arc...
double theta1 = atan2( ys - yc, xs - xc );
double theta2 = atan2( ye - yc, xe - xc );
int fArc; // flag for large or small arc 0 means less than 180 degrees
if( fabs( theta2 - theta1 ) > M_PI )
fArc = 1;else
fArc = 0;
int fSweep;
if( (theta2 - theta1) > 0 )
fSweep = 0;else
fSweep = 1;
float Axis_rotation = 0.0;
// Draw a single arc:
s.Printf( wxT( "<path d=\"M%d,%d A%d,%d %g %d %d %d,%d" ),
int (xs), int (ys),
int (rx), int (ry),
Axis_rotation,
fArc, fSweep, int (xe), int (ye) );
s = s + wxT( " \" /> " ) + newline;
if( m_OK )
{
write( s );
}
}
void wxSVGFileDC::DoGetTextExtent( const wxString& string,
wxCoord* w,
wxCoord* h,
wxCoord* descent,
wxCoord* externalLeading,
wxFont* font ) const
{
wxScreenDC sDC;
sDC.SetFont( m_font );
if( font != NULL )
sDC.SetFont( *font );
sDC.GetTextExtent( string, w, h, descent, externalLeading );
}
wxCoord wxSVGFileDC::GetCharHeight() const
{
wxScreenDC sDC;
sDC.SetFont( m_font );
return sDC.GetCharHeight();
}
wxCoord wxSVGFileDC::GetCharWidth() const
{
wxScreenDC sDC;
sDC.SetFont( m_font );
return sDC.GetCharWidth();
}
/// Set Functions /////////////////////////////////////////////////////////////////
void wxSVGFileDC::SetBackground( const wxBrush& brush )
{
m_backgroundBrush = brush;
return;
}
void wxSVGFileDC::SetBackgroundMode( int mode )
{
m_backgroundMode = mode;
return;
}
void wxSVGFileDC::SetBrush( const wxBrush& brush )
{
m_brush = brush;
m_graphics_changed = true;
}
void wxSVGFileDC::SetPen( const wxPen& pen )
{
// width, color, ends, joins : currently implemented
// dashes, stipple : not implemented
m_pen = pen;
m_graphics_changed = true;
}
void wxSVGFileDC::NewGraphics()
{
int w = m_pen.GetWidth();
wxColour c = m_pen.GetColour();
wxString s, sBrush, sPenCap, sPenJoin, sPenStyle, sLast, sWarn;
sBrush = wxT( "</g>\n<g style=\"" ) + wxBrushString( m_brush.GetColour(), m_brush.GetStyle() )
+ wxT( " stroke:#" ) + wxColStr( c ) + wxT( "; " );
switch( m_pen.GetCap() )
{
case wxCAP_PROJECTING:
sPenCap = wxT( "stroke-linecap:square; " );
break;
case wxCAP_BUTT:
sPenCap = wxT( "stroke-linecap:butt; " );
break;
case wxCAP_ROUND:
default:
sPenCap = wxT( "stroke-linecap:round; " );
}
;
switch( m_pen.GetJoin() )
{
case wxJOIN_BEVEL:
sPenJoin = wxT( "stroke-linejoin:bevel; " );
break;
case wxJOIN_MITER:
sPenJoin = wxT( "stroke-linejoin:miter; " );
break;
case wxJOIN_ROUND:
default:
sPenJoin = wxT( "stroke-linejoin:round; " );
}
;
switch( m_pen.GetStyle() )
{
case wxSOLID:
sPenStyle = wxT( "stroke-opacity:1.0; stroke-opacity:1.0; " );
break;
case wxTRANSPARENT:
sPenStyle = wxT( "stroke-opacity:0.0; stroke-opacity:0.0; " );
break;
default:
wxASSERT_MSG( false,
wxT( "wxSVGFileDC::SetPen Call called to set a Style which is not available" )
);
sWarn = sWarn + wxT(
"<!--- wxSVGFileDC::SetPen Call called to set a Style which is not available --> \n" );
}
sLast.Printf( wxT(
"stroke-width:%d\" \n transform=\"translate(%.2g %.2g) scale(%.2g %.2g)\">" ),
w, m_OriginX, m_OriginY, m_scaleX, m_scaleY );
s = sBrush + sPenCap + sPenJoin + sPenStyle + sLast + newline + sWarn;
write( s );
m_graphics_changed = false;
}
void wxSVGFileDC::SetFont( const wxFont& font )
{
m_font = font;
}
void wxSVGFileDC::ComputeScaleAndOrigin()
{
m_scaleX = m_logicalScaleX * m_userScaleX;
m_scaleY = m_logicalScaleY * m_userScaleY;
m_OriginX = m_logicalOriginX * m_logicalScaleX + m_deviceOriginX;
m_OriginY = m_logicalOriginY * m_logicalScaleY + m_deviceOriginY;
m_graphics_changed = true;
}
int wxSVGFileDC::GetMapMode()
{
return m_mappingMode;
}
void wxSVGFileDC::SetMapMode( int mode )
{
switch( mode )
{
case wxMM_TWIPS:
SetLogicalScale( twips2mm * m_mm_to_pix_x, twips2mm * m_mm_to_pix_y );
break;
case wxMM_POINTS:
SetLogicalScale( pt2mm * m_mm_to_pix_x, pt2mm * m_mm_to_pix_y );
break;
case wxMM_METRIC:
SetLogicalScale( m_mm_to_pix_x, m_mm_to_pix_y );
break;
case wxMM_LOMETRIC:
SetLogicalScale( m_mm_to_pix_x / 10.0, m_mm_to_pix_y / 10.0 );
break;
default:
case wxMM_TEXT:
SetLogicalScale( 1.0, 1.0 );
break;
}
m_mappingMode = mode;
/* we don't do this mega optimisation
* if (mode != wxMM_TEXT)
* {
* m_needComputeScaleX = true;
* m_needComputeScaleY = true;
* }
*/
}
void wxSVGFileDC::GetUserScale( double* x, double* y ) const
{
*x = m_userScaleX;
*y = m_userScaleY;
}
void wxSVGFileDC::SetUserScale( double x, double y )
{
// allow negative ? -> no
m_userScaleX = x;
m_userScaleY = y;
ComputeScaleAndOrigin();
}
void wxSVGFileDC::SetLogicalScale( double x, double y )
{
// allow negative ?
m_logicalScaleX = x;
m_logicalScaleY = y;
ComputeScaleAndOrigin();
}
void wxSVGFileDC::SetLogicalOrigin( wxCoord x, wxCoord y )
{
// is this still correct ?
m_logicalOriginX = x * m_signX;
m_logicalOriginY = y * m_signY;
ComputeScaleAndOrigin();
}
void wxSVGFileDC::SetDeviceOrigin( wxCoord x, wxCoord y )
{
// only wxPostScripDC has m_signX = -1,
m_deviceOriginX = x;
m_deviceOriginY = y;
ComputeScaleAndOrigin();
}
void wxSVGFileDC::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
{
// only wxPostScripDC has m_signX = -1,
m_signX = (xLeftRight ? 1 : -1);
m_signY = (yBottomUp ? -1 : 1);
ComputeScaleAndOrigin();
}
// export a bitmap as a raster image in png
bool wxSVGFileDC::DoBlit( wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height,
wxDC* source, wxCoord xsrc, wxCoord ysrc,
int logicalFunc /*= wxCOPY*/, bool useMask /*= false*/,
wxCoord /*xsrcMask = -1*/, wxCoord /*ysrcMask = -1*/ )
{
if( logicalFunc != wxCOPY )
{
wxASSERT_MSG( false,
wxT( "wxSVGFileDC::DoBlit Call requested nonCopy mode; this is not possible" )
);
return false;
}
if( useMask != false )
{
wxASSERT_MSG( false,
wxT( "wxSVGFileDC::DoBlit Call requested False mask ; this is not possible" )
);
return false;
}
wxBitmap myBitmap( width, height );
wxMemoryDC memDC;
memDC.SelectObject( myBitmap );
memDC.Blit( 0, 0, width, height, source, xsrc, ysrc );
memDC.SelectObject( wxNullBitmap );
DoDrawBitmap( myBitmap, xdest, ydest );
return false;
}
void wxSVGFileDC::DoDrawIcon( const class wxIcon& myIcon, wxCoord x, wxCoord y )
{
wxBitmap myBitmap( myIcon.GetWidth(), myIcon.GetHeight() );
wxMemoryDC memDC;
memDC.SelectObject( myBitmap );
memDC.DrawIcon( myIcon, 0, 0 );
memDC.SelectObject( wxNullBitmap );
DoDrawBitmap( myBitmap, x, y );
return;
}
void wxSVGFileDC::DoDrawBitmap( const class wxBitmap& bmp,
wxCoord x,
wxCoord y,
bool bTransparent /*=0*/ )
{
if( m_graphics_changed )
NewGraphics();
wxString sTmp, s, sPNG;
wxImage::AddHandler( new wxPNGHandler );
// create suitable file name
sTmp.Printf( wxT( "_image%d.png" ), m_sub_images );
sPNG = m_filename.BeforeLast( wxT( '.' ) ) + sTmp;
while( wxFile::Exists( sPNG ) )
{
m_sub_images++;
sTmp.Printf( wxT( "_image%d.png" ), m_sub_images );
sPNG = m_filename.BeforeLast( wxT( '.' ) ) + sTmp;
}
//create copy of bitmap (wxGTK doesn't like saving a constant bitmap)
wxBitmap myBitmap = bmp;
//save it
bool bPNG_OK = myBitmap.SaveFile( sPNG, wxBITMAP_TYPE_PNG );
// refrence the bitmap from the SVG doc
int w = myBitmap.GetWidth();
int h = myBitmap.GetHeight();
sTmp.Printf( wxT( " <image x=\"%d\" y=\"%d\" width=\"%dpx\" height=\"%dpx\" " ), x, y, w, h );
s = s + sTmp;
sTmp.Printf( wxT( " xlink:href=\"%s\"> \n" ), GetChars( sPNG ) );
s = s + sTmp + wxT( "<title>Image from wxSVG</title> </image>" ) + newline;
if( m_OK && bPNG_OK )
{
write( s );
}
m_OK = m_outfile->Ok() && bPNG_OK;
return;
}
// ---------------------------------------------------------------------------
// coordinates transformations
// ---------------------------------------------------------------------------
wxCoord wxSVGFileDC::DeviceToLogicalX( wxCoord x ) const
{
return XDEV2LOG( x );
}
wxCoord wxSVGFileDC::DeviceToLogicalY( wxCoord y ) const
{
return YDEV2LOG( y );
}
wxCoord wxSVGFileDC::DeviceToLogicalXRel( wxCoord x ) const
{
return XDEV2LOGREL( x );
}
wxCoord wxSVGFileDC::DeviceToLogicalYRel( wxCoord y ) const
{
return YDEV2LOGREL( y );
}
wxCoord wxSVGFileDC::LogicalToDeviceX( wxCoord x ) const
{
return XLOG2DEV( x );
}
wxCoord wxSVGFileDC::LogicalToDeviceY( wxCoord y ) const
{
return YLOG2DEV( y );
}
wxCoord wxSVGFileDC::LogicalToDeviceXRel( wxCoord x ) const
{
return XLOG2DEVREL( x );
}
wxCoord wxSVGFileDC::LogicalToDeviceYRel( wxCoord y ) const
{
return YLOG2DEVREL( y );
}
void wxSVGFileDC::write( const wxString& s )
{
const wxWX2MBbuf buf = s.mb_str( wxConvUTF8 );
m_outfile->Write( buf, strlen( (const char*) buf ) );
m_OK = m_outfile->Ok();
}
#endif // wxCHECK_VERSION