991 lines
26 KiB
C++
991 lines
26 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// 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
|
|
|
|
#if wxCHECK_VERSION( 2, 9, 0 )
|
|
// Do nothing, because wxWidgets 3 supports the SVG format
|
|
// previously, was a contribution library, not included in wxWidgets base
|
|
|
|
#else
|
|
|
|
#include <dcsvg.h>
|
|
|
|
#include <wx/image.h>
|
|
#include <macros.h>
|
|
|
|
#define wxSVG_DEBUG false
|
|
|
|
// or true to see the calls being executed
|
|
#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;
|
|
}
|
|
|
|
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( wxString f, int Width, int Height, float dpi )
|
|
/***********************************************************************/
|
|
|
|
/* set up things first wxDCBase does all this?
|
|
*/
|
|
{
|
|
m_width = Width;
|
|
m_height = Height;
|
|
|
|
m_clipping = false;
|
|
m_OK = true;
|
|
|
|
m_mm_to_pix_x = dpi / 25.4;
|
|
m_mm_to_pix_y = dpi / 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( f );
|
|
m_OK = m_outfile->Ok();
|
|
if( m_OK )
|
|
{
|
|
m_filename = f;
|
|
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=\"0 0 %d %d \"\n" ),
|
|
double (Width) / dpi, double (Height) / dpi, Width, Height );
|
|
write( s );
|
|
s.Printf( wxT( ">\n" ) );
|
|
write( s );
|
|
|
|
s = wxT( " <title>SVG Picture created as " ) + wxFileNameFromPath( f ) +
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|
|
// constructors
|
|
wxSVGFileDC::wxSVGFileDC( wxString f )
|
|
{
|
|
// quarter 640x480 screen display at 72 dpi
|
|
Init( f, 320, 240, 72.0 );
|
|
}
|
|
|
|
wxSVGFileDC::wxSVGFileDC( wxString f, int Width, int Height )
|
|
{
|
|
Init( f, Width, Height, 72.0 );
|
|
}
|
|
|
|
wxSVGFileDC::wxSVGFileDC( wxString f, int Width, int Height, float dpi )
|
|
{
|
|
Init( f, Width, Height, dpi );
|
|
}
|
|
|
|
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 );
|
|
}
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DrawLine Call executed" ) );
|
|
CalcBoundingBox( x1, y1 );
|
|
CalcBoundingBox( x2, y2 );
|
|
return;
|
|
}
|
|
|
|
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 );
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DrawText Call executed" ) );
|
|
}
|
|
|
|
|
|
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
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG,
|
|
wxT( "wxSVGFileDC::Draw Rotated Text Call plotting 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 );
|
|
}
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DrawRotatedText Call executed" ) );
|
|
}
|
|
|
|
|
|
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 );
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawRoundedRectangle Call executed" ) );
|
|
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 );
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawPolygon Call executed" ) );
|
|
}
|
|
|
|
|
|
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 );
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawEllipse Call executed" ) );
|
|
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 );
|
|
}
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawArc Call executed" ) );
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawEllipticArc Call executed" ) );
|
|
}
|
|
|
|
|
|
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 );
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::GetTextExtent Call executed" ) );
|
|
}
|
|
|
|
|
|
wxCoord wxSVGFileDC::GetCharHeight() const
|
|
|
|
{
|
|
wxScreenDC sDC;
|
|
|
|
sDC.SetFont( m_font );
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::GetCharHeight Call executing" ) );
|
|
return sDC.GetCharHeight();
|
|
}
|
|
|
|
|
|
wxCoord wxSVGFileDC::GetCharWidth() const
|
|
{
|
|
wxScreenDC sDC;
|
|
|
|
sDC.SetFont( m_font );
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::GetCharWidth Call executing" ) );
|
|
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;
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::SetBrush Call executed" ) );
|
|
}
|
|
|
|
|
|
void wxSVGFileDC::SetPen( const wxPen& pen )
|
|
{
|
|
// width, color, ends, joins : currently implemented
|
|
// dashes, stipple : not implemented
|
|
m_pen = pen;
|
|
|
|
m_graphics_changed = true;
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::SetPen Call executed" ) );
|
|
}
|
|
|
|
|
|
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;
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::NewGraphics Call executed" ) );
|
|
}
|
|
|
|
|
|
void wxSVGFileDC::SetFont( const wxFont& font )
|
|
|
|
{
|
|
m_font = font;
|
|
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::SetFont Call executed" ) );
|
|
}
|
|
|
|
|
|
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 );
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoBlit Call executed" ) );
|
|
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 );
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawIcon Call executed" ) );
|
|
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;
|
|
wxASSERT_MSG( !wxSVG_DEBUG, wxT( "wxSVGFileDC::DoDrawBitmap Call executed" ) );
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma warn .rch
|
|
#pragma warn .ccc
|
|
#endif
|
|
|
|
#endif // wxCHECK_VERSION
|