vcl/qa/cppunit/pdfexport/pdfexport.cxx  |   18 ++------------
 vcl/qa/cppunit/pdfexport/pdfexport2.cxx |   40 ++++++++++++++++++++++++--------
 vcl/source/font/PhysicalFontFace.cxx    |    7 ++++-
 vcl/source/pdf/pdfwriter_impl.cxx       |    5 ++++
 4 files changed, 44 insertions(+), 26 deletions(-)

New commits:
commit 150b3deb2ac4e1b6930d296799bec3ded43f1b3d
Author:     Khaled Hosny <[email protected]>
AuthorDate: Wed Mar 4 21:20:08 2026 +0200
Commit:     Khaled Hosny <[email protected]>
CommitDate: Thu Mar 5 02:15:01 2026 +0100

    Downgrade CFF2 table to CFF if we have a new enough HarfBuzz
    
    Instead of drawing the outlines as Type 3 fonts, we now convert CFF2
    table to CFF which then goes to our regular PDF embedding process (i.e
    it gets converted to Type 1 fonts for now).
    
    The test expectations are updated to reflect that.
    
    Change-Id: I1923010a72241e0178967da3924ab91fe2df9910
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200976
    Reviewed-by: Khaled Hosny <[email protected]>
    Tested-by: Jenkins

diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 1d8050154634..27e7b7188ed5 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -1874,23 +1874,11 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf66597_2)
             auto pType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"_ostr));
             if (pType && pType->GetValue() == "Font")
             {
-                // Static font identified under "BaseFont"
                 auto pName
                     = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("BaseFont"_ostr));
-                OString fontName;
-                if (pName)
-                {
-                    fontName = pName->GetValue().copy(7); // skip the subset id
-                }
-                else // Variable font identified under "Name", try that
-                {
-                    pName
-                        = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Name"_ostr));
-                    CPPUNIT_ASSERT_MESSAGE("No 'BaseFont' or 'Name' element 
found for font!",
-                                           pName);
-                    fontName = pName->GetValue();
-                }
-                CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected font name", 
"ReemKufi"_ostr, fontName);
+                CPPUNIT_ASSERT(pName);
+                OString aFontName = pName->GetValue().copy(7); // skip the 
subset id
+                CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected font name", 
"ReemKufi"_ostr, aFontName);
 
                 auto pToUnicodeRef = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(
                     pObject->Lookup("ToUnicode"_ostr));
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
index 1b2f3bba3a10..1c593cdfd680 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -28,7 +28,6 @@
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/sequenceashashmap.hxx>
 #include <basegfx/vector/b2dsize.hxx>
-#include <test/unoapi_test.hxx>
 #include <unotools/tempfile.hxx>
 #include <vcl/filter/pdfdocument.hxx>
 #include <tools/zcodec.hxx>
@@ -52,8 +51,30 @@
 #include <cmath>
 #include <libxml/xpathInternals.h>
 
+#if !defined _WIN32
+#include <set>
+static std::ostream& operator<<(std::ostream& rStream, const 
std::set<rtl::OString>& rSet);
+#endif
+
+#include <test/unoapi_test.hxx>
+
 using namespace ::com::sun::star;
 
+#if !defined _WIN32
+static std::ostream& operator<<(std::ostream& rStream, const 
std::set<OString>& rSet)
+{
+    rStream << "{ ";
+    for (auto it = rSet.begin(); it != rSet.end(); ++it)
+    {
+        if (it != rSet.begin())
+            rStream << ", ";
+        rStream << *it;
+    }
+    rStream << " }";
+    return rStream;
+}
+#endif
+
 namespace
 {
 /// Tests the PDF export filter.
@@ -5202,7 +5223,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155161)
     CPPUNIT_ASSERT(aDocument.Read(aStream));
 
     // Check that all fonts in the document are Type 3 fonts
-    int nFonts = 0;
+    std::set<OString> aFontNames;
     for (const auto& aElement : aDocument.GetElements())
     {
         auto pObject = 
dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get());
@@ -5214,24 +5235,23 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155161)
             auto pSubtype
                 = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Subtype"_ostr));
             CPPUNIT_ASSERT(pSubtype);
