From 4076b7589a7c856beb4fe2f7f5cd784443415cd3 Mon Sep 17 00:00:00 2001 From: Mark Hung Date: Tue, 19 Feb 2019 23:54:55 +0800 Subject: tdf#123557 PPTX: Export timenode condition targets. Refactor the code and reuse WriteAnimationTarget to export the target element for the condition element in stCondLst and endCondLst. Add testTdf123557 to make sure import-export-import works. As SdOOXMLExportTest2::testTdf90627 no longer produce corupt pptx that has endCondLst without Cond element, make the xpath more specific about what is testing. Update schema because drawooo:enhanced-path incluced in the test case. Change-Id: If5d64f5c23aa2652cfa72471f9f7886c7dd956eb Reviewed-on: https://gerrit.libreoffice.org/68211 Tested-by: Jenkins Reviewed-by: Mark Hung --- .../OpenDocument-schema-v1.3+libreoffice.rng | 5 + sd/qa/unit/data/pptx/trigger.pptx | Bin 0 -> 32278 bytes sd/qa/unit/export-tests-ooxml2.cxx | 4 +- sd/qa/unit/export-tests.cxx | 24 ++ sd/source/filter/eppt/pptx-animations.cxx | 307 +++++++++++---------- 5 files changed, 198 insertions(+), 142 deletions(-) create mode 100644 sd/qa/unit/data/pptx/trigger.pptx diff --git a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng index 19786f82a7e1..e5a0116bd029 100644 --- a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng +++ b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng @@ -2266,6 +2266,11 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + diff --git a/sd/qa/unit/data/pptx/trigger.pptx b/sd/qa/unit/data/pptx/trigger.pptx new file mode 100644 index 000000000000..fdfb397402a0 Binary files /dev/null and b/sd/qa/unit/data/pptx/trigger.pptx differ diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index 0d63f6b0f508..ae60952752fc 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -1764,8 +1764,8 @@ void SdOOXMLExportTest2::testTdf90627() xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile); xmlDocPtr pXmlDocContent = parseExport(tempFile, "ppt/slides/slide1.xml"); - // Don't export empty conditions - assertXPath(pXmlDocContent, "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst[1]/p:seq/p:cTn/p:childTnLst[1]/p:par[2]/p:cTn/p:childTnLst[1]/p:par/p:cTn/p:childTnLst[1]/p:par/p:cTn/p:endCondLst", 0); + // Don't export empty endCondLst without cond. + assertXPath(pXmlDocContent, "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst[1]/p:seq/p:cTn/p:childTnLst[1]/p:par[2]/p:cTn/p:childTnLst[1]/p:par/p:cTn/p:childTnLst[1]/p:par/p:cTn/p:endCondLst[not(*)]", 0); xDocShRef->DoClose(); } diff --git a/sd/qa/unit/export-tests.cxx b/sd/qa/unit/export-tests.cxx index dc2fa945194e..d7fa712c3b5a 100644 --- a/sd/qa/unit/export-tests.cxx +++ b/sd/qa/unit/export-tests.cxx @@ -103,6 +103,7 @@ public: void testBulletsAsImage(); void testTdf113818(); void testTdf119629(); + void testTdf123557(); void testTdf113822(); CPPUNIT_TEST_SUITE(SdExportTest); @@ -132,6 +133,7 @@ public: CPPUNIT_TEST(testBulletsAsImage); CPPUNIT_TEST(testTdf113818); CPPUNIT_TEST(testTdf119629); + CPPUNIT_TEST(testTdf123557); CPPUNIT_TEST(testTdf113822); CPPUNIT_TEST_SUITE_END(); @@ -153,6 +155,7 @@ public: { "text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0" }, { "xlink", "http://www.w3c.org/1999/xlink" }, { "loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" }, + { "smil", "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" }, // user-defined { "foo", "http://example.com/" }, }; @@ -1160,6 +1163,27 @@ void SdExportTest::testTdf119629() xDocShRef->DoClose(); } +void SdExportTest::testTdf123557() +{ + utl::TempFile tempFile; + sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/pptx/trigger.pptx"), PPTX); + xDocShRef = saveAndReload(xDocShRef.get(), PPTX); + xDocShRef = saveAndReload(xDocShRef.get(), ODP, &tempFile); + xmlDocPtr pXmlDoc = parseExport(tempFile, "content.xml"); + + // Contians 2 interactive sequence and 3 triggered effects. + assertXPath(pXmlDoc, "//draw:page", 1); + assertXPath(pXmlDoc, "//draw:page/anim:par", 1); + assertXPath(pXmlDoc, "//draw:page" + "/anim:par[@presentation:node-type='timing-root']" + "/anim:seq[@presentation:node-type='interactive-sequence']", 2); + assertXPath(pXmlDoc, "//draw:page" + "/anim:par[@presentation:node-type='timing-root']" + "/anim:seq[@presentation:node-type='interactive-sequence']" + "/anim:par[@smil:begin]",3); + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdExportTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx index 23a2ba7c2378..7e0967d19789 100644 --- a/sd/source/filter/eppt/pptx-animations.cxx +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -231,126 +231,60 @@ void WriteAnimateValues(const FSHelperPtr& pFS, const Reference& rXAni pFS->endElementNS(XML_p, XML_tavLst); } -void WriteAnimationCondition(const FSHelperPtr& pFS, const char* pDelay, const char* pEvent, - double fDelay, bool bHasFDelay, sal_Int32 nToken) +// Write condition list ( either prevCondlst or nextCondlst ) of Seq. +void WriteAnimationCondListForSeq(const FSHelperPtr& pFS, sal_Int32 nToken) { - if (!(bHasFDelay || pDelay || pEvent)) - return; + const char* pEvent = (nToken == XML_prevCondLst) ? "onPrev" : "onNext"; pFS->startElementNS(XML_p, nToken, FSEND); - - if (!pEvent) - pFS->singleElementNS(XML_p, XML_cond, XML_delay, - bHasFDelay ? I64S(static_cast(fDelay * 1000.0)) : pDelay, - FSEND); - else - { - pFS->startElementNS(XML_p, XML_cond, XML_delay, - bHasFDelay ? I64S(static_cast(fDelay * 1000.0)) : pDelay, - XML_evt, pEvent, FSEND); - - pFS->startElementNS(XML_p, XML_tgtEl, FSEND); - pFS->singleElementNS(XML_p, XML_sldTgt, FSEND); - pFS->endElementNS(XML_p, XML_tgtEl); - - pFS->endElementNS(XML_p, XML_cond); - } - + pFS->startElementNS(XML_p, XML_cond, XML_evt, pEvent, FSEND); + pFS->startElementNS(XML_p, XML_tgtEl, FSEND); + pFS->singleElementNS(XML_p, XML_sldTgt, FSEND); + pFS->endElementNS(XML_p, XML_tgtEl); + pFS->endElementNS(XML_p, XML_cond); pFS->endElementNS(XML_p, nToken); } -void WriteAnimationCondition(const FSHelperPtr& pFS, Any const& rAny, bool bWriteEvent, - bool bMainSeqChild, sal_Int32 nToken) +const char* convertEventTrigger(sal_Int16 nTrigger) { - bool bHasFDelay = false; - double fDelay = 0; - Timing eTiming; - Event aEvent; - Reference xShape; - const char* pDelay = nullptr; const char* pEvent = nullptr; - - if (rAny >>= fDelay) - bHasFDelay = true; - else if (rAny >>= eTiming) - { - if (eTiming == Timing_INDEFINITE) - pDelay = "indefinite"; - } - else if (rAny >>= aEvent) - { - // TODO - - SAL_INFO("sd.eppt", "animation condition event: TODO"); - SAL_INFO("sd.eppt", "event offset has value: " - << aEvent.Offset.hasValue() << " trigger: " << aEvent.Trigger - << " source has value: " << aEvent.Source.hasValue()); - if (!bWriteEvent && aEvent.Trigger == EventTrigger::ON_NEXT && bMainSeqChild) - pDelay = "indefinite"; - else if (bWriteEvent) - { - switch (aEvent.Trigger) - { - case EventTrigger::ON_NEXT: - pEvent = "onNext"; - break; - case EventTrigger::ON_PREV: - pEvent = "onPrev"; - break; - case EventTrigger::BEGIN_EVENT: - pEvent = "begin"; - break; - case EventTrigger::END_EVENT: - pEvent = "end"; - break; - case EventTrigger::ON_BEGIN: - pEvent = "onBegin"; - break; - case EventTrigger::ON_END: - pEvent = "onEnd"; - break; - case EventTrigger::ON_CLICK: - pEvent = "onClick"; - break; - case EventTrigger::ON_DBL_CLICK: - pEvent = "onDblClick"; - break; - case EventTrigger::ON_STOP_AUDIO: - pEvent = "onStopAudio"; - break; - case EventTrigger::ON_MOUSE_ENTER: - pEvent = "onMouseOver"; // not exact? - break; - case EventTrigger::ON_MOUSE_LEAVE: - pEvent = "onMouseOut"; - break; - } - } - - if (aEvent.Offset >>= fDelay) - { - bHasFDelay = true; - SAL_INFO("sd.eppt", "event offset: " << fDelay); - } - else if (aEvent.Offset >>= eTiming) - { - if (eTiming == Timing_INDEFINITE) - pDelay = "indefinite"; - SAL_INFO("sd.eppt", "event offset timing: " << static_cast(eTiming)); - } - } - else if (rAny >>= xShape) + switch (nTrigger) { - SAL_INFO("sd.eppt", "Got the xShape: " << xShape->getShapeType()); - if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape" - || xShape->getShapeType() == "com.sun.star.presentation.MediaShape") - { - // write the default - bHasFDelay = true; - } + case EventTrigger::ON_NEXT: + pEvent = "onNext"; + break; + case EventTrigger::ON_PREV: + pEvent = "onPrev"; + break; + case EventTrigger::BEGIN_EVENT: + pEvent = "begin"; + break; + case EventTrigger::END_EVENT: + pEvent = "end"; + break; + case EventTrigger::ON_BEGIN: + pEvent = "onBegin"; + break; + case EventTrigger::ON_END: + pEvent = "onEnd"; + break; + case EventTrigger::ON_CLICK: + pEvent = "onClick"; + break; + case EventTrigger::ON_DBL_CLICK: + pEvent = "onDblClick"; + break; + case EventTrigger::ON_STOP_AUDIO: + pEvent = "onStopAudio"; + break; + case EventTrigger::ON_MOUSE_ENTER: + pEvent = "onMouseOver"; // not exact? + break; + case EventTrigger::ON_MOUSE_LEAVE: + pEvent = "onMouseOut"; + break; } - - WriteAnimationCondition(pFS, pDelay, pEvent, fDelay, bHasFDelay, nToken); + return pEvent; } void WriteAnimationAttributeName(const FSHelperPtr& pFS, const OUString& rAttributeName) @@ -622,8 +556,57 @@ public: const OUString& getEffectPresetSubType() const { return msEffectPresetSubType; } bool isValid() const { return mbValid; } const std::vector& getChildNodes() const { return maChildNodes; }; + Any getCondition(bool bBegin) const; }; +struct Cond +{ + OString msDelay; + const char* mpEvent; + Reference mxShape; + + Cond(const Any& rAny, bool bIsMainSeqChild); + + bool isValid() { return msDelay.getLength() || mpEvent; } + const char* getDelay() const { return msDelay.getLength() ? msDelay.getStr() : nullptr; } +}; + +Cond::Cond(const Any& rAny, bool bIsMainSeqChild) + : mpEvent(nullptr) +{ + bool bHasFDelay = false; + double fDelay = 0; + Timing eTiming; + Event aEvent; + + if (rAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + msDelay = "indefinite"; + } + else if (rAny >>= aEvent) + { + if (aEvent.Trigger == EventTrigger::ON_NEXT && bIsMainSeqChild) + msDelay = "indefinite"; + else + { + mpEvent = convertEventTrigger(aEvent.Trigger); + aEvent.Source >>= mxShape; + + if (aEvent.Offset >>= fDelay) + bHasFDelay = true; + } + } + else if (rAny >>= fDelay) + bHasFDelay = true; + + if (bHasFDelay) + { + sal_Int32 nDelay = static_cast(fDelay * 1000.0); + msDelay = OString::number(nDelay); + } +} + class PPTXAnimationExport { void WriteAnimationNode(const NodeContextPtr& pContext); @@ -634,6 +617,8 @@ class PPTXAnimationExport void WriteAnimationNodeCommand(); void WriteAnimationNodeCommonPropsStart(); void WriteAnimationTarget(const Any& rTarget); + void WriteAnimationCondList(const Any& rAny, sal_Int32 nToken); + void WriteAnimationCond(const Cond& rCond); bool isMainSeqChild(); const Reference& getCurrentNode(); @@ -721,6 +706,64 @@ void PPTXAnimationExport::WriteAnimationTarget(const Any& rTarget) mpFS->endElementNS(XML_p, XML_tgtEl); } +void PPTXAnimationExport::WriteAnimationCondList(const Any& rAny, sal_Int32 nToken) +{ + if (!rAny.hasValue()) + return; + + std::vector aList; + + bool bIsMainSeqChild = isMainSeqChild(); + + Sequence aCondSeq; + if (rAny >>= aCondSeq) + { + for (int i = 0; i < aCondSeq.getLength(); i++) + { + Cond aCond(aCondSeq[i], bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + } + else + { + Cond aCond(rAny, bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + + if (aList.size() > 0) + { + mpFS->startElementNS(XML_p, nToken, FSEND); + + for (const Cond& rCond : aList) + WriteAnimationCond(rCond); + + mpFS->endElementNS(XML_p, nToken); + } +} + +void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond) +{ + if (rCond.mpEvent) + { + if (rCond.mxShape.is()) + { + mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent, FSEND); + WriteAnimationTarget(makeAny(rCond.mxShape)); + mpFS->endElementNS(XML_p, XML_cond); + } + else + { + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent, FSEND); + } + } + else + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), FSEND); +} + void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType) { const Reference& rXNode = getCurrentNode(); @@ -917,7 +960,6 @@ void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple, bool bWr void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart() { const Reference& rXNode = getCurrentNode(); - bool bMainSeqChild = isMainSeqChild(); const char* pDuration = nullptr; const char* pRestart = nullptr; const char* pNodeType = nullptr; @@ -999,33 +1041,8 @@ void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart() pFill, XML_presetClass, pPresetClass, XML_presetID, bPresetId ? I64S(nPresetId) : nullptr, XML_presetSubtype, bPresetSubType ? I64S(nPresetSubType) : nullptr, FSEND); - aAny = rXNode->getBegin(); - if (aAny.hasValue()) - { - Sequence aCondSeq; - - if (aAny >>= aCondSeq) - { - for (int i = 0; i < aCondSeq.getLength(); i++) - WriteAnimationCondition(mpFS, aCondSeq[i], false, bMainSeqChild, XML_stCondLst); - } - else - WriteAnimationCondition(mpFS, aAny, false, bMainSeqChild, XML_stCondLst); - } - - aAny = rXNode->getEnd(); - if (aAny.hasValue()) - { - Sequence aCondSeq; - - if (aAny >>= aCondSeq) - { - for (int i = 0; i < aCondSeq.getLength(); i++) - WriteAnimationCondition(mpFS, aCondSeq[i], false, bMainSeqChild, XML_endCondLst); - } - else - WriteAnimationCondition(mpFS, aAny, false, bMainSeqChild, XML_endCondLst); - } + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); if (rXNode->getType() == AnimationNodeType::ITERATE) { @@ -1063,8 +1080,8 @@ void PPTXAnimationExport::WriteAnimationNodeSeq() WriteAnimationNodeCommonPropsStart(); - WriteAnimationCondition(mpFS, nullptr, "onPrev", 0, true, XML_prevCondLst); - WriteAnimationCondition(mpFS, nullptr, "onNext", 0, true, XML_nextCondLst); + WriteAnimationCondListForSeq(mpFS, XML_prevCondLst); + WriteAnimationCondListForSeq(mpFS, XML_nextCondLst); mpFS->endElementNS(XML_p, XML_seq); } @@ -1298,4 +1315,14 @@ bool NodeContext::initChildNodes() } return bValid; } + +Any NodeContext::getCondition(bool bBegin) const +{ + const bool bParent + = (mnEffectNodeType != EffectNodeType::INTERACTIVE_SEQUENCE || maChildNodes.empty()); + const Reference& rNode = bParent ? mxNode : maChildNodes[0]->getNode(); + + return bBegin ? rNode->getBegin() : rNode->getEnd(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit