diff options
-rw-r--r-- | filter/source/svg/svgwriter.cxx | 46 | ||||
-rw-r--r-- | include/svx/svdomedia.hxx | 3 | ||||
-rw-r--r-- | sd/qa/unit/SVGExportTests.cxx | 35 | ||||
-rw-r--r-- | sd/qa/unit/data/odp/slide-video-thumbnail.odp | bin | 0 -> 38222 bytes | |||
-rw-r--r-- | svx/source/svdraw/svdomedia.cxx | 10 |
5 files changed, 90 insertions, 4 deletions
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 Binary files differnew file mode 100644 index 000000000000..2bb4ed5a86ac --- /dev/null +++ b/sd/qa/unit/data/odp/slide-video-thumbnail.odp 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 ); |