summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--filter/source/svg/svgwriter.cxx46
-rw-r--r--include/svx/svdomedia.hxx3
-rw-r--r--sd/qa/unit/SVGExportTests.cxx35
-rw-r--r--sd/qa/unit/data/odp/slide-video-thumbnail.odpbin0 -> 38222 bytes
-rw-r--r--svx/source/svdraw/svdomedia.cxx10
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
new file mode 100644
index 000000000000..2bb4ed5a86ac
--- /dev/null
+++ b/sd/qa/unit/data/odp/slide-video-thumbnail.odp
Binary files 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 );