/* -*- 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 <comphelper/diagnose_ex.hxx>
#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),
mbShouldCompress(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;
mbShouldCompress = 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);
}
else if(sFilterName == "draw_svgz_Export")
{
mbShouldCompress = true;
}
break;
}
}
return filterImpressOrDraw(rDescriptor);
}
css::uno::Reference<css::frame::XController> SVGFilter::fillDrawImpressSelectedPages()
{
uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
uno::Reference<frame::XFrame> xFrame = xDesktop->getCurrentFrame(); // Manage headless case
if (!xFrame)
return {};
uno::Reference<frame::XController> xController(xFrame->getController(), uno::UNO_SET_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(
{}, u""_ustr, 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<view::XSelectionSupplier> xSelectionSupplier(
xConfigController->getResource(rResId), uno::UNO_QUERY);
if (!xSelectionSupplier)
continue;
Sequence<Reference<XInterface>> aSelectedPageSequence;
if (xSelectionSupplier->getSelection() >>= aSelectedPageSequence)
{
for (auto& xInterface : aSelectedPageSequence)
{
uno::Reference<drawing::XDrawPage> xDrawPage(xInterface, uno::UNO_QUERY);
if (Reference<XPropertySet> xPropSet{ xDrawPage, UNO_QUERY })
{
Reference<XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
if (xPropSetInfo && xPropSetInfo->hasPropertyByName(u"Visible"_ustr))
{
bool bIsSlideVisible = true; // default: true
xPropSet->getPropertyValue(u"Visible"_ustr) >>= bIsSlideVisible;
if (!bIsSlideVisible)
continue;
}
}
mSelectedPages.push_back(xDrawPage);
}
// and stop looping. It is likely not getting better
break;
}
}
}
if (mSelectedPages.empty())
{
// apparently failed to clean selection - fallback to current page
uno::Reference<drawing::XDrawView> xDrawView(xController, uno::UNO_QUERY_THROW);
mSelectedPages.push_back(xDrawView->getCurrentPage());
}
return xController;
}
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());
rtl::Reference< SdrGrafObj > aNewSdrGrafObj;
// tdf#118232 only add an SdrGrafObj when we have Geometry
if(!bContainsNoGeometry)
{
aNewSdrGrafObj =
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<tools::Long>((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.get());
}
// 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::XController > xController;
if (!bPageProvided)
xController = fillDrawImpressSelectedPages();
/*
* 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();
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.push_back(xDrawPage);
}
else
{
uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
Reference< XPropertySet > xPropSet( xDrawPage, UNO_QUERY );
bool bIsSlideVisible = true; // default: true
if (xPropSet.is())
{
Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(u"Visible"_ustr))
{
xPropSet->getPropertyValue( u"Visible"_ustr ) >>= bIsSlideVisible;
if (!bIsSlideVisible)
continue;
}
}
mSelectedPages.push_back(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)
{
mMasterPageTargets[i++].set(masterPageTarget, uno::UNO_QUERY);
}
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 (uno::Reference<frame::XFrame> xFrame = xDesktop->getCurrentFrame())
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();
mSelectedPages.resize( 1 );
mSelectedPages[0].set(xDrawPages->getByIndex(0), uno::UNO_QUERY);
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(std::size(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(std::size(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(std::size(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(std::size(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 u"com.sun.star.comp.Draw.SVGFilter"_ustr;
}
css::uno::Sequence< OUString > SVGFilter::getSupportedServiceNames()
{
return { u"com.sun.star.document.ImportFilter"_ustr,
u"com.sun.star.document.ExportFilter"_ustr,
u"com.sun.star.document.ExtendedTypeDetection"_ustr };
}
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: */
↑ V547 Expression '!bIsSlideVisible' is always false.
↑ V547 Expression '!bIsSlideVisible' is always false.
↑ V547 Expression 'bSelectionOnly' is always false.
↑ V547 Expression '!bSelectionOnly' is always true.
↑ V560 A part of conditional expression is always false: nPageToExport != - 1.
↑ V560 A part of conditional expression is always false: nPageToExport == i.