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

Reply via email to