summaryrefslogtreecommitdiffstats
path: root/filter/source/svg/svgfilter.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'filter/source/svg/svgfilter.cxx')
-rw-r--r--filter/source/svg/svgfilter.cxx847
1 files changed, 847 insertions, 0 deletions
diff --git a/filter/source/svg/svgfilter.cxx b/filter/source/svg/svgfilter.cxx
new file mode 100644
index 000000000..4538e5021
--- /dev/null
+++ b/filter/source/svg/svgfilter.cxx
@@ -0,0 +1,847 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cstdio>
+
+#include <comphelper/lok.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
+#include <com/sun/star/drawing/XMasterPageTarget.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XConfiguration.hpp>
+#include <com/sun/star/drawing/framework/AnchorBindingMode.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/drawing/framework/XResource.hpp>
+
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/zcodec.hxx>
+
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+#include "svgfilter.hxx"
+
+#include <svx/unopage.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/window.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdograf.hxx>
+#include <svl/itempool.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ constexpr OUStringLiteral constFilterNameDraw = u"svg_Scalable_Vector_Graphics_Draw";
+ constexpr OUStringLiteral constFilterName = u"svg_Scalable_Vector_Graphics";
+}
+
+SVGFilter::SVGFilter( const Reference< XComponentContext >& rxCtx ) :
+ mxContext( rxCtx ),
+ mpSVGDoc( nullptr ),
+ mpSVGFontExport( nullptr ),
+ mpSVGWriter( nullptr ),
+ mbSinglePage( false ),
+ mnVisiblePage( -1 ),
+ mpObjects( nullptr ),
+ mbExportShapeSelection(false),
+ mbIsPreview(false),
+ mbWriterFilter(false),
+ mbCalcFilter(false),
+ mbImpressFilter(false),
+ mpDefaultSdrPage( nullptr ),
+ mbPresentation( false )
+{
+}
+
+SVGFilter::~SVGFilter()
+{
+ DBG_ASSERT( mpSVGDoc == nullptr, "mpSVGDoc not destroyed" );
+ DBG_ASSERT( mpSVGExport == nullptr, "mpSVGExport not destroyed" );
+ DBG_ASSERT( mpSVGFontExport == nullptr, "mpSVGFontExport not destroyed" );
+ DBG_ASSERT( mpSVGWriter == nullptr, "mpSVGWriter not destroyed" );
+ DBG_ASSERT( mpObjects == nullptr, "mpObjects not destroyed" );
+}
+
+sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescriptor )
+{
+ mbWriterFilter = false;
+ mbCalcFilter = false;
+ mbImpressFilter = false;
+
+ if(mxDstDoc.is()) // Import works for Impress / draw only
+ return filterImpressOrDraw(rDescriptor);
+
+ if(!mxSrcDoc)
+ return false;
+
+ for (const PropertyValue& rProp : rDescriptor)
+ {
+ if (rProp.Name == "IsPreview")
+ {
+ rProp.Value >>= mbIsPreview;
+ break;
+ }
+ }
+
+ for (const PropertyValue& rProp : rDescriptor)
+ {
+ if (rProp.Name == "FilterName")
+ {
+ OUString sFilterName;
+ rProp.Value >>= sFilterName;
+ if(sFilterName == "impress_svg_Export")
+ {
+ mbImpressFilter = true;
+ return filterImpressOrDraw(rDescriptor);
+ }
+ else if(sFilterName == "writer_svg_Export")
+ {
+ mbWriterFilter = true;
+ return filterWriterOrCalc(rDescriptor);
+ }
+ else if(sFilterName == "calc_svg_Export")
+ {
+ mbCalcFilter = true;
+ return filterWriterOrCalc(rDescriptor);
+ }
+ break;
+ }
+ }
+ return filterImpressOrDraw(rDescriptor);
+}
+
+bool SVGFilter::filterImpressOrDraw( const Sequence< PropertyValue >& rDescriptor )
+{
+ SolarMutexGuard aGuard;
+ vcl::Window* pFocusWindow(Application::GetFocusWindow());
+ bool bRet(false);
+
+ if(pFocusWindow)
+ {
+ pFocusWindow->EnterWait();
+ }
+
+ if(mxDstDoc.is())
+ {
+ // Import. Use an endless loop to have easy exits for error handling
+ while(true)
+ {
+ // use MediaDescriptor to get needed data out of Sequence< PropertyValue >
+ utl::MediaDescriptor aMediaDescriptor(rDescriptor);
+ uno::Reference<io::XInputStream> xInputStream;
+
+ xInputStream.set(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], UNO_QUERY);
+
+ if(!xInputStream.is())
+ {
+ // we need the InputStream
+ break;
+ }
+
+ // get the DrawPagesSupplier
+ uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxDstDoc, uno::UNO_QUERY );
+
+ if(!xDrawPagesSupplier.is())
+ {
+ // we need the DrawPagesSupplier
+ break;
+ }
+
+ // get the DrawPages
+ uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
+
+ if(!xDrawPages.is())
+ {
+ // we need the DrawPages
+ break;
+ }
+
+ // check DrawPageCount (there should be one by default)
+ sal_Int32 nDrawPageCount(xDrawPages->getCount());
+
+ if(0 == nDrawPageCount)
+ {
+ // at least one DrawPage should be there - we need that
+ break;
+ }
+
+ // get that DrawPage
+ uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
+
+ if(!xDrawPage.is())
+ {
+ // we need that DrawPage
+ break;
+ }
+
+ // get that DrawPage's UNO API implementation
+ SvxDrawPage* pSvxDrawPage(comphelper::getFromUnoTunnel<SvxDrawPage>(xDrawPage));
+
+ if(nullptr == pSvxDrawPage || nullptr == pSvxDrawPage->GetSdrPage())
+ {
+ // we need a SvxDrawPage
+ break;
+ }
+
+ // get the SvStream to work with
+ std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+
+ if (!aStream)
+ {
+ // we need the SvStream
+ break;
+ }
+
+ // create a GraphicFilter and load the SVG (format already known, thus *could*
+ // be handed over to ImportGraphic - but detection is fast).
+ // As a bonus, zipped data is already detected and handled there
+ GraphicFilter aGraphicFilter;
+ Graphic aGraphic;
+ const ErrCode nGraphicFilterErrorCode(
+ aGraphicFilter.ImportGraphic(aGraphic, u"", *aStream));
+
+ if(ERRCODE_NONE != nGraphicFilterErrorCode)
+ {
+ // SVG import error, cannot continue
+ break;
+ }
+
+ // get the GraphicPrefSize early to check if we have any content
+ // (the SVG may contain nothing and/or just <g visibility="hidden"> stuff...)
+ const Size aGraphicPrefSize(aGraphic.GetPrefSize());
+
+ if(0 == aGraphicPrefSize.Width() || 0 == aGraphicPrefSize.Height())
+ {
+ // SVG has no displayable content, stop import.
+ // Also possible would be to get the sequence< Primitives >
+ // from aGraphic and check if it is empty.
+ // Possibility to set some error message here to tell
+ // the user what/why loading went wrong, but I do not
+ // know how this could be done here
+ break;
+ }
+
+ // tdf#118232 Get the sequence of primitives and check if geometry is completely
+ // hidden. If so, there is no need to add a SdrObject at all
+ auto const & rVectorGraphicData(aGraphic.getVectorGraphicData());
+ bool bContainsNoGeometry(false);
+
+ if(bool(rVectorGraphicData) && VectorGraphicDataType::Svg == rVectorGraphicData->getType())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aContainer(rVectorGraphicData->getPrimitive2DSequence());
+
+ if(!aContainer.empty())
+ {
+ bool bAllAreHiddenGeometry(true);
+
+ for(const auto& rCandidate : aContainer)
+ {
+ if(rCandidate && PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D != rCandidate->getPrimitive2DID())
+ {
+ bAllAreHiddenGeometry = false;
+ break;
+ }
+ }
+
+ if(bAllAreHiddenGeometry)
+ {
+ bContainsNoGeometry = true;
+ }
+ }
+ }
+
+ // create a SdrModel-GraphicObject to insert to page
+ SdrPage* pTargetSdrPage(pSvxDrawPage->GetSdrPage());
+ std::unique_ptr< SdrGrafObj, SdrObjectFreeOp > aNewSdrGrafObj;
+
+ // tdf#118232 only add an SdrGrafObj when we have Geometry
+ if(!bContainsNoGeometry)
+ {
+ aNewSdrGrafObj.reset(
+ new SdrGrafObj(
+ pTargetSdrPage->getSdrModelFromSdrPage(),
+ aGraphic));
+ }
+
+ // Evtl. adapt the GraphicPrefSize to target-MapMode of target-Model
+ // (should be 100thmm here, but just stay safe by doing the conversion)
+ const MapMode aGraphicPrefMapMode(aGraphic.GetPrefMapMode());
+ const MapUnit eDestUnit(pTargetSdrPage->getSdrModelFromSdrPage().GetItemPool().GetMetric(0));
+ const MapUnit eSrcUnit(aGraphicPrefMapMode.GetMapUnit());
+ Size aGraphicSize(aGraphicPrefSize);
+
+ if (eDestUnit != eSrcUnit)
+ {
+ aGraphicSize = Size(
+ OutputDevice::LogicToLogic(aGraphicSize.Width(), eSrcUnit, eDestUnit),
+ OutputDevice::LogicToLogic(aGraphicSize.Height(), eSrcUnit, eDestUnit));
+ }
+
+ // Based on GraphicSize, set size of Page. Do not forget to adapt PageBorders,
+ // but interpret them relative to PageSize so that they do not 'explode/shrink'
+ // in comparison. Use a common scaling factor for hor/ver to not get
+ // asynchronous border distances, though. All in all this will adapt borders
+ // nicely and is based on office-defaults for standard-page-border-sizes.
+ const Size aPageSize(pTargetSdrPage->GetSize());
+ const double fBorderRelation((
+ static_cast< double >(pTargetSdrPage->GetLeftBorder()) / aPageSize.Width() +
+ static_cast< double >(pTargetSdrPage->GetRightBorder()) / aPageSize.Width() +
+ static_cast< double >(pTargetSdrPage->GetUpperBorder()) / aPageSize.Height() +
+ static_cast< double >(pTargetSdrPage->GetLowerBorder()) / aPageSize.Height()) / 4.0);
+ const tools::Long nAllBorder(basegfx::fround((aGraphicSize.Width() + aGraphicSize.Height()) * fBorderRelation * 0.5));
+
+ // Adapt PageSize and Border stuff. To get all MasterPages and PresObjs
+ // correctly adapted, do not just use
+ // pTargetSdrPage->SetBorder(...) and
+ // pTargetSdrPage->SetSize(...),
+ // but ::adaptSizeAndBorderForAllPages
+ // Do use original Size and borders to get as close to original
+ // as possible for better turn-arounds.
+ pTargetSdrPage->getSdrModelFromSdrPage().adaptSizeAndBorderForAllPages(
+ Size(
+ aGraphicSize.Width(),
+ aGraphicSize.Height()),
+ nAllBorder,
+ nAllBorder,
+ nAllBorder,
+ nAllBorder);
+
+ // tdf#118232 set pos/size at SdrGraphicObj - use zero position for
+ // better turn-around results
+ if(!bContainsNoGeometry)
+ {
+ aNewSdrGrafObj->SetSnapRect(
+ tools::Rectangle(
+ Point(0, 0),
+ aGraphicSize));
+
+ // insert to page (owner change of SdrGrafObj)
+ pTargetSdrPage->InsertObject(aNewSdrGrafObj.release());
+ }
+
+ // done - set positive result now
+ bRet = true;
+
+ // always leave helper endless loop
+ break;
+ }
+ }
+ else if( mxSrcDoc.is() )
+ {
+ // #i124608# detect selection
+ bool bSelectionOnly = false;
+ bool bGotSelection = false;
+
+ // when using LibreOfficeKit, default to exporting everything (-1)
+ bool bPageProvided = comphelper::LibreOfficeKit::isActive();
+ sal_Int32 nPageToExport = -1;
+
+ for( const PropertyValue& rProp : rDescriptor )
+ {
+ if (rProp.Name == "SelectionOnly")
+ {
+ // #i124608# extract single selection wanted from dialog return values
+ rProp.Value >>= bSelectionOnly;
+ bPageProvided = false;
+ }
+ else if (rProp.Name == "PagePos")
+ {
+ rProp.Value >>= nPageToExport;
+ bPageProvided = true;
+ }
+ }
+
+ uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
+ uno::Reference<frame::XController > xController;
+ if (xDesktop->getCurrentFrame().is() && !bPageProvided) // Manage headless case
+ {
+ uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
+ xController.set(xFrame->getController(), uno::UNO_SET_THROW);
+ uno::Reference<drawing::XDrawView> xDrawView(xController, uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::framework::XControllerManager> xManager(xController, uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::framework::XConfigurationController> xConfigController(xManager->getConfigurationController());
+
+ // which view configuration are we in?
+ //
+ // * traverse Impress resources to find slide preview pane, grab selection from there
+ // * otherwise, fallback to current slide
+ //
+ const uno::Sequence<uno::Reference<drawing::framework::XResourceId> > aResIds(
+ xConfigController->getCurrentConfiguration()->getResources(
+ uno::Reference<drawing::framework::XResourceId>(),
+ "",
+ drawing::framework::AnchorBindingMode_INDIRECT));
+
+ for( const uno::Reference<drawing::framework::XResourceId>& rResId : aResIds )
+ {
+ // can we somehow obtain the slidesorter from the Impress framework?
+ if( rResId->getResourceURL() == "private:resource/view/SlideSorter" )
+ {
+ // got it, grab current selection from there
+ uno::Reference<drawing::framework::XResource> xRes(
+ xConfigController->getResource(rResId));
+
+ uno::Reference< view::XSelectionSupplier > xSelectionSupplier(
+ xRes,
+ uno::UNO_QUERY );
+ if( xSelectionSupplier.is() )
+ {
+ uno::Any aSelection = xSelectionSupplier->getSelection();
+ if( aSelection.hasValue() )
+ {
+ Sequence< Reference< XInterface > > aSelectedPageSequence;
+ aSelection >>= aSelectedPageSequence;
+ mSelectedPages.resize( aSelectedPageSequence.getLength() );
+ for( size_t j=0; j<mSelectedPages.size(); ++j )
+ {
+ uno::Reference< drawing::XDrawPage > xDrawPage( aSelectedPageSequence[j],
+ uno::UNO_QUERY );
+ mSelectedPages[j] = xDrawPage;
+ }
+
+ // and stop looping. It is likely not getting better
+ break;
+ }
+ }
+ }
+ }
+
+ if( mSelectedPages.empty() )
+ {
+ // apparently failed to clean selection - fallback to current page
+ mSelectedPages.resize( 1 );
+ mSelectedPages[0] = xDrawView->getCurrentPage();
+ }
+ }
+
+ /*
+ * Export all slides, or requested "PagePos"
+ */
+ if( mSelectedPages.empty() )
+ {
+ uno::Reference< drawing::XMasterPagesSupplier > xMasterPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
+ uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
+
+ if( xMasterPagesSupplier.is() && xDrawPagesSupplier.is() )
+ {
+ uno::Reference< drawing::XDrawPages > xMasterPages = xMasterPagesSupplier->getMasterPages();
+ uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
+ if( xMasterPages.is() && xMasterPages->getCount() &&
+ xDrawPages.is() && xDrawPages->getCount() )
+ {
+ sal_Int32 nDPCount = xDrawPages->getCount();
+
+ mSelectedPages.resize( nPageToExport != -1 ? 1 : nDPCount );
+ sal_Int32 i;
+ for( i = 0; i < nDPCount; ++i )
+ {
+ if( nPageToExport != -1 && nPageToExport == i )
+ {
+ uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
+ mSelectedPages[0] = xDrawPage;
+ }
+ else
+ {
+ uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
+ mSelectedPages[i] = xDrawPage;
+ }
+ }
+ }
+ }
+ }
+
+ if (bSelectionOnly)
+ {
+ // #i124608# when selection only is wanted, get the current object selection
+ // from the DrawView
+ Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
+
+ if (xSelection.is())
+ {
+ bGotSelection
+ = ( xSelection->getSelection() >>= maShapeSelection );
+ }
+ }
+
+ if(bSelectionOnly && bGotSelection && 0 == maShapeSelection->getCount())
+ {
+ // #i124608# export selection, got maShapeSelection but no shape selected -> nothing
+ // to export, we are done (maybe return true, but a hint that nothing was done
+ // may be useful; it may have happened by error)
+ bRet = false;
+ }
+ else
+ {
+ /*
+ * We get all master page that are targeted by at least one draw page.
+ * The master page are put in an unordered set.
+ */
+ ObjectSet aMasterPageTargetSet;
+ for(const uno::Reference<drawing::XDrawPage> & mSelectedPage : mSelectedPages)
+ {
+ uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mSelectedPage, uno::UNO_QUERY );
+ if( xMasterPageTarget.is() )
+ {
+ aMasterPageTargetSet.insert( xMasterPageTarget->getMasterPage() );
+ }
+ }
+ // Later we move them to an uno::Sequence so we can get them by index
+ mMasterPageTargets.resize( aMasterPageTargetSet.size() );
+ sal_Int32 i = 0;
+ for (auto const& masterPageTarget : aMasterPageTargetSet)
+ {
+ uno::Reference< drawing::XDrawPage > xMasterPage( masterPageTarget, uno::UNO_QUERY );
+ mMasterPageTargets[i++] = xMasterPage;
+ }
+
+ bRet = implExport( rDescriptor );
+ }
+ }
+ else
+ bRet = false;
+
+ if( pFocusWindow )
+ pFocusWindow->LeaveWait();
+
+ return bRet;
+}
+
+bool SVGFilter::filterWriterOrCalc( const Sequence< PropertyValue >& rDescriptor )
+{
+ bool bSelectionOnly = false;
+
+ for (const PropertyValue& rProp : rDescriptor)
+ {
+ if (rProp.Name == "SelectionOnly")
+ {
+ rProp.Value >>= bSelectionOnly;
+ break;
+ }
+ }
+
+ if(!bSelectionOnly) // For Writer only the selection-only mode is supported
+ return false;
+
+ uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
+ uno::Reference<frame::XController > xController;
+ if (xDesktop->getCurrentFrame().is())
+ {
+ uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
+ xController.set(xFrame->getController(), uno::UNO_SET_THROW);
+ }
+
+ Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
+ if (!xSelection.is())
+ return false;
+
+ // Select only one draw page
+ uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
+ uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
+ mSelectedPages.resize( 1 );
+ mSelectedPages[0] = xDrawPage;
+
+ bool bGotSelection = xSelection->getSelection() >>= maShapeSelection;
+
+ if (!bGotSelection)
+ {
+ if (mbWriterFilter)
+ {
+ // For Writer we might have a non-shape graphic
+ bGotSelection = implExportWriterTextGraphic(xSelection);
+ }
+ if (!bGotSelection)
+ return false;
+ }
+
+ return implExport( rDescriptor );
+}
+
+void SAL_CALL SVGFilter::cancel( )
+{
+}
+
+void SAL_CALL SVGFilter::setSourceDocument( const Reference< XComponent >& xDoc )
+{
+ mxSrcDoc = xDoc;
+}
+
+void SAL_CALL SVGFilter::setTargetDocument( const Reference< XComponent >& xDoc )
+{
+ mxDstDoc = xDoc;
+}
+
+namespace {
+
+// There is already another SVG-Type_Detector, see
+// vcl/source/filter/graphicfilter.cxx ("DOCTYPE svg"),
+// but since these start from different preconditions it is not
+// easy to unify these. For now, use this local helper.
+class SVGFileInfo
+{
+private:
+ const uno::Reference<io::XInputStream>& mxInput;
+ uno::Sequence< sal_Int8 > mnFirstBytes;
+ sal_Int32 mnFirstBytesSize;
+ sal_Int32 mnFirstRead;
+ bool mbProcessed;
+ bool mbIsSVG;
+
+ bool impCheckForMagic(
+ const sal_Int8* pMagic,
+ const sal_Int32 nMagicSize)
+ {
+ const sal_Int8* pBuffer(mnFirstBytes.getConstArray());
+ return std::search(
+ pBuffer,
+ pBuffer + mnFirstRead,
+ pMagic,
+ pMagic + nMagicSize) != pBuffer + mnFirstRead;
+ }
+
+ void impEnsureProcessed()
+ {
+ if(mbProcessed)
+ {
+ return;
+ }
+
+ mbProcessed = true;
+
+ if(!mxInput.is())
+ {
+ return;
+ }
+
+ if(0 == mnFirstBytesSize)
+ {
+ return;
+ }
+
+ mnFirstBytes.realloc(mnFirstBytesSize);
+
+ if(mnFirstBytesSize != mnFirstBytes.getLength())
+ {
+ return;
+ }
+
+ std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(mxInput, true));
+
+ if (!aStream)
+ {
+ return;
+ }
+
+ const sal_uInt64 nStreamLen(aStream->remainingSize());
+
+ if(aStream->GetError())
+ {
+ return;
+ }
+
+ mnFirstRead = aStream->ReadBytes(
+ &mnFirstBytes.getArray()[0],
+ std::min(nStreamLen, static_cast<sal_uInt64>(mnFirstBytesSize)));
+
+ if(aStream->GetError())
+ {
+ return;
+ }
+
+ // check if it is gzipped -> svgz
+ if (mnFirstBytes[0] == 0x1F && static_cast<sal_uInt8>(mnFirstBytes[1]) == 0x8B)
+ {
+ ZCodec aCodec;
+
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ mnFirstRead = aCodec.Read(
+ *aStream,
+ reinterpret_cast< sal_uInt8* >(mnFirstBytes.getArray()),
+ mnFirstBytesSize);
+ aCodec.EndCompression();
+
+ if (mnFirstRead < 0)
+ return;
+ }
+
+ if(!mbIsSVG)
+ {
+ const sal_Int8 aMagic[] = {'<', 's', 'v', 'g'};
+ const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
+
+ mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
+ }
+
+ if(!mbIsSVG)
+ {
+ const sal_Int8 aMagic[] = {'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 's', 'v', 'g'};
+ const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
+
+ mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
+ }
+
+ return;
+ }
+
+public:
+ SVGFileInfo(
+ const uno::Reference<io::XInputStream>& xInput)
+ : mxInput(xInput),
+ mnFirstBytesSize(2048),
+ mnFirstRead(0),
+ mbProcessed(false),
+ mbIsSVG(false)
+ {
+ // For the default buffer size: Use not too big
+ // (not more than 16K), but also not too small
+ // (not less than 1/2K), see comments at
+ // ImpPeekGraphicFormat, SVG section.
+ // I remember these cases and it *can* happen
+ // that SVGs have quite massive comments in their
+ // headings (!)
+ // Limit to plausible sizes, also for security reasons
+ mnFirstBytesSize = std::min(sal_Int32(512), mnFirstBytesSize);
+ mnFirstBytesSize = std::max(sal_Int32(16384), mnFirstBytesSize);
+ }
+
+ bool isSVG()
+ {
+ impEnsureProcessed();
+
+ return mbIsSVG;
+ }
+
+ bool isOwnFormat()
+ {
+ impEnsureProcessed();
+
+ if(mbIsSVG)
+ {
+ // xmlns:ooo
+ const sal_Int8 aMagic[] = {'x', 'm', 'l', 'n', 's', ':', 'o', 'o', 'o'};
+ const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
+
+ return impCheckForMagic(aMagic, nMagicSize);
+ }
+
+ return false;
+ }
+
+ bool isImpress()
+ {
+ impEnsureProcessed();
+
+ if(mbIsSVG)
+ {
+ // ooo:meta_slides
+ const sal_Int8 aMagic[] = {'o', 'o', 'o', ':', 'm', 'e', 't', 'a', '_', 's', 'l', 'i', 'd', 'e', 's'};
+ const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
+
+ return impCheckForMagic(aMagic, nMagicSize);
+ }
+
+ return false;
+ }
+};
+
+}
+
+OUString SAL_CALL SVGFilter::detect(Sequence<PropertyValue>& rDescriptor)
+{
+ utl::MediaDescriptor aMediaDescriptor(rDescriptor);
+ uno::Reference<io::XInputStream> xInput(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], UNO_QUERY);
+ OUString aRetval;
+
+ if (!xInput.is())
+ {
+ return aRetval;
+ }
+
+ try
+ {
+ SVGFileInfo aSVGFileInfo(xInput);
+
+ if(aSVGFileInfo.isSVG())
+ {
+ // We have SVG - set default document format to Draw
+ aRetval = OUString(constFilterNameDraw);
+
+ if(aSVGFileInfo.isOwnFormat())
+ {
+ // it's a file that was written/exported by LO
+ if(aSVGFileInfo.isImpress())
+ {
+ // it was written by Impress export. Set document
+ // format for import to Impress
+ aRetval = OUString(constFilterName);
+ }
+ }
+ }
+ }
+ catch (css::io::IOException &)
+ {
+ TOOLS_WARN_EXCEPTION("filter.svg", "");
+ }
+
+ return aRetval;
+}
+
+// XServiceInfo
+sal_Bool SVGFilter::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+OUString SVGFilter::getImplementationName()
+{
+ return "com.sun.star.comp.Draw.SVGFilter";
+}
+css::uno::Sequence< OUString > SVGFilter::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.ImportFilter",
+ "com.sun.star.document.ExportFilter",
+ "com.sun.star.document.ExtendedTypeDetection" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+filter_SVGFilter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SVGFilter(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */