vcl/qa/cppunit/pdfexport/data/variable-font-psname-1.odt |binary vcl/qa/cppunit/pdfexport/data/variable-font-psname-2.odt |binary vcl/qa/cppunit/pdfexport/pdfexport.cxx | 98 +++++++++++++++ vcl/source/font/PhysicalFontFace.cxx | 70 ++++++++++ 4 files changed, 168 insertions(+)
New commits: commit a91a41527e0a6e5100ee2dcffb039e719e79afa1 Author: Khaled Hosny <[email protected]> AuthorDate: Fri Feb 20 15:06:34 2026 +0200 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Tue Mar 17 02:41:07 2026 +0100 Add tests for generating PostScript names of variable fonts Change-Id: I936ebe92bbbce17ce053baf8e1ba33a4866eda72 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199865 Tested-by: Jenkins Reviewed-by: Khaled Hosny <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201840 Reviewed-by: Tomaž Vajngerl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/vcl/qa/cppunit/pdfexport/data/variable-font-psname-1.odt b/vcl/qa/cppunit/pdfexport/data/variable-font-psname-1.odt new file mode 100644 index 000000000000..c1896f0bf852 Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/variable-font-psname-1.odt differ diff --git a/vcl/qa/cppunit/pdfexport/data/variable-font-psname-2.odt b/vcl/qa/cppunit/pdfexport/data/variable-font-psname-2.odt new file mode 100644 index 000000000000..c319125625aa Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/variable-font-psname-2.odt differ diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 2b7e3367731d..2d8ac206e939 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -17,6 +17,7 @@ #include <com/sun/star/view/XPrintable.hpp> #include <comphelper/propertysequence.hxx> +#include <comphelper/sequenceashashmap.hxx> #include <test/unoapi_test.hxx> #include <unotools/mediadescriptor.hxx> #include <unotools/tempfile.hxx> @@ -26,8 +27,30 @@ #include <vcl/filter/PDFiumLibrary.hxx> +#if defined MACOSX || 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 MACOSX || 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. @@ -2066,6 +2089,81 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf105954) CPPUNIT_ASSERT_LESS(static_cast<tools::Long>(250), aMeta.getWidth()); } +CPPUNIT_TEST_FIXTURE(PdfExportTest, testVariableFontPSName1) +{ +// Embedding variable fonts does not work on Linux, only the default instance is enumerated +// https://bugs.documentfoundation.org/show_bug.cgi?id=155853 +#if defined MACOSX || defined _WIN32 + loadFromFile(u"variable-font-psname-1.odt"); + save(TestFilter::PDF_WRITER); + + vcl::filter::PDFDocument aDocument; + SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ); + CPPUNIT_ASSERT(aDocument.Read(aStream)); + + std::set<OString> aFontNames; + for (const auto& aElement : aDocument.GetElements()) + { + auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get()); + if (!pObject) + continue; + auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"_ostr)); + if (pType && pType->GetValue() == "Font") + { + auto pName + = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("BaseFont"_ostr)); + aFontNames.insert(pName->GetValue().copy(7)); // skip the subset id + } + } + + std::set<OString> aExpected{ "STIXTwoText"_ostr, + "STIXTwoTextRoman-Bold"_ostr, + "STIXTwoText-Italic"_ostr, + "STIXTwoTextItalic-BoldItalic"_ostr, + "STIXTwoTextRoman-SemiBold"_ostr, + "STIXTwoTextItalic-SemiBoldItalic"_ostr }; + + CPPUNIT_ASSERT_EQUAL(aExpected, aFontNames); +#endif +} + +CPPUNIT_TEST_FIXTURE(PdfExportTest, testVariableFontPSName2) +{ +// Embedding variable fonts does not work on Linux, only the default instance is enumerated +// https://bugs.documentfoundation.org/show_bug.cgi?id=155853 +#if defined MACOSX || defined _WIN32 + loadFromFile(u"variable-font-psname-2.odt"); + save(TestFilter::PDF_WRITER); + + vcl::filter::PDFDocument aDocument; + SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ); + CPPUNIT_ASSERT(aDocument.Read(aStream)); + + std::set<OString> aFontNames; + for (const auto& aElement : aDocument.GetElements()) + { + auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get()); + if (!pObject) + continue; + auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"_ostr)); + if (pType && pType->GetValue() == "Font") + { + auto pName + = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("BaseFont"_ostr)); + aFontNames.insert(pName->GetValue().copy(7)); // skip the subset id + } + } + + std::set<OString> aExpected{ + "SourceCodePro-Regular"_ostr, "SourceCodePro-Bold"_ostr, + "SourceCodePro-Italic"_ostr, "SourceCodePro-BoldItalic"_ostr, + "SourceCodePro-SemiBold"_ostr, "SourceCodePro-SemiBoldItalic"_ostr + }; + + CPPUNIT_ASSERT_EQUAL(aExpected, aFontNames); +#endif +} + CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157679) { // Import the bugdoc and export as PDF. commit c27baf5a2915b7083a8fc1cea28b5368e834258a Author: Khaled Hosny <[email protected]> AuthorDate: Fri Feb 20 11:47:53 2026 +0200 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Tue Mar 17 02:40:56 2026 +0100 Generate PostScript name for variable font arbitrary instances Implements Adobe Technical Note #5902: “Generating PostScript Names for Fonts Using OpenType Font Variations” https://adobe-type-tools.github.io/font-tech-notes/pdfs/5902.AdobePSNameGeneration.pdf Change-Id: I0f83bd9565c3bafa5e82265535dbbed4c95fc571 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199864 Reviewed-by: Khaled Hosny <[email protected]> Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201839 Reviewed-by: Tomaž Vajngerl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/vcl/source/font/PhysicalFontFace.cxx b/vcl/source/font/PhysicalFontFace.cxx index abd874ede001..40b5efa2848c 100644 --- a/vcl/source/font/PhysicalFontFace.cxx +++ b/vcl/source/font/PhysicalFontFace.cxx @@ -351,6 +351,72 @@ OUString GetNamedInstancePSName(const PhysicalFontFace& rFontFace, return OUString(); } + +// Implements Adobe Technical Note #5902: “Generating PostScript Names for Fonts +// Using OpenType Font Variations” +// https://adobe-type-tools.github.io/font-tech-notes/pdfs/5902.AdobePSNameGeneration.pdf +OUString GenerateVariableFontPSName(const PhysicalFontFace& rFace, + const std::vector<hb_variation_t>& rVariations) +{ + hb_face_t* pHbFace = rFace.GetHbFace(); + OUString aPrefix = rFace.GetName(NAME_ID_VARIATIONS_PS_PREFIX); + if (aPrefix.isEmpty()) + { + aPrefix = rFace.GetName(NAME_ID_TYPOGRAPHIC_FAMILY); + if (aPrefix.isEmpty()) + aPrefix = rFace.GetName(NAME_ID_FONT_FAMILY); + } + + if (aPrefix.isEmpty()) + return OUString(); + + OUStringBuffer aName; + for (sal_Int32 i = 0; i < aPrefix.getLength(); ++i) + { + auto c = aPrefix[i]; + if (rtl::isAsciiAlphanumeric(c)) + aName.append(c); + } + + if (auto nIndex = GetNamedInstanceIndex(pHbFace, rVariations)) + { + aName.append('-'); + auto nPSNameID = hb_ot_var_named_instance_get_subfamily_name_id(pHbFace, *nIndex); + OUString aSubFamilyName = rFace.GetName(static_cast<NameID>(nPSNameID)); + for (sal_Int32 i = 0; i < aSubFamilyName.getLength(); ++i) + { + auto c = aSubFamilyName[i]; + if (rtl::isAsciiAlphanumeric(c)) + aName.append(c); + } + } + else + { + for (const auto& rVariation : rVariations) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info(pHbFace, rVariation.tag, &info)) + { + if (rVariation.value == info.default_value) + continue; + char aTag[5]; + hb_tag_to_string(rVariation.tag, aTag); + aName.append("_" + OUString::number(rVariation.value) + + o3tl::trim(OUString::createFromAscii(aTag))); + } + } + } + + if (aName.getLength() > 127) + { + auto nIndex = aName.indexOf(u'-') + 1; + auto aHash = static_cast<sal_uInt32>(aName.copy(nIndex).makeStringAndClear().hashCode()); + aName.truncate(nIndex); + aName.append(OUString::number(aHash, 16).toAsciiUpperCase() + "..."); + } + + return aName.makeStringAndClear(); +} } // These are “private” HarfBuzz metrics tags, they are supported by not exposed @@ -422,7 +488,11 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer, // If this is a named instance and it has a PostScript name, we want to use it. if (bIsVariableFont) + { rInfo.m_aPSName = GetNamedInstancePSName(*this, rVariations); + if (rInfo.m_aPSName.isEmpty() && !rVariations.empty()) + rInfo.m_aPSName = GenerateVariableFontPSName(*this, rVariations); + } if (rInfo.m_aPSName.isEmpty()) rInfo.m_aPSName = GetName(NAME_ID_POSTSCRIPT_NAME);
