include/oox/export/chartexport.hxx | 5 oox/source/export/chartexport.cxx | 45 ++++-- sw/qa/extras/ooxmlexport/data/tdf143269_missingEmbeddings.odt |binary sw/qa/extras/ooxmlexport/data/tdf143269_zeroSizeEmbeddings.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport10.cxx | 22 +++ sw/source/filter/ww8/docxexport.cxx | 66 ++++++++-- 6 files changed, 108 insertions(+), 30 deletions(-)
New commits: commit 2bcca77bbb74de73c6d1a379d0b1e307e6e8ad1c Author: Justin Luth <[email protected]> AuthorDate: Wed Mar 4 13:41:44 2026 -0500 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Mar 9 08:37:06 2026 +0100 tdf#143269 docx export: no LinkToExternalData if invalid embeddings This backport includes the fixes for the original commit. MS Word reports a document as corrupt if there is a relationship to a non-existing embeddings/file. It also complains if the referenced embeddings file is size 0. make CppunitTest_sw_ooxmlexport10 \ CPPUNIT_TEST_NAME=testTdf143269_missingEmbeddings make CppunitTest_sw_ooxmlexport10 \ CPPUNIT_TEST_NAME=testTdf143269_zeroSizeEmbeddings Change-Id: I40ad7f9d470db88c56c613a1a8da9f3d86ac9e81 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201058 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201165 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/include/oox/export/chartexport.hxx b/include/oox/export/chartexport.hxx index 81f63ae3cb96..5d809d3deed1 100644 --- a/include/oox/export/chartexport.hxx +++ b/include/oox/export/chartexport.hxx @@ -175,8 +175,7 @@ private: bool bIncludeTable ); void exportChart( const css::uno::Reference< css::chart::XChartDocument >& rChartDoc ); - void exportExternalData( const css::uno::Reference< - css::chart::XChartDocument >& rChartDoc ); + void exportExternalData(); void exportLegend( const css::uno::Reference< css::chart::XChartDocument >& rChartDoc ); void exportTitle( const css::uno::Reference< css::drawing::XShape >& xShape, @@ -279,6 +278,8 @@ public: void InitRangeSegmentationProperties( const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc ); + + OOX_DLLPUBLIC OUString GetExternalDataPath() const; }; } diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index b26e270541ed..1abbf4ff7930 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -82,6 +82,8 @@ #include <com/sun/star/drawing/FillStyle.hpp> #include <com/sun/star/drawing/LineStyle.hpp> #include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XStreamListener.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/lang/XServiceName.hpp> @@ -972,7 +974,7 @@ void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument exportShapeProps( xPropSet ); //XML_externalData - exportExternalData(xChartDoc); + exportExternalData(); // export additional shapes in chart exportAdditionalShapes(xChartDoc); @@ -980,27 +982,38 @@ void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument pFS->endElement( FSNS( XML_c, XML_chartSpace ) ); } -void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& xChartDoc ) +OUString ChartExport::GetExternalDataPath() const +{ + OUString sRet; + + const Reference<css::chart::XChartDocument> xChartDoc(getModel(), uno::UNO_QUERY); + if (!xChartDoc.is()) + return sRet; + + const Reference<beans::XPropertySet> xDocPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY); + if (!xDocPropSet.is()) + return sRet; + + try + { + Any aAny(xDocPropSet->getPropertyValue(u"ExternalData"_ustr)); + aAny >>= sRet; + } + catch(beans::UnknownPropertyException&) + { + } + + return sRet; +} + +void ChartExport::exportExternalData() { // Embedded external data is grab bagged for docx file hence adding export part of // external data for docx files only. if(!mbLinkToExternalData || GetDocumentType() != DOCUMENT_DOCX) return; - OUString externalDataPath; - Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY ); - if( xDocPropSet.is()) - { - try - { - Any aAny( xDocPropSet->getPropertyValue( u"ExternalData"_ustr )); - aAny >>= externalDataPath; - } - catch( beans::UnknownPropertyException & ) - { - SAL_WARN("oox", "Required property not found in ChartDocument"); - } - } + const OUString externalDataPath = GetExternalDataPath(); if(externalDataPath.isEmpty()) return; diff --git a/sw/qa/extras/ooxmlexport/data/tdf143269_missingEmbeddings.odt b/sw/qa/extras/ooxmlexport/data/tdf143269_missingEmbeddings.odt new file mode 100644 index 000000000000..34d973d4d1a8 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143269_missingEmbeddings.odt differ diff --git a/sw/qa/extras/ooxmlexport/data/tdf143269_zeroSizeEmbeddings.docx b/sw/qa/extras/ooxmlexport/data/tdf143269_zeroSizeEmbeddings.docx new file mode 100644 index 000000000000..cc29d5151a72 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143269_zeroSizeEmbeddings.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx index f55c4e1fe627..2f585635ef04 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx @@ -753,6 +753,28 @@ DECLARE_OOXMLEXPORT_TEST(testMsoBrightnessContrast, "msobrightnesscontrast.docx" CPPUNIT_ASSERT_EQUAL(Color( 0xce, 0xce, 0xce ), aColor); } +CPPUNIT_TEST_FIXTURE(Test, testTdf143269_zeroSizeEmbeddings) +{ + // Given a (broken) document that contains a word/embeddings xlsx file that is size 0 + createSwDoc("tdf143269_zeroSizeEmbeddings.docx"); + save(TestFilter::DOCX); + + // MS Word reports corrupt if embeddings/file referred is zero-sized + xmlDocUniquePtr pXmlChart1 = parseExport(u"word/charts/chart1.xml"_ustr); + assertXPath(pXmlChart1, "//c:externalData", 0); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf143269_missingEmbeddings) +{ + // Given a XLSX->ODT document (I presume) that has lost the word/embeddings xlsx file + createSwDoc("tdf143269_missingEmbeddings.odt"); + save(TestFilter::DOCX); + + // MS Word reports corrupt if embeddings/file referred to does not exist + xmlDocUniquePtr pXmlChart1 = parseExport(u"word/charts/chart1.xml"_ustr); + assertXPath(pXmlChart1, "//c:externalData", 0); +} + DECLARE_OOXMLEXPORT_TEST(testChartSize, "chart-size.docx") { // When chart was in a TextFrame, its size was too large. diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 57e06b2612a4..a48c15f936f0 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -114,6 +114,31 @@ using oox::vml::VMLExport; using sw::mark::MarkBase; +namespace +{ + +uno::Sequence<beans::PropertyValue> +lcl_getEmbeddingsList(const rtl::Reference<SwXTextDocument>& xTextDoc) +{ + uno::Sequence<beans::PropertyValue> aRet; + + if (!xTextDoc->getPropertySetInfo()->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG)) + return aRet; + + uno::Sequence<beans::PropertyValue> xPropertyList; + xTextDoc->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= xPropertyList; + auto pProp = std::find_if( + std::cbegin(xPropertyList), std::cend(xPropertyList), + [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXEmbeddings"; }); + + if (pProp != std::cend(xPropertyList)) + pProp->Value >>= aRet; + + return aRet; +} + +} // end anonymous namespace + AttributeOutputBase& DocxExport::AttrOutput() const { return *m_pAttrOutput; @@ -417,6 +442,34 @@ OString DocxExport::OutputChart( uno::Reference< frame::XModel > const & xModel, break; } + // verify that the to-be-embedded-file being linked to is available and valid. + if (aChartExport.mbLinkToExternalData) + { + // missing or zero-sized embedded files are reported as corrupt by MS Word + aChartExport.mbLinkToExternalData = false; // assume something wrong until proven valid + const OUString sExternalDataPath = aChartExport.GetExternalDataPath(); + if (!sExternalDataPath.isEmpty()) + { + for (const auto& rEmbedding : lcl_getEmbeddingsList(m_xTextDoc)) + { + if (rEmbedding.Name == sExternalDataPath) + { + uno::Reference<io::XInputStream> embeddingsStream; + rEmbedding.Value >>= embeddingsStream; + if (embeddingsStream) + { + uno::Reference<io::XSeekable> xSeekable(embeddingsStream, uno::UNO_QUERY); + if (xSeekable && xSeekable->getLength()) + { + aChartExport.mbLinkToExternalData = true; + break; + } + } + } + } + } + } + aChartExport.ExportContent(); m_aExportedCharts.push_back(xModel); if (!bOldModified && xModifiable && xModifiable->isModified()) @@ -1940,18 +1993,7 @@ void DocxExport::WriteEmbeddings() if (!pShell) return; - uno::Reference< beans::XPropertySetInfo > xPropSetInfo = m_xTextDoc->getPropertySetInfo(); - OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; - if ( !xPropSetInfo->hasPropertyByName( aName ) ) - return; - - uno::Sequence< beans::PropertyValue > embeddingsList; - uno::Sequence< beans::PropertyValue > propList; - m_xTextDoc->getPropertyValue( aName ) >>= propList; - auto pProp = std::find_if(std::cbegin(propList), std::cend(propList), - [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXEmbeddings"; }); - if (pProp != std::cend(propList)) - pProp->Value >>= embeddingsList; + uno::Sequence<beans::PropertyValue> embeddingsList = lcl_getEmbeddingsList(m_xTextDoc); for (const auto& rEmbedding : embeddingsList) { OUString embeddingPath = rEmbedding.Name;
