include/vcl/glyphitemcache.hxx  |    5 +++--
 vcl/qa/unx/generic/generic.cxx  |   30 ++++++++++++++++++++++++++++++
 vcl/source/gdi/impglyphitem.cxx |    7 ++++++-
 3 files changed, 39 insertions(+), 3 deletions(-)

New commits:
commit ef1870810ec8c069e26538fd7626ad0656bed276
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Jul 31 13:08:42 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Jul 31 16:56:26 2024 +0200

    tdf#162280 vcl: consider font family type for glyph caching
    
    Open tdf105820-1.doc from crashtesting in a dbgutil build, save as PDF,
    we assert in checkGlyphsEqual().
    
    This is a problem since commit 6dfac38bacd449c64a13363797b56aff49cf8f52
    (tdf#162072 vcl, fontconfig: consider font-family-generic for
    substitute, 2024-07-18), because now the font name no longer determines
    the fallback name alone, so 2 paragraphs may share the cached vcl-level
    text layout, even if the underlying fallback font is different. One
    would naively expect that in the SalLayoutGlyphsCache::CachedGlyphsKey
    ctor, the fontMetric.GetFamilyType() already has the correct family
    type, but it turns out that is always the default, and only
    outputDevice->GetFont().GetFamilyType() has the up to date setting:
    
    debug:12372:12372: SalLayoutGlyphsCache ctor: output device font family 
name is 'Verdana', output device font family type is roman, font metric family 
name is 'Verdana', font metric family type is 'swiss'
    debug:12372:12372: SalLayoutGlyphsCache ctor: output device font family 
name is 'Verdana', output device font family type is swiss, font metric family 
name is 'Verdana', font metric family type is 'swiss'
    
    Fix the problem by explicitly including the output device font family
    type in the cache key.
    
    Note that this only happens in practice if the same font is used in the
    document with different family types, which is probably never the
    intention of the user. E.g. Verdana is meant to be sans, a serif Verdana
    is some weird corner-case.
    
    Change-Id: Id6cc60809a35a3dcdc6e83122a559ddfbe5d5d0c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171280
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/vcl/glyphitemcache.hxx b/include/vcl/glyphitemcache.hxx
index 79f05e606550..373f270a65bc 100644
--- a/include/vcl/glyphitemcache.hxx
+++ b/include/vcl/glyphitemcache.hxx
@@ -72,8 +72,7 @@ public:
     {
     }
 
-private:
-    struct SAL_DLLPRIVATE CachedGlyphsKey
+    struct CachedGlyphsKey
     {
         OUString text;
         sal_Int32 index;
@@ -94,6 +93,8 @@ private:
                         tools::Long w);
         bool operator==(const CachedGlyphsKey& other) const;
     };
+
+private:
     struct CachedGlyphsHash
     {
         size_t operator()(const CachedGlyphsKey& key) const { return 
key.hashValue; }
diff --git a/vcl/qa/unx/generic/generic.cxx b/vcl/qa/unx/generic/generic.cxx
index 7d71bec072e5..22176c28633c 100644
--- a/vcl/qa/unx/generic/generic.cxx
+++ b/vcl/qa/unx/generic/generic.cxx
@@ -10,6 +10,10 @@
 #include <test/unoapi_test.hxx>
 
 #include <vcl/font.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/wintypes.hxx>
+#include <vcl/window.hxx>
+#include <vcl/glyphitemcache.hxx>
 
 #include <font/FontSelectPattern.hxx>
 #include <unx/fontmanager.hxx>
@@ -61,6 +65,32 @@ CPPUNIT_TEST_FIXTURE(Test, testFontFallbackSerif)
     // i.e. we got a sans fallback for a serif pattern, which is clearly poor.
     CPPUNIT_ASSERT_EQUAL(sResolvedSerif, sPlexFallback);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testFontFallbackCaching)
+{
+    // Given a vcl-level text layout, created for the serif Verdana:
+    ScopedVclPtrInstance<vcl::Window> pWindow(nullptr, WB_APP | WB_STDWORK);
+    VclPtr<OutputDevice> pOutDev = pWindow->GetOutDev();
+    vcl::Font aFont;
+    aFont.SetFamilyName("Verdana");
+    aFont.SetFamily(FAMILY_ROMAN);
+    pOutDev->SetFont(aFont);
+    OUString aText = u"1-1-2017"_ustr;
+    sal_Int32 nIndex = 0;
+    sal_Int32 nLength = aText.getLength();
+    tools::Long nLogicWidth = 0;
+    SalLayoutGlyphsCache::CachedGlyphsKey aKey1(pOutDev, aText, nIndex, 
nLength, nLogicWidth);
+
+    // When creating a layout for the sans Verdana:
+    aFont.SetFamily(FAMILY_SWISS);
+    pOutDev->SetFont(aFont);
+    SalLayoutGlyphsCache::CachedGlyphsKey aKey2(pOutDev, aText, nIndex, 
nLength, nLogicWidth);
+
+    // Then make sure these two layouts don't match:
+    // Without the accompanying fix in place, this test would have failed, the 
Noto Serif layout was
+    // reused for the Noto Sans layout if those were selected as fallbacks.
+    CPPUNIT_ASSERT(aKey1 != aKey2);
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/source/gdi/impglyphitem.cxx b/vcl/source/gdi/impglyphitem.cxx
index 184e9f819b5a..3ac42bef226a 100644
--- a/vcl/source/gdi/impglyphitem.cxx
+++ b/vcl/source/gdi/impglyphitem.cxx
@@ -555,9 +555,14 @@ SalLayoutGlyphsCache::CachedGlyphsKey::CachedGlyphsKey(
     o3tl::hash_combine(hashValue, artificialBold);
     o3tl::hash_combine(hashValue, layoutMode);
     o3tl::hash_combine(hashValue, digitLanguage.get());
+
+    // In case the font name is the same, but the font family differs, then 
the font metric won't
+    // contain that custom font family, so explicitly include the font family 
from the output device
+    // font.
+    o3tl::hash_combine(hashValue, outputDevice->GetFont().GetFamilyType());
 }
 
-inline bool SalLayoutGlyphsCache::CachedGlyphsKey::operator==(const 
CachedGlyphsKey& other) const
+bool SalLayoutGlyphsCache::CachedGlyphsKey::operator==(const CachedGlyphsKey& 
other) const
 {
     return hashValue == other.hashValue && index == other.index && len == 
other.len
            && logicWidth == other.logicWidth && mapMode == other.mapMode && 
rtl == other.rtl

Reply via email to