filter/source/svg/svgwriter.cxx               |   46 +++++++++++++++++++++++---
 include/svx/svdomedia.hxx                     |    3 +
 sd/qa/unit/SVGExportTests.cxx                 |   35 +++++++++++++++++++
 sd/qa/unit/data/odp/slide-video-thumbnail.odp |binary
 svx/source/svdraw/svdomedia.cxx               |   10 +++++
 5 files changed, 90 insertions(+), 4 deletions(-)

New commits:
commit f994f4312d8e166e79b36f7bf0f8c41c3f1082f4
Author:     Ashod Nakashian <ashod.nakash...@collabora.co.uk>
AuthorDate: Sun Oct 23 17:56:50 2022 -0400
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Nov 4 08:28:25 2022 +0100

    svg: export embedded video
    
    Change-Id: Ie5dcd1fb4abbaf53f48107e7def0f42daad24596
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142145
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index fb0193e15418..14e355f3916e 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -38,6 +38,7 @@
 #include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
 #include <i18nlangtag/languagetag.hxx>
 #include <o3tl/string_view.hxx>
+#include <svx/svdomedia.hxx>
 
 #include <com/sun/star/container/XEnumerationAccess.hpp>
 #include <com/sun/star/container/XIndexReplace.hpp>
@@ -2997,12 +2998,49 @@ void SVGActionWriter::ImplWriteBmp( const BitmapEx& 
rBmpEx,
     mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrWidth, 
OUString::number( aSz.Width() ) );
     mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrHeight, 
OUString::number( aSz.Height() ) );
 
-    // the image must be scaled to aSz in a non-uniform way
-    mrExport.AddAttribute( XML_NAMESPACE_NONE, "preserveAspectRatio", "none" );
+    // If we have a media object (a video), export the video.
+    // Also, use the image generated above as the video poster (thumbnail).
+    SdrMediaObj* pMediaObj
+        = pShape ? 
dynamic_cast<SdrMediaObj*>(SdrObject::getSdrObjectFromXShape(*pShape)) : 
nullptr;
+    const bool embedVideo = (pMediaObj && !pMediaObj->getTempURL().isEmpty());
 
-    mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, 
aBuffer.makeStringAndClear() );
+    if (!embedVideo)
     {
-        SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, "image", true, 
true );
+        // the image must be scaled to aSz in a non-uniform way
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "preserveAspectRatio", 
"none");
+
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, 
aBuffer.makeStringAndClear());
+
+        SvXMLElementExport aElem(mrExport, XML_NAMESPACE_NONE, "image", true, 
true);
+    }
+    else
+    {
+        // <foreignObject xmlns="http://www.w3.org/2000/svg"; 
overflow="visible" width="499.6" height="374.33333333333337" x="705" y="333">
+        //     <body xmlns="http://www.w3.org/1999/xhtml";>
+        //         <video controls="controls" width="499.6" 
height="374.33333333333337">
+        //             <source src="file:///tmp/abcdef.mp4" type="video/mp4">
+        //         </video>
+        //     </body>
+        // </foreignObject>
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "xmlns", 
"http://www.w3.org/2000/svg";);
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "overflow", "visible");
+        SvXMLElementExport aForeignObject(mrExport, XML_NAMESPACE_NONE, 
"foreignObject", true,
+                                          true);
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "xmlns", 
"http://www.w3.org/1999/xhtml";);
+        SvXMLElementExport aBody(mrExport, XML_NAMESPACE_NONE, "body", true, 
true);
+
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrWidth, 
OUString::number(aSz.Width()));
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrHeight, 
OUString::number(aSz.Height()));
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "autoplay", "autoplay");
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "controls", "controls");
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "loop", "loop");
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "preload", "auto");
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "poster", 
aBuffer.makeStringAndClear());
+        SvXMLElementExport aVideo(mrExport, XML_NAMESPACE_NONE, "video", true, 
true);
+
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "src", 
pMediaObj->getTempURL());
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "type", "video/mp4"); 
//FIXME: set mime type.
+        SvXMLElementExport aSource(mrExport, XML_NAMESPACE_NONE, "source", 
true, true);
     }
 }
 
diff --git a/include/svx/svdomedia.hxx b/include/svx/svdomedia.hxx
index 6199fcaffe5b..6f08611a7002 100644
--- a/include/svx/svdomedia.hxx
+++ b/include/svx/svdomedia.hxx
@@ -61,6 +61,9 @@ public:
         void                        setURL( const OUString& rURL, const 
OUString& rReferer, const OUString& rMimeType = OUString() );
         const OUString&      getURL() const;
 
