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