tdf#124230 pptx: make exported animation sound work.
1. Fix target path for the embedded media. Move the media
file to /ppt/media ( was /media ) and use the relative path
../media for the target path when adding the relation. This
is necessary for MSO to play the sound.
2. Write timenode id for the start or end conditions if the
animation node for the begin event or the end event is
available. Events like BEGIN or END has to refer a timenode.
Without specifying referred timenode in start and end
condition, Impress will not activate the audio node after
importing the document.
Change-Id: I6027be2e836e2f86061e401c8af806b2b1993a49
Reviewed-on: https://gerrit.libreoffice.org/71427
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 7c27e0a..66c6154 100644
--- a/sd/qa/unit/export-tests-ooxml2.cxx
+++ b/sd/qa/unit/export-tests-ooxml2.cxx
@@ -2087,31 +2087,38 @@ void SdOOXMLExportTest2::testTdf44223()
= 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"));
std::shared_ptr<SvStream> const pStream1(parseExportStream(tempFile, "ppt/media/audio1.wav"));
CPPUNIT_ASSERT_EQUAL(sal_uInt64(11140), pStream1->remainingSize());
std::shared_ptr<SvStream> const pStream2(parseExportStream(tempFile, "media/audio2.wav"));
std::shared_ptr<SvStream> const pStream2(parseExportStream(tempFile, "ppt/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:Types/ContentType:Override[@PartName='/ppt/media/audio1.wav']",
"ContentType",
"audio/x-wav");
assertXPath(pXmlContentType,
"/ContentType:Types/ContentType:Override[@PartName='/media/audio2.wav']",
"/ContentType:Types/ContentType:Override[@PartName='/ppt/media/audio2.wav']",
"ContentType",
"audio/x-wav");
xmlDocPtr pDoc1 = parseExport(tempFile, "ppt/slides/slide1.xml");
// 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");
xmlDocPtr pDoc2 = parseExport(tempFile, "ppt/slides/slide2.xml");
assertXPath(pDoc2 , "//p:transition/p:sndAc/p:stSnd/p:snd[@r:embed]", 2);
xmlDocPtr pRels1 = parseExport(tempFile, "ppt/slides/_rels/slide1.xml.rels");
assertXPath(pRels1, "//rels:Relationship[@Id='rId1']", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio");
assertXPath(pRels1, "//rels:Relationship[@Id='rId1']", "Target", "../media/audio1.wav");
xDocShRef->DoClose();
}
diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx
index a8c062b..da5223c 100644
--- a/sd/source/filter/eppt/pptx-animations.cxx
+++ b/sd/source/filter/eppt/pptx-animations.cxx
@@ -59,6 +59,7 @@
#include "pptexanimations.hxx"
#include "pptx-animations.hxx"
#include "../ppt/pptanimations.hxx"
#include <comphelper/stl_types.hxx>
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::container;
@@ -569,6 +570,7 @@ struct Cond
OString msDelay;
const char* mpEvent;
Reference<XShape> mxShape;
Reference<XAnimationNode> mxNode;
Cond(const Any& rAny, bool bIsMainSeqChild);
@@ -596,7 +598,8 @@ Cond::Cond(const Any& rAny, bool bIsMainSeqChild)
else
{
mpEvent = convertEventTrigger(aEvent.Trigger);
aEvent.Source >>= mxShape;
if (!(aEvent.Source >>= mxShape))
aEvent.Source >>= mxNode;
if (aEvent.Offset >>= fDelay)
bHasFDelay = true;
@@ -632,6 +635,11 @@ class PPTXAnimationExport
const FSHelperPtr& mpFS;
const NodeContext* mpContext;
std::map<Reference<XAnimationNode>, sal_Int32, ::comphelper::OInterfaceCompare<XAnimationNode>>
maAnimationNodeIdMap;
sal_Int32 GetNextAnimationNodeId(const Reference<XAnimationNode>& rNode);
sal_Int32 GetAnimationNodeId(const Reference<XAnimationNode>& rNode);
public:
PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS);
void WriteAnimations(const Reference<XDrawPage>& rXDrawPage);
@@ -751,6 +759,7 @@ void PPTXAnimationExport::WriteAnimationCondList(const Any& rAny, sal_Int32 nTok
void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond)
{
sal_Int32 nId = -1;
if (rCond.mpEvent)
{
if (rCond.mxShape.is())
@@ -760,6 +769,13 @@ void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond)
WriteAnimationTarget(makeAny(rCond.mxShape));
mpFS->endElementNS(XML_p, XML_cond);
}
else if (rCond.mxNode.is() && (nId = GetAnimationNodeId(rCond.mxNode)) != -1)
{
mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
rCond.mpEvent);
mpFS->singleElementNS(XML_p, XML_tn, XML_val, OString::number(nId));
mpFS->endElementNS(XML_p, XML_cond);
}
else
{
mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
@@ -1041,8 +1057,7 @@ void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart()
bool bAutoReverse = rXNode->getAutoReverse();
mpFS->startElementNS(
XML_p, XML_cTn, XML_id, OString::number(mrPowerPointExport.GetNextAnimationNodeID()),
XML_dur,
XML_p, XML_cTn, XML_id, OString::number(GetNextAnimationNodeId(rXNode)), XML_dur,
fDuration != 0 ? OString::number(static_cast<sal_Int32>(fDuration * 1000.0)).getStr()
: pDuration,
XML_autoRev, bAutoReverse ? "1" : nullptr, XML_restart, pRestart, XML_nodeType, pNodeType,
@@ -1261,6 +1276,24 @@ void PPTXAnimationExport::WriteAnimations(const Reference<XDrawPage>& rXDrawPage
}
}
sal_Int32 PPTXAnimationExport::GetNextAnimationNodeId(const Reference<XAnimationNode>& xNode)
{
sal_Int32 nId = mrPowerPointExport.GetNextAnimationNodeID();
maAnimationNodeIdMap[xNode] = nId;
return nId;
}
sal_Int32 PPTXAnimationExport::GetAnimationNodeId(const Reference<XAnimationNode>& xNode)
{
sal_Int32 nId = -1;
const auto& aIter = maAnimationNodeIdMap.find(xNode);
if (aIter != maAnimationNodeIdMap.end())
{
nId = aIter->second;
}
return nId;
}
NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild,
bool bIsIterateChild)
: mxNode(xNode)
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx
index abe09c4..a7d51cf 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -1971,14 +1971,15 @@ void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString&
int nLastSlash = sUrl.lastIndexOf('/');
sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0);
OUString sPath = OUStringBuffer().append("/media/")
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");
uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, "/ppt"),
"audio/x-wav");
comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream);
}