include/oox/export/chartexport.hxx | 7 - oox/source/export/chartexport.cxx | 24 ++---- oox/source/export/drawingml.cxx | 87 +++++++++++++++++++--- oox/source/export/shapes.cxx | 19 ++++ sd/source/filter/eppt/pptx-animations-nodectx.cxx | 13 ++- sd/source/filter/eppt/pptx-animations-nodectx.hxx | 5 + sd/source/filter/eppt/pptx-animations.cxx | 21 +++++ 7 files changed, 143 insertions(+), 33 deletions(-)
New commits: commit add8878c5bf28e7daff76eda22ce785912ec08c9 Author: Karthik Godha <[email protected]> AuthorDate: Wed Dec 10 21:39:49 2025 +0530 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Jan 12 09:44:27 2026 +0100 tdf#169924: ODP -> PPTX export invalid animations When exporting animations in a slide, animations attached to shapes which are not in the slide are also being exported. Change-Id: I2dbc5cd5755ce6656a9fc2efa453c5837ffeaa6a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195391 Reviewed-by: Michael Stahl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> (cherry picked from commit 30d71445d40904fd7c47639575e3f7ffa499dcad) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196541 Tested-by: Jenkins Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196670 (cherry picked from commit e3d6fddc2e1384af1b5a6863722854d6608ab1eb) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196901 diff --git a/sd/source/filter/eppt/pptx-animations-nodectx.cxx b/sd/source/filter/eppt/pptx-animations-nodectx.cxx index 0e3c2ee3d4d5..11c9d8ed481f 100644 --- a/sd/source/filter/eppt/pptx-animations-nodectx.cxx +++ b/sd/source/filter/eppt/pptx-animations-nodectx.cxx @@ -83,10 +83,12 @@ bool initCondList(const Any& rAny, std::vector<Cond>& rList, bool bIsMainSeqChil } } -NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, PowerPointExport& rExport, - bool bMainSeqChild, bool bIsIterateChild) +NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, + const std::unordered_set<sal_Int32>& rSlideShapeIDs, + PowerPointExport& rExport, bool bMainSeqChild, bool bIsIterateChild) : mxNode(xNode) , mbValid(true) + , mrSlideShapeIDs(rSlideShapeIDs) , mrPowerPointExport(rExport) , mbOnSubTnLst(false) , mnEffectNodeType(-1) @@ -111,7 +113,7 @@ bool NodeContext::isValidTarget(const Any& rTarget) Reference<XShape> xShape; if ((rTarget >>= xShape) && drawingml::ShapeExport::IsShapeTypeKnown(xShape) - && (mrPowerPointExport.GetShapeID(xShape) != -1)) + && (mrSlideShapeIDs.find(mrPowerPointExport.GetShapeID(xShape)) != mrSlideShapeIDs.end())) return true; ParagraphTarget aParagraphTarget; @@ -216,8 +218,9 @@ bool NodeContext::initChildNodes() Reference<XAnimationNode> xChildNode(xEnumeration->nextElement(), UNO_QUERY); if (xChildNode.is()) { - auto pChildContext = std::make_unique<NodeContext>( - xChildNode, mrPowerPointExport, bIsMainSeq, bIsIterateChild); + auto pChildContext = std::make_unique<NodeContext>(xChildNode, mrSlideShapeIDs, + mrPowerPointExport, + bIsMainSeq, bIsIterateChild); if (pChildContext->isValid()) bValid = true; maChildNodes.push_back(std::move(pChildContext)); diff --git a/sd/source/filter/eppt/pptx-animations-nodectx.hxx b/sd/source/filter/eppt/pptx-animations-nodectx.hxx index 1830845ea5da..865c23715b60 100644 --- a/sd/source/filter/eppt/pptx-animations-nodectx.hxx +++ b/sd/source/filter/eppt/pptx-animations-nodectx.hxx @@ -10,6 +10,7 @@ #include <com/sun/star/uno/Reference.hxx> #include <com/sun/star/animations/XAnimationNode.hpp> +#include <unordered_set> #include <vector> #include "epptooxml.hxx" #include "pptx-animations-cond.hxx" @@ -30,6 +31,7 @@ class NodeContext // if the node has valid target or contains at least one valid target. bool mbValid; // Required to check if the associated shape is present in export or not + const std::unordered_set<sal_Int32>& mrSlideShapeIDs; PowerPointExport& mrPowerPointExport; // if the node should be on SubTnLst or ChildTnLst @@ -55,7 +57,8 @@ class NodeContext public: NodeContext(const css::uno::Reference<css::animations::XAnimationNode>& xNode, - PowerPointExport& rExport, bool bMainSeqChild, bool bIsIterateChild); + const std::unordered_set<sal_Int32>& rSlideShapeIDs, PowerPointExport& rExport, + bool bMainSeqChild, bool bIsIterateChild); const css::uno::Reference<css::animations::XAnimationNode>& getNode() const { return mxNode; } sal_Int16 getEffectNodeType() const { return mnEffectNodeType; } sal_Int16 getEffectPresetClass() const { return mnEffectPresetClass; } diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx index fec8e702acbf..85c4e4fc0198 100644 --- a/sd/source/filter/eppt/pptx-animations.cxx +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -25,6 +25,7 @@ #include <sal/log.hxx> #include <rtl/math.hxx> #include <comphelper/sequenceashashmap.hxx> +#include <unordered_set> #include <com/sun/star/animations/AnimationAdditiveMode.hpp> #include <com/sun/star/animations/AnimationCalcMode.hpp> @@ -53,6 +54,7 @@ #include <com/sun/star/presentation/TextAnimationType.hpp> #include <com/sun/star/text/XSimpleText.hpp> #include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <oox/export/utils.hxx> #include <oox/ppt/pptfilterhelpers.hxx> @@ -76,6 +78,7 @@ using namespace oox; using ::com::sun::star::beans::NamedValue; using ::com::sun::star::drawing::XDrawPage; using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; using ::com::sun::star::text::XSimpleText; using ::sax_fastparser::FSHelperPtr; @@ -1230,7 +1233,23 @@ void PPTXAnimationExport::WriteAnimations(const Reference<XDrawPage>& rXDrawPage if (!(xEnumeration.is() && xEnumeration->hasMoreElements())) return; - auto pNodeContext = std::make_unique<NodeContext>(xNode, mrPowerPointExport, false, false); + Reference<XShapes> xShapes = rXDrawPage; + sal_uInt32 nShapes = xShapes->getCount(); + std::unordered_set<sal_Int32> aSlideShapeIDs; + if (xShapes.is()) + { + for (sal_uInt32 i = 0; i < nShapes; i++) + { + Reference<XShape> xShape; + xShapes->getByIndex(i) >>= xShape; + sal_Int32 nId = mrPowerPointExport.GetShapeID(xShape); + if (nId != -1) + aSlideShapeIDs.insert(nId); + } + } + + auto pNodeContext + = std::make_unique<NodeContext>(xNode, aSlideShapeIDs, mrPowerPointExport, false, false); if (pNodeContext->isValid()) { mpFS->startElementNS(XML_p, XML_timing); commit 0c32b2d5a23acbf2a1ece3b38bc8db6fd8b16cf1 Author: Karthik Godha <[email protected]> AuthorDate: Thu Dec 11 18:27:12 2025 +0530 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Jan 12 09:44:22 2026 +0100 tdf#169941: ODP->PPTX exporting shape hyperlinks LO supports hyperlinks to shapes within the document. PowerPoint doesn't have this feature. Export of these shape hyperlinks is not handled. Now hyperlinks to shapes are converted to slides in which thy are present. Change-Id: I53f971c35a4f0f58532048b4041b0f35814b2715 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195465 Reviewed-by: Michael Stahl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> (cherry picked from commit ef58ed0184cfd362175d880f4efb3d297a3d2641) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196540 Tested-by: Jenkins Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196665 (cherry picked from commit b93d0eca0f7bdb5aabbc2abf4fada51342f5ba42) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196880 diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 35382799fe00..462d68303a97 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -2478,6 +2478,25 @@ static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xMo sTarget = "slide" + OUString::number(i + 1) + ".xml"; break; } + else // If URL is linked to a shape, assign it's slide as target + { + Reference<XShapes> xShapes = xDrawPage; + sal_uInt32 nShapes = xShapes->getCount(); + for (sal_uInt32 j = 0; j < nShapes; j++) + { + Reference<XShape> xShape; + xShapes->getByIndex(j) >>= xShape; + Reference<container::XNamed> xName(xShape, UNO_QUERY); + if (!xName) + continue; + OUString sShapeName = "#" + xName->getName(); + if (rURL == sShapeName) + { + sTarget = "slide" + OUString::number(i + 1) + ".xml"; + break; + } + } + } } if (sTarget.isEmpty()) { @@ -2907,17 +2926,21 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bool bExtURL = URLTransformer().isExternalURL(sURL); sURL = bExtURL ? sURL : lcl_GetTarget(GetFB()->getModel(), sURL); - OUString sRelId - = mpFB->addRelation(mpFS->getOutputStream(), - bExtURL ? oox::getRelationship(Relationship::HYPERLINK) - : oox::getRelationship(Relationship::SLIDE), - sURL, bExtURL); - + OUString sRelId; + if (!sURL.isEmpty()) + { + sRelId + = mpFB->addRelation(mpFS->getOutputStream(), + bExtURL ? oox::getRelationship(Relationship::HYPERLINK) + : oox::getRelationship(Relationship::SLIDE), + sURL, bExtURL); + } if (bExtURL) mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId); else - mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, - XML_action, "ppaction://hlinksldjump"); + mpFS->singleElementNS( + XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, XML_action, + sURL.isEmpty() ? "ppaction://noaction" : "ppaction://hlinksldjump"); } else { diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx index 695629d40795..7b239ad00db8 100644 --- a/oox/source/export/shapes.cxx +++ b/oox/source/export/shapes.cxx @@ -754,6 +754,25 @@ static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xMo sTarget = "slide" + OUString::number(i + 1) + ".xml"; break; } + else // If URL is linked to a shape, assign it's slide as target + { + Reference<XShapes> xShapes = xDrawPage; + sal_uInt32 nShapes = xShapes->getCount(); + for (sal_uInt32 j = 0; j < nShapes; j++) + { + Reference<XShape> xShape; + xShapes->getByIndex(j) >>= xShape; + Reference<container::XNamed> xName(xShape, UNO_QUERY); + if (!xName) + continue; + OUString sShapeName = "#" + xName->getName(); + if (rURL == sShapeName) + { + sTarget = "slide" + OUString::number(i + 1) + ".xml"; + break; + } + } + } } return sTarget; commit cdf095cec203f8ced45328559c09f1ef9777817d Author: Karthik Godha <[email protected]> AuthorDate: Thu Dec 25 15:11:01 2025 +0530 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Jan 12 09:44:17 2026 +0100 tdf#170035:Add OOXML formula check for guidelist Add condition check to skip exported of internal names/equations to OOXML. Change-Id: I0c03bbaa7ea61dd4f526fc0476482571df7e7cf7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196209 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 0f0bb6ba234c371e1c5a6420ba1e441322c9c354) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196545 Tested-by: Jenkins Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196668 (cherry picked from commit e412f35e266bd08fb1a6d9f5f2a82d7761ea6cec) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196862 diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 1652c06c8b25..35382799fe00 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -39,6 +39,7 @@ #include <sax/fastattribs.hxx> #include <comphelper/diagnose_ex.hxx> #include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> #include <i18nlangtag/languagetag.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <basegfx/range/b2drange.hxx> @@ -46,6 +47,7 @@ #include <numeric> #include <string_view> +#include <set> #include <com/sun/star/awt/CharSet.hpp> #include <com/sun/star/awt/FontDescriptor.hpp> @@ -4758,6 +4760,47 @@ void prepareTextArea(const EnhancedCustomShape2d& rEnhancedCustomShape2d, return; } +bool IsValidOOXMLFormula(std::u16string_view sFormula) +{ + // Accepted Formulas + // "val n1" + // "abs n1" + // "sqrt n1" + // "min n1 n2" + // "max n1 n2" + // "*/ n1 n2 n3" + // "+- n1 n2 n3" + // "?: n1 n2 n3" + + // Below vector contains validTokens for the 1st token based on the number of tokens in the formula. The order is: 2, 3, 4 + const std::vector<std::set<OUString>> validTokens + = { { "val", "abs", "sqrt" }, { "min", "max" }, { "*/", "+-", "?:" } }; + const std::set<OUString> builtInVariables = { "w", "h", "t", "b", "l", "r" }; + const std::vector<OUString> strTokens = comphelper::string::split(sFormula, ' '); + sal_uInt16 nSize = strTokens.size(); + + if (nSize > 1 && nSize < 5) + { + auto aTokens = validTokens[nSize - 2]; + + // Check whether the 1st token is valid + if (aTokens.find(strTokens[0]) == aTokens.end()) + return false; + + // Check that the remaining tokens are either numbers or buit-in variables + for (sal_Int16 i = 1; i < nSize; i++) + { + OUString sVal = strTokens[i]; + sal_Int64 nVal = sVal.toInt64(); + if (builtInVariables.find(sVal) == builtInVariables.end() + && OUString::number(nVal) != sVal) + return false; + } + return true; + } + return false; +} + OUString GetFormula(const OUString& sEquation, const OUString& sReplace, const OUString& sNewStr) { OUString sFormula = sEquation; @@ -4768,7 +4811,10 @@ OUString GetFormula(const OUString& sEquation, const OUString& sReplace, const O sFormula = "*/ " + sModifiedEquation; } - return sFormula; + if (IsValidOOXMLFormula(sFormula)) + return sFormula; + + return OUString(); } void prepareGluePoints(std::vector<Guide>& rGuideList, commit ef353879509bc2478adb4e5086a6c4b205edbaa1 Author: Karthik Godha <[email protected]> AuthorDate: Mon Dec 15 16:56:58 2025 +0530 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Jan 12 09:44:12 2026 +0100 tdf#169980: Fix ODP->PPTX export of stock chart In OOXML chart export in `c:stockChart` there are multiple `c:ser` elements with the same `c:idx` value. Change-Id: Id5c5983118214013a3610ae937b9893c479a0ea0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195657 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit c5b4614b927bd8056995d7d4adb07a4f0c965480) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196543 Tested-by: Jenkins Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196666 (cherry picked from commit d4a7e5c7ada7692597f4127e43624419892b7205) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196842 diff --git a/include/oox/export/chartexport.hxx b/include/oox/export/chartexport.hxx index 8953e64889fb..ef7d216f4e7f 100644 --- a/include/oox/export/chartexport.hxx +++ b/include/oox/export/chartexport.hxx @@ -226,10 +226,9 @@ private: void exportVaryColors(const css::uno::Reference<css::chart2::XChartType>& xChartType); void exportCandleStickSeries( - const css::uno::Sequence< - css::uno::Reference< - css::chart2::XDataSeries > > & aSeriesSeq, - bool& rPrimaryAxes ); + const css::uno::Sequence<css::uno::Reference<css::chart2::XDataSeries>>& aSeriesSeq, + bool& rPrimaryAxes, sal_uInt32& nIdx); + void exportSeriesText( const css::uno::Reference< css::chart2::data::XDataSequence >& xValueSeq, bool bIsChartex ); void exportSeriesCategory( diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index 7ba674ed2537..b64caa859b16 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -3026,13 +3026,15 @@ void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChar // Use a dummy data series to output needed basic chart-related XML even in case of empty charts aSplitDataSeries.push_back({}); } + + sal_uInt32 nIdx = 0; for (const auto& splitDataSeries : aSplitDataSeries) { pFS->startElement(FSNS(XML_c, XML_stockChart)); bool bPrimaryAxes = true; if (splitDataSeries.hasElements()) - exportCandleStickSeries(splitDataSeries, bPrimaryAxes); + exportCandleStickSeries(splitDataSeries, bPrimaryAxes, nIdx); // export stock properties Reference< css::chart::XStatisticDisplay > xStockPropProvider(mxDiagram, uno::UNO_QUERY); @@ -3368,8 +3370,7 @@ void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType, } void ChartExport::exportCandleStickSeries( - const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq, - bool& rPrimaryAxes) + const Sequence<Reference<chart2::XDataSeries>>& aSeriesSeq, bool& rPrimaryAxes, sal_uInt32& nIdx) { for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq ) { @@ -3384,12 +3385,13 @@ void ChartExport::exportCandleStickSeries( Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( xSource->getDataSequences()); - const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr}; + const char* sSeries[] = {"values-first","values-max","values-min","values-last"}; - for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ ) + for (const char* series : sSeries) { - Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) ); - if( xLabeledSeq.is()) + Reference<chart2::data::XLabeledDataSequence> xLabeledSeq( + lcl_getDataSequenceByRole(aSeqCnt, OUString::createFromAscii(series))); + if (xLabeledSeq.is()) { Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel()); Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues()); @@ -3397,12 +3399,8 @@ void ChartExport::exportCandleStickSeries( FSHelperPtr pFS = GetFS(); pFS->startElement(FSNS(XML_c, XML_ser)); - // TODO: idx and order - // idx attribute should start from 1 and not from 0. - pFS->singleElement( FSNS( XML_c, XML_idx ), - XML_val, OString::number(idx+1) ); - pFS->singleElement( FSNS( XML_c, XML_order ), - XML_val, OString::number(idx+1) ); + pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(++nIdx)); + pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(nIdx)); // export label if( xLabelSeq.is() )
