Example #1
0
void wxGCDCImpl::DoDrawArc( wxCoord x1, wxCoord y1,
                        wxCoord x2, wxCoord y2,
                        wxCoord xc, wxCoord yc )
{
    wxCHECK_RET( IsOk(), wxT("wxGCDC(cg)::DoDrawArc - invalid DC") );

    if ( !m_logicalFunctionSupported )
        return;

    double dx = x1 - xc;
    double dy = y1 - yc;
    double radius = sqrt((double)(dx * dx + dy * dy));
    wxCoord rad = (wxCoord)radius;
    double sa, ea;
    if (x1 == x2 && y1 == y2)
    {
        sa = 0.0;
        ea = 360.0;
    }
    else if (radius == 0.0)
    {
        sa = ea = 0.0;
    }
    else
    {
        sa = (x1 - xc == 0) ?
     (y1 - yc < 0) ? 90.0 : -90.0 :
             -atan2(double(y1 - yc), double(x1 - xc)) * RAD2DEG;
        ea = (x2 - xc == 0) ?
     (y2 - yc < 0) ? 90.0 : -90.0 :
             -atan2(double(y2 - yc), double(x2 - xc)) * RAD2DEG;
    }

    bool fill = m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT;

    wxGraphicsPath path = m_graphicContext->CreatePath();
    if ( fill && ((x1!=x2)||(y1!=y2)) )
        path.MoveToPoint( xc, yc );
    // since these angles (ea,sa) are measured counter-clockwise, we invert them to
    // get clockwise angles
    path.AddArc( xc, yc , rad, wxDegToRad(-sa), wxDegToRad(-ea), false );
    if ( fill && ((x1!=x2)||(y1!=y2)) )
        path.AddLineToPoint( xc, yc );
    m_graphicContext->DrawPath(path);

    wxRect2DDouble box = path.GetBox();
    CalcBoundingBox(wxRound(box.m_x), wxRound(box.m_y));
    CalcBoundingBox(wxRound(box.m_x + box.m_width),
                    wxRound(box.m_y + box.m_height));
}
Example #2
0
void wxGCDCImpl::DoDrawRotatedText(const wxString& text, wxCoord x, wxCoord y,
                               double angle)
{
    wxCHECK_RET( IsOk(), wxT("wxGCDC(cg)::DoDrawRotatedText - invalid DC") );

    if ( text.empty() )
        return;
    if ( !m_logicalFunctionSupported )
        return;

    // we test that we have some font because otherwise we should still use the
    // "else" part below to avoid that DrawRotatedText(angle = 180) and
    // DrawRotatedText(angle = 0) use different fonts (we can't use the default
    // font for drawing rotated fonts unfortunately)
    if ( (angle == 0.0) && m_font.IsOk() )
    {
        DoDrawText(text, x, y);
        
        // Bounding box already updated by DoDrawText(), no need to do it again.
        return;
    }
            
    // Get extent of whole text.
    wxCoord w, h, heightLine;
    GetOwner()->GetMultiLineTextExtent(text, &w, &h, &heightLine);
    
    // Compute the shift for the origin of the next line.
    const double rad = wxDegToRad(angle);
    const double dx = heightLine * sin(rad);
    const double dy = heightLine * cos(rad);
    
    // Draw all text line by line
    const wxArrayString lines = wxSplit(text, '\n', '\0');
    for ( size_t lineNum = 0; lineNum < lines.size(); lineNum++ )
    {
        // Calculate origin for each line to avoid accumulation of
        // rounding errors.
        if ( m_backgroundMode == wxTRANSPARENT )
            m_graphicContext->DrawText( lines[lineNum], x + wxRound(lineNum*dx), y + wxRound(lineNum*dy), wxDegToRad(angle ));
        else
            m_graphicContext->DrawText( lines[lineNum], x + wxRound(lineNum*dx), y + wxRound(lineNum*dy), wxDegToRad(angle ), m_graphicContext->CreateBrush(m_textBackgroundColour) );
   }
            
    // call the bounding box by adding all four vertices of the rectangle
    // containing the text to it (simpler and probably not slower than
    // determining which of them is really topmost/leftmost/...)
    
    // "upper left" and "upper right"
    CalcBoundingBox(x, y);
    CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
    
    // "bottom left" and "bottom right"
    x += (wxCoord)(h*sin(rad));
    y += (wxCoord)(h*cos(rad));
    CalcBoundingBox(x, y);
    CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
}
Example #3
0
void wxGCDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord w, wxCoord h,
                                double sa, double ea )
{
    wxCHECK_RET( IsOk(), wxT("wxGCDC(cg)::DoDrawEllipticArc - invalid DC") );

    if ( !m_logicalFunctionSupported )
        return;

    wxCoord dx = x + w / 2.0;
    wxCoord dy = y + h / 2.0;
    wxDouble factor = ((wxDouble) w) / h;
    m_graphicContext->PushState();
    m_graphicContext->Translate(dx, dy);
    m_graphicContext->Scale(factor, 1.0);
    wxGraphicsPath path = m_graphicContext->CreatePath();

    // If end angle equals start angle we want draw a full ellipse.
    if (ea == sa)
    {
        ea += 360.0;
    }
    // since these angles (ea,sa) are measured counter-clockwise, we invert them to
    // get clockwise angles
    if ( m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT )
    {
        path.MoveToPoint( 0, 0 );
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), false );
        path.AddLineToPoint( 0, 0 );
        m_graphicContext->FillPath( path );

        path = m_graphicContext->CreatePath();
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), false );
        m_graphicContext->StrokePath( path );
    }
    else
    {
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), false );
        m_graphicContext->DrawPath( path );
    }

    wxRect2DDouble box = path.GetBox();
    // apply the transformation to the box
    box.m_x *= factor;
    box.m_width *= factor;
    box.m_x += dx;
    box.m_y += dy;

    CalcBoundingBox(wxRound(box.m_x), wxRound(box.m_y));
    CalcBoundingBox(wxRound(box.m_x + box.m_width),
                    wxRound(box.m_y + box.m_height));

    m_graphicContext->PopState();
}
Example #4
0
void wxGCDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord w, wxCoord h,
                                    double sa, double ea )
{
    wxCHECK_RET( IsOk(), wxT("wxGCDC(cg)::DoDrawEllipticArc - invalid DC") );

    if ( !m_logicalFunctionSupported )
        return;

    m_graphicContext->PushState();
    m_graphicContext->Translate(x+w/2.0,y+h/2.0);
    wxDouble factor = ((wxDouble) w) / h;
    m_graphicContext->Scale( factor , 1.0);

    // since these angles (ea,sa) are measured counter-clockwise, we invert them to
    // get clockwise angles
    if ( m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT )
    {
        wxGraphicsPath path = m_graphicContext->CreatePath();
        path.MoveToPoint( 0, 0 );
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), sa > ea );
        path.AddLineToPoint( 0, 0 );
        m_graphicContext->FillPath( path );

        path = m_graphicContext->CreatePath();
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), sa > ea );
        m_graphicContext->StrokePath( path );
    }
    else
    {
        wxGraphicsPath path = m_graphicContext->CreatePath();
        path.AddArc( 0, 0, h/2.0, wxDegToRad(-sa), wxDegToRad(-ea), sa > ea );
        m_graphicContext->DrawPath( path );
    }

    m_graphicContext->PopState();
}
Example #5
0
void wxSVGFileDCImpl::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

    NewGraphicsIfNeeded();

    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 (wxDegToRad(sa));
    xe = xc + rx * cos (wxDegToRad(ea));
    ys = yc - ry * sin (wxDegToRad(sa));
    ye = yc - ry * sin (wxDegToRad(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;

    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)  );

    s += wxT(" \" /> \n");

    if (m_OK)
    {
        write(s);
    }
}
Example #6
0
void wxSVGFileDCImpl::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
    NewGraphicsIfNeeded();
    wxString s, sTmp;

    // calculate bounding box
    wxCoord w, h, desc;
    DoGetTextExtent(sText, &w, &h, &desc);

    double rad = wxDegToRad(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")
    CalcBoundingBox((wxCoord)(x + h*sin(rad)), (wxCoord)(y + h*cos(rad)));
    CalcBoundingBox((wxCoord)(x + h*sin(rad) + w*cos(rad)), (wxCoord)(y + h*cos(rad) - w*sin(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, 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);
    }

    // convert x,y to SVG text x,y (the coordinates of the text baseline)
    x = (wxCoord)(x + (h-desc)*sin(rad));
    y = (wxCoord)(y + (h-desc)*cos(rad));

    //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 + wxMarkupParser::Quote(sText) + wxT("</text> ") + wxT("\n");
    if (m_OK)
    {
        write(s);
    }
}
Example #7
0
void GraphicsContextDrawingTestCase::DoFontDrawings (wxGraphicsContext *gc)
{
#ifdef __WXGTK__
    wxGCC_WARNING_SUPPRESS(deprecated-declarations)
    g_type_init();
    wxGCC_WARNING_RESTORE()
#endif

    // This test is expected to treat about fonts/texts. Fonts are a bit special
    // because we cannot expect the same rendering by several engines, and the
    // dimensions of the same text in same font will vary.

    wxGraphicsBrush gbBackground =
        gc->CreateBrush( wxBrush ( wxColour ( 240, 240, 240 ) ) );

    gc->SetBrush( gbBackground );
    gc->DrawRectangle(0, 0, 800, 600);

    wxGraphicsBrush gbTextBackground =
        gc->CreateBrush( wxBrush ( wxColour ( 192, 192, 192 ) ) );

    // set underlined font for testing
    gc->SetFont( wxFont(wxFontInfo(12).Family(wxFONTFAMILY_MODERN).Underlined()), *wxBLACK );
    gc->DrawText( wxT("This is text"), 110, 10, gbTextBackground );
    gc->DrawText( wxT("That is text"), 20, 10, wxDegToRad(-45), gbTextBackground );

    // use wxSWISS_FONT and not wxNORMAL_FONT as the latter can't be rotated
    // under Win9x (it is not TrueType)
    gc->SetFont( wxFont(wxFontInfo(12).Family(wxFONTFAMILY_SWISS)), *wxBLACK );

    wxString text;

    for ( int n = -180; n < 180; n += 30 )
    {
        text.Printf(wxT("     %d rotated text"), n);
        gc->DrawText(text , 400, 400, wxDegToRad(n) );
    }

    wxFont swissDcFont( wxFontInfo(18).Family(wxFONTFAMILY_SWISS) );
    wxGraphicsFont swissFont = gc->CreateFont( swissDcFont, *wxBLACK );
    gc->SetFont( swissFont );

    gc->DrawText( wxT("This is Swiss 18pt text."), 110, 40 );

    wxDouble length;
    wxDouble height;
    wxDouble descent;
    gc->GetTextExtent( wxT("This is Swiss 18pt text."), &length, &height, &descent );
    text.Printf( wxT("Dimensions are length %f, height %f, descent %f"), length, height, descent );
    gc->DrawText( text, 110, 80 );

    // (did not find equivalent to CharHeight())

    gc->SetBrush( *wxWHITE_BRUSH );

    gc->DrawRectangle( 100, 40, 4, height );

    // test the logical function effect
    wxCoord y = 150;
    // text drawing should ignore logical function
    gc->DrawText( wxT("There should be a text below"), 110, 150 );
    gc->DrawRectangle( 110, y, 100, height );

    y += height;
    gc->DrawText( wxT("Visible text"), 110, y );
    gc->DrawRectangle( 110, y, 100, height );
    gc->DrawText( wxT("Visible text"), 110, y );
    gc->DrawRectangle( 110, y, 100, height );

    y += height;
    gc->DrawRectangle( 110, y, 100, height );
    gc->DrawText( wxT("Another visible text"), 110, y );

    y += height;
    gc->DrawText("And\nmore\ntext on\nmultiple\nlines", 110, y);
    y += 5*height;

    gc->SetFont( swissDcFont, *wxBLUE );
    gc->DrawText( "Rotated text\ncan have\nmultiple lines\nas well", 110, y, wxDegToRad(15) );
}
Example #8
0
void wxSVGFileDCImpl::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.

    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. */

    //radius
    double rx = w / 2.0;
    double ry = h / 2.0;
    // center
    double xc = x + rx;
    double yc = y + ry;

    // start and end coords
    double xs, ys, xe, ye;
    xs = xc + rx * cos (wxDegToRad(sa));
    xe = xc + rx * cos (wxDegToRad(ea));
    ys = yc - ry * sin (wxDegToRad(sa));
    ye = yc - ry * sin (wxDegToRad(ea));

    // svg arcs have 0 degrees at 12-o'clock instead of 3-o'clock
    double start = (sa - 90);
    if (start < 0)
        start += 360;
    while (abs(start) > 360)
        start -= (start / abs(start)) * 360;

    double end = (ea - 90);
    if (end < 0)
        end += 360;
    while (abs(end) > 360)
        end -= (end / abs(end)) * 360;

    // svg arcs are in clockwise direction, reverse angle
    double angle = end - start;
    if (angle <= 0)
        angle += 360;

    int fArc = angle > 180 ? 1 : 0; // flag for large or small arc
    int fSweep = 0;                 // flag for sweep always 0

    wxString arcPath;
    if (angle == 360)
    {
        // Drawing full circle fails with default arc. Draw two half arcs instead.
        fArc = 1;
        arcPath = wxString::Format(wxS("  <path d=\"M%s %s a%s %s 0 %d %d %s %s a%s %s 0 %d %d %s %s"),
            NumStr(x), NumStr(y + ry),
            NumStr(rx), NumStr(ry), fArc, fSweep, NumStr( rx * 2), NumStr(0),
            NumStr(rx), NumStr(ry), fArc, fSweep, NumStr(-rx * 2), NumStr(0));
    }
    else
    {
        arcPath = wxString::Format(wxS("  <path d=\"M%s %s A%s %s 0 %d %d %s %s"),
            NumStr(xs), NumStr(ys),
            NumStr(rx), NumStr(ry), fArc, fSweep, NumStr(xe), NumStr(ye));
    }

    // Workaround so SVG does not draw an extra line from the centre of the drawn arc
    // to the start point of the arc.
    // First draw the arc with the current brush, without a border,
    // then draw the border without filling the arc.
    if (GetBrush().GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
    {
        wxDCPenChanger setTransp(*GetOwner(), *wxTRANSPARENT_PEN);
        NewGraphicsIfNeeded();

        wxString arcFill = arcPath;
        arcFill += wxString::Format(wxS(" L%s %s z"), NumStr(xc), NumStr(yc));
        arcFill += wxS("\"/>\n");
        write(arcFill);
    }

    wxDCBrushChanger setTransp(*GetOwner(), *wxTRANSPARENT_BRUSH);
    NewGraphicsIfNeeded();

    wxString arcLine = arcPath + wxS("\"/>\n");
    write(arcLine);
}
Example #9
0
void wxSVGFileDCImpl::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
    NewGraphicsIfNeeded();
    wxString s;

    // Get extent of whole text.
    wxCoord w, h, heightLine;
    GetOwner()->GetMultiLineTextExtent(sText, &w, &h, &heightLine);

    // Compute the shift for the origin of the next line.
    const double rad = wxDegToRad(angle);
    const double dx = heightLine * sin(rad);
    const double dy = heightLine * cos(rad);

    // wxS("upper left") and wxS("upper right")
    CalcBoundingBox(x, y);
    CalcBoundingBox((wxCoord)(x + w*cos(rad)), (wxCoord)(y - h*sin(rad)));

    // wxS("bottom left") and wxS("bottom right")
    CalcBoundingBox((wxCoord)(x + h*sin(rad)), (wxCoord)(y + h*cos(rad)));
    CalcBoundingBox((wxCoord)(x + h*sin(rad) + w*cos(rad)), (wxCoord)(y + h*cos(rad) - w*sin(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
        s += wxString::Format(wxS("  <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "), x, y, w, h);
        s += wxS("style=\"") + wxBrushString(m_textBackgroundColour);
        s += wxS("stroke-width:1; ") + wxPenString(m_textBackgroundColour);
        s += wxString::Format(wxS("\" transform=\"rotate(%s %d %d)\"/>"), NumStr(-angle), x, y);
        s += wxS("\n");
        write(s);
    }

    // Draw all text line by line
    const wxArrayString lines = wxSplit(sText, '\n', '\0');
    for (size_t lineNum = 0; lineNum < lines.size(); lineNum++)
    {
        // convert x,y to SVG text x,y (the coordinates of the text baseline)
        wxCoord ww, hh, desc;
        DoGetTextExtent(lines[lineNum], &ww, &hh, &desc);
        int xx = x + wxRound(lineNum * dx) + (hh - desc) * sin(rad);
        int yy = y + wxRound(lineNum * dy) + (hh - desc) * cos(rad);

        //now do the text itself
        s += wxString::Format(wxS("  <text x=\"%d\" y=\"%d\" textLength=\"%d\" "), xx, yy, ww);

        wxString fontName(m_font.GetFaceName());
        if (fontName.Len() > 0)
            s += wxS("style=\"font-family:") + fontName + wxS("; ");
        else
            s += wxS("style=\" ");

        wxString fontweight;
        switch (m_font.GetWeight())
        {
            case wxFONTWEIGHT_MAX:
                wxFAIL_MSG(wxS("invalid font weight value"));
                wxFALLTHROUGH;

            case wxFONTWEIGHT_NORMAL:
                fontweight = wxS("normal");
                break;

            case wxFONTWEIGHT_LIGHT:
                fontweight = wxS("lighter");
                break;

            case wxFONTWEIGHT_BOLD:
                fontweight = wxS("bold");
                break;
        }

        wxASSERT_MSG(!fontweight.empty(), wxS("unknown font weight value"));

        s += wxS("font-weight:") + fontweight + wxS("; ");

        wxString fontstyle;
        switch (m_font.GetStyle())
        {
            case wxFONTSTYLE_MAX:
                wxFAIL_MSG(wxS("invalid font style value"));
                wxFALLTHROUGH;

            case wxFONTSTYLE_NORMAL:
                fontstyle = wxS("normal");
                break;

            case wxFONTSTYLE_ITALIC:
                fontstyle = wxS("italic");
                break;

            case wxFONTSTYLE_SLANT:
                fontstyle = wxS("oblique");
                break;
        }

        wxASSERT_MSG(!fontstyle.empty(), wxS("unknown font style value"));

        s += wxS("font-style:") + fontstyle + wxS("; ");

        wxString textDecoration;
        if (m_font.GetUnderlined())
            textDecoration += wxS(" underline");
        if (m_font.GetStrikethrough())
            textDecoration += wxS(" line-through");
        if (textDecoration.IsEmpty())
            textDecoration = wxS(" none");

        s += wxS("text-decoration:") + textDecoration + wxS("; ");

        s += wxString::Format(wxS("font-size:%dpt; "), m_font.GetPointSize());
        //text will be solid, unless alpha value isn't opaque in the foreground colour
        s += wxBrushString(m_textForegroundColour) + wxPenString(m_textForegroundColour);
        s += wxString::Format(wxS("stroke-width:0;\" transform=\"rotate(%s %d %d)\""), NumStr(-angle), xx, yy);
        s += wxS(" xml:space=\"preserve\">");
        s += wxMarkupParser::Quote(lines[lineNum]) + wxS("</text>\n");

        write(s);
    }
}