sd/inc/ModelTraverser.hxx                |   16 ++
 sd/source/core/ModelTraverser.cxx        |   45 +++++--
 sd/source/filter/eppt/epptooxml.hxx      |    5 
 sd/source/filter/eppt/pptx-epptooxml.cxx |  179 ++++++++++++++++++++++++++++++-
 sd/source/ui/tools/GraphicSizeCheck.cxx  |    2 
 5 files changed, 230 insertions(+), 17 deletions(-)

New commits:
commit 6e6c02475588f5899db2cb4d02a3adcaf34170a6
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Fri Apr 18 15:34:04 2025 +0900
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon May 5 09:29:50 2025 +0200

    oox: embed used fonts only and font scripts for PPTX export
    
    Support properties "EmbedOnlyUsedFonts", "EmbedLatinScriptFonts",
    "EmbedAsianScriptFonts", "EmbedComplexScriptFonts" when exporting
    embedded fonts. This brings it to the same functionality as other
    embedded font exports.
    
    Change-Id: I9e463c0fbc8a41b165b30fa11222648e930dbca3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184348
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    Tested-by: Jenkins
    (cherry picked from commit 875443e7d93b1d1728be2863d3784e0792a77f41)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184823
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sd/inc/ModelTraverser.hxx b/sd/inc/ModelTraverser.hxx
index 209ace6047e4..9903470015f6 100644
--- a/sd/inc/ModelTraverser.hxx
+++ b/sd/inc/ModelTraverser.hxx
@@ -14,6 +14,7 @@
 #include "drawdoc.hxx"
 
 class SdrObject;
+class SdrPage;
 
 namespace sd
 {
@@ -28,6 +29,13 @@ public:
     virtual void handleSdrObject(SdrObject* pObject) = 0;
 };
 
+/** Options to change how the traverser is traversing the tree, what is 
included and what not */
+struct TraverserOptions
+{
+    bool mbPages = true;
+    bool mbMasterPages = false;
+};
+
 /**
  * Traverses the DOM and calls a handler for each object (SdrObject) it
  * encounters.
@@ -37,10 +45,16 @@ class ModelTraverser
 private:
     std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler;
     SdDrawDocument* m_pDocument;
+    TraverserOptions m_aTraverserOptions;
+
+    void traverseObjects(SdrPage const& rPage);
+    void traversePages();
+    void traverseMasterPages();
 
 public:
-    ModelTraverser(SdDrawDocument* pDocument)
+    ModelTraverser(SdDrawDocument* pDocument, TraverserOptions const& 
rTraverserOptions)
         : m_pDocument(pDocument)
+        , m_aTraverserOptions(rTraverserOptions)
     {
     }
 
diff --git a/sd/source/core/ModelTraverser.cxx 
b/sd/source/core/ModelTraverser.cxx
index 6eb15de0a6ee..e87f06ffb581 100644
--- a/sd/source/core/ModelTraverser.cxx
+++ b/sd/source/core/ModelTraverser.cxx
@@ -11,8 +11,7 @@
 
 #include <svx/svdobj.hxx>
 #include <svx/svdpage.hxx>
-
-using namespace css;
+#include <svx/svditer.hxx>
 
 namespace sd
 {
@@ -21,19 +20,43 @@ void ModelTraverser::traverse()
     if (!m_pDocument)
         return;
 
+    if (m_aTraverserOptions.mbPages)
+        traversePages();
+
+    if (m_aTraverserOptions.mbMasterPages)
+        traverseMasterPages();
+}
+
+void ModelTraverser::traverseObjects(SdrPage const& rPage)
+{
+    SdrObjListIter aIterator(&rPage, SdrIterMode::DeepWithGroups);
+    while (aIterator.IsMore())
+    {
+        SdrObject* pObject = aIterator.Next();
+        if (!pObject)
+            continue;
+        for (auto& pNodeHandler : m_pNodeHandler)
+            pNodeHandler->handleSdrObject(pObject);
+    }
+}
+
+void ModelTraverser::traversePages()
+{
     for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); ++nPage)
     {
         SdrPage* pPage = m_pDocument->GetPage(nPage);
         if (pPage)
-        {
-            for (const rtl::Reference<SdrObject>& pObject : *pPage)
-            {
-                for (auto& pNodeHandler : m_pNodeHandler)
-                {
-                    pNodeHandler->handleSdrObject(pObject.get());
-                }
-            }
-        }
+            traverseObjects(*pPage);
+    }
+}
+
+void ModelTraverser::traverseMasterPages()
+{
+    for (sal_uInt16 nMasterPage = 0; nMasterPage < 
m_pDocument->GetMasterPageCount(); ++nMasterPage)
+    {
+        SdrPage* pMasterPage = m_pDocument->GetMasterPage(nMasterPage);
+        if (pMasterPage)
+            traverseObjects(*pMasterPage);
     }
 }
 
diff --git a/sd/source/filter/eppt/epptooxml.hxx 
b/sd/source/filter/eppt/epptooxml.hxx
index 50d8956ece35..4c1ed63b0843 100644
--- a/sd/source/filter/eppt/epptooxml.hxx
+++ b/sd/source/filter/eppt/epptooxml.hxx
@@ -202,13 +202,18 @@ private:
 
     void WriteModifyVerifier();
 
+    /// Embedded font related members
     bool mbEmbedFonts = false;
     bool mbEmbedUsedOnly = false;
     bool mbEmbedLatinScript = true;
     bool mbEmbedAsianScript = true;
     bool mbEmbedComplexScript = true;
 
+    /// Write the embedded font list element
     void WriteEmbeddedFontList();
+
+    /// Get the font names that are used in the document (and styles)
+    std::unordered_set<OUString> getUsedFontList();
 };
 
 }
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx 
b/sd/source/filter/eppt/pptx-epptooxml.cxx
index c7ed70bbd861..a8841e2c1ba6 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -29,6 +29,13 @@
 #include <unokywds.hxx>
 #include <osl/file.hxx>
 
+#include <editeng/fontitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <drawdoc.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/section.hxx>
+#include <editeng/editeng.hxx>
+
 #include <comphelper/sequenceashashmap.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <comphelper/xmltools.hxx>
@@ -67,6 +74,7 @@
 #include <oox/export/utils.hxx>
 #include <oox/export/ThemeExport.hxx>
 #include <docmodel/theme/Theme.hxx>
+#include <ModelTraverser.hxx>
 
 #include "pptx-animations.hxx"
 #include "../ppt/pptanimations.hxx"
@@ -543,19 +551,179 @@ struct EmbeddedFont
 };
 } // end anonymous namespace
 
-// Writers the list of all embedded fonts and reference to the fonts
-void PowerPointExport::WriteEmbeddedFontList()
+namespace
 {
-    if (!mbEmbedFonts)
-        return;
+class FontCollector : public sd::ModelTraverseHandler
+{
+private:
+    std::unordered_set<OUString>& mrUsedFontNames;
+    bool mbEmbedLatinScript;
+    bool mbEmbedAsianScript;
+    bool mbEmbedComplexScript;
+
+    static const SvxFontItem* getFontItem(const editeng::Section& rSection, 
sal_uInt16 eFontWhich)
+    {
+        auto iterator = std::find_if(rSection.maAttributes.begin(), 
rSection.maAttributes.end(), [eFontWhich] (const SfxPoolItem* pPoolItem) {
+            return pPoolItem->Which() == eFontWhich;
+        });
+
+        if (iterator != rSection.maAttributes.end())
+            return static_cast<const SvxFontItem*>(*iterator);
+        return nullptr;
+    }
+
+    void traverseEditEng(SdrTextObj* pTextObject)
+    {
+        OutlinerParaObject* pOutlinerParagraphObject = 
pTextObject->GetOutlinerParaObject();
+        if (!pOutlinerParagraphObject)
+            return;
+
+        const EditTextObject& rEditText = 
pOutlinerParagraphObject->GetTextObject();
+        std::vector<editeng::Section> aSections;
+        rEditText.GetAllSections(aSections);
+
+        for (editeng::Section const& rSection : aSections)
+        {
+            if (SvxFontItem const* pFontItem = getFontItem(rSection, 
EE_CHAR_FONTINFO); pFontItem && mbEmbedLatinScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+            if (SvxFontItem const * pFontItem = getFontItem(rSection, 
EE_CHAR_FONTINFO_CJK); pFontItem && mbEmbedAsianScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+            if (SvxFontItem const* pFontItem = getFontItem(rSection, 
EE_CHAR_FONTINFO_CTL); pFontItem && mbEmbedComplexScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+        }
+    }
+
+public:
+    FontCollector(std::unordered_set<OUString>& rUsedFontNames, bool 
bEmbedLatinScript, bool bEmbedAsianScript, bool bEmbedComplexScript)
+        : mrUsedFontNames(rUsedFontNames)
+        , mbEmbedLatinScript(bEmbedLatinScript)
+        , mbEmbedAsianScript(bEmbedAsianScript)
+        , mbEmbedComplexScript(bEmbedComplexScript)
+    {}
+
+protected:
+    void handleSdrObject(SdrObject* pObject) override
+    {
+        SdrTextObj* pTextShape = DynCastSdrTextObj(pObject);
+        if (pTextShape && !pTextShape->IsEmptyPresObj())
+        {
+            auto& rItemSet = pTextShape->GetMergedItemSet();
+
+            if (SvxFontItem const* pFontItem = 
rItemSet.GetItemIfSet(EE_CHAR_FONTINFO, true); pFontItem && mbEmbedLatinScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+            if (SvxFontItem const* pFontItem = 
rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CJK, true); pFontItem && 
mbEmbedAsianScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+            if (SvxFontItem const* pFontItem = 
rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CTL, true); pFontItem && 
mbEmbedComplexScript)
+            {
+                mrUsedFontNames.insert(pFontItem->GetFamilyName());
+            }
+            traverseEditEng(pTextShape);
+        }
+    }
+};
+
+} // end anonymous namespace
+
+
+std::unordered_set<OUString> PowerPointExport::getUsedFontList()
+{
+    std::unordered_set<OUString> aReturnSet;
 
     SdDrawDocument* pDocument = nullptr;
     if (auto* pSdXImpressDocument = 
dynamic_cast<SdXImpressDocument*>(mXModel.get()))
         pDocument = pSdXImpressDocument->GetDoc();
 
     if (!pDocument)
+        return aReturnSet;
+
+    uno::Reference<style::XStyleFamiliesSupplier> xFamiliesSupp(getModel(), 
UNO_QUERY);
+    if (!xFamiliesSupp.is())
+        return aReturnSet;
+
+    // Check styles first
+    uno::Reference<container::XNameAccess> xFamilies = 
xFamiliesSupp->getStyleFamilies();
+    if (!xFamilies.is())
+        return aReturnSet;
+
+    const uno::Sequence<OUString> aFamilyNames = xFamilies->getElementNames();
+    for (OUString const & sFamilyName : aFamilyNames)
+    {
+        uno::Reference<container::XNameAccess> xStyleContainer;
+        xFamilies->getByName(sFamilyName) >>= xStyleContainer;
+
+        if (!xStyleContainer.is())
+            continue;
+
+        for (OUString const& rName : xStyleContainer->getElementNames())
+        {
+            uno::Reference<style::XStyle> xStyle;
+            xStyleContainer->getByName(rName) >>= xStyle;
+            if (!xStyle->isInUse())
+                continue;
+
+            uno::Reference<beans::XPropertySet> xPropertySet(xStyle, 
UNO_QUERY);
+            if (!xPropertySet.is())
+                continue;
+
+            uno::Reference<beans::XPropertySetInfo> xInfo = 
xPropertySet->getPropertySetInfo();
+            if (!xInfo.is())
+                continue;
+
+            if (mbEmbedLatinScript && 
xInfo->hasPropertyByName(u"CharFontName"_ustr))
+            {
+                OUString sCharFontName;
+                Any aFontAny = 
xPropertySet->getPropertyValue(u"CharFontName"_ustr);
+                aFontAny >>= sCharFontName;
+                if (!sCharFontName.isEmpty())
+                    aReturnSet.insert(sCharFontName);
+            }
+            if (mbEmbedAsianScript && 
xInfo->hasPropertyByName(u"CharFontNameAsian"_ustr))
+            {
+                OUString sCharFontNameAsian;
+                Any aFontAny = 
xPropertySet->getPropertyValue(u"CharFontNameAsian"_ustr);
+                aFontAny >>= sCharFontNameAsian;
+                if (!sCharFontNameAsian.isEmpty())
+                    aReturnSet.insert(sCharFontNameAsian);
+            }
+            if (mbEmbedComplexScript && 
xInfo->hasPropertyByName(u"CharFontNameComplex"_ustr))
+            {
+                OUString sCharFontNameComplex;
+                Any aFontAny = 
xPropertySet->getPropertyValue(u"CharFontNameComplex"_ustr);
+                aFontAny >>= sCharFontNameComplex;
+                if (!sCharFontNameComplex.isEmpty())
+                    aReturnSet.insert(sCharFontNameComplex);
+            }
+        }
+    }
+
+    std::shared_ptr<FontCollector> pFontCollector(new 
FontCollector(aReturnSet, mbEmbedLatinScript, mbEmbedAsianScript, 
mbEmbedComplexScript));
+    sd::ModelTraverser aModelTraverser(pDocument, { .mbPages = true, 
.mbMasterPages = true });
+    aModelTraverser.addNodeHandler(pFontCollector);
+    aModelTraverser.traverse();
+
+    return aReturnSet;
+}
+
+// Writers the list of all embedded fonts and reference to the fonts
+void PowerPointExport::WriteEmbeddedFontList()
+{
+    if (!mbEmbedFonts)
         return;
 
+    std::unordered_set<OUString> aUsedFonts;
+    if (mbEmbedUsedOnly)
+        aUsedFonts = getUsedFontList();
+
     int nextFontId = 1;
 
     std::unordered_set<OUString> aFontFamilyNameSet;
@@ -591,6 +759,9 @@ void PowerPointExport::WriteEmbeddedFontList()
         aAnySeq[nSeqIndex++] >>= ePitch;
         aAnySeq[nSeqIndex++] >>= eCharSet;
 
+        if (mbEmbedUsedOnly && !aUsedFonts.contains(sFamilyName))
+            continue;
+
         if (aFontFamilyNameSet.contains(sFamilyName))
             continue;
 
diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx 
b/sd/source/ui/tools/GraphicSizeCheck.cxx
index d145de1ad22d..1743da4a2b83 100644
--- a/sd/source/ui/tools/GraphicSizeCheck.cxx
+++ b/sd/source/ui/tools/GraphicSizeCheck.cxx
@@ -95,7 +95,7 @@ void GraphicSizeCheck::check()
 
     auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, 
m_aGraphicSizeViolationList);
 
-    ModelTraverser aModelTraverser(m_pDocument);
+    ModelTraverser aModelTraverser(m_pDocument, { .mbPages = true, 
.mbMasterPages = false });
     aModelTraverser.addNodeHandler(pHandler);
     aModelTraverser.traverse();
 }

Reply via email to