include/vcl/outdev.hxx         |    2 ++
 vcl/qa/cppunit/complextext.cxx |   37 ++++++++++++++++++++++++++++++++++---
 vcl/source/outdev/font.cxx     |   32 +++++++++++++++++++++++++++++++-
 vcl/source/outdev/outdev.cxx   |    6 ++++--
 4 files changed, 71 insertions(+), 6 deletions(-)

New commits:
commit 042dc4a9186f526b625817dde50bb2f5a91fd2c6
Author:     Chris Sherlock <chris.sherloc...@gmail.com>
AuthorDate: Thu Nov 9 18:19:41 2023 +1100
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Tue Dec 5 04:39:05 2023 +0100

    vcl: add unit tests for a mixture of CJK and latin characters
    
    To influence the fallback fonts chosen, I have introduced a new
    OutputDevice function ForceFallbackFont() which injects ("forces") a
    font as the first one in the font fallback chain.
    
    Change-Id: I05856cbe829fde0eb140bb48a37795a84d780900
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159221
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index bea1e00d9b7f..329364559108 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -184,6 +184,7 @@ private:
     mutable VclPtr<OutputDevice>    mpNextGraphics;     ///< Next output 
device in list
     GDIMetaFile*                    mpMetaFile;
     mutable rtl::Reference<LogicalFontInstance> mpFontInstance;
+    rtl::Reference<LogicalFontInstance> mpForcedFallbackInstance;
     mutable std::unique_ptr<vcl::font::PhysicalFontFaceCollection>  
mpFontFaceCollection;
     std::vector<vcl::State>        maOutDevStateStack;
     std::unique_ptr<ImplOutDevData> mpOutDevData;
@@ -1165,6 +1166,7 @@ public:
     SAL_DLLPRIVATE static void  ImplUpdateAllFontData( bool bNewFontLists );
 
     LogicalFontInstance const* GetFontInstance() const;
+    bool ForceFallbackFont(vcl::Font const& rFallbackFont);
 
 protected:
     SAL_DLLPRIVATE tools::Long GetEmphasisAscent() const { return 
mnEmphasisAscent; }
diff --git a/vcl/qa/cppunit/complextext.cxx b/vcl/qa/cppunit/complextext.cxx
index 633dc2210e07..d4eb3db67093 100644
--- a/vcl/qa/cppunit/complextext.cxx
+++ b/vcl/qa/cppunit/complextext.cxx
@@ -491,6 +491,40 @@ CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf153440)
 #endif
 }
 
+CPPUNIT_TEST_FIXTURE(VclComplexTextTest, 
testMixedCJKLatinScript_glyph_advancements)
+{
+#if HAVE_MORE_FONTS
+#if !defined _WIN32
+    OUString aTestScript(u"根据10.1(37BA) Eng"_ustr);
+
+    ScopedVclPtrInstance<VirtualDevice> pOutDev;
+    // note you can only run this once and it was designed for tdf#107718
+    bool bAdded = addFont(pOutDev, u"tdf107718.otf", u"Source Han Sans");
+    CPPUNIT_ASSERT_EQUAL(true, bAdded);
+
+    vcl::Font aFont(u"Source Han Sans"_ustr, u"Regular"_ustr, Size(0, 72));
+    pOutDev->SetFont( aFont );
+
+    vcl::Font aFallbackFont("DejaVu Sans", "Book", Size(0, 72));
+    pOutDev->ForceFallbackFont(aFallbackFont);
+
+    // absolute character widths AKA text array.
+    tools::Long nRefTextWidth = 704;
+    std::vector<sal_Int32> aRefCharWidths = { 72, 144, 190, 236, 259, 305, 
333, 379, 425, 474, 523, 551, 567, 612, 658, 704 };
+    KernArray aCharWidths;
+    tools::Long nTextWidth = pOutDev->GetTextArray(aTestScript, &aCharWidths);
+
+    CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths.get_subunit_array());
+    CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(nTextWidth), aCharWidths.back());
+
+    // text advance width and line height
+    CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aTestScript));
+    CPPUNIT_ASSERT_EQUAL(tools::Long(105), pOutDev->GetTextHeight());
+#endif
+#endif
+}
+
 CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf107718)
 {
 #if !defined _WIN32 // TODO: Fails on jenkins but passes locally
@@ -498,9 +532,6 @@ CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf107718)
 
     ScopedVclPtrInstance<VirtualDevice> pOutDev;
 
