sd/qa/unit/export-tests-ooxml3.cxx | 10 +++++++--- sd/source/filter/eppt/pptx-animations-nodectx.cxx | 20 +++++++++++++++++--- sd/source/filter/eppt/pptx-animations-nodectx.hxx | 4 ++++ sd/source/filter/eppt/pptx-animations.cxx | 19 ++++++++++++++++++- 4 files changed, 46 insertions(+), 7 deletions(-)
New commits: commit 4e6601cf1b99c1aa52934388e9cefd527389cf80 Author: Mark Hung <mark...@gmail.com> AuthorDate: Sat Jan 28 18:39:49 2023 +0800 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Sun Jan 29 06:01:05 2023 +0000 tdf#124230 export subTnLst for event triggered nodes. LibreOffice export all the child nodes of a timenode to childTnLst. It seems that PowerPoint anticipate there being fixed starting-time timenodes on childTnLst. This patch export event-triggered audio time nodes to subTnLst to make audio playback work. Other node types can be added in the future once more test cases are found. Change-Id: Ic96ec50876f568145bdde122d01dec10c1ac7c50 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146295 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/sd/qa/unit/export-tests-ooxml3.cxx b/sd/qa/unit/export-tests-ooxml3.cxx index db023bef2592..7753abe651ee 100644 --- a/sd/qa/unit/export-tests-ooxml3.cxx +++ b/sd/qa/unit/export-tests-ooxml3.cxx @@ -1113,10 +1113,14 @@ void SdOOXMLExportTest3::testTdf44223() xmlDocUniquePtr pDoc1 = parseExport("ppt/slides/slide1.xml"); + // tdf#124230 all nodes were under p:childTnLst, but event triggered nodes need + // to be under p:subTnLst, especially for audio to work correctly. // Start condition: 0s after timenode id 5 begins. - assertXPath(pDoc1, "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "evt", "begin"); - assertXPath(pDoc1, "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "delay", "0"); - assertXPath(pDoc1, "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond/p:tn", "val", "5"); + assertXPath(pDoc1, "//p:subTnLst/p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "evt", + "begin"); + assertXPath(pDoc1, "//p:subTnLst/p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "delay", "0"); + assertXPath(pDoc1, "//p:subTnLst/p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond/p:tn", "val", + "5"); xmlDocUniquePtr pDoc2 = parseExport("ppt/slides/slide2.xml"); assertXPath(pDoc2, "//p:transition/p:sndAc/p:stSnd/p:snd[@r:embed]", 2); diff --git a/sd/source/filter/eppt/pptx-animations-nodectx.cxx b/sd/source/filter/eppt/pptx-animations-nodectx.cxx index 957301a5afd8..e538a0ca4e99 100644 --- a/sd/source/filter/eppt/pptx-animations-nodectx.cxx +++ b/sd/source/filter/eppt/pptx-animations-nodectx.cxx @@ -57,10 +57,11 @@ bool IsAudioURL(const OUString& rURL) /// Returns if rURL has an extension which is a video format. bool IsVideoURL(const OUString& rURL) { return rURL.endsWithIgnoreAsciiCase(".mp4"); } -void initCondList(const Any& rAny, std::vector<Cond>& rList, bool bIsMainSeqChild) +bool initCondList(const Any& rAny, std::vector<Cond>& rList, bool bIsMainSeqChild) { + bool bEventTrigger = false; if (!rAny.hasValue()) - return; + return false; Sequence<Any> aCondSeq; if (rAny >>= aCondSeq) @@ -69,15 +70,24 @@ void initCondList(const Any& rAny, std::vector<Cond>& rList, bool bIsMainSeqChil { Cond aCond(rCond, bIsMainSeqChild); if (aCond.isValid()) + { rList.push_back(aCond); + if (aCond.mpEvent) + bEventTrigger = true; + } } } else { Cond aCond(rAny, bIsMainSeqChild); if (aCond.isValid()) + { rList.push_back(aCond); + if (aCond.mpEvent) + bEventTrigger = true; + } } + return bEventTrigger; } } @@ -86,6 +96,7 @@ NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqCh : mxNode(xNode) , mbMainSeqChild(bMainSeqChild) , mbValid(true) + , mbOnSubTnLst(false) , mnEffectNodeType(-1) , mnEffectPresetClass(css::presentation::EffectPresetClass::CUSTOM) { @@ -95,7 +106,10 @@ NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqCh initValid(initChildNodes(), bIsIterateChild); - initCondList(getNodeForCondition()->getBegin(), maBeginCondList, mbMainSeqChild); + // Put event triggered Audio time nodes to SubTnLst. + // Add other types of nodes once we find more test cases. + mbOnSubTnLst = initCondList(getNodeForCondition()->getBegin(), maBeginCondList, mbMainSeqChild) + && mxNode->getType() == AnimationNodeType::AUDIO; initCondList(getNodeForCondition()->getEnd(), maEndCondList, mbMainSeqChild); } diff --git a/sd/source/filter/eppt/pptx-animations-nodectx.hxx b/sd/source/filter/eppt/pptx-animations-nodectx.hxx index e9f884063c2e..5e3ac0010602 100644 --- a/sd/source/filter/eppt/pptx-animations-nodectx.hxx +++ b/sd/source/filter/eppt/pptx-animations-nodectx.hxx @@ -30,6 +30,9 @@ class NodeContext // if the node has valid target or contains at least one valid target. bool mbValid; + // if the node should be on SubTnLst or ChildTnLst + bool mbOnSubTnLst; + // Attributes initialized from mxNode->getUserData(). sal_Int16 mnEffectNodeType; sal_Int16 mnEffectPresetClass; @@ -56,6 +59,7 @@ public: const OUString& getEffectPresetId() const { return msEffectPresetId; } const OUString& getEffectPresetSubType() const { return msEffectPresetSubType; } bool isValid() const { return mbValid; } + bool isOnSubTnLst() const { return mbOnSubTnLst; } const std::vector<NodeContextPtr>& getChildNodes() const { return maChildNodes; }; const css::uno::Reference<css::animations::XAnimationNode>& getNodeForCondition() const; const std::vector<Cond>& getBeginCondList() const { return maBeginCondList; } diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx index 0cb6a5c72319..1effa82a9ab8 100644 --- a/sd/source/filter/eppt/pptx-animations.cxx +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -943,13 +943,30 @@ void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart() const std::vector<NodeContextPtr>& aChildNodes = mpContext->getChildNodes(); if (!aChildNodes.empty()) { + bool bSubTnLst = false; mpFS->startElementNS(XML_p, XML_childTnLst); for (const NodeContextPtr& pChildContext : aChildNodes) { if (pChildContext->isValid()) - WriteAnimationNode(pChildContext); + { + if (pChildContext->isOnSubTnLst()) + bSubTnLst = true; + else + WriteAnimationNode(pChildContext); + } } mpFS->endElementNS(XML_p, XML_childTnLst); + + if (bSubTnLst) + { + mpFS->startElementNS(XML_p, XML_subTnLst); + for (const NodeContextPtr& pChildContext : aChildNodes) + { + if (pChildContext->isValid() && pChildContext->isOnSubTnLst()) + WriteAnimationNode(pChildContext); + } + mpFS->endElementNS(XML_p, XML_subTnLst); + } } mpFS->endElementNS(XML_p, XML_cTn); }