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