vcl/unx/generic/gdi/cairotextrender.cxx | 112 +++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 23 deletions(-)
New commits: commit 53e24da3698ddb5f976cec0ae8eb8b2a2ab2c4c6 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Tue Apr 4 19:46:05 2023 +0100 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Wed Apr 5 09:41:41 2023 +0200 tdf#132112 excessive stretch of braces can trigger freetype into an error which propogates to cairo and once a cairo surface is in an error state, that cannot be cleared and all subsequent drawing fails, so bodge that with a high degree of stretch we draw the brace without stretch to a temp surface and stretch that to give a far poorer visual result, but one that can at least be rendered. Change-Id: I6b8bb079e82c3cf03a908de750c5c445ad230750 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150031 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx index 8f093c02c126..32561cbb036e 100644 --- a/vcl/unx/generic/gdi/cairotextrender.cxx +++ b/vcl/unx/generic/gdi/cairotextrender.cxx @@ -285,7 +285,8 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG cairo_glyphs.push_back(aGlyph); } - if (cairo_glyphs.empty()) + const size_t nGlyphs = cairo_glyphs.size(); + if (!nGlyphs) return; const vcl::font::FontSelectPattern& rFSD = rInstance.GetFontSelectPattern(); @@ -303,14 +304,6 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG return; } - int nRatio = nWidth * 10 / nHeight; - if (nRatio >= 5120) - { - // as seen with freetype 2.12.1, so cairo surface status is "fail" - SAL_WARN("vcl", "rendering text would fail with stretch of: " << nRatio / 10.0); - return; - } - #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if (nHeight > 8000) { @@ -323,7 +316,78 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG SAL_WARN("vcl", "rendering text would use > 2G Memory: " << nWidth); return; } +#endif + + clipRegion(cr); + + cairo_set_source_rgb(cr, + mnTextColor.GetRed()/255.0, + mnTextColor.GetGreen()/255.0, + mnTextColor.GetBlue()/255.0); + + int nRatio = nWidth * 10 / nHeight; + + // tdf#132112 excessive stretch of underbrace and overbrace can trigger freetype into an error, which propogates to cairo + // and once a cairo surface is in an error state, that cannot be cleared and all subsequent drawing fails, so bodge that + // with a high degree of stretch we draw the brace without stretch to a temp surface and stretch that to give a far + // poorer visual result, but one that can be rendered. + if (nGlyphs == 1 && nRatio > 100 && (cairo_glyphs[0].index == 974 || cairo_glyphs[0].index == 975) && + rFSD.maTargetName == "OpenSymbol" && !glyph_extrarotation.back() && !rLayout.GetOrientation()) + { + CairoFontsCache::CacheId aId = makeCacheId(rLayout); + aId.mbVerticalMetrics = false; + + ApplyFont(cr, aId, nWidth, nHeight, 0, rLayout); + cairo_text_extents_t stretched_extents; + cairo_glyph_extents(cr, cairo_glyphs.data(), nGlyphs, &stretched_extents); + + ApplyFont(cr, aId, nHeight, nHeight, 0, rLayout); + cairo_text_extents_t unstretched_extents; + cairo_glyph_extents(cr, cairo_glyphs.data(), nGlyphs, &unstretched_extents); + + cairo_surface_t *target = cairo_get_target(cr); + cairo_surface_t *temp_surface = cairo_surface_create_similar(target, cairo_surface_get_content(target), + unstretched_extents.width, unstretched_extents.height); + cairo_t *temp_cr = cairo_create(temp_surface); + cairo_glyph_t glyph; + glyph.x = -unstretched_extents.x_bearing; + glyph.y = -unstretched_extents.y_bearing; + glyph.index = cairo_glyphs[0].index; + + ApplyFont(temp_cr, aId, nHeight, nHeight, 0, rLayout); + cairo_set_source_rgb(temp_cr, + mnTextColor.GetRed()/255.0, + mnTextColor.GetGreen()/255.0, + mnTextColor.GetBlue()/255.0); + + cairo_show_glyphs(temp_cr, &glyph, 1); + cairo_destroy(temp_cr); + + cairo_set_source_surface(cr, temp_surface, cairo_glyphs[0].x, cairo_glyphs[0].y + stretched_extents.y_bearing); + + cairo_pattern_t* sourcepattern = cairo_get_source(cr); + cairo_matrix_t matrix; + cairo_pattern_get_matrix(sourcepattern, &matrix); + cairo_matrix_scale(&matrix, unstretched_extents.width / stretched_extents.width, 1); + cairo_pattern_set_matrix(sourcepattern, &matrix); + + cairo_rectangle(cr, cairo_glyphs[0].x, cairo_glyphs[0].y + stretched_extents.y_bearing, stretched_extents.width, stretched_extents.height); + cairo_fill(cr); + + cairo_surface_destroy(temp_surface); + + return; + } + + if (nRatio >= 5120) + { + // as seen with freetype 2.12.1, so cairo surface status is "fail" + SAL_WARN("vcl", "rendering text would fail with stretch of: " << nRatio / 10.0); + return; + } + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if (__lsan_disable) __lsan_disable(); #endif @@ -364,13 +428,6 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG cairo_set_font_options(cr, pFontOptions); } - clipRegion(cr); - - cairo_set_source_rgb(cr, - mnTextColor.GetRed()/255.0, - mnTextColor.GetGreen()/255.0, - mnTextColor.GetBlue()/255.0); - CairoFontsCache::CacheId aId = makeCacheId(rLayout); std::vector<int>::const_iterator aEnd = glyph_extrarotation.end(); commit 2510d4805482d2cec9059065174825b4260adf0f Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Tue Apr 4 17:26:24 2023 +0100 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Wed Apr 5 09:41:32 2023 +0200 split out a makeCacheId Change-Id: I89fbaca1fa7303904681f75ee4a7fc90aeb9ef8e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150030 Tested-by: Caolán McNamara <caol...@redhat.com> Reviewed-by: Caolán McNamara <caol...@redhat.com> diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx index f3fb3bde6b83..8f093c02c126 100644 --- a/vcl/unx/generic/gdi/cairotextrender.cxx +++ b/vcl/unx/generic/gdi/cairotextrender.cxx @@ -188,7 +188,7 @@ static void ApplyFont(cairo_t* cr, const CairoFontsCache::CacheId& rId, double n if (nGlyphRotation) cairo_matrix_rotate(&m, toRadian(Degree10(nGlyphRotation * 900))); - const FreetypeFontInstance& rInstance = static_cast<FreetypeFontInstance&>(rLayout.GetFont()); + const LogicalFontInstance& rInstance = rLayout.GetFont(); if (rInstance.NeedsArtificialItalic()) { cairo_matrix_t shear; @@ -200,11 +200,24 @@ static void ApplyFont(cairo_t* cr, const CairoFontsCache::CacheId& rId, double n cairo_set_font_matrix(cr, &m); } -void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGraphics& rGraphics) +static CairoFontsCache::CacheId makeCacheId(const GenericSalLayout& rLayout) { const FreetypeFontInstance& rInstance = static_cast<FreetypeFontInstance&>(rLayout.GetFont()); const FreetypeFont& rFont = rInstance.GetFreetypeFont(); + FT_Face aFace = rFont.GetFtFace(); + CairoFontsCache::CacheId aId; + aId.maFace = aFace; + aId.mpOptions = rFont.GetFontOptions(); + aId.mbEmbolden = rInstance.NeedsArtificialBold(); + + return aId; +} + +void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGraphics& rGraphics) +{ + const LogicalFontInstance& rInstance = rLayout.GetFont(); + const bool bResolutionIndependentLayoutEnabled = rLayout.GetTextRenderModeForResolutionIndependentLayout(); /* @@ -358,11 +371,7 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG mnTextColor.GetGreen()/255.0, mnTextColor.GetBlue()/255.0); - FT_Face aFace = rFont.GetFtFace(); - CairoFontsCache::CacheId aId; - aId.maFace = aFace; - aId.mpOptions = rFont.GetFontOptions(); - aId.mbEmbolden = rInstance.NeedsArtificialBold(); + CairoFontsCache::CacheId aId = makeCacheId(rLayout); std::vector<int>::const_iterator aEnd = glyph_extrarotation.end(); std::vector<int>::const_iterator aStart = glyph_extrarotation.begin();