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.grandin@collabora.co.uk>
This commit is contained in:
Mark Hung
2023-01-29 06:01:05 +00:00
committed by Noel Grandin
parent f640a0c5d2
commit 4e6601cf1b
4 changed files with 46 additions and 7 deletions
+7 -3
View File
@@ -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);
@@ -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);
}
@@ -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; }
+18 -1
View File
@@ -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);
}