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:
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user