sd/qa/filter/eppt/data/video-loop.pptx    |binary
 sd/qa/filter/eppt/eppt.cxx                |   26 ++++++++++++++++
 sd/source/filter/eppt/pptx-animations.cxx |   47 ++++++++++++++++++++++++------
 3 files changed, 65 insertions(+), 8 deletions(-)

New commits:
commit 38671e21d7dbcd5019912b9468305018de0c922e
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Sep 2 14:19:41 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Sep 2 15:41:56 2022 +0200

    Related: tdf#149969 PPTX export: add loop from the animation of a media 
shape
    
    The PPTX import maps media nodes to XAudio nodes, but the export side in
    PPTXAnimationExport::WriteAnimationNodeAudio() only handled audio, not
    video.
    
    This is fine, that code was added for audio narration purposes, but the
    same animation handles looping for videos, so this needs extending.
    
    Fix the problem by exporting <p:audio> conditionally and write video
    markup (especially info about repeat count) when the content of the
    media shape is video, not audio.
    
    Change-Id: Iba6bb4901b984c4363023f05232efc06ff069022
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139261
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sd/qa/filter/eppt/data/video-loop.pptx 
b/sd/qa/filter/eppt/data/video-loop.pptx
new file mode 100644
index 000000000000..4cb7e20b7428
Binary files /dev/null and b/sd/qa/filter/eppt/data/video-loop.pptx differ
diff --git a/sd/qa/filter/eppt/eppt.cxx b/sd/qa/filter/eppt/eppt.cxx
index 1e8e2c7e1491..151b9cfce27a 100644
--- a/sd/qa/filter/eppt/eppt.cxx
+++ b/sd/qa/filter/eppt/eppt.cxx
@@ -125,6 +125,32 @@ CPPUNIT_TEST_FIXTURE(Test, testThemeExport)
     // i.e. the RGB color was lost on export.
     xComponent->dispose();
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testLoopingFromAnimation)
+{
+    // Given a media shape that has an animation that specifies looping for 
the video:
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"video-loop.pptx";
+    getComponent() = loadFromDesktop(aURL);
+
+    // When exporting that to PPTX:
+    utl::TempFile aTempFile;
+    uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY);
+    utl::MediaDescriptor aMediaDescriptor;
+    aMediaDescriptor["FilterName"] <<= OUString("Impress Office Open XML");
+    aTempFile.EnableKillingFile();
+    xStorable->storeToURL(aTempFile.GetURL(), 
aMediaDescriptor.getAsConstPropertyValueList());
+    validate(aTempFile.GetFileName(), test::OOXML);
+
+    // Then make sure that the "infinite" repeat count is written:
+    std::unique_ptr<SvStream> pStream = parseExportStream(aTempFile, 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // Without the fix in place, this test would have failed with:
+    // - Expected: 1
+    // - Actual  : 0
+    // - In <>, XPath '//p:cMediaNode/p:cTn' number of nodes is incorrect
+    // i.e. the media node was lost on export, the video no longer looped.
+    assertXPath(pXmlDoc, "//p:cMediaNode/p:cTn", "repeatCount", "indefinite");
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/source/filter/eppt/pptx-animations.cxx 
b/sd/source/filter/eppt/pptx-animations.cxx
index 54dab17d1972..1c901573c00a 100644
--- a/sd/source/filter/eppt/pptx-animations.cxx
+++ b/sd/source/filter/eppt/pptx-animations.cxx
@@ -659,6 +659,9 @@ bool IsAudioURL(const OUString& rURL)
 {
     return rURL.endsWithIgnoreAsciiCase(".wav") || 
rURL.endsWithIgnoreAsciiCase(".m4a");
 }
+
+/// Returns if rURL has an extension which is a video format.
+bool IsVideoURL(const OUString& rURL) { return 
rURL.endsWithIgnoreAsciiCase(".mp4"); }
 }
 
 namespace oox::core
@@ -1235,6 +1238,7 @@ void PPTXAnimationExport::WriteAnimationNodeAudio()
         bValid = true;
     }
 
+    bool bVideo = false;
     if (!bValid)
     {
         if (xAudio->getSource() >>= xShape)
@@ -1243,7 +1247,8 @@ void PPTXAnimationExport::WriteAnimationNodeAudio()
             bool bHasMediaURL = 
xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL");
             if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") >>= 
sUrl))
             {
-                bValid = IsAudioURL(sUrl);
+                bVideo = IsVideoURL(sUrl);
+                bValid = IsAudioURL(sUrl) || bVideo;
             }
         }
     }
@@ -1256,12 +1261,31 @@ void PPTXAnimationExport::WriteAnimationNodeAudio()
         mrPowerPointExport.embedEffectAudio(mpFS, sUrl, sRelId, sName);
     }
 
-    bool bNarration = xAudio->getNarration();
-    mpFS->startElementNS(XML_p, XML_audio, XML_isNarration, bNarration ? "1" : 
"0");
-    bool bHideDuringShow = xAudio->getHideDuringShow();
-    mpFS->startElementNS(XML_p, XML_cMediaNode, XML_showWhenStopped, 
bHideDuringShow ? "0" : "1");
+    if (bVideo)
+    {
+        mpFS->startElementNS(XML_p, XML_video);
+        mpFS->startElementNS(XML_p, XML_cMediaNode);
+    }
+    else
+    {
+        bool bNarration = xAudio->getNarration();
+        mpFS->startElementNS(XML_p, XML_audio, XML_isNarration, bNarration ? 
"1" : "0");
+        bool bHideDuringShow = xAudio->getHideDuringShow();
+        mpFS->startElementNS(XML_p, XML_cMediaNode, XML_showWhenStopped,
+                             bHideDuringShow ? "0" : "1");
+    }
 
-    mpFS->startElementNS(XML_p, XML_cTn);
+    animations::Timing eTiming{};
+    bool bLooping
+        = (xAudio->getRepeatCount() >>= eTiming) && eTiming == 
animations::Timing_INDEFINITE;
+    if (bVideo && bLooping)
+    {
+        mpFS->startElementNS(XML_p, XML_cTn, XML_repeatCount, "indefinite");
+    }
+    else
+    {
+        mpFS->startElementNS(XML_p, XML_cTn);
+    }
     WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst);
     WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst);
     mpFS->endElementNS(XML_p, XML_cTn);
@@ -1281,7 +1305,14 @@ void PPTXAnimationExport::WriteAnimationNodeAudio()
     mpFS->endElementNS(XML_p, XML_tgtEl);
 
     mpFS->endElementNS(XML_p, XML_cMediaNode);
-    mpFS->endElementNS(XML_p, XML_audio);
+    if (bVideo)
+    {
+        mpFS->endElementNS(XML_p, XML_video);
+    }
+    else
+    {
+        mpFS->endElementNS(XML_p, XML_audio);
+    }
 }
 
 void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext)
@@ -1456,7 +1487,7 @@ void NodeContext::initValid(bool bHasValidChild, bool 
bIsIterateChild)
                     = 
xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL");
                 if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") 
>>= sURL))
                 {
-                    mbValid = IsAudioURL(sURL);
+                    mbValid = IsAudioURL(sURL) || IsVideoURL(sURL);
                 }
             }
         }

Reply via email to