-    bool bAdded = addFont(pOutDev, u"tdf107718.otf", u"Source Han Sans");
-    CPPUNIT_ASSERT_EQUAL(true, bAdded);
-
     OUString aText(u"\u4E16\u1109\u1168\u11BC\u302E"_ustr);
     for (bool bVertical : { false, true })
     {
diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx
index ea0727b294f9..0e37c7613ded 100644
--- a/vcl/source/outdev/font.cxx
+++ b/vcl/source/outdev/font.cxx
@@ -1020,6 +1020,22 @@ std::unique_ptr<SalLayout> 
OutputDevice::getFallbackLayout(
     return pFallback;
 }
 
+bool OutputDevice::ForceFallbackFont(vcl::Font const& rFallbackFont)
+{
+    vcl::Font aOldFont = GetFont();
+    SetFont(rFallbackFont);
+    InitFont();
+
+    mpForcedFallbackInstance = mpFontInstance;
+    SetFont(aOldFont);
+    InitFont();
+
+    if (mpForcedFallbackInstance)
+        return true;
+
+    return false;
+}
+
 std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( 
std::unique_ptr<SalLayout> pSalLayout,
     vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs ) 
const
 {
@@ -1051,12 +1067,22 @@ std::unique_ptr<SalLayout> 
OutputDevice::ImplGlyphFallbackLayout( std::unique_pt
     vcl::font::FontSelectPattern 
aFontSelData(mpFontInstance->GetFontSelectPattern());
     SalLayoutGlyphsImpl* pGlyphsImpl = pGlyphs ? pGlyphs->Impl(1) : nullptr;
 
+    bool bHasUsedFallback = false;
+
     // try if fallback fonts support the missing code units
     for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; 
++nFallbackLevel )
     {
         rtl::Reference<LogicalFontInstance> pFallbackFont;
-        if(pGlyphsImpl != nullptr)
+        if (!bHasUsedFallback && mpForcedFallbackInstance)
+        {
+            pFallbackFont = mpForcedFallbackInstance;
+            bHasUsedFallback = true;
+        }
+        else if(pGlyphsImpl != nullptr)
+        {
             pFallbackFont = pGlyphsImpl->GetFont();
+        }
+
         // find a font family suited for glyph fallback
         // GetGlyphFallbackFont() needs a valid FontInstance
         // if the system-specific glyph fallback is active
@@ -1067,6 +1093,9 @@ std::unique_ptr<SalLayout> 
OutputDevice::ImplGlyphFallbackLayout( std::unique_pt
         if( !pFallbackFont )
             break;
 
+        SAL_INFO("vcl", "Fallback font (level " << nFallbackLevel << "): 
family: " << pFallbackFont->GetFontFace()->GetFamilyName()
+                << ", style: " << 
pFallbackFont->GetFontFace()->GetStyleName());
+
         if( nFallbackLevel < MAX_FALLBACK-1)
         {
             // ignore fallback font if it is the same as the original font
@@ -1255,6 +1284,7 @@ void OutputDevice::ImplReleaseFonts()
     mbInitFont = true;
 
     mpFontInstance.clear();
+    mpForcedFallbackInstance.clear();
     mpFontFaceCollection.reset();
 }
 
diff --git a/vcl/source/outdev/outdev.cxx b/vcl/source/outdev/outdev.cxx
index b4c1eac4499e..828a121cca91 100644
--- a/vcl/source/outdev/outdev.cxx
+++ b/vcl/source/outdev/outdev.cxx
@@ -67,8 +67,9 @@ OutputDevice::OutputDevice(OutDevType eOutDevType) :
     mpPrevGraphics                  = nullptr;
     mpNextGraphics                  = nullptr;
     mpMetaFile                      = nullptr;
-    mpFontInstance                     = nullptr;
-    mpFontFaceCollection                = nullptr;
+    mpFontInstance                  = nullptr;
+    mpForcedFallbackInstance        = nullptr;
+    mpFontFaceCollection            = nullptr;
     mpAlphaVDev                     = nullptr;
     mpExtOutDevData                 = nullptr;
     mnOutOffX                       = 0;
@@ -166,6 +167,7 @@ void OutputDevice::dispose()
 
     // release the active font instance
     mpFontInstance.clear();
+    mpForcedFallbackInstance.clear();
 
     // remove cached results of GetDevFontList/GetDevSizeList
     mpFontFaceCollection.reset();

Reply via email to