/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
cairo_t* syncCairoContext(cairo_t* cr)
{ //rhbz#1283420 tdf#117413 bodge to force a read from the underlying surface which has //the side effect of making the mysterious xrender related problem go away
cairo_surface_t *target = cairo_get_target(cr); if (cairo_surface_get_type(target) == CAIRO_SURFACE_TYPE_XLIB)
{
cairo_surface_t *throw_away = cairo_surface_create_similar(target, cairo_surface_get_content(target), 1, 1);
cairo_t *force_read_cr = cairo_create(throw_away);
cairo_set_source_surface(force_read_cr, target, 0, 0);
cairo_paint(force_read_cr);
cairo_destroy(force_read_cr);
cairo_surface_destroy(throw_away);
} return cr;
}
}
namespace { struct CairoFontOptions
{ // https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/235 // I don't want to have CAIRO_ROUND_GLYPH_POS_ON set in the cairo // surfaces font_options, but that's private, so tricky to achieve
cairo_font_options_t* mpRoundGlyphPosOffOptions;
CairoFontOptions()
{ // https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/235 // I don't want to have CAIRO_ROUND_GLYPH_POS_ON set in the cairo surfaces // font_options when trying subpixel rendering, but that's a private // feature of cairo_font_options_t, so tricky to achieve. Hack this by // getting the font options of a backend known to set this private feature // to CAIRO_ROUND_GLYPH_POS_OFF and then set to defaults the public // features and the result can be merged with new font options to set // CAIRO_ROUND_GLYPH_POS_OFF in those
mpRoundGlyphPosOffOptions = cairo_font_options_create(); #ifdefined(CAIRO_HAS_SVG_SURFACE) // svg, pdf and ps backends have CAIRO_ROUND_GLYPH_POS_OFF by default
cairo_surface_t* hack = cairo_svg_surface_create(nullptr, 1, 1); #elifdefined(CAIRO_HAS_PDF_SURFACE)
cairo_surface_t* hack = cairo_pdf_surface_create(nullptr, 1, 1); #endif
cairo_surface_get_font_options(hack, mpRoundGlyphPosOffOptions);
cairo_surface_destroy(hack);
cairo_font_options_set_antialias(mpRoundGlyphPosOffOptions, CAIRO_ANTIALIAS_DEFAULT);
cairo_font_options_set_subpixel_order(mpRoundGlyphPosOffOptions, CAIRO_SUBPIXEL_ORDER_DEFAULT);
cairo_font_options_set_hint_style(mpRoundGlyphPosOffOptions, CAIRO_HINT_STYLE_DEFAULT);
cairo_font_options_set_hint_metrics(mpRoundGlyphPosOffOptions, CAIRO_HINT_METRICS_DEFAULT);
}
~CairoFontOptions()
{
cairo_font_options_destroy(mpRoundGlyphPosOffOptions);
} staticconst cairo_font_options_t *get()
{ static CairoFontOptions opts; return opts.mpRoundGlyphPosOffOptions;
}
};
}
void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGraphics& rGraphics)
{ /* * It might be ideal to cache surface and cairo context between calls and * only destroy it when the drawable changes, but to do that we need to at * least change the SalFrame etc impls to dtor the SalGraphics *before* the * destruction of the windows they reference
*/
cairo_t *cr = syncCairoContext(getCairoContext()); if (!cr)
{
SAL_WARN("vcl", "no cairo context for text"); return;
}
comphelper::ScopeGuard releaseContext([this, cr]() { releaseCairoContext(cr); });
if (bSubpixelPositioning)
{ // tdf#150507 like skia, even when subpixel rendering pixel, snap y if (!bVertical)
aGlyph.y = std::floor(aGlyph.y + 0.5); else
aGlyph.x = std::floor(aGlyph.x + 0.5);
// tdf#152094 snap to 1/4 of a pixel after a run of whitespace, // probably a little dubious, but maybe worth a shot for lodpi double& rGlyphDimension = !bVertical ? aGlyph.x : aGlyph.y; constint nSubPixels = 4 * (!bVertical ? nXScale : nYScale); if (pGlyph->IsSpacing())
nSnapToSubPixelDiff = 0; elseif (pPrevGlyph && pPrevGlyph->IsSpacing())
{ double nSnapToSubPixel = std::floor(rGlyphDimension * nSubPixels) / nSubPixels;
nSnapToSubPixelDiff = rGlyphDimension - nSnapToSubPixel;
rGlyphDimension = nSnapToSubPixel;
} else
rGlyphDimension -= nSnapToSubPixelDiff;
pPrevGlyph = pGlyph;
}
cairo_glyphs.push_back(aGlyph);
}
const size_t nGlyphs = cairo_glyphs.size(); if (!nGlyphs) return;
if (nHeight > SAL_MAX_UINT16)
{ // as seen with freetype 2.11.0, so cairo surface status is "fail" // ("error occurred in libfreetype") and no further operations are // executed, so this error then leads to later leaks
SAL_WARN("vcl", "rendering text would fail with height: " << nHeight); return;
}
#ifdefined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if (nHeight > 7000)
{
SAL_WARN("vcl", "rendering text would use > 2G Memory: " << nHeight); return;
}
if (nWidth > 2000)
{
SAL_WARN("vcl", "rendering text would use > 2G Memory: " << nWidth); return;
} #endif
if (nullptr != pCairoCommon)
pCairoCommon->clipRegion(cr);
// tdf#132112 excessive stretch of underbrace and overbrace can trigger freetype into an error, which propagates 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);
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;
}
#ifdefined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if (__lsan_disable)
__lsan_disable(); #endif
if (bDisableAA || !bAllowedHintStyle || bSubpixelPositioning)
{ // Disable font AA in case global AA setting is supposed to affect // font rendering (not the default) and AA is disabled.
cairo_font_options_t* pOptions = pFontOptions ? cairo_font_options_copy(pFontOptions) : cairo_font_options_create();
if (bDisableAA)
cairo_font_options_set_antialias(pOptions, CAIRO_ANTIALIAS_NONE); if (!bAllowedHintStyle)
cairo_font_options_set_hint_style(pOptions, CAIRO_HINT_STYLE_SLIGHT); if (bSubpixelPositioning)
{ // Disable private CAIRO_ROUND_GLYPH_POS_ON by merging with // font options known to have CAIRO_ROUND_GLYPH_POS_OFF
cairo_font_options_merge(pOptions, CairoFontOptions::get());
// a) tdf#153699 skip this with cairo 1.17.8 as it has a problem // See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/643 // b) tdf#152675 a similar report for cairo: 1.16.0-4ubuntu1, // assume that everything <= 1.17.8 is unsafe to disable this if (cairo_version() > CAIRO_VERSION_ENCODE(1, 17, 8))
cairo_font_options_set_hint_metrics(pOptions, CAIRO_HINT_METRICS_OFF);
}
cairo_set_font_options(cr, pOptions);
cairo_font_options_destroy(pOptions);
} elseif (pFontOptions)
cairo_set_font_options(cr, pFontOptions);
}
CairoFontsCache::CacheId aId = makeCacheId(rLayout);
std::vector<int>::const_iterator aEnd = glyph_extrarotation.end();
std::vector<int>::const_iterator aStart = glyph_extrarotation.begin();
std::vector<int>::const_iterator aI = aStart; while (aI != aEnd)
{ int nGlyphRotation = *aI;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.