diff options
-rw-r--r-- | include/oox/export/drawingml.hxx | 14 | ||||
-rw-r--r-- | oox/source/export/drawingml.cxx | 103 | ||||
-rw-r--r-- | oox/source/token/namespaces-strict.txt | 1 | ||||
-rw-r--r-- | oox/source/token/namespaces.txt | 1 | ||||
-rw-r--r-- | oox/source/token/tokens.txt | 2 | ||||
-rw-r--r-- | sw/qa/extras/ooxmlexport/data/SvgImageTest.odt | bin | 0 -> 13608 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport20.cxx | 27 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 16 | ||||
-rw-r--r-- | test/source/xmltesttools.cxx | 2 |
9 files changed, 157 insertions, 9 deletions
diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 9028cfdc0f9f..dcbb1b544390 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -259,16 +259,25 @@ private: DocumentType meDocumentType; OUString writeNewEntryToStorage(const Graphic& rGraphic, bool bRelPathToMedia); + OUString writeNewSvgEntryToStorage(const Graphic& rGraphic, bool bRelPathToMedia); public: + enum class TypeHint + { + Detect, + SVG + }; + GraphicExport(sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* pFilterBase, DocumentType eDocumentType) : mpFS(pFS) , mpFilterBase(pFilterBase) , meDocumentType(eDocumentType) {} - OUString writeToStorage(Graphic const& rGraphic, bool bRelPathToMedia = false); + OUString writeToStorage(Graphic const& rGraphic, bool bRelPathToMedia = false, TypeHint eHint = TypeHint::Detect); + void writeBlip(Graphic const& rGraphic, std::vector<model::BlipEffect> const& rEffects, bool bRelPathToMedia = false); + void writeSvgExtension(OUString const& rSvgRelId); }; class OOX_DLLPUBLIC DrawingML @@ -353,7 +362,7 @@ public: void SetBackgroundDark(bool bIsDark) { mbIsBackgroundDark = bIsDark; } /// If bRelPathToMedia is true add "../" to image folder path while adding the image relationship - OUString writeGraphicToStorage(const Graphic &rGraphic , bool bRelPathToMedia = false); + OUString writeGraphicToStorage(const Graphic &rGraphic , bool bRelPathToMedia = false, GraphicExport::TypeHint eHint = GraphicExport::TypeHint::Detect); void WriteColor( ::Color nColor, sal_Int32 nAlpha = MAX_PERCENT ); void WriteColor( const OUString& sColorSchemeName, const css::uno::Sequence< css::beans::PropertyValue >& aTransformations, sal_Int32 nAlpha = MAX_PERCENT ); @@ -516,6 +525,7 @@ public: const OUString& sRelationshipType, OUString* pRelationshipId ); + std::shared_ptr<GraphicExport> createGraphicExport(); }; } diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 228aa2326cc0..05c96c9ad798 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -1283,12 +1283,34 @@ OUString DrawingML::GetRelationCompPrefix() const return OUString(getRelationCompPrefix(meDocumentType)); } +void GraphicExport::writeSvgExtension(OUString const& rSvgRelId) +{ + if (rSvgRelId.isEmpty()) + return; + + mpFS->startElementNS(XML_a, XML_extLst); + mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"); + mpFS->singleElementNS(XML_asvg, XML_svgBlip, + FSNS(XML_xmlns, XML_asvg), mpFilterBase->getNamespaceURL(OOX_NS(asvg)), + FSNS(XML_r, XML_embed), rSvgRelId); + mpFS->endElementNS(XML_a, XML_ext); + mpFS->endElementNS( XML_a, XML_extLst); +} + void GraphicExport::writeBlip(Graphic const& rGraphic, std::vector<model::BlipEffect> const& rEffects, bool bRelPathToMedia) { OUString sRelId = writeToStorage(rGraphic, bRelPathToMedia); mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId); + auto const& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData(); + + if (rVectorGraphicDataPtr && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Svg) + { + OUString sSvgRelId = writeToStorage(rGraphic, bRelPathToMedia, TypeHint::SVG); + writeSvgExtension(sSvgRelId); + } + for (auto const& rEffect : rEffects) { switch (rEffect.meType) @@ -1514,19 +1536,72 @@ OUString GraphicExport::writeNewEntryToStorage(const Graphic& rGraphic, bool bRe return sPath; } -OUString GraphicExport::writeToStorage(const Graphic& rGraphic , bool bRelPathToMedia) +namespace +{ +BitmapChecksum makeChecksumUniqueForSVG(BitmapChecksum const& rChecksum) +{ + // need to modify the checksum so we know it's for SVG - just invert it + return ~rChecksum; +} + +} // end anonymous namespace + +OUString GraphicExport::writeNewSvgEntryToStorage(const Graphic& rGraphic, bool bRelPathToMedia) +{ + OUString sMediaType = u"image/svg"_ustr; + OUString aExtension = u"svg"_ustr; + + GfxLink const& rLink = rGraphic.GetGfxLink(); + if (rLink.GetType() != GfxLinkType::NativeSvg) + return OUString(); + + const void* aData = rLink.GetData(); + std::size_t nDataSize = rLink.GetDataSize(); + + GraphicExportCache& rGraphicExportCache = GraphicExportCache::get(); + auto sImageCountString = OUString::number(rGraphicExportCache.nextImageCount()); + + OUString sComponentDir(getComponentDir(meDocumentType)); + + OUString sImagePath = sComponentDir + u"/media/image"_ustr + sImageCountString + u"."_ustr + aExtension; + + Reference<XOutputStream> xOutStream = mpFilterBase->openFragmentStream(sImagePath, sMediaType); + xOutStream->writeBytes(Sequence<sal_Int8>(static_cast<const sal_Int8*>(aData), nDataSize)); + xOutStream->closeOutput(); + + OUString sRelationCompPrefix; + if (bRelPathToMedia) + sRelationCompPrefix = u"../"_ustr; + else + sRelationCompPrefix = getRelationCompPrefix(meDocumentType); + + OUString sPath = sRelationCompPrefix + u"media/image"_ustr + sImageCountString + u"."_ustr + aExtension; + + rGraphicExportCache.addExportGraphics(makeChecksumUniqueForSVG(rGraphic.GetChecksum()), sPath); + + return sPath; +} + +OUString GraphicExport::writeToStorage(const Graphic& rGraphic, bool bRelPathToMedia, TypeHint eHint) { OUString sPath; + auto aChecksum = rGraphic.GetChecksum(); + if (eHint == TypeHint::SVG) + aChecksum = makeChecksumUniqueForSVG(aChecksum); + GraphicExportCache& rGraphicExportCache = GraphicExportCache::get(); - sPath = rGraphicExportCache.findExportGraphics(rGraphic.GetChecksum()); + sPath = rGraphicExportCache.findExportGraphics(aChecksum); if (sPath.isEmpty()) { - sPath = writeNewEntryToStorage(rGraphic, bRelPathToMedia); + if (eHint == TypeHint::SVG) + sPath = writeNewSvgEntryToStorage(rGraphic, bRelPathToMedia); + else + sPath = writeNewEntryToStorage(rGraphic, bRelPathToMedia); if (sPath.isEmpty()) - return OUString(); // couldn't store - just return empty string + return OUString(); // couldn't store } OUString sRelId = mpFilterBase->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::IMAGE), sPath); @@ -1534,10 +1609,15 @@ OUString GraphicExport::writeToStorage(const Graphic& rGraphic , bool bRelPathTo return sRelId; } -OUString DrawingML::writeGraphicToStorage( const Graphic& rGraphic , bool bRelPathToMedia ) +std::shared_ptr<GraphicExport> DrawingML::createGraphicExport() +{ + return std::make_shared<GraphicExport>(mpFS, mpFB, meDocumentType); +} + +OUString DrawingML::writeGraphicToStorage(const Graphic& rGraphic , bool bRelPathToMedia, GraphicExport::TypeHint eHint) { GraphicExport aExporter(mpFS, mpFB, meDocumentType); - return aExporter.writeToStorage(rGraphic, bRelPathToMedia); + return aExporter.writeToStorage(rGraphic, bRelPathToMedia, eHint); } void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape) @@ -1706,10 +1786,21 @@ void DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rX return; Graphic aGraphic(rxGraphic); + sRelId = writeGraphicToStorage(aGraphic, bRelPathToMedia); mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId); + auto pVectorGraphicDataPtr = aGraphic.getVectorGraphicData(); + + if (pVectorGraphicDataPtr && pVectorGraphicDataPtr->getType() == VectorGraphicDataType::Svg) + { + GraphicExport aExporter(mpFS, mpFB, meDocumentType); + OUString sSvgRelId = aExporter.writeToStorage(aGraphic, bRelPathToMedia, GraphicExport::TypeHint::SVG); + if (!sSvgRelId.isEmpty()) + aExporter.writeSvgExtension(sSvgRelId); + } + WriteImageBrightnessContrastTransparence(rXPropSet); WriteArtisticEffect(rXPropSet); diff --git a/oox/source/token/namespaces-strict.txt b/oox/source/token/namespaces-strict.txt index beed3238ae2d..2b6c53807377 100644 --- a/oox/source/token/namespaces-strict.txt +++ b/oox/source/token/namespaces-strict.txt @@ -94,6 +94,7 @@ xr2 http://schemas.microsoft.com/office/spreadsheetml/2015/r # extlst namespaces adec http://schemas.microsoft.com/office/drawing/2017/decorative +asvg http://schemas.microsoft.com/office/drawing/2016/SVG/main # xls14Lst for features introduced by excel 2010 xls14Lst http://schemas.microsoft.com/office/spreadsheetml/2009/9/main diff --git a/oox/source/token/namespaces.txt b/oox/source/token/namespaces.txt index dbe29f19a220..c2e30e0f8421 100644 --- a/oox/source/token/namespaces.txt +++ b/oox/source/token/namespaces.txt @@ -94,6 +94,7 @@ xr2 http://schemas.microsoft.com/office/spreadsheetml/2015/r # extlst namespaces adec http://schemas.microsoft.com/office/drawing/2017/decorative +asvg http://schemas.microsoft.com/office/drawing/2016/SVG/main # xls14Lst for features introduced by excel 2010 xls14Lst http://schemas.microsoft.com/office/spreadsheetml/2009/9/main diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt index 56e17dc35c22..47d6b07f2470 100644 --- a/oox/source/token/tokens.txt +++ b/oox/source/token/tokens.txt @@ -682,6 +682,7 @@ aspectratio assign asst asteriskTotals +asvg atEnd atLeast atMost @@ -5091,6 +5092,7 @@ suppressTopSpacing suppressTopSpacingWP surface3DChart surfaceChart +svgBlip swAng swCell swapBordersFacingPages diff --git a/sw/qa/extras/ooxmlexport/data/SvgImageTest.odt b/sw/qa/extras/ooxmlexport/data/SvgImageTest.odt Binary files differnew file mode 100644 index 000000000000..3b37fe7998ef --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/SvgImageTest.odt diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx index 8b5f6cab824b..263e769297d7 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx @@ -961,6 +961,33 @@ CPPUNIT_TEST_FIXTURE(Test, testZOrderInHeader) CPPUNIT_ASSERT(nBackground < nFrontShape); } +CPPUNIT_TEST_FIXTURE(Test, testSvgExtensionsSupport) +{ + loadAndSave("SvgImageTest.odt"); + + xmlDocUniquePtr pXmlDocRels = parseExport("word/_rels/document.xml.rels"); + + // Check we have 2 relationships - one for PNG and one for SVG files + assertXPath(pXmlDocRels, + "/rels:Relationships/rels:Relationship[@Target='media/image1.png']"_ostr, "Id"_ostr, + "rId2"); + + assertXPath(pXmlDocRels, + "/rels:Relationships/rels:Relationship[@Target='media/image2.svg']"_ostr, "Id"_ostr, + "rId3"); + + // Check there is the extension present + xmlDocUniquePtr pXmlDocContent = parseExport("word/document.xml"); + + OString aPath( + "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip"_ostr); + assertXPath(pXmlDocContent, aPath, "embed"_ostr, "rId2"); + + assertXPath(pXmlDocContent, aPath + "/a:extLst/a:ext"_ostr, "uri"_ostr, + "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"); + assertXPath(pXmlDocContent, aPath + "/a:extLst/a:ext/asvg:svgBlip"_ostr, "embed"_ostr, "rId3"); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index bf6380b12ccd..8949ffe58a77 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -52,6 +52,7 @@ #include <oox/token/relationship.hxx> #include <oox/export/vmlexport.hxx> #include <oox/ole/olehelper.hxx> +#include <oox/export/drawingml.hxx> #include <editeng/autokernitem.hxx> #include <editeng/unoprnms.hxx> @@ -5181,6 +5182,7 @@ void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat; // create the relation ID OString aRelId; + OUString sSvgRelId; sal_Int32 nImageType; if ( pGrfNode && pGrfNode->IsLinkedFile() ) { @@ -5216,9 +5218,14 @@ void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size aGraphic = *pOLENode->GetGraphic(); m_rDrawingML.SetFS(m_pSerializer); // to be sure that we write to the right stream - OUString aImageId = m_rDrawingML.writeGraphicToStorage(aGraphic, false); + auto pGraphicExport = m_rDrawingML.createGraphicExport(); + OUString aImageId = pGraphicExport->writeToStorage(aGraphic, false); aRelId = OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8); + if (aGraphic.getVectorGraphicData() && aGraphic.getVectorGraphicData()->getType() == VectorGraphicDataType::Svg) + { + sSvgRelId = pGraphicExport->writeToStorage(aGraphic, false, drawingml::GraphicExport::TypeHint::SVG); + } nImageType = XML_embed; } @@ -5354,6 +5361,13 @@ void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) ); } + + if (!sSvgRelId.isEmpty()) + { + auto pGraphicExport = m_rDrawingML.createGraphicExport(); + pGraphicExport->writeSvgExtension(sSvgRelId); + } + m_pSerializer->endElementNS( XML_a, XML_blip ); if (xShapePropSet) diff --git a/test/source/xmltesttools.cxx b/test/source/xmltesttools.cxx index 8f96a399caff..14f953557f5f 100644 --- a/test/source/xmltesttools.cxx +++ b/test/source/xmltesttools.cxx @@ -455,6 +455,8 @@ void XmlTestTools::registerOOXMLNamespaces(xmlXPathContextPtr& pXmlXpathCtx) BAD_CAST("http://schemas.microsoft.com/office/drawing/2012/chart")); xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("xr2"), BAD_CAST("http://schemas.microsoft.com/office/spreadsheetml/2015/revision2")); + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("asvg"), + BAD_CAST("http://schemas.microsoft.com/office/drawing/2016/SVG/main")); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |