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 <marklh9@gmail.com>
diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx
index ae60952..eda6827 100644
--- a/sd/qa/unit/export-tests-ooxml2.cxx
+++ b/sd/qa/unit/export-tests-ooxml2.cxx
@@ -198,6 +198,7 @@
    void testTdf119118();
    void testTdf99213();
    void testPotxExport();
    void testTdf44223();

    CPPUNIT_TEST_SUITE(SdOOXMLExportTest2);

@@ -278,6 +279,7 @@
    CPPUNIT_TEST(testTdf119118);
    CPPUNIT_TEST(testTdf99213);
    CPPUNIT_TEST(testPotxExport);
    CPPUNIT_TEST(testTdf44223);

    CPPUNIT_TEST_SUITE_END();

@@ -297,6 +299,7 @@
            { "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 @@
    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<SvStream> const pStream1(parseExportStream(tempFile, "media/audio1.wav"));
    CPPUNIT_ASSERT_EQUAL(sal_uInt64(11140), pStream1->remainingSize());

    std::shared_ptr<SvStream> 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 684dacb..44464e2 100644
--- a/sd/qa/unit/import-tests.cxx
+++ b/sd/qa/unit/import-tests.cxx
@@ -44,8 +44,6 @@

#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/presentation/ClickAction.hpp>
#include <com/sun/star/presentation/XPresentationPage.hpp>
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
@@ -194,7 +192,6 @@
    void testTdf123090();
    void testTdf120028();
    void testTdf120028b();
    void testTdf44223();
    void testDescriptionImport();
    void testTdf83247();
    void testTdf47365();
@@ -282,7 +279,6 @@
    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 @@
    xDocShRef->DoClose();
}

void SdImportTest::testTdf44223()
{
    ::sd::DrawDocShellRef xDocShRef
        = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/tdf44223.pptx"), PPTX);
    uno::Reference<document::XStorageBasedDocument> xSBD(xDocShRef->GetDoc()->getUnoModel(), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xSBD.is());

    uno::Reference<embed::XStorage> xStorage = xSBD->getDocumentStorage();
    CPPUNIT_ASSERT(xStorage.is());

    uno::Reference<container::XNameAccess> xNameAccess(xStorage, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xNameAccess.is());

    uno::Reference<embed::XStorage> 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 5d6be22..5791c02 100644
--- a/sd/source/filter/eppt/epptooxml.hxx
+++ b/sd/source/filter/eppt/epptooxml.hxx
@@ -76,6 +76,9 @@

    sal_Int32 GetShapeID(const css::uno::Reference<css::drawing::XShape>& 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 7e0967d..d484fcc 100644
--- a/sd/source/filter/eppt/pptx-animations.cxx
+++ b/sd/source/filter/eppt/pptx-animations.cxx
@@ -41,6 +41,7 @@
#include <com/sun/star/animations/XAnimationNodeSupplier.hpp>
#include <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XCommand.hpp>
#include <com/sun/star/animations/XAudio.hpp>
#include <com/sun/star/animations/XTransitionFilter.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
@@ -393,6 +394,9 @@
        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 @@
    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 @@
    mpFS->endElementNS(XML_p, XML_cmd);
}

void PPTXAnimationExport::WriteAnimationNodeAudio()
{
    SAL_INFO("sd.eppt", "write animation node audio");
    Reference<XAudio> 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 @@
        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 @@
    }
    else if (nType == AnimationNodeType::AUDIO)
    {
        SAL_WARN("sd.eppt", "Export AUDIO node is not supported yet.");
        mbValid = false;
        Reference<XAudio> 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 b91c265..2d244f2 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -115,6 +115,21 @@
    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 @@
    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 @@
    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 @@
                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 @@
                             FSEND);
    }

    if (!sSoundRelId.isEmpty())
        WriteSndAc(pFS, sSoundRelId, sSoundName);

    pFS->endElementNS(XML_p, XML_transition);

    if (nTransition14 || pPresetTransition || isTransitionDurationSet)
@@ -1935,6 +1963,51 @@
    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<io::XInputStream> xAudioStream;
    if (sUrl.startsWith("vnd.sun.star.Package:"))
    {
        uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(getModel(), uno::UNO_QUERY);
        if (!xStorageBasedDocument.is())
            return;

        uno::Reference<embed::XStorage> xDocumentStorage(xStorageBasedDocument->getDocumentStorage(), uno::UNO_QUERY);
        if (!xDocumentStorage.is())
            return;

        uno::Reference<io::XStream> 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<io::XOutputStream> xOutputStream = openFragmentStream(sPath, "audio/x-wav");

    comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream);
}

sal_Int32 PowerPointExport::GetShapeID(const Reference<XShape>& rXShape)
{
    return ShapeExport::GetShapeID(rXShape, &maShapeMap);