From d98d3e2a0bc087ec9157e8e32e9f0ea4207d36e2 Mon Sep 17 00:00:00 2001 From: Mark Hung Date: Fri, 1 Mar 2019 10:09:30 +0800 Subject: tdf#44223: Export the audio of effects and transitions. This will allow to round trip the test case for the slide transition and the animation effect audio. Change-Id: Iac524e6bbcdb0a29491cfeba63121c845685fd11 Reviewed-on: https://gerrit.libreoffice.org/68540 Tested-by: Jenkins Reviewed-by: Mark Hung --- sd/qa/unit/export-tests-ooxml2.cxx | 37 ++++++++++++++++ sd/qa/unit/import-tests.cxx | 27 ------------ sd/source/filter/eppt/epptooxml.hxx | 3 ++ sd/source/filter/eppt/pptx-animations.cxx | 47 +++++++++++++++++++- sd/source/filter/eppt/pptx-epptooxml.cxx | 73 +++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 29 deletions(-) diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index ae60952752fc..eda6827a6869 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -198,6 +198,7 @@ public: void testTdf119118(); void testTdf99213(); void testPotxExport(); + void testTdf44223(); CPPUNIT_TEST_SUITE(SdOOXMLExportTest2); @@ -278,6 +279,7 @@ public: CPPUNIT_TEST(testTdf119118); CPPUNIT_TEST(testTdf99213); CPPUNIT_TEST(testPotxExport); + CPPUNIT_TEST(testTdf44223); CPPUNIT_TEST_SUITE_END(); @@ -297,6 +299,7 @@ public: { "wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" }, { "p", "http://schemas.openxmlformats.org/presentationml/2006/main" }, { "p14", "http://schemas.microsoft.com/office/powerpoint/2010/main" }, + { "r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" }, { "w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main" }, { "a14", "http://schemas.microsoft.com/office/drawing/2010/main" }, { "wps", "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" }, @@ -2066,6 +2069,40 @@ void SdOOXMLExportTest2::testPotxExport() assertXPath(pContentTypes, "/ContentType:Types/ContentType:Override[@PartName='/ppt/presentation.xml']", "ContentType", "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"); } + +void SdOOXMLExportTest2::testTdf44223() +{ + utl::TempFile tempFile; + ::sd::DrawDocShellRef xDocShRef + = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/tdf44223.pptx"), PPTX); + xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile); + + std::shared_ptr const pStream1(parseExportStream(tempFile, "media/audio1.wav")); + CPPUNIT_ASSERT_EQUAL(sal_uInt64(11140), pStream1->remainingSize()); + + std::shared_ptr const pStream2(parseExportStream(tempFile, "media/audio2.wav")); + CPPUNIT_ASSERT_EQUAL(sal_uInt64(28074), pStream2->remainingSize()); + + xmlDocPtr pXmlContentType = parseExport(tempFile, "[Content_Types].xml"); + assertXPath(pXmlContentType, + "/ContentType:Types/ContentType:Override[@PartName='/media/audio1.wav']", + "ContentType", + "audio/x-wav"); + + assertXPath(pXmlContentType, + "/ContentType:Types/ContentType:Override[@PartName='/media/audio2.wav']", + "ContentType", + "audio/x-wav"); + + xmlDocPtr pDoc1 = parseExport(tempFile, "ppt/slides/slide1.xml"); + assertXPath(pDoc1 , "//p:audio/p:cMediaNode/p:tgtEl/p:sndTgt[@r:embed]", 1); + + xmlDocPtr pDoc2 = parseExport(tempFile, "ppt/slides/slide2.xml"); + assertXPath(pDoc2 , "//p:transition/p:sndAc/p:stSnd/p:snd[@r:embed]", 2); + + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest2); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sd/qa/unit/import-tests.cxx b/sd/qa/unit/import-tests.cxx index 684dacbb7a84..44464e2559a2 100644 --- a/sd/qa/unit/import-tests.cxx +++ b/sd/qa/unit/import-tests.cxx @@ -44,8 +44,6 @@ #include #include -#include -#include #include #include #include @@ -194,7 +192,6 @@ public: void testTdf123090(); void testTdf120028(); void testTdf120028b(); - void testTdf44223(); void testDescriptionImport(); void testTdf83247(); void testTdf47365(); @@ -282,7 +279,6 @@ public: CPPUNIT_TEST(testTdf123090); CPPUNIT_TEST(testTdf120028); CPPUNIT_TEST(testTdf120028b); - CPPUNIT_TEST(testTdf44223); CPPUNIT_TEST(testDescriptionImport); CPPUNIT_TEST(testTdf83247); CPPUNIT_TEST(testTdf47365); @@ -2632,29 +2628,6 @@ void SdImportTest::testTdf120028b() xDocShRef->DoClose(); } -void SdImportTest::testTdf44223() -{ - ::sd::DrawDocShellRef xDocShRef - = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/tdf44223.pptx"), PPTX); - uno::Reference xSBD(xDocShRef->GetDoc()->getUnoModel(), uno::UNO_QUERY); - CPPUNIT_ASSERT(xSBD.is()); - - uno::Reference xStorage = xSBD->getDocumentStorage(); - CPPUNIT_ASSERT(xStorage.is()); - - uno::Reference xNameAccess(xStorage, uno::UNO_QUERY); - CPPUNIT_ASSERT(xNameAccess.is()); - - uno::Reference xStorage_2(xNameAccess->getByName("Media"), uno::UNO_QUERY); - CPPUNIT_ASSERT(xStorage_2.is()); - uno::Reference< container::XNameAccess > xNameAccess_2(xStorage_2, uno::UNO_QUERY); - - CPPUNIT_ASSERT(xNameAccess_2->hasByName("audio1.wav")); - CPPUNIT_ASSERT(xNameAccess_2->hasByName("audio2.wav")); - - xDocShRef->DoClose(); -} - void SdImportTest::testDescriptionImport() { sd::DrawDocShellRef xDocShRef diff --git a/sd/source/filter/eppt/epptooxml.hxx b/sd/source/filter/eppt/epptooxml.hxx index 5d6be2286af9..5791c0249b24 100644 --- a/sd/source/filter/eppt/epptooxml.hxx +++ b/sd/source/filter/eppt/epptooxml.hxx @@ -76,6 +76,9 @@ public: sal_Int32 GetShapeID(const css::uno::Reference& rXShape); sal_Int32 GetNextAnimationNodeID(); + + void embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName); + private: virtual void ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 nMode, diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx index 7e0967d19789..d484fcc91cb0 100644 --- a/sd/source/filter/eppt/pptx-animations.cxx +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -393,6 +394,9 @@ sal_Int32 extractNodeType(const Reference& rXNode) case AnimationNodeType::COMMAND: xmlNodeType = XML_cmd; break; + case AnimationNodeType::AUDIO: + xmlNodeType = XML_audio; + break; default: SAL_WARN("sd.eppt", "unhandled animation node: " << nType); break; @@ -615,6 +619,7 @@ class PPTXAnimationExport void WriteAnimationNodeSeq(); void WriteAnimationNodeEffect(); void WriteAnimationNodeCommand(); + void WriteAnimationNodeAudio(); void WriteAnimationNodeCommonPropsStart(); void WriteAnimationTarget(const Any& rTarget); void WriteAnimationCondList(const Any& rAny, sal_Int32 nToken); @@ -1144,6 +1149,39 @@ void PPTXAnimationExport::WriteAnimationNodeCommand() mpFS->endElementNS(XML_p, XML_cmd); } +void PPTXAnimationExport::WriteAnimationNodeAudio() +{ + SAL_INFO("sd.eppt", "write animation node audio"); + Reference xAudio(getCurrentNode(), UNO_QUERY); + + OUString sUrl; + OUString sRelId; + OUString sName; + + if (!(xAudio.is() && (xAudio->getSource() >>= sUrl) && !sUrl.isEmpty() + && sUrl.endsWithIgnoreAsciiCase(".wav"))) + return; + + mrPowerPointExport.embedEffectAudio(mpFS, sUrl, sRelId, sName); + + mpFS->startElementNS(XML_p, XML_audio, FSEND); + mpFS->startElementNS(XML_p, XML_cMediaNode, FSEND); + + mpFS->startElementNS(XML_p, XML_cTn, FSEND); + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); + mpFS->endElementNS(XML_p, XML_cTn); + + mpFS->startElementNS(XML_p, XML_tgtEl, FSEND); + mpFS->singleElementNS(XML_p, XML_sndTgt, FSNS(XML_r, XML_embed), + sRelId.isEmpty() ? nullptr : USS(sRelId), XML_name, + sUrl.isEmpty() ? nullptr : USS(sName), FSEND); + mpFS->endElementNS(XML_p, XML_tgtEl); + + mpFS->endElementNS(XML_p, XML_cMediaNode); + mpFS->endElementNS(XML_p, XML_audio); +} + void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext) { const NodeContext* pSavedContext = mpContext; @@ -1178,6 +1216,9 @@ void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext) case XML_cmd: WriteAnimationNodeCommand(); break; + case XML_audio: + WriteAnimationNodeAudio(); + break; default: SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType); break; @@ -1278,8 +1319,10 @@ void NodeContext::initValid(bool bHasValidChild, bool bIsIterateChild) } else if (nType == AnimationNodeType::AUDIO) { - SAL_WARN("sd.eppt", "Export AUDIO node is not supported yet."); - mbValid = false; + Reference xAudio(mxNode, UNO_QUERY); + OUString sURL; + mbValid + = xAudio.is() && (xAudio->getSource() >>= sURL) && sURL.endsWithIgnoreAsciiCase(".wav"); } else { diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx index b91c265b90bd..2d244f2fb523 100644 --- a/sd/source/filter/eppt/pptx-epptooxml.cxx +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -115,6 +115,21 @@ public: bool WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster); }; + +namespace +{ +void WriteSndAc(const FSHelperPtr& pFS, const OUString& sSoundRelId, const OUString& sSoundName) +{ + pFS->startElementNS(XML_p, XML_sndAc, FSEND); + pFS->startElementNS(XML_p, XML_stSnd, FSEND); + pFS->singleElementNS(XML_p, XML_snd, + FSNS(XML_r, XML_embed), sSoundRelId.isEmpty() ? nullptr : USS(sSoundRelId), + XML_name, sSoundName.isEmpty() ? nullptr : USS(sSoundName), FSEND); + pFS->endElement(FSNS(XML_p, XML_stSnd)); + pFS->endElement(FSNS(XML_p, XML_sndAc)); +} +} + } } @@ -546,6 +561,10 @@ void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) sal_Int8 nPPTTransitionType = 0; sal_uInt8 nDirection = 0; + OUString sSoundUrl; + OUString sSoundRelId; + OUString sSoundName; + if (ImplGetPropertyValue(mXPagePropSet, "TransitionType") && (mAny >>= nTransitionType) && ImplGetPropertyValue(mXPagePropSet, "TransitionSubtype") && (mAny >>= nTransitionSubtype)) nPPTTransitionType = GetTransition(nTransitionType, nTransitionSubtype, eFadeEffect, nDirection); @@ -553,6 +572,9 @@ void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) if (!nPPTTransitionType && eFadeEffect != FadeEffect_NONE) nPPTTransitionType = GetTransition(eFadeEffect, nDirection); + if (ImplGetPropertyValue(mXPagePropSet, "Sound") && (mAny >>= sSoundUrl)) + embedEffectAudio(pFS, sSoundUrl, sSoundRelId, sSoundName); + bool bOOXmlSpecificTransition = false; sal_Int32 nTransition = 0; @@ -866,6 +888,9 @@ void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) FSEND); } + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + pFS->endElement(FSNS(XML_p, XML_transition)); pFS->endElement(FSNS(XML_mc, XML_Choice)); @@ -887,6 +912,9 @@ void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) FSEND); } + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + pFS->endElementNS(XML_p, XML_transition); if (nTransition14 || pPresetTransition || isTransitionDurationSet) @@ -1935,6 +1963,51 @@ void PowerPointExport::WriteNotesMaster() SAL_INFO("sd.eppt", "----------------"); } +void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName) +{ + comphelper::LifecycleProxy aProxy; + + if (!sUrl.endsWithIgnoreAsciiCase(".wav")) + return; + + uno::Reference xAudioStream; + if (sUrl.startsWith("vnd.sun.star.Package:")) + { + uno::Reference xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference xDocumentStorage(xStorageBasedDocument->getDocumentStorage(), uno::UNO_QUERY); + if (!xDocumentStorage.is()) + return; + + uno::Reference xStream = comphelper::OStorageHelper::GetStreamAtPackageURL(xDocumentStorage, sUrl, + css::embed::ElementModes::READ, aProxy); + + if (xStream.is()) + xAudioStream = xStream->getInputStream(); + } + else + xAudioStream = comphelper::OStorageHelper::GetInputStreamFromURL(sUrl, getComponentContext()); + + if (!xAudioStream.is()) + return; + + int nLastSlash = sUrl.lastIndexOf('/'); + sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0); + + OUString sPath = OUStringBuffer().append("/media/") + .append(sName) + .makeStringAndClear(); + + sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::AUDIO), sPath); + + uno::Reference xOutputStream = openFragmentStream(sPath, "audio/x-wav"); + + comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream); +} + sal_Int32 PowerPointExport::GetShapeID(const Reference& rXShape) { return ShapeExport::GetShapeID(rXShape, &maShapeMap); -- cgit