include/oox/export/drawingml.hxx         |    5 
 oox/source/export/drawingml.cxx          |   41 +++++--
 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 
 7 files changed, 267 insertions(+), 26 deletions(-)

New commits:
commit 0c1b3c1c97bbfdc8d8f84b36fb5879400095c5e0
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Mon Apr 28 15:43:41 2025 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Apr 30 13:38:15 2025 +0200

    oox: Don't substitute fonts if embedding fonts is enabled
    
    This is needed to prevent that we embed a font but then export
    a different (substituted) font name into the document, which
    would defeat the purpose of embedding in the first place.
    
    This is not ideal either as a we probably need better control of
    what we can just substitue and what we need to embed.
    
    Change-Id: Icd29789ceb739b6950576379358a96cc0449b3b1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184696
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 14df9e9cad8c..d0b08d8bb305 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -305,6 +305,8 @@ protected:
     /// True when exporting presentation placeholder shape.
     bool mbPlaceholder;
 
+    bool mbEmbedFonts = false;
+
     bool GetProperty( const css::uno::Reference< css::beans::XPropertySet >& 
rXPropSet, const OUString& aName );
     bool GetPropertyAndState( const css::uno::Reference< 
css::beans::XPropertySet >& rXPropSet,
                   const css::uno::Reference< css::beans::XPropertyState >& 
rXPropState,
@@ -352,8 +354,7 @@ protected:
         const bool bReplaceGeoWidth, const bool bReplaceGeoHeight);
 
 public:
-    DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* 
pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = 
nullptr )
-        : meDocumentType( eDocumentType ), mpTextExport(pTextExport), 
mpFS(std::move( pFS )), mpFB( pFB ), mbIsBackgroundDark( false ), 
mbPlaceholder(false) {}
+    OOX_DLLPUBLIC DrawingML(::sax_fastparser::FSHelperPtr pFS, 
::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType = DOCUMENT_PPTX, 
DMLTextExport* pTextExport = nullptr);
     void SetFS(const ::sax_fastparser::FSHelperPtr& pFS) { mpFS = pFS; }
     const ::sax_fastparser::FSHelperPtr& GetFS() const { return mpFS; }
     ::oox::core::XmlFilterBase* GetFB() { return mpFB; }
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 52acf955fd76..ee5a0f58fd19 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -250,6 +250,27 @@ sal_Int32 DrawingML::mnDrawingMLCount = 0;
 sal_Int32 DrawingML::mnVmlCount = 0;
 sal_Int32 DrawingML::mnChartCount = 0;
 
+DrawingML::DrawingML(::sax_fastparser::FSHelperPtr pFS, 
::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* 
pTextExport)
+    : meDocumentType(eDocumentType)
+    , mpTextExport(pTextExport)
+    , mpFS(std::move(pFS))
+    , mpFB(pFB)
+    , mbIsBackgroundDark(false)
+    , mbPlaceholder(false)
+{
+    uno::Reference<beans::XPropertySet> 
xSettings(pFB->getModelFactory()->createInstance(u"com.sun.star.document.Settings"_ustr),
 uno::UNO_QUERY);
+    if (xSettings.is())
+    {
+        try
+        {
+            xSettings->getPropertyValue(u"EmbedFonts"_ustr) >>= mbEmbedFonts;
+        }
+        catch (Exception& )
+        {
+        }
+    }
+}
+
 sal_Int16 DrawingML::GetScriptType(const OUString& rStr)
 {
     if (rStr.getLength() > 0)
@@ -2814,9 +2835,13 @@ void DrawingML::WriteRunProperties( const Reference< 
XPropertySet >& rRun, bool
         OUString usTypeface;
 
         mAny >>= usTypeface;
-        OUString aSubstName( GetSubsFontName( usTypeface, 
SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
-        if (!aSubstName.isEmpty())
-            usTypeface = aSubstName;
+
+        if (!mbEmbedFonts)
+        {
+            OUString aSubstName( GetSubsFontName( usTypeface, 
SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
+            if (!aSubstName.isEmpty())
+                usTypeface = aSubstName;
+        }
 
         mpFS->singleElementNS( XML_a, XML_latin,
                                XML_typeface, usTypeface,
@@ -2836,10 +2861,12 @@ void DrawingML::WriteRunProperties( const Reference< 
XPropertySet >& rRun, bool
         OUString usTypeface;
 
         mAny >>= usTypeface;
-        OUString aSubstName( GetSubsFontName( usTypeface, 
SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
-        if (!aSubstName.isEmpty())
-            usTypeface = aSubstName;
-
+        if (!mbEmbedFonts)
+        {
+            OUString aSubstName( GetSubsFontName( usTypeface, 
SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
+            if (!aSubstName.isEmpty())
+                usTypeface = aSubstName;
+        }
         mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
                                XML_typeface, usTypeface,
                                XML_pitchFamily, pitch,
commit 875443e7d93b1d1728be2863d3784e0792a77f41
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Fri Apr 18 15:34:04 2025 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Apr 30 13:38:00 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

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 b2b472412aea..3db4dc9e15b8 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 baf6db88883a..3fddf9c8e8d0 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