-            CPPUNIT_ASSERT_EQUAL("Type3"_ostr, pSubtype->GetValue());
-
-            auto pName = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Name"_ostr));
+            CPPUNIT_ASSERT_EQUAL("Type1"_ostr, pSubtype->GetValue());
+            auto pName
+                = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("BaseFont"_ostr));
             CPPUNIT_ASSERT(pName);
-            CPPUNIT_ASSERT_EQUAL("Cantarell-Regular"_ostr, pName->GetValue());
-
-            nFonts++;
+            aFontNames.insert(pName->GetValue().copy(7)); // skip the subset id
         }
     }
 
 #ifdef MACOSX
     // There must be two fonts
-    CPPUNIT_ASSERT_EQUAL(2, nFonts);
+    std::set<OString> aExpected{ "Cantarell-Regular"_ostr, 
"Cantarell-Bold"_ostr };
 #else
     // But it seems that embedded variable fonts don’t register all supported
     // styles on Linux, so the bold and regular text use the same regular font.
-    CPPUNIT_ASSERT(nFonts);
+    std::set<OString> aExpected{ "Cantarell-Regular"_ostr };
 #endif
+    CPPUNIT_ASSERT_EQUAL(aExpected, aFontNames);
 #endif
 }
 
diff --git a/vcl/source/font/PhysicalFontFace.cxx 
b/vcl/source/font/PhysicalFontFace.cxx
index f3c591a5545b..1bdf8faaf183 100644
--- a/vcl/source/font/PhysicalFontFace.cxx
+++ b/vcl/source/font/PhysicalFontFace.cxx
@@ -437,6 +437,11 @@ bool 
PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer,
     if (!pInput)
         return false;
 
+#if HB_VERSION_ATLEAST(13, 0, 0)
+    // If the font has CFF2 table, we need to downgrade it to CFF, as we can’t 
embed CFF2 in PDF.
+    hb_subset_input_set_flags(pInput, HB_SUBSET_FLAGS_DOWNGRADE_CFF2);
+#endif
+
     // Add the requested glyph IDs to the subset input, and set up
     // old-to-new glyph ID mapping so that each glyph appears at the
     // GID position matching its encoding byte.
@@ -456,7 +461,7 @@ bool 
PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer,
         HB_TAG('l', 'o', 'c', 'a'), HB_TAG('m', 'a', 'x', 'p'), HB_TAG('g', 
'l', 'y', 'f'),
         HB_TAG('C', 'F', 'F', ' '), HB_TAG('p', 'o', 's', 't'), HB_TAG('n', 
'a', 'm', 'e'),
         HB_TAG('O', 'S', '/', '2'), HB_TAG('c', 'v', 't', ' '), HB_TAG('f', 
'p', 'g', 'm'),
-        HB_TAG('p', 'r', 'e', 'p'),
+        HB_TAG('p', 'r', 'e', 'p'), HB_TAG('C', 'F', 'F', '2'),
     };
 
     hb_set_t* pDropTableSet = hb_subset_input_set(pInput, 
HB_SUBSET_SETS_DROP_TABLE_TAG);
diff --git a/vcl/source/pdf/pdfwriter_impl.cxx 
b/vcl/source/pdf/pdfwriter_impl.cxx
index 25354a1e4604..d04618aaa4e0 100644
--- a/vcl/source/pdf/pdfwriter_impl.cxx
+++ b/vcl/source/pdf/pdfwriter_impl.cxx
@@ -5521,7 +5521,12 @@ void PDFWriterImpl::registerGlyph(const sal_GlyphId 
nFontGlyphId,
     // PDF doesn't support CFF2 table and we currently don't convert them to
     // Type 1 (like we do with CFF table), so embed as Type 3 fonts.
     // Non-CFF2 variable fonts are instanced via hb-subset and embedded 
normally.
+    // With HarfBuzz 13.0.0, we downgrade CFF2 to CFF when subsetting.
+#if HB_VERSION_ATLEAST(13, 0, 0)
+    bool bCFF2 = false;
+#else
     bool bCFF2 = !pFace->GetRawFontData(HB_TAG('C', 'F', 'F', '2')).empty();
+#endif
 
     if (pFace->IsColorFont() || bCFF2)
     {

Reply via email to