Hi Caolan, took your patch, handled one more case - could you please review & commit to libreoffice-3-3 if ok? Cheers, -- Thorsten
From 25ae4e3bf1632150aa54769ed5b3235cbfa2af2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com> Date: Fri, 3 Dec 2010 11:15:24 +0100 Subject: [PATCH] Fix glyph fallback for cairocanvas (fdo#31243) * set font fallback info on glyph vector inside vcl * pick actual fallback font when rendering glyphs in cairocanvas --- canvas/source/cairo/cairo_textlayout.cxx | 323 +++++++++++++++++------------- vcl/inc/vcl/sysdata.hxx | 9 +- vcl/source/gdi/outdev3.cxx | 8 +- 3 files changed, 189 insertions(+), 151 deletions(-) diff --git a/canvas/source/cairo/cairo_textlayout.cxx b/canvas/source/cairo/cairo_textlayout.cxx index d8b8126..506c9d5 100644 --- a/canvas/source/cairo/cairo_textlayout.cxx +++ b/canvas/source/cairo/cairo_textlayout.cxx @@ -106,6 +106,11 @@ namespace cairocanvas // as required at the API spec rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); } + + bool compareFallbacks(const SystemGlyphData&rA, const SystemGlyphData &rB) + { + return rA.fallbacklevel < rB.fallbacklevel; + } } TextLayout::TextLayout( const rendering::StringContext& aText, @@ -364,7 +369,6 @@ namespace cairocanvas return true; } - /** * TextLayout::draw * @@ -405,18 +409,41 @@ namespace cairocanvas ::canvas::tools::numeric_cast<USHORT>(maText.Length), maLogicalAdvancements.getLength() ? aOffsets.get() : NULL); + // Sort them so that all glyphs on the same glyph fallback level are consecutive + std::sort(aSysLayoutData.rGlyphData.begin(), aSysLayoutData.rGlyphData.end(), compareFallbacks); + bool bCairoRenderable = true; + + //Pull all the fonts we need to render the text + typedef std::pair<SystemFontData,int> FontLevel; + typedef std::vector<FontLevel> FontLevelVector; + FontLevelVector aFontData; + SystemGlyphDataVector::const_iterator aIter=aSysLayoutData.rGlyphData.begin(); + const SystemGlyphDataVector::const_iterator aEnd=aSysLayoutData.rGlyphData.end(); + for( ; aIter != aEnd; ++aIter ) + { + if( aFontData.empty() || aIter->fallbacklevel != aFontData.back().second ) + { + aFontData.push_back(FontLevel(rOutDev.GetSysFontData(aIter->fallbacklevel), + aIter->fallbacklevel)); + if( !isCairoRenderable(aFontData.back().first) ) + { + bCairoRenderable = false; + OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): VCL FALLBACK %s%s%s%s - %s", + maLogicalAdvancements.getLength() ? "ADV " : "", + aFontData.back().first.bAntialias ? "AA " : "", + aFontData.back().first.bFakeBold ? "FB " : "", + aFontData.back().first.bFakeItalic ? "FI " : "", + ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), + RTL_TEXTENCODING_UTF8 ).getStr()); + break; + } + } + } + // The ::GetSysTextLayoutData(), i.e. layouting of text to glyphs can change the font being used. // The fallback checks need to be done after final font is known. - if (!isCairoRenderable(aSysLayoutData.aSysFontData)) // VCL FALLBACKS + if (!bCairoRenderable) // VCL FALLBACKS { - OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): VCL FALLBACK %s%s%s%s - %s", - maLogicalAdvancements.getLength() ? "ADV " : "", - aSysLayoutData.aSysFontData.bAntialias ? "AA " : "", - aSysLayoutData.aSysFontData.bFakeBold ? "FB " : "", - aSysLayoutData.aSysFontData.bFakeItalic ? "FI " : "", - ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), - RTL_TEXTENCODING_UTF8 ).getStr()); - if (maLogicalAdvancements.getLength()) // VCL FALLBACK - with glyph advances { rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets.get(), @@ -438,145 +465,159 @@ namespace cairocanvas /** * Setup platform independent glyph vector into cairo-based glyphs vector. **/ - - // setup glyphs - std::vector<cairo_glyph_t> cairo_glyphs; - cairo_glyphs.reserve( 256 ); - - for( int nStart = 0; nStart < (int) aSysLayoutData.rGlyphData.size(); nStart++ ) + + // Loop through the fonts used and render the matching glyphs for each + FontLevelVector::const_iterator aFontDataIter = aFontData.begin(); + const FontLevelVector::const_iterator aFontDataEnd = aFontData.end(); + for( ; aFontDataIter != aFontDataEnd; ++aFontDataIter ) { - cairo_glyph_t aGlyph; - SystemGlyphData systemGlyph = aSysLayoutData.rGlyphData.at(nStart); - aGlyph.index = systemGlyph.index; -#ifdef CAIRO_HAS_WIN32_SURFACE - // Cairo requires standard glyph indexes (ETO_GLYPH_INDEX), while vcl/win/* uses ucs4 chars. - // Convert to standard indexes - aGlyph.index = cairo::ucs4toindex((unsigned int) aGlyph.index, aSysLayoutData.aSysFontData.hFont); -#endif - aGlyph.x = systemGlyph.x; - aGlyph.y = systemGlyph.y; - cairo_glyphs.push_back(aGlyph); - } - - if (cairo_glyphs.empty()) return true; //true or false?? - - /** - * Setup font - **/ - cairo_font_face_t* font_face = NULL; + const SystemFontData &rSysFontData = aFontDataIter->first; -#ifdef CAIRO_HAS_QUARTZ_SURFACE - // TODO: use cairo_quartz_font_face_create_for_cgfont(cgFont) - // when CGFont (Mac OS X 10.5 API) is provided by the AQUA VCL backend. - font_face = cairo_quartz_font_face_create_for_atsu_font_id((ATSUFontID) aSysLayoutData.aSysFontData.aATSUFontID); - -#elif defined CAIRO_HAS_WIN32_SURFACE - #if (OSL_DEBUG_LEVEL > 1) - GetObjectW( aSysLayoutData.aSysFontData.hFont, sizeof(logfont), &logfont ); - #endif - // Note: cairo library uses logfont fallbacks when lfEscapement, lfOrientation and lfWidth are not zero. - // VCL always has non-zero value for lfWidth - font_face = cairo_win32_font_face_create_for_hfont(aSysLayoutData.aSysFontData.hFont); - -#elif defined CAIRO_HAS_XLIB_SURFACE - font_face = cairo_ft_font_face_create_for_ft_face((FT_Face)aSysLayoutData.aSysFontData.nFontId, - aSysLayoutData.aSysFontData.nFontFlags); -#else -# error Native API needed. -#endif + // setup glyphs + std::vector<cairo_glyph_t> cairo_glyphs; + cairo_glyphs.reserve( 256 ); + + SystemGlyphDataVector::const_iterator aIter=aSysLayoutData.rGlyphData.begin(); + const SystemGlyphDataVector::const_iterator aEnd=aSysLayoutData.rGlyphData.end(); + for( ; aIter != aEnd; ++aIter ) + { + SystemGlyphData systemGlyph = *aIter; + if( systemGlyph.fallbacklevel != aFontDataIter->second ) + continue; + + cairo_glyph_t aGlyph; + aGlyph.index = systemGlyph.index; + #ifdef CAIRO_HAS_WIN32_SURFACE + // Cairo requires standard glyph indexes (ETO_GLYPH_INDEX), while vcl/win/* uses ucs4 chars. + // Convert to standard indexes + aGlyph.index = cairo::ucs4toindex((unsigned int) aGlyph.index, rSysFontData.hFont); + #endif + aGlyph.x = systemGlyph.x; + aGlyph.y = systemGlyph.y; + cairo_glyphs.push_back(aGlyph); + } - CairoSharedPtr pSCairo = pSurface->getCairo(); - - cairo_set_font_face( pSCairo.get(), font_face); - - // create default font options. cairo_get_font_options() does not retrieve the surface defaults, - // only what has been set before with cairo_set_font_options() - cairo_font_options_t* options = cairo_font_options_create(); - if (aSysLayoutData.aSysFontData.bAntialias) { - // CAIRO_ANTIALIAS_GRAY provides more similar result to VCL Canvas, - // so we're not using CAIRO_ANTIALIAS_SUBPIXEL - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); - } - cairo_set_font_options( pSCairo.get(), options); - - // Font color - Color mTextColor = rOutDev.GetTextColor(); - cairo_set_source_rgb(pSCairo.get(), - mTextColor.GetRed()/255.0, - mTextColor.GetGreen()/255.0, - mTextColor.GetBlue()/255.0); - - // Font rotation and scaling - cairo_matrix_t m; - Font aFont = rOutDev.GetFont(); - FontMetric aMetric( rOutDev.GetFontMetric(aFont) ); - long nWidth = 0; - - // width calculation is deep magic and platform/font dependant. - // width == 0 means no scaling, and usually width == height means the same. - // Other values mean horizontal scaling (narrow or stretching) - // see issue #101566 - - //proper scale calculation across platforms - if (aFont.GetWidth() == 0) { - nWidth = aFont.GetHeight(); - } else { - // any scaling needs to be relative to the platform-dependent definition - // of height of the font - nWidth = aFont.GetWidth() * aFont.GetHeight() / aMetric.GetHeight(); - } - - cairo_matrix_init_identity(&m); - - if (aSysLayoutData.orientation) cairo_matrix_rotate(&m, (3600 - aSysLayoutData.orientation) * M_PI / 1800.0); - - cairo_matrix_scale(&m, nWidth, aFont.GetHeight()); + if (cairo_glyphs.empty()) + continue; + + /** + * Setup font + **/ + cairo_font_face_t* font_face = NULL; + + #ifdef CAIRO_HAS_QUARTZ_SURFACE + // TODO: use cairo_quartz_font_face_create_for_cgfont(cgFont) + // when CGFont (Mac OS X 10.5 API) is provided by the AQUA VCL backend. + font_face = cairo_quartz_font_face_create_for_atsu_font_id((ATSUFontID) rSysFontData.aATSUFontID); + + #elif defined CAIRO_HAS_WIN32_SURFACE + #if (OSL_DEBUG_LEVEL > 1) + GetObjectW( rSysFontData.hFont, sizeof(logfont), &logfont ); + #endif + // Note: cairo library uses logfont fallbacks when lfEscapement, lfOrientation and lfWidth are not zero. + // VCL always has non-zero value for lfWidth + font_face = cairo_win32_font_face_create_for_hfont(rSysFontData.hFont); + + #elif defined CAIRO_HAS_XLIB_SURFACE + font_face = cairo_ft_font_face_create_for_ft_face((FT_Face)rSysFontData.nFontId, + rSysFontData.nFontFlags); + #else + # error Native API needed. + #endif + + CairoSharedPtr pSCairo = pSurface->getCairo(); + + cairo_set_font_face( pSCairo.get(), font_face); + + // create default font options. cairo_get_font_options() does not retrieve the surface defaults, + // only what has been set before with cairo_set_font_options() + cairo_font_options_t* options = cairo_font_options_create(); + if (rSysFontData.bAntialias) { + // CAIRO_ANTIALIAS_GRAY provides more similar result to VCL Canvas, + // so we're not using CAIRO_ANTIALIAS_SUBPIXEL + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + } + cairo_set_font_options( pSCairo.get(), options); + + // Font color + Color mTextColor = rOutDev.GetTextColor(); + cairo_set_source_rgb(pSCairo.get(), + mTextColor.GetRed()/255.0, + mTextColor.GetGreen()/255.0, + mTextColor.GetBlue()/255.0); + + // Font rotation and scaling + cairo_matrix_t m; + Font aFont = rOutDev.GetFont(); + FontMetric aMetric( rOutDev.GetFontMetric(aFont) ); + long nWidth = 0; + + // width calculation is deep magic and platform/font dependant. + // width == 0 means no scaling, and usually width == height means the same. + // Other values mean horizontal scaling (narrow or stretching) + // see issue #101566 + + //proper scale calculation across platforms + if (aFont.GetWidth() == 0) { + nWidth = aFont.GetHeight(); + } else { + // any scaling needs to be relative to the platform-dependent definition + // of height of the font + nWidth = aFont.GetWidth() * aFont.GetHeight() / aMetric.GetHeight(); + } + + cairo_matrix_init_identity(&m); + + if (aSysLayoutData.orientation) cairo_matrix_rotate(&m, (3600 - aSysLayoutData.orientation) * M_PI / 1800.0); + + cairo_matrix_scale(&m, nWidth, aFont.GetHeight()); - //faux italics - if (aSysLayoutData.aSysFontData.bFakeItalic) m.xy = -m.xx * 0x6000L / 0x10000L; - - cairo_set_font_matrix(pSCairo.get(), &m); - - OSL_TRACE("\r\n:cairocanvas::TextLayout::draw(S,O,p,v,r): Size:(%d,%d), W:%d->%d, Pos (%d,%d), G(%d,%d,%d) %s%s%s%s || Name:%s - %s", - aFont.GetWidth(), - aFont.GetHeight(), - aMetric.GetWidth(), - nWidth, - (int) rOutpos.X(), - (int) rOutpos.Y(), - cairo_glyphs[0].index, cairo_glyphs[1].index, cairo_glyphs[2].index, - maLogicalAdvancements.getLength() ? "ADV " : "", - aSysLayoutData.aSysFontData.bAntialias ? "AA " : "", - aSysLayoutData.aSysFontData.bFakeBold ? "FB " : "", - aSysLayoutData.aSysFontData.bFakeItalic ? "FI " : "", -#if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1) - ::rtl::OUStringToOString( reinterpret_cast<const sal_Unicode*> (logfont.lfFaceName), RTL_TEXTENCODING_UTF8 ).getStr(), -#else - ::rtl::OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(), -#endif - ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), - RTL_TEXTENCODING_UTF8 ).getStr() - ); - - cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); - - //faux bold - if (aSysLayoutData.aSysFontData.bFakeBold) { - double bold_dx = 0.5 * sqrt( 0.7 * aFont.GetHeight() ); - int total_steps = 2 * ((int) (bold_dx + 0.5)); + //faux italics + if (rSysFontData.bFakeItalic) m.xy = -m.xx * 0x6000L / 0x10000L; + + cairo_set_font_matrix(pSCairo.get(), &m); + + OSL_TRACE("\r\n:cairocanvas::TextLayout::draw(S,O,p,v,r): Size:(%d,%d), W:%d->%d, Pos (%d,%d), G(%d,%d,%d) %s%s%s%s || Name:%s - %s", + aFont.GetWidth(), + aFont.GetHeight(), + aMetric.GetWidth(), + nWidth, + (int) rOutpos.X(), + (int) rOutpos.Y(), + cairo_glyphs[0].index, cairo_glyphs[1].index, cairo_glyphs[2].index, + maLogicalAdvancements.getLength() ? "ADV " : "", + rSysFontData.bAntialias ? "AA " : "", + rSysFontData.bFakeBold ? "FB " : "", + rSysFontData.bFakeItalic ? "FI " : "", + #if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1) + ::rtl::OUStringToOString( reinterpret_cast<const sal_Unicode*> (logfont.lfFaceName), RTL_TEXTENCODING_UTF8 ).getStr(), + #else + ::rtl::OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(), + #endif + ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), + RTL_TEXTENCODING_UTF8 ).getStr() + ); + + cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); - // loop to draw the text for every half pixel of displacement - for (int nSteps = 0; nSteps < total_steps; nSteps++) { - for(int nGlyphIdx = 0; nGlyphIdx < (int) cairo_glyphs.size(); nGlyphIdx++) { - cairo_glyphs[nGlyphIdx].x += bold_dx * nSteps / total_steps; + //faux bold + if (rSysFontData.bFakeBold) { + double bold_dx = 0.5 * sqrt( 0.7 * aFont.GetHeight() ); + int total_steps = 2 * ((int) (bold_dx + 0.5)); + + // loop to draw the text for every half pixel of displacement + for (int nSteps = 0; nSteps < total_steps; nSteps++) { + for(int nGlyphIdx = 0; nGlyphIdx < (int) cairo_glyphs.size(); nGlyphIdx++) { + cairo_glyphs[nGlyphIdx].x += bold_dx * nSteps / total_steps; + } + cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); } - cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); + OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): FAKEBOLD - dx:%d", (int) bold_dx); } - OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): FAKEBOLD - dx:%d", (int) bold_dx); + + cairo_restore( pSCairo.get() ); + cairo_font_face_destroy(font_face); } - - cairo_restore( pSCairo.get() ); - cairo_font_face_destroy(font_face); return true; } diff --git a/vcl/inc/vcl/sysdata.hxx b/vcl/inc/vcl/sysdata.hxx index 018872b..1a8f56d 100644 --- a/vcl/inc/vcl/sysdata.hxx +++ b/vcl/inc/vcl/sysdata.hxx @@ -151,6 +151,7 @@ struct SystemGlyphData unsigned long index; double x; double y; + int fallbacklevel; }; @@ -179,12 +180,12 @@ struct SystemFontData // - SystemTextLayoutData - // -------------------- +typedef std::vector<SystemGlyphData> SystemGlyphDataVector; struct SystemTextLayoutData { - unsigned long nSize; // size in bytes of this structure - std::vector<SystemGlyphData> rGlyphData; // glyph data - int orientation; // Text orientation - SystemFontData aSysFontData; // Font data for the text layout + unsigned long nSize; // size in bytes of this structure + SystemGlyphDataVector rGlyphData; // glyph data + int orientation; // Text orientation }; #endif // _SV_SYSDATA_HXX diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx index b898258..96bfab5 100644 --- a/vcl/source/gdi/outdev3.cxx +++ b/vcl/source/gdi/outdev3.cxx @@ -7339,7 +7339,6 @@ SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, c // setup glyphs Point aPos; sal_GlyphId aGlyphId; - int nFallbacklevel = 0; for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) { // NOTE: Windows backend is producing unicode chars (ucs4), so on windows, @@ -7349,15 +7348,12 @@ SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, c aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK); aGlyph.x = aPos.X(); aGlyph.y = aPos.Y(); - aSysLayoutData.rGlyphData.push_back(aGlyph); - int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; - if (nLevel > nFallbacklevel && nLevel < MAX_FALLBACK) - nFallbacklevel = nLevel; + aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0; + aSysLayoutData.rGlyphData.push_back(aGlyph); } // Get font data - aSysLayoutData.aSysFontData = GetSysFontData(nFallbacklevel); aSysLayoutData.orientation = rLayout->GetOrientation(); rLayout->Release(); -- 1.7.1
Attachment:
pgpK149_K69ZZ.pgp
Description: PGP signature