diff --git a/filter/CppunitTest_filter_svg.mk b/filter/CppunitTest_filter_svg.mk new file mode 100644 index 000000000000..ec0841929f3f --- /dev/null +++ b/filter/CppunitTest_filter_svg.mk @@ -0,0 +1,47 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,filter_svg)) + +$(eval $(call gb_CppunitTest_use_externals,filter_svg,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,filter_svg, \ + filter/qa/unit/svg \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,filter_svg, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + test \ + unotest \ + utl \ + tl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,filter_svg)) + +$(eval $(call gb_CppunitTest_use_ure,filter_svg)) +$(eval $(call gb_CppunitTest_use_vcl,filter_svg)) + +$(eval $(call gb_CppunitTest_use_rdb,filter_svg,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,filter_svg,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,filter_svg)) + +# vim: set noet sw=4 ts=4: diff --git a/filter/Module_filter.mk b/filter/Module_filter.mk index 2d380fbd91ac..a01baa0b48a7 100644 --- a/filter/Module_filter.mk +++ b/filter/Module_filter.mk @@ -67,6 +67,7 @@ $(eval $(call gb_Module_add_check_targets,filter,\ CppunitTest_filter_ras_test \ CppunitTest_filter_tiff_test \ CppunitTest_filter_tga_test \ + CppunitTest_filter_svg \ )) endif diff --git a/filter/qa/unit/data/preserve-jpg.odt b/filter/qa/unit/data/preserve-jpg.odt new file mode 100644 index 000000000000..83768bd47afb Binary files /dev/null and b/filter/qa/unit/data/preserve-jpg.odt differ diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx new file mode 100644 index 000000000000..49e22e0ebd16 --- /dev/null +++ b/filter/qa/unit/svg.cxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +char const DATA_DIRECTORY[] = "/filter/qa/unit/data/"; + +/// SVG filter tests. +class SvgFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools +{ +private: + uno::Reference mxComponent; + +public: + void setUp() override; + void tearDown() override; + void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; + uno::Reference& getComponent() { return mxComponent; } + void load(const OUString& rURL); +}; + +void SvgFilterTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void SvgFilterTest::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +void SvgFilterTest::load(const OUString& rFileName) +{ + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName; + mxComponent = loadFromDesktop(aURL); +} + +void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) +{ + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg")); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg) +{ +#if !defined(MACOSX) + // Load a document with a jpeg image in it. + load("preserve-jpg.odt"); + + // Select the image. + dispatchCommand(getComponent(), ".uno:JumpToNextFrame", {}); + + // Export the selection to SVG. + uno::Reference xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export"); + aMediaDescriptor["SelectionOnly"] <<= true; + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Make sure the the original JPG data is reused and we don't perform a PNG re-compress. + xmlDocPtr pXmlDoc = parseXmlStream(&aStream); + OUString aAttributeValue = getXPath(pXmlDoc, "//svg:image", "href"); + + // Without the accompanying fix in place, this test would have failed with: + // - Expression: aAttributeValue.startsWith("data:image/jpeg") + // i.e. the SVG export result re-compressed the image as PNG, even if the original and the + // transformed image is the same, so there is no need for that. + CPPUNIT_ASSERT(aAttributeValue.startsWith("data:image/jpeg")); +#endif +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx index c717d00596ef..aa071ca37f50 100644 --- a/filter/source/svg/svgexport.cxx +++ b/filter/source/svg/svgexport.cxx @@ -693,13 +693,20 @@ bool SVGFilter::implExportWriterTextGraphic( const Reference< view::XSelectionSu if (xSelection.is() && xSelection->supportsService("com.sun.star.text.TextGraphicObject")) { uno::Reference xPropertySet(xSelection, uno::UNO_QUERY); - uno::Reference xGraphic; - xPropertySet->getPropertyValue("TransformedGraphic") >>= xGraphic; - if (!xGraphic.is()) + uno::Reference xOriginalGraphic; + xPropertySet->getPropertyValue("Graphic") >>= xOriginalGraphic; + const Graphic aOriginalGraphic(xOriginalGraphic); + + uno::Reference xTransformedGraphic; + xPropertySet->getPropertyValue("TransformedGraphic") >>= xTransformedGraphic; + + if (!xTransformedGraphic.is()) return false; - - const Graphic aGraphic(xGraphic); + const Graphic aTransformedGraphic(xTransformedGraphic); + bool bChecksumMatches = aOriginalGraphic.GetChecksum() == aTransformedGraphic.GetChecksum(); + const Graphic aGraphic = bChecksumMatches ? aOriginalGraphic : aTransformedGraphic; + uno::Reference xGraphic = bChecksumMatches ? xOriginalGraphic : xTransformedGraphic; // Calculate size from Graphic Point aPos( OutputDevice::LogicToLogic(aGraphic.GetPrefMapMode().GetOrigin(), aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) ); diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index 9ccd18eaafee..8d491d122d0b 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -2688,20 +2688,22 @@ void SVGActionWriter::ImplWriteText( const Point& rPos, const OUString& rText, namespace { -SdrGrafObj* GetSdrGrafObjFromXShape(const css::uno::Reference* pShape) +void GetGraphicFromXShape(const css::uno::Reference* pShape, Graphic& rGraphic) { if (!pShape) { - return nullptr; + return; } - auto pObject = dynamic_cast(pShape->get()); - if (!pObject) + uno::Reference xPropertySet(*pShape, uno::UNO_QUERY); + if (!xPropertySet.is()) { - return nullptr; + return; } - return dynamic_cast(pObject->GetSdrObject()); + uno::Reference xGraphic; + xPropertySet->getPropertyValue("Graphic") >>= xGraphic; + rGraphic= Graphic(xGraphic); } } @@ -2724,34 +2726,30 @@ void SVGActionWriter::ImplWriteBmp( const BitmapEx& rBmpEx, SvMemoryStream aOStm( 65535, 65535 ); bool bCached = false; - SdrGrafObj* pGrafObj = nullptr; + Graphic aGraphic; bool bPNG = false; bool bJPG = false; if (pShape) { - pGrafObj = GetSdrGrafObjFromXShape(pShape); - if (pGrafObj) + GetGraphicFromXShape(pShape, aGraphic); + if (aGraphic.GetType() == GraphicType::Bitmap) { - const Graphic& rGraphic = pGrafObj->GetGraphic(); - if (rGraphic.GetType() == GraphicType::Bitmap) + const BitmapEx& rGraphicBitmap = aGraphic.GetBitmapExRef(); + if (rGraphicBitmap.GetChecksum() == rBmpEx.GetChecksum()) { - const BitmapEx& rGraphicBitmap = rGraphic.GetBitmapExRef(); - if (rGraphicBitmap.GetChecksum() == rBmpEx.GetChecksum()) + GfxLink aGfxLink = aGraphic.GetGfxLink(); + if (aGfxLink.GetType() == GfxLinkType::NativePng) { - GfxLink aGfxLink = rGraphic.GetGfxLink(); - if (aGfxLink.GetType() == GfxLinkType::NativePng) - { - bPNG = true; - } - else if (aGfxLink.GetType() == GfxLinkType::NativeJpg) - { - bJPG = true; - } - if (bPNG || bJPG) - { - aOStm.WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize()); - bCached = true; - } + bPNG = true; + } + else if (aGfxLink.GetType() == GfxLinkType::NativeJpg) + { + bJPG = true; + } + if (bPNG || bJPG) + { + aOStm.WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize()); + bCached = true; } } }