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) {
