diff --git a/include/wx/msw/dc.h b/include/wx/msw/dc.h index a27b70dab3..50dd717d9f 100644 --- a/include/wx/msw/dc.h +++ b/include/wx/msw/dc.h @@ -77,6 +77,7 @@ public: virtual bool CanGetTextExtent() const wxOVERRIDE; virtual int GetDepth() const wxOVERRIDE; virtual wxSize GetPPI() const wxOVERRIDE; + virtual double GetContentScaleFactor() const wxOVERRIDE; virtual void SetMapMode(wxMappingMode mode) wxOVERRIDE; diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 6b9cab2b3b..6229cd863f 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -273,6 +273,28 @@ wxString wxCreateBrushFill(wxBrush& brush) return s; } +void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) +{ + const double scale = sDC.GetContentScaleFactor(); + if ( scale > 1 ) + { + // wxScreenDC uses the DPI of the main screen to determine the text + // extent and character width/height. Because the SVG should be + // DPI-independent we want the text extent of the default (96) DPI. + // + // We can't just divide the returned sizes by the scale factor, because + // text does not scale linear (at least on Windows). Therefore, we scale + // the font size instead. + wxFont scaledFont = font; + scaledFont.SetFractionalPointSize(scaledFont.GetFractionalPointSize() / scale); + sDC.SetFont(scaledFont); + } + else + { + sDC.SetFont(font); + } +} + } // anonymous namespace // ---------------------------------------------------------------------------- @@ -535,93 +557,100 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor const double dx = heightLine * sin(rad); const double dy = heightLine * cos(rad); - // wxS("upper left") and wxS("upper right") + // Update bounding box: upper left, upper right, bottom left, bottom 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) + // Create text style string + wxString fontstyle; + switch (m_font.GetStyle()) { - // 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(" "), NumStr(-angle), x, y); - s += wxS("\n"); - write(s); + 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; } + wxString textDecoration; + if (m_font.GetUnderlined()) + textDecoration += wxS(" underline"); + if (m_font.GetStrikethrough()) + textDecoration += wxS(" line-through"); + if (textDecoration.IsEmpty()) + textDecoration = wxS(" none"); + + wxString style = wxS("style=\""); + style += wxString::Format(wxS("font-family:%s; "), m_font.GetFaceName()); + style += wxString::Format(wxS("font-weight:%d; "), m_font.GetWeight()); + style += wxString::Format(wxS("font-style:%s; "), fontstyle); + style += wxString::Format(wxS("font-size:%spt; "), NumStr(m_font.GetFractionalPointSize())); + style += wxString::Format(wxS("text-decoration:%s; "), textDecoration); + style += wxString::Format(wxS("%s %s stroke-width:0; "), + wxBrushString(m_textForegroundColour), + wxPenString(m_textForegroundColour)); + style += wxS("white-space: pre;"); + style += wxS("\""); + + // this is deprecated in favour of "white-space: pre", keep it for now to + // support SVG viewers that do not support the new tag + style += wxS(" xml:space=\"preserve\""); + // Draw all text line by line const wxArrayString lines = wxSplit(sText, '\n', '\0'); for (size_t lineNum = 0; lineNum < lines.size(); lineNum++) { + const int xRect = x + wxRound(lineNum * dx); + const int yRect = y + wxRound(lineNum * dy); + // 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); + wxString const& line = lines[lineNum]; + DoGetTextExtent(line, &ww, &hh, &desc); + const int xText = xRect + (hh - desc) * sin(rad); + const int yText = yRect + (hh - desc) * cos(rad); - //now do the text itself - s += wxString::Format(wxS(" 0) - s += wxS("style=\"font-family:") + fontName + wxS("; "); - else - s += wxS("style=\" "); - - wxString fontweight = wxString::Format(wxS("%d"), m_font.GetWeight()); - - s += wxS("font-weight:") + fontweight + wxS("; "); - - wxString fontstyle; - switch (m_font.GetStyle()) + if (m_backgroundMode == wxBRUSHSTYLE_SOLID) { - case wxFONTSTYLE_MAX: - wxFAIL_MSG(wxS("invalid font style value")); - wxFALLTHROUGH; + // draw text background + const wxString rectStyle = wxString::Format( + wxS("style=\"%s %s stroke-width:1;\""), + wxBrushString(m_textBackgroundColour), + wxPenString(m_textBackgroundColour)); - case wxFONTSTYLE_NORMAL: - fontstyle = wxS("normal"); - break; + const wxString rectTransform = wxString::Format( + wxS("transform=\"rotate(%s %d %d)\""), + NumStr(-angle), xRect, yRect); - case wxFONTSTYLE_ITALIC: - fontstyle = wxS("italic"); - break; + s = wxString::Format( + wxS(" \n"), + xRect, yRect, ww, hh, rectStyle, rectTransform); - case wxFONTSTYLE_SLANT: - fontstyle = wxS("oblique"); - break; + write(s); } - wxASSERT_MSG(!fontstyle.empty(), wxS("unknown font style value")); + const wxString transform = wxString::Format( + wxS("transform=\"rotate(%s %d %d)\""), + NumStr(-angle), xText, yText); - 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 = wxString::Format( + wxS(" %s\n"), + xText, yText, ww, style, transform, #if wxUSE_MARKUP - s += wxMarkupParser::Quote(lines[lineNum]) + wxS("\n"); + wxMarkupParser::Quote(line) #else - s += lines[lineNum] + wxS("\n"); + line #endif + ); write(s); } @@ -949,17 +978,15 @@ void wxSVGFileDCImpl::DestroyClippingRegion() void wxSVGFileDCImpl::DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, wxCoord *descent, wxCoord *externalLeading, const wxFont *font) const { wxScreenDC sDC; + wxSetScaledScreenDCFont(sDC, font ? *font : m_font); - sDC.SetFont(m_font); - if (font != NULL) - sDC.SetFont(*font); sDC.GetTextExtent(string, w, h, descent, externalLeading); } wxCoord wxSVGFileDCImpl::GetCharHeight() const { wxScreenDC sDC; - sDC.SetFont(m_font); + wxSetScaledScreenDCFont(sDC, m_font); return sDC.GetCharHeight(); @@ -968,7 +995,7 @@ wxCoord wxSVGFileDCImpl::GetCharHeight() const wxCoord wxSVGFileDCImpl::GetCharWidth() const { wxScreenDC sDC; - sDC.SetFont(m_font); + wxSetScaledScreenDCFont(sDC, m_font); return sDC.GetCharWidth(); } diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 13bf1135f0..6710f8c2ae 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2422,6 +2422,11 @@ wxSize wxMSWDCImpl::GetPPI() const return wxSize(x, y); } +double wxMSWDCImpl::GetContentScaleFactor() const +{ + return GetPPI().y / 96.0; +} + // ---------------------------------------------------------------------------- // DC caching // ----------------------------------------------------------------------------