external/pdfium/UnpackedTarball_pdfium.mk |    1 
 external/pdfium/getfontdictobjnum.patch.1 |   32 +++++++++++++++
 include/vcl/filter/PDFiumLibrary.hxx      |    2 
 include/vcl/filter/embedfontinfo.hxx      |   44 +++++++++++++++++++++
 include/vcl/gfxlink.hxx                   |    8 +++
 svx/source/inc/svdpdf.hxx                 |   16 -------
 svx/source/svdraw/svdpdf.cxx              |   62 ++++++++++++++++++------------
 vcl/source/pdf/PDFiumLibrary.cxx          |    4 +
 8 files changed, 130 insertions(+), 39 deletions(-)

New commits:
commit 0d9285c771e3598211a8a7e837af6eb1290decc9
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri Oct 24 10:46:18 2025 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Sun Oct 26 12:09:15 2025 +0100

    only import fonts once from a pdf
    
    typically there's one pdf that multiple Graphics back on to, one per
    page, so we only need to import the fonts once for those to be
    available. But we do need to know the mappings that were extracted.
    
    Change-Id: Icb27cee4b6e94585aaa0f751a3388b34566b610a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192982
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/include/vcl/filter/embedfontinfo.hxx 
b/include/vcl/filter/embedfontinfo.hxx
new file mode 100644
index 000000000000..5ca51c4ce302
--- /dev/null
+++ b/include/vcl/filter/embedfontinfo.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <tools/fontenum.hxx>
+
+// A description of an imported font as LibreOffice sees it
+// e.g. "Name SemiBold"
+struct OfficeFontInfo
+{
+    sal_Int32 nUniqueIdent;
+    OUString sFontName;
+    FontWeight eFontWeight;
+};
+
+struct OfficeFontInfoCmp
+{
+    bool operator()(const OfficeFontInfo& rA, const OfficeFontInfo& rB) const
+    {
+        return rA.nUniqueIdent < rB.nUniqueIdent;
+    }
+
+    bool operator()(int nAUniqueIdent, const OfficeFontInfo& rB) const
+    {
+        return nAUniqueIdent < rB.nUniqueIdent;
+    }
+
+    bool operator()(const OfficeFontInfo& rA, int nBUniqueIdent) const
+    {
+        return rA.nUniqueIdent < nBUniqueIdent;
+    }
+};
+
+typedef o3tl::sorted_vector<OfficeFontInfo, OfficeFontInfoCmp> ImportedFontMap;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/vcl/gfxlink.hxx b/include/vcl/gfxlink.hxx
index 1f0e0cf99f53..55e294aba70e 100644
--- a/include/vcl/gfxlink.hxx
+++ b/include/vcl/gfxlink.hxx
@@ -23,6 +23,7 @@
 #include <tools/gen.hxx>
 #include <vcl/dllapi.h>
 #include <vcl/mapmod.hxx>
+#include <vcl/filter/embedfontinfo.hxx>
 #include <vcl/BinaryDataContainer.hxx>
 
 class SvStream;
@@ -63,6 +64,10 @@ private:
     GfxLinkType     meType;
     sal_uInt32      mnUserId;
     BinaryDataContainer maDataContainer;
+    /// Useful for PDF, where multiple Graphics referring to individual pages
+    /// of a single PDF share a GfxLink, to describe PDF fonts that are already
+    /// imported from the PDF.
+    std::shared_ptr<ImportedFontMap> mxImportedFonts;
     mutable size_t  maHash;
     MapMode         maPrefMapMode;
     Size            maPrefSize;
@@ -101,6 +106,9 @@ public:
     void                SetPrefMapMode( const MapMode& rPrefMapMode );
     bool                IsPrefMapModeValid() const { return 
mbPrefMapModeValid;}
 
+    void setImportedFonts(const std::shared_ptr<ImportedFontMap>& 
rImportedFonts) { mxImportedFonts = rImportedFonts; }
+    const std::shared_ptr<ImportedFontMap>& getImportedFonts() const { return 
mxImportedFonts; }
+
     bool                IsNative() const;
 
     bool LoadNative(Graphic& rGraphic, sal_Int32 nPageNum = -1) const;
diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx
index 6942ab90b720..c0783ff3fb5a 100644
--- a/svx/source/inc/svdpdf.hxx
+++ b/svx/source/inc/svdpdf.hxx
@@ -80,24 +80,11 @@ struct EmbeddedFontInfo
     FontWeight eFontWeight;
 };
 
-// A description of such a final font as LibreOffice sees it
-// e.g. "Name SemiBold"
-struct OfficeFontInfo
-{
-    OUString sFontName;
-    FontWeight eFontWeight;
-};
-
 // Helper Class to import PDF
 class ImpSdrPdfImport final
 {
     std::vector<rtl::Reference<SdrObject>> maTmpList;
 
-    std::map<sal_Int32, OfficeFontInfo> maImportedFonts;
-    std::map<OUString, SubSetInfo> maDifferentSubsetsForFont;
-    // map of PostScriptName->Merged Font File for that font
-    std::map<OUString, EmbeddedFontInfo> maEmbeddedFonts;
-
     ScopedVclPtr<VirtualDevice> mpVD;
     tools::Rectangle maScaleRect;
     size_t mnMapScalingOfs; // from here on, not edited with MapScaling
@@ -105,6 +92,7 @@ class ImpSdrPdfImport final
     std::unique_ptr<SfxItemSet> mpFillAttr;
     std::unique_ptr<SfxItemSet> mpTextAttr;
     SdrModel* mpModel;
+    std::shared_ptr<ImportedFontMap> mxImportedFonts;
     SdrLayerID mnLayer;
     sal_Int32 mnLineWidth;
     static constexpr css::drawing::LineCap gaLineCap = 
css::drawing::LineCap_BUTT;
@@ -185,7 +173,7 @@ class ImpSdrPdfImport final
 
 #if HAVE_FEATURE_PDFIMPORT
 
-    void CollectFonts();
+    static ImportedFontMap CollectFonts(vcl::pdf::PDFiumDocument& 
rPdfDocument);
 
     static EmbeddedFontInfo convertToOTF(SubSetInfo& rSubSetInfo, const 
OUString& fileUrl,
                                          const OUString& fontName, const 
OUString& baseFontName,
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 054ffe74bbbe..333260ac8ffc 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -111,7 +111,7 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, 
SdrLayerID nLay, const tools:
 
     // Load the buffer using pdfium.
     auto const& rVectorGraphicData = rGraphic.getVectorGraphicData();
-    auto* pData = rVectorGraphicData->getBinaryDataContainer().getData();
+    const auto* pData = rVectorGraphicData->getBinaryDataContainer().getData();
     sal_Int32 nSize = rVectorGraphicData->getBinaryDataContainer().getSize();
     mpPdfDocument = mpPDFium ? mpPDFium->openDocument(pData, nSize, OString()) 
: nullptr;
     if (!mpPdfDocument)
@@ -120,7 +120,15 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, 
SdrLayerID nLay, const tools:
     mnPageCount = mpPdfDocument->getPageCount();
 
 #if HAVE_FEATURE_PDFIMPORT
-    CollectFonts();
+    const std::shared_ptr<GfxLink> xGrfLink = rGraphic.GetSharedGfxLink();
+    if (xGrfLink)
+        mxImportedFonts = xGrfLink->getImportedFonts();
+    if (!mxImportedFonts)
+    {
+        mxImportedFonts = 
std::make_shared<ImportedFontMap>(CollectFonts(*mpPdfDocument));
+        if (xGrfLink)
+            xGrfLink->setImportedFonts(mxImportedFonts);
+    }
 #endif
 
     // Same as SdModule
@@ -220,13 +228,19 @@ OUString stripPostScriptStyle(const OUString& 
postScriptName, FontWeight& eWeigh
 
 // Possibly there is some alternative route to query pdfium for all fonts 
without
 // iterating through every object to see what font each uses
-void ImpSdrPdfImport::CollectFonts()
+ImportedFontMap ImpSdrPdfImport::CollectFonts(vcl::pdf::PDFiumDocument& 
rPdfDocument)
 {
-    const int nPageCount = mpPdfDocument->getPageCount();
+    ImportedFontMap aImportedFonts;
+
+    std::map<OUString, SubSetInfo> aDifferentSubsetsForFont;
+    // map of PostScriptName->Merged Font File for that font
+    std::map<OUString, EmbeddedFontInfo> aEmbeddedFonts;
+
+    const int nPageCount = rPdfDocument.getPageCount();
 
     for (int nPageIndex = 0; nPageIndex < nPageCount; ++nPageIndex)
     {
-        auto pPdfPage = mpPdfDocument->openPage(nPageIndex);
+        auto pPdfPage = rPdfDocument.openPage(nPageIndex);
         if (!pPdfPage)
         {
             SAL_WARN("sd.filter", "ImpSdrPdfImport missing page: " << 
nPageIndex);
@@ -252,8 +266,8 @@ void ImpSdrPdfImport::CollectFonts()
             if (!font)
                 continue;
 
-            auto itImportedFont = 
maImportedFonts.find(font->getFontDictObjNum());
-            if (itImportedFont == maImportedFonts.end())
+            auto itImportedFont = 
aImportedFonts.find(font->getFontDictObjNum());
+            if (itImportedFont == aImportedFonts.end())
             {
                 OUString sPostScriptName = 
GetPostScriptName(pPageObject->getBaseFontName());
 
@@ -274,8 +288,8 @@ void ImpSdrPdfImport::CollectFonts()
                 {
                     SAL_WARN("sd.filter", "skipping not embedded font, map: "
                                               << sFontName << " to " << 
sPostScriptFontFamily);
-                    maImportedFonts.emplace(font->getFontDictObjNum(),
-                                            OfficeFontInfo{ 
sPostScriptFontFamily, eFontWeight });
+                    aImportedFonts.insert(OfficeFontInfo{ 
font->getFontDictObjNum(),
+                                                          
sPostScriptFontFamily, eFontWeight });
                     continue;
                 }
 
@@ -289,9 +303,9 @@ void ImpSdrPdfImport::CollectFonts()
                 SubSetInfo* pSubSetInfo;
 
                 SAL_INFO("sd.filter", "importing font: " << font);
-                auto itFontName = 
maDifferentSubsetsForFont.find(sPostScriptName);
+                auto itFontName = 
aDifferentSubsetsForFont.find(sPostScriptName);
                 OUString sFontFileName = sPostScriptName;
-                if (itFontName != maDifferentSubsetsForFont.end())
+                if (itFontName != aDifferentSubsetsForFont.end())
                 {
                     sFontFileName += 
OUString::number(itFontName->second.aComponents.size());
                     itFontName->second.aComponents.emplace_back();
@@ -303,7 +317,7 @@ void ImpSdrPdfImport::CollectFonts()
                     SubSetInfo aSubSetInfo;
                     aSubSetInfo.aComponents.emplace_back();
                     auto result
-                        = maDifferentSubsetsForFont.emplace(sPostScriptName, 
aSubSetInfo).first;
+                        = aDifferentSubsetsForFont.emplace(sPostScriptName, 
aSubSetInfo).first;
                     pSubSetInfo = &result->second;
                 }
                 bool bTTF = EmbeddedFontsManager::analyzeTTF(aFontData.data(), 
aFontData.size(),
@@ -330,30 +344,32 @@ void ImpSdrPdfImport::CollectFonts()
 
                 if (fileUrl.getLength())
                 {
-                    maImportedFonts.emplace(font->getFontDictObjNum(),
-                                            OfficeFontInfo{ sFontName, 
eFontWeight });
-                    maEmbeddedFonts[sPostScriptName]
+                    aImportedFonts.insert(
+                        OfficeFontInfo{ font->getFontDictObjNum(), sFontName, 
eFontWeight });
+                    aEmbeddedFonts[sPostScriptName]
                         = EmbeddedFontInfo{ sFontName, fileUrl, eFontWeight };
                 }
             }
             else
             {
                 SAL_INFO("sd.filter", "already saw font " << font << " and 
used "
-                                                          << 
itImportedFont->second.sFontName
+                                                          << 
itImportedFont->sFontName
                                                           << " as name");
             }
         }
     }
 
-    if (!maEmbeddedFonts.empty())
+    if (!aEmbeddedFonts.empty())
     {
-        EmbeddedFontsManager aEmbeddedFontsManager(nullptr); //TODO get model 
we want fonts in
-        for (const auto& fontinfo : maEmbeddedFonts)
+        EmbeddedFontsManager aEmbeddedFontsManager(nullptr);
+        for (const auto& fontinfo : aEmbeddedFonts)
         {
             aEmbeddedFontsManager.addEmbeddedFont(fontinfo.second.sFontFile,
                                                   fontinfo.second.sFontName, 
true);
         }
     }
+
+    return aImportedFonts;
 }
 
 #endif
@@ -1892,13 +1908,13 @@ void 
ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
     FontWeight eFontWeight(WEIGHT_DONTKNOW);
     auto xFont = pPageObject->getFont();
     auto itImportedFont
-        = xFont ? maImportedFonts.find(xFont->getFontDictObjNum()) : 
maImportedFonts.end();
-    if (itImportedFont != maImportedFonts.end())
+        = xFont ? mxImportedFonts->find(xFont->getFontDictObjNum()) : 
mxImportedFonts->end();
+    if (itImportedFont != mxImportedFonts->end())
     {
         // We expand a name like "Foo" with non-traditional styles like
         // "SemiBold" to "Foo SemiBold";
-        sFontName = itImportedFont->second.sFontName;
-        eFontWeight = itImportedFont->second.eFontWeight;
+        sFontName = itImportedFont->sFontName;
+        eFontWeight = itImportedFont->eFontWeight;
     }
     else
     {
commit 9a03816d8ba8cd0ddca3a76b04464f31a754a927
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri Oct 24 12:33:26 2025 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Sun Oct 26 12:09:07 2025 +0100

    font dict obj num
    
    Change-Id: I40a5575bd26eedae99c8010638ae5602f65af2bc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192981
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/external/pdfium/UnpackedTarball_pdfium.mk 
b/external/pdfium/UnpackedTarball_pdfium.mk
index 61f5ccf41b93..b5d69b871e2c 100644
--- a/external/pdfium/UnpackedTarball_pdfium.mk
+++ b/external/pdfium/UnpackedTarball_pdfium.mk
@@ -22,6 +22,7 @@ pdfium_patches += system-abseil.diff
 # expose this mapping information
 pdfium_patches += tounicodeinfo.patch.1
 pdfium_patches += charcodetoglyphindex.patch.1
+pdfium_patches += getfontdictobjnum.patch.1
 
 # TODO, attempt upstream
 pdfium_patches += ofz451333752.patch
diff --git a/external/pdfium/getfontdictobjnum.patch.1 
b/external/pdfium/getfontdictobjnum.patch.1
new file mode 100644
index 000000000000..b2d0512245cb
--- /dev/null
+++ b/external/pdfium/getfontdictobjnum.patch.1
@@ -0,0 +1,32 @@
+--- pdfium.orig/public/fpdf_edit.h
++++ pdfium/public/fpdf_edit.h
+@@ -1627,6 +1627,11 @@
+ FPDFFont_GetGlyphIndexFromCharCode(FPDF_FONT font, uint32_t charcode);
+ 
+ // Experimental API.
++// Get FontDictObjNum of the PDF character code
++FPDF_EXPORT int FPDF_CALLCONV
++FPDFFont_GetFontDictObjNum(FPDF_FONT font);
++
++// Experimental API.
+ // Get number of segments inside glyphpath.
+ //
+ // glyphpath - handle to a glyph path.
+--- pdfium.orig/fpdfsdk/fpdf_edittext.cpp
++++ pdfium/fpdfsdk/fpdf_edittext.cpp
+@@ -1173,6 +1173,15 @@
+   return FPDFGlyphPathFromCFXPath(pPath);
+ }
+ 
++FPDF_EXPORT int FPDF_CALLCONV
++FPDFFont_GetFontDictObjNum(FPDF_FONT font) {
++  auto* pFont = CPDFFontFromFPDFFont(font);
++  if (!pFont)
++    return -1;
++
++  return pFont->GetFontDictObjNum();
++}
++
+ FPDF_EXPORT uint32_t FPDF_CALLCONV
+ FPDFFont_GetGlyphIndexFromCharCode(FPDF_FONT font, uint32_t char_code) {
+   auto* pFont = CPDFFontFromFPDFFont(font);
diff --git a/include/vcl/filter/PDFiumLibrary.hxx 
b/include/vcl/filter/PDFiumLibrary.hxx
index 1c33778d3977..57350f87d32e 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -69,7 +69,7 @@ public:
     // A return of 0 represents not-found.
     virtual sal_uInt32 getGlyphIndexFromCharCode(const sal_uInt32 nCharCode) 
const = 0;
 
-    virtual sal_Int64 getUniqueId() const = 0;
+    virtual sal_Int32 getFontDictObjNum() const = 0;
 };
 
 class VCL_DLLPUBLIC PDFium
diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx
index 3d265df68bf3..6942ab90b720 100644
--- a/svx/source/inc/svdpdf.hxx
+++ b/svx/source/inc/svdpdf.hxx
@@ -93,7 +93,7 @@ class ImpSdrPdfImport final
 {
     std::vector<rtl::Reference<SdrObject>> maTmpList;
 
-    std::map<sal_Int64, OfficeFontInfo> maImportedFonts;
+    std::map<sal_Int32, OfficeFontInfo> maImportedFonts;
     std::map<OUString, SubSetInfo> maDifferentSubsetsForFont;
     // map of PostScriptName->Merged Font File for that font
     std::map<OUString, EmbeddedFontInfo> maEmbeddedFonts;
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 702cc4377bc3..054ffe74bbbe 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -252,7 +252,7 @@ void ImpSdrPdfImport::CollectFonts()
             if (!font)
                 continue;
 
-            auto itImportedFont = maImportedFonts.find(font->getUniqueId());
+            auto itImportedFont = 
maImportedFonts.find(font->getFontDictObjNum());
             if (itImportedFont == maImportedFonts.end())
             {
                 OUString sPostScriptName = 
GetPostScriptName(pPageObject->getBaseFontName());
@@ -274,7 +274,7 @@ void ImpSdrPdfImport::CollectFonts()
                 {
                     SAL_WARN("sd.filter", "skipping not embedded font, map: "
                                               << sFontName << " to " << 
sPostScriptFontFamily);
-                    maImportedFonts.emplace(font->getUniqueId(),
+                    maImportedFonts.emplace(font->getFontDictObjNum(),
                                             OfficeFontInfo{ 
sPostScriptFontFamily, eFontWeight });
                     continue;
                 }
@@ -330,7 +330,7 @@ void ImpSdrPdfImport::CollectFonts()
 
                 if (fileUrl.getLength())
                 {
-                    maImportedFonts.emplace(font->getUniqueId(),
+                    maImportedFonts.emplace(font->getFontDictObjNum(),
                                             OfficeFontInfo{ sFontName, 
eFontWeight });
                     maEmbeddedFonts[sPostScriptName]
                         = EmbeddedFontInfo{ sFontName, fileUrl, eFontWeight };
@@ -1892,7 +1892,7 @@ void 
ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
     FontWeight eFontWeight(WEIGHT_DONTKNOW);
     auto xFont = pPageObject->getFont();
     auto itImportedFont
-        = xFont ? maImportedFonts.find(xFont->getUniqueId()) : 
maImportedFonts.end();
+        = xFont ? maImportedFonts.find(xFont->getFontDictObjNum()) : 
maImportedFonts.end();
     if (itImportedFont != maImportedFonts.end())
     {
         // We expand a name like "Foo" with non-traditional styles like
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 992845808b26..7490959abded 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -454,7 +454,7 @@ public:
 
     sal_uInt32 getGlyphIndexFromCharCode(const sal_uInt32 nCharCode) const 
override;
 
-    sal_Int64 getUniqueId() const override { return 
reinterpret_cast<sal_Int64>(mpFont); }
+    sal_Int32 getFontDictObjNum() const override;
 };
 
 class PDFiumSearchHandleImpl final : public PDFiumSearchHandle
@@ -1304,6 +1304,8 @@ sal_uInt32 
PDFiumFontImpl::getGlyphIndexFromCharCode(const sal_uInt32 nCharCode)
     return FPDFFont_GetGlyphIndexFromCharCode(mpFont, nCharCode);
 }
 
+sal_Int32 PDFiumFontImpl::getFontDictObjNum() const { return 
FPDFFont_GetFontDictObjNum(mpFont); }
+
 bool PDFiumPageObjectImpl::getFontProperties(FontWeight& weight)
 {
     // FPDFFont_GetWeight turns out not to be that useful. It seems to just

Reply via email to