filter/source/svg/svgwriter.cxx | 46 +++++++++++++++++++++++--- include/svx/svdomedia.hxx | 3 + sd/qa/unit/SVGExportTests.cxx | 34 +++++++++++++++++++ sd/qa/unit/data/odp/slide-video-thumbnail.odp |binary svx/source/svdraw/svdomedia.cxx | 10 +++++ 5 files changed, 89 insertions(+), 4 deletions(-)
New commits: commit 77f9b14071fd5361f3c2d7eba8a9799d41bf3159 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sun Oct 23 17:56:50 2022 -0400 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Oct 25 08:22:14 2022 +0200 svg: export embedded video Signed-off-by: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Change-Id: Ie5dcd1fb4abbaf53f48107e7def0f42daad24596 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141690 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index e43ff6fce264..1cb2076f4480 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -37,6 +37,7 @@ #include <xmloff/namespacemap.hxx> #include <xmloff/unointerfacetouniqueidentifiermapper.hxx> #include <i18nlangtag/languagetag.hxx> +#include <svx/svdomedia.hxx> #include <com/sun/star/container/XEnumerationAccess.hpp> #include <com/sun/star/container/XIndexReplace.hpp> @@ -2995,12 +2996,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 c47dda4d4717..36b00f276d80 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 d43815264d26..a28e61373053 100644 --- a/sd/qa/unit/SVGExportTests.cxx +++ b/sd/qa/unit/SVGExportTests.cxx @@ -35,6 +35,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; @@ -222,6 +225,36 @@ 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() + { + executeExport("slide-video-thumbnail.odp"); + + xmlDocUniquePtr svgDoc = parseXml(maTempFile); + 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() { executeExport("slide-bitmap-background.odp"); @@ -339,6 +372,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 2f5124dc8ee7..5b0bb80d4920 100644 --- a/svx/source/svdraw/svdomedia.cxx +++ b/svx/source/svdraw/svdomedia.cxx @@ -254,6 +254,16 @@ static OUString ret; #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 );