+        /// Returns the URL to the temporary extracted media file.
+        const OUString&      getTempURL() const;
+
         void                        setMediaProperties( const 
::avmedia::MediaItem& rState );
         const ::avmedia::MediaItem& getMediaProperties() const;
 
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 9dfe60abfd82..1f973645142a 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -32,6 +32,9 @@
 #define SVG_USE *[name()='use']
 #define SVG_PATTERN *[name()='pattern']
 #define SVG_RECT *[name()='rect']
+#define SVG_FOREIGNOBJECT *[name()='foreignObject']
+#define SVG_BODY *[name()='body']
+#define SVG_VIDEO *[name()='video']
 
 using namespace css;
 
@@ -175,6 +178,37 @@ public:
         assertXPathContent(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN
 ), "<number>");
     }
 
+    void testSVGExportEmbeddedVideo()
+    {
+        loadFromURL(u"slide-video-thumbnail.odp");
+        utl::TempFileNamed aTempFile = save("impress_svg_Export");
+
+        xmlDocUniquePtr svgDoc = parseXml(aTempFile);
+        CPPUNIT_ASSERT(svgDoc);
+
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2] ), "class", 
"SlideGroup");
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1] ), 
"visibility", "hidden");
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1] 
), "id", "container-id1");
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1] ), "class", "Slide");
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1] ), "class", "Page");
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1] ), "class", 
"TitleText");
+
+        // First one has no valid video, so we just generate the stock 
thumbnail as an image.
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[2] ), "class", 
"com.sun.star.presentation.MediaShape");
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[2]/SVG_G[1]/SVG_IMAGE
 ), 1);
+
+        // The second one is a valid video, with the thumbnail embedded.
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5] ), "class", 
"com.sun.star.drawing.MediaShape");
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5]/SVG_G[1] ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5]/SVG_G[1]/SVG_FOREIGNOBJECT
 ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5]/SVG_G[1]/SVG_FOREIGNOBJECT/SVG_BODY
 ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5]/SVG_G[1]/SVG_FOREIGNOBJECT/SVG_BODY/SVG_VIDEO
 ), "preload", "auto");
+
+        const OUString poster = getXPath(svgDoc, SAL_STRINGIFY( 
/SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[1]/SVG_G[5]/SVG_G[1]/SVG_FOREIGNOBJECT/SVG_BODY/SVG_VIDEO),
 "poster");
+        CPPUNIT_ASSERT_MESSAGE("The video poster is invalid", 
poster.startsWith("data:image/png;base64,"));
+    }
+
     void testSVGExportSlideBitmapBackground()
     {
         loadFromURL(u"slide-bitmap-background.odp");
@@ -295,6 +329,7 @@ public:
     CPPUNIT_TEST(testSVGExportJavascriptURL);
     CPPUNIT_TEST(testSVGExportSlideCustomBackground);
     CPPUNIT_TEST(testSVGExportTextFieldsInMasterPage);
+    CPPUNIT_TEST(testSVGExportEmbeddedVideo);
     CPPUNIT_TEST(testSVGExportSlideBitmapBackground);
     CPPUNIT_TEST(testSVGExportSlideTileBitmapBackground);
     CPPUNIT_TEST(testSVGPlaceholderLocale);
diff --git a/sd/qa/unit/data/odp/slide-video-thumbnail.odp 
b/sd/qa/unit/data/odp/slide-video-thumbnail.odp
new file mode 100644
index 000000000000..2bb4ed5a86ac
Binary files /dev/null and b/sd/qa/unit/data/odp/slide-video-thumbnail.odp 
differ
diff --git a/svx/source/svdraw/svdomedia.cxx b/svx/source/svdraw/svdomedia.cxx
index e2e4731e129a..9b17b7bf278a 100644
--- a/svx/source/svdraw/svdomedia.cxx
+++ b/svx/source/svdraw/svdomedia.cxx
@@ -277,6 +277,16 @@ const OUString& SdrMediaObj::getURL() const
 #endif
 }
 
+const OUString& SdrMediaObj::getTempURL() const
+{
+#if HAVE_FEATURE_AVMEDIA
+    return m_xImpl->m_MediaProperties.getTempURL();
+#else
+static OUString ret;
+    return ret;
+#endif
+}
+
 void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem& rState )
 {
     mediaPropertiesChanged( rState );

Reply via email to