From b0152155c0cd48d38004fc246a2540cd5fec438f Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Fri, 2 Aug 2019 22:49:28 +0200 Subject: [PATCH 1/9] Implement GetContentScaleFactor for wxMSWDCImpl --- include/wx/msw/dc.h | 1 + src/msw/dc.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/wx/msw/dc.h b/include/wx/msw/dc.h index e541499f9a..83777b8c85 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/msw/dc.cpp b/src/msw/dc.cpp index ddeb2d6ba9..fdf9837d41 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2440,6 +2440,11 @@ wxSize wxMSWDCImpl::GetPPI() const return wxSize(x, y); } +double wxMSWDCImpl::GetContentScaleFactor() const +{ + return GetPPI().y / 96.0; +} + // ---------------------------------------------------------------------------- // DC caching // ---------------------------------------------------------------------------- From c34f04b7582d632008313034a0bfecb6b45a1d7e Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Fri, 2 Aug 2019 23:00:14 +0200 Subject: [PATCH 2/9] Refactor wxSVGFileDCImpl::DoDrawRotatedText Create the style string outside the loop, it can determined once and used for each line. --- src/common/dcsvg.cpp | 111 ++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 59 deletions(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 6b9cab2b3b..470c12ca22 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -535,11 +535,9 @@ 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))); @@ -555,73 +553,68 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor write(s); } + // Create text style string + 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; + } + + 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:%dpt; "), m_font.GetPointSize()); + 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("\""); + 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++) { // 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 xx = x + wxRound(lineNum * dx) + (hh - desc) * sin(rad); + const int yy = y + wxRound(lineNum * dy) + (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()) - { - 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 = wxString::Format( + wxS(" %s\n"), + xx, yy, 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); } From b677c8d1b22fb2074cbcc3315af98160583a219b Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Fri, 2 Aug 2019 23:03:28 +0200 Subject: [PATCH 3/9] Add replacement for deprecated white-space tag in wxSVGFileDC output --- src/common/dcsvg.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 470c12ca22..770c8580bf 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -588,7 +588,11 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor 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 From ae875842c13a977ce6b42dbba07cf6834cb75c69 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Fri, 2 Aug 2019 23:12:57 +0200 Subject: [PATCH 4/9] Fix wxSVGFileDC text size when using non-default screen DPI The text in a wxDC is adjusted to the screen DPI, but text in a SVG file is DPI independent. Scale the text attribute so it has the same size as in the wxDC. Scaling will modify the position of the attribute, correct this with translate. --- src/common/dcsvg.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 770c8580bf..17966701a7 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -595,6 +595,12 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor // support SVG viewers that do not support the new tag style += wxS(" xml:space=\"preserve\""); + // The text in a wxDC is adjusted to the screen DPI, but text in a SVG file + // is DPI independent. Scale the text attribute with the wxDC scale factor + // so it will have the same size as in the wxDC. + wxScreenDC dc; + const double scale = dc.GetContentScaleFactor(); + // Draw all text line by line const wxArrayString lines = wxSplit(sText, '\n', '\0'); for (size_t lineNum = 0; lineNum < lines.size(); lineNum++) @@ -606,13 +612,17 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor const int xx = x + wxRound(lineNum * dx) + (hh - desc) * sin(rad); const int yy = y + wxRound(lineNum * dy) + (hh - desc) * cos(rad); + const int tx = xx - (int)ceil(xx * scale); + const int ty = yy - (int)ceil(yy * scale); + const int len = (int)floor(ww / scale); + const wxString transform = wxString::Format( - wxS("transform=\"rotate(%s %d %d)\""), - NumStr(-angle), xx, yy); + wxS("transform=\"rotate(%s %d %d) translate(%d %d) scale(%s)\""), + NumStr(-angle), xx, yy, tx, ty, NumStr(scale)); s = wxString::Format( wxS(" %s\n"), - xx, yy, ww, style, transform, + xx, yy, len, style, transform, #if wxUSE_MARKUP wxMarkupParser::Quote(line) #else From 8d25f0afa86b0ffeee83935ab24f1fc040fa437f Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Sun, 4 Aug 2019 14:25:18 +0200 Subject: [PATCH 5/9] Improve wxSVGFileDC text background colour Draw the background rectangle per line, instead of one big rectangle. When there are multiple lines and one line is shorter, the area behind that line should not get a background colour. --- src/common/dcsvg.cpp | 46 +++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 17966701a7..74e87dc0b7 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -541,18 +541,6 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor 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(" "), NumStr(-angle), x, y); - s += wxS("\n"); - write(s); - } - // Create text style string wxString fontstyle; switch (m_font.GetStyle()) @@ -605,24 +593,46 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor 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; wxString const& line = lines[lineNum]; DoGetTextExtent(line, &ww, &hh, &desc); - const int xx = x + wxRound(lineNum * dx) + (hh - desc) * sin(rad); - const int yy = y + wxRound(lineNum * dy) + (hh - desc) * cos(rad); + const int xText = xRect + (hh - desc) * sin(rad); + const int yText = yRect + (hh - desc) * cos(rad); - const int tx = xx - (int)ceil(xx * scale); - const int ty = yy - (int)ceil(yy * scale); + const int tx = xText - (int)ceil(xText * scale); + const int ty = yText - (int)ceil(yText * scale); const int len = (int)floor(ww / scale); + if (m_backgroundMode == wxBRUSHSTYLE_SOLID) + { + // draw text background + const wxString rectStyle = wxString::Format( + wxS("style=\"%s %s stroke-width:1;\""), + wxBrushString(m_textBackgroundColour), + wxPenString(m_textBackgroundColour)); + + const wxString rectTransform = wxString::Format( + wxS("transform=\"rotate(%s %d %d)\""), + NumStr(-angle), xRect, yRect); + + s = wxString::Format( + wxS(" \n"), + xRect, yRect, ww, hh, rectStyle, rectTransform); + + write(s); + } + const wxString transform = wxString::Format( wxS("transform=\"rotate(%s %d %d) translate(%d %d) scale(%s)\""), - NumStr(-angle), xx, yy, tx, ty, NumStr(scale)); + NumStr(-angle), xText, yText, tx, ty, NumStr(scale)); s = wxString::Format( wxS(" %s\n"), - xx, yy, len, style, transform, + xText, yText, len, style, transform, #if wxUSE_MARKUP wxMarkupParser::Quote(line) #else From b5dcee2e35f3686a4e31e68e95100f0471067806 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Sun, 4 Aug 2019 14:31:34 +0200 Subject: [PATCH 6/9] Use fractional point size for font in wxSVGFileDC output --- src/common/dcsvg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 74e87dc0b7..852974a97a 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -571,7 +571,7 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor 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:%dpt; "), m_font.GetPointSize()); + 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), From 16a02e63381e93172759a5ac35878e5351e81cb2 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Tue, 6 Aug 2019 00:08:12 +0200 Subject: [PATCH 7/9] Use DPI independent text size in wxSVGFileDC Don't let the text size in the SVG file depend on the DPI of the system it was created on. Let GetTextExtent, GetCharHeight and GetCharWidth return sizes based on the default (96) DPI. --- src/common/dcsvg.cpp | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 852974a97a..63e270c56f 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -273,6 +273,25 @@ wxString wxCreateBrushFill(wxBrush& brush) return s; } +void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) +{ + sDC.SetFont(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 = sDC.GetFont(); + scaledFont.SetFractionalPointSize(scaledFont.GetFractionalPointSize() / scale); + sDC.SetFont(scaledFont); + } +} + } // anonymous namespace // ---------------------------------------------------------------------------- @@ -583,12 +602,6 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor // support SVG viewers that do not support the new tag style += wxS(" xml:space=\"preserve\""); - // The text in a wxDC is adjusted to the screen DPI, but text in a SVG file - // is DPI independent. Scale the text attribute with the wxDC scale factor - // so it will have the same size as in the wxDC. - wxScreenDC dc; - const double scale = dc.GetContentScaleFactor(); - // Draw all text line by line const wxArrayString lines = wxSplit(sText, '\n', '\0'); for (size_t lineNum = 0; lineNum < lines.size(); lineNum++) @@ -603,10 +616,6 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor const int xText = xRect + (hh - desc) * sin(rad); const int yText = yRect + (hh - desc) * cos(rad); - const int tx = xText - (int)ceil(xText * scale); - const int ty = yText - (int)ceil(yText * scale); - const int len = (int)floor(ww / scale); - if (m_backgroundMode == wxBRUSHSTYLE_SOLID) { // draw text background @@ -627,12 +636,12 @@ void wxSVGFileDCImpl::DoDrawRotatedText(const wxString& sText, wxCoord x, wxCoor } const wxString transform = wxString::Format( - wxS("transform=\"rotate(%s %d %d) translate(%d %d) scale(%s)\""), - NumStr(-angle), xText, yText, tx, ty, NumStr(scale)); + wxS("transform=\"rotate(%s %d %d)\""), + NumStr(-angle), xText, yText); s = wxString::Format( wxS(" %s\n"), - xText, yText, len, style, transform, + xText, yText, ww, style, transform, #if wxUSE_MARKUP wxMarkupParser::Quote(line) #else @@ -966,17 +975,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(); @@ -985,7 +992,7 @@ wxCoord wxSVGFileDCImpl::GetCharHeight() const wxCoord wxSVGFileDCImpl::GetCharWidth() const { wxScreenDC sDC; - sDC.SetFont(m_font); + wxSetScaledScreenDCFont(sDC, m_font); return sDC.GetCharWidth(); } From f3adef1047fd8fee6293b783c513db088a0a228f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 9 Aug 2019 23:38:01 +0200 Subject: [PATCH 8/9] Minor formatting fix --- src/common/dcsvg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 63e270c56f..40dda0af5e 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -278,7 +278,8 @@ void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) sDC.SetFont(font); const double scale = sDC.GetContentScaleFactor(); - if (scale > 1) { + 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. From c91c94ab0e8cd41885ae04529c50a4d413617bab Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 9 Aug 2019 23:39:23 +0200 Subject: [PATCH 9/9] Avoid calling wxScreenDC::SetFont() more than once This seems unnecessary when we can call it only once even with high DPI screens. --- src/common/dcsvg.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 40dda0af5e..6229cd863f 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -275,8 +275,6 @@ wxString wxCreateBrushFill(wxBrush& brush) void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) { - sDC.SetFont(font); - const double scale = sDC.GetContentScaleFactor(); if ( scale > 1 ) { @@ -287,10 +285,14 @@ void wxSetScaledScreenDCFont(wxScreenDC& sDC, const wxFont& font) // 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 = sDC.GetFont(); + wxFont scaledFont = font; scaledFont.SetFractionalPointSize(scaledFont.GetFractionalPointSize() / scale); sDC.SetFont(scaledFont); } + else + { + sDC.SetFont(font); + } } } // anonymous namespace