build fix
[LibreOffice.git] / filter / source / svg / svgfilter.cxx
blobc1faa06469b56415b3dbc8277c83d0cbd557a568
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <cstdio>
23 #include <comphelper/lok.hxx>
24 #include <comphelper/servicedecl.hxx>
25 #include <uno/environment.h>
26 #include <com/sun/star/drawing/XDrawPage.hpp>
27 #include <com/sun/star/drawing/XDrawView.hpp>
28 #include <com/sun/star/frame/Desktop.hpp>
29 #include <com/sun/star/frame/XController.hpp>
30 #include <com/sun/star/view/XSelectionSupplier.hpp>
31 #include <com/sun/star/drawing/XDrawSubController.hpp>
32 #include <com/sun/star/container/XNamed.hpp>
33 #include <com/sun/star/uno/XComponentContext.hpp>
34 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
35 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
36 #include <com/sun/star/drawing/framework/XConfiguration.hpp>
37 #include <com/sun/star/drawing/framework/AnchorBindingMode.hpp>
38 #include <com/sun/star/drawing/framework/XResourceId.hpp>
39 #include <com/sun/star/drawing/framework/XResource.hpp>
40 #include <com/sun/star/drawing/framework/XView.hpp>
41 #include <com/sun/star/drawing/framework/ResourceId.hpp>
42 #include <comphelper/processfactory.hxx>
44 #include <unotools/mediadescriptor.hxx>
45 #include <unotools/ucbstreamhelper.hxx>
46 #include <unotools/streamwrap.hxx>
47 #include <tools/zcodec.hxx>
49 #include <osl/mutex.hxx>
51 #include "svgfilter.hxx"
52 #include "svgwriter.hxx"
54 #include <memory>
56 using namespace ::com::sun::star;
58 namespace
60 static const char constFilterName[] = "svg_Scalable_Vector_Graphics";
63 SVGFilter::SVGFilter( const Reference< XComponentContext >& rxCtx ) :
64 mxContext( rxCtx ),
65 mpSVGDoc( nullptr ),
66 mpSVGExport( nullptr ),
67 mpSVGFontExport( nullptr ),
68 mpSVGWriter( nullptr ),
69 mpDefaultSdrPage( nullptr ),
70 mpSdrModel( nullptr ),
71 mbPresentation( false ),
72 mbSinglePage( false ),
73 mnVisiblePage( -1 ),
74 mpObjects( nullptr ),
75 mxSrcDoc(),
76 mxDstDoc(),
77 mxDefaultPage(),
78 maFilterData(),
79 maShapeSelection(),
80 mbExportShapeSelection(false),
81 maOldFieldHdl()
85 SVGFilter::~SVGFilter()
87 DBG_ASSERT( mpSVGDoc == nullptr, "mpSVGDoc not destroyed" );
88 DBG_ASSERT( mpSVGExport == nullptr, "mpSVGExport not destroyed" );
89 DBG_ASSERT( mpSVGFontExport == nullptr, "mpSVGFontExport not destroyed" );
90 DBG_ASSERT( mpSVGWriter == nullptr, "mpSVGWriter not destroyed" );
91 DBG_ASSERT( mpObjects == nullptr, "mpObjects not destroyed" );
94 sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescriptor )
95 throw (RuntimeException, std::exception)
97 SolarMutexGuard aGuard;
98 vcl::Window* pFocusWindow = Application::GetFocusWindow();
99 bool bRet;
101 if( pFocusWindow )
102 pFocusWindow->EnterWait();
104 if( mxDstDoc.is() )
105 bRet = implImport( rDescriptor );
106 #ifndef DISABLE_EXPORT
107 else if( mxSrcDoc.is() )
109 // #i124608# detect selection
110 bool bSelectionOnly = false;
111 bool bGotSelection = false;
113 // when using LibreOfficeKit, default to exporting everything (-1)
114 bool bPageProvided = comphelper::LibreOfficeKit::isActive();
115 sal_Int32 nPageToExport = -1;
117 for (sal_Int32 nInd = 0; nInd < rDescriptor.getLength(); nInd++)
119 if (rDescriptor[nInd].Name == "SelectionOnly")
121 // #i124608# extract single selection wanted from dialog return values
122 rDescriptor[nInd].Value >>= bSelectionOnly;
124 else if (rDescriptor[nInd].Name == "PagePos")
126 rDescriptor[nInd].Value >>= nPageToExport;
127 bPageProvided = true;
131 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
132 uno::Reference<frame::XController > xController;
133 if (xDesktop->getCurrentFrame().is() && !bPageProvided) // Manage headless case
135 uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_QUERY_THROW);
136 xController.set(xFrame->getController(), uno::UNO_QUERY_THROW);
137 uno::Reference<drawing::XDrawView> xDrawView(xController, uno::UNO_QUERY_THROW);
138 uno::Reference<drawing::framework::XControllerManager> xManager(xController, uno::UNO_QUERY_THROW);
139 uno::Reference<drawing::framework::XConfigurationController> xConfigController(xManager->getConfigurationController());
141 // which view configuration are we in?
143 // * traverse Impress resources to find slide preview pane, grab selection from there
144 // * otherwise, fallback to current slide
146 uno::Sequence<uno::Reference<drawing::framework::XResourceId> > aResIds(
147 xConfigController->getCurrentConfiguration()->getResources(
148 uno::Reference<drawing::framework::XResourceId>(),
150 drawing::framework::AnchorBindingMode_INDIRECT));
152 for( sal_Int32 i=0; i<aResIds.getLength(); ++i )
154 // can we somehow obtain the slidesorter from the Impress framework?
155 if( aResIds[i]->getResourceURL() == "private:resource/view/SlideSorter" )
157 // got it, grab current selection from there
158 uno::Reference<drawing::framework::XResource> xRes(
159 xConfigController->getResource(aResIds[i]));
161 uno::Reference< view::XSelectionSupplier > xSelectionSupplier(
162 xRes,
163 uno::UNO_QUERY );
164 if( xSelectionSupplier.is() )
166 uno::Any aSelection = xSelectionSupplier->getSelection();
167 if( aSelection.hasValue() )
169 ObjectSequence aSelectedPageSequence;
170 aSelection >>= aSelectedPageSequence;
171 mSelectedPages.resize( aSelectedPageSequence.getLength() );
172 for( size_t j=0; j<mSelectedPages.size(); ++j )
174 uno::Reference< drawing::XDrawPage > xDrawPage( aSelectedPageSequence[j],
175 uno::UNO_QUERY );
176 mSelectedPages[j] = xDrawPage;
179 // and stop looping. it is likely not getting better
180 break;
186 if( mSelectedPages.empty() )
188 // apparently failed to clean selection - fallback to current page
189 mSelectedPages.resize( 1 );
190 mSelectedPages[0] = xDrawView->getCurrentPage();
195 * Export all slides, or requested "PagePos"
197 if( mSelectedPages.empty() )
199 uno::Reference< drawing::XMasterPagesSupplier > xMasterPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
200 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
202 if( xMasterPagesSupplier.is() && xDrawPagesSupplier.is() )
204 uno::Reference< drawing::XDrawPages > xMasterPages( xMasterPagesSupplier->getMasterPages(), uno::UNO_QUERY );
205 uno::Reference< drawing::XDrawPages > xDrawPages( xDrawPagesSupplier->getDrawPages(), uno::UNO_QUERY );
206 if( xMasterPages.is() && xMasterPages->getCount() &&
207 xDrawPages.is() && xDrawPages->getCount() )
209 sal_Int32 nDPCount = xDrawPages->getCount();
211 mSelectedPages.resize( nPageToExport != -1 ? 1 : nDPCount );
212 sal_Int32 i;
213 for( i = 0; i < nDPCount; ++i )
215 if( nPageToExport != -1 && nPageToExport == i )
217 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
218 mSelectedPages[0] = xDrawPage;
220 else
222 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
223 mSelectedPages[i] = xDrawPage;
230 if (bSelectionOnly)
232 // #i124608# when selection only is wanted, get the current object selection
233 // from the DrawView
234 Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
236 if (xSelection.is())
238 bGotSelection
239 = ( xSelection->getSelection() >>= maShapeSelection );
243 if(bSelectionOnly && bGotSelection && 0 == maShapeSelection->getCount())
245 // #i124608# export selection, got maShapeSelection but no shape selected -> nothing
246 // to export, we are done (maybe return true, but a hint that nothing was done
247 // may be useful; it may have happened by error)
248 bRet = false;
250 else
253 * We get all master page that are targeted by at least one draw page.
254 * The master page are put in an unordered set.
256 ObjectSet aMasterPageTargetSet;
257 for(uno::Reference<drawing::XDrawPage> & mSelectedPage : mSelectedPages)
259 uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mSelectedPage, uno::UNO_QUERY );
260 if( xMasterPageTarget.is() )
262 aMasterPageTargetSet.insert( xMasterPageTarget->getMasterPage() );
265 // Later we move them to a uno::Sequence so we can get them by index
266 mMasterPageTargets.resize( aMasterPageTargetSet.size() );
267 ObjectSet::const_iterator aElem = aMasterPageTargetSet.begin();
268 for( sal_Int32 i = 0; aElem != aMasterPageTargetSet.end(); ++aElem, ++i)
270 uno::Reference< drawing::XDrawPage > xMasterPage( *aElem, uno::UNO_QUERY );
271 mMasterPageTargets[i] = xMasterPage;
274 bRet = implExport( rDescriptor );
277 #endif
278 else
279 bRet = false;
281 if( pFocusWindow )
282 pFocusWindow->LeaveWait();
284 return bRet;
287 void SAL_CALL SVGFilter::cancel( ) throw (RuntimeException, std::exception)
291 void SAL_CALL SVGFilter::setSourceDocument( const Reference< XComponent >& xDoc )
292 throw (IllegalArgumentException, RuntimeException, std::exception)
294 mxSrcDoc = xDoc;
297 void SAL_CALL SVGFilter::setTargetDocument( const Reference< XComponent >& xDoc )
298 throw (css::lang::IllegalArgumentException, RuntimeException, std::exception)
300 mxDstDoc = xDoc;
303 bool SVGFilter::isStreamGZip(const uno::Reference<io::XInputStream>& xInput)
305 uno::Reference<io::XSeekable> xSeek(xInput, uno::UNO_QUERY);
306 if(xSeek.is())
307 xSeek->seek(0);
309 uno::Sequence<sal_Int8> aBuffer(2);
310 const sal_uInt64 nBytes = xInput->readBytes(aBuffer, 2);
311 if (nBytes == 2)
313 const sal_Int8* pBuffer = aBuffer.getConstArray();
314 if (pBuffer[0] == 0x1F && static_cast<sal_uInt8>(pBuffer[1]) == 0x8B)
315 return true;
317 return false;
320 bool SVGFilter::isStreamSvg(const uno::Reference<io::XInputStream>& xInput)
322 uno::Reference<io::XSeekable> xSeek(xInput, uno::UNO_QUERY);
323 if(xSeek.is())
324 xSeek->seek(0);
326 const sal_Int32 nLookAhead = 1024;
327 uno::Sequence<sal_Int8> aBuffer(nLookAhead);
328 const sal_uInt64 nBytes = xInput->readBytes(aBuffer, nLookAhead);
329 const sal_Int8* pBuffer = aBuffer.getConstArray();
331 sal_Int8 aMagic1[] = {'<', 's', 'v', 'g'};
332 sal_Int32 aMagic1Size = sizeof(aMagic1) / sizeof(*aMagic1);
334 if (std::search(pBuffer, pBuffer + nBytes, aMagic1, aMagic1 + aMagic1Size) != pBuffer + nBytes )
335 return true;
337 sal_Int8 aMagic2[] = {'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 's', 'v', 'g'};
338 sal_Int32 aMagic2Size = sizeof(aMagic2) / sizeof(*aMagic2);
340 if (std::search(pBuffer, pBuffer + nBytes, aMagic2, aMagic2 + aMagic2Size) != pBuffer + nBytes)
341 return true;
343 return false;
346 OUString SAL_CALL SVGFilter::detect(Sequence<PropertyValue>& rDescriptor) throw (RuntimeException, std::exception)
348 utl::MediaDescriptor aMediaDescriptor(rDescriptor);
349 uno::Reference<io::XInputStream> xInput(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM()], UNO_QUERY);
351 if (!xInput.is())
352 return OUString();
354 try {
355 if (isStreamGZip(xInput))
357 std::unique_ptr<SvStream> aStream(utl::UcbStreamHelper::CreateStream(xInput, true ));
358 if(!aStream.get())
359 return OUString();
361 SvStream* pMemoryStream = new SvMemoryStream;
362 uno::Reference<io::XSeekable> xSeek(xInput, uno::UNO_QUERY);
363 if (!xSeek.is())
364 return OUString();
365 xSeek->seek(0);
367 ZCodec aCodec;
368 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false, true);
369 aCodec.Decompress(*aStream.get(), *pMemoryStream);
370 aCodec.EndCompression();
371 pMemoryStream->Seek(STREAM_SEEK_TO_BEGIN);
372 uno::Reference<io::XInputStream> xDecompressedInput(new utl::OSeekableInputStreamWrapper(pMemoryStream, true));
374 if (xDecompressedInput.is() && isStreamSvg(xDecompressedInput))
375 return OUString(constFilterName);
377 else
379 if (isStreamSvg(xInput))
380 return OUString(constFilterName);
382 } catch (css::io::IOException & e) {
383 SAL_WARN("filter.svg", "caught IOException " + e.Message);
385 return OUString();
388 #define SVG_FILTER_IMPL_NAME "com.sun.star.comp.Draw.SVGFilter"
389 #define SVG_WRITER_IMPL_NAME "com.sun.star.comp.Draw.SVGWriter"
391 namespace sdecl = comphelper::service_decl;
392 sdecl::class_<SVGFilter> serviceFilterImpl;
393 const sdecl::ServiceDecl svgFilter(
394 serviceFilterImpl,
395 SVG_FILTER_IMPL_NAME,
396 "com.sun.star.document.ImportFilter;"
397 "com.sun.star.document.ExportFilter;"
398 "com.sun.star.document.ExtendedTypeDetection" );
400 sdecl::class_<SVGWriter, sdecl::with_args<true> > serviceWriterImpl;
401 const sdecl::ServiceDecl svgWriter(
402 serviceWriterImpl,
403 SVG_WRITER_IMPL_NAME,
404 "com.sun.star.svg.SVGWriter" );
406 // The C shared lib entry points
407 extern "C" SAL_DLLPUBLIC_EXPORT void* SAL_CALL svgfilter_component_getFactory(
408 sal_Char const* pImplName, void*, void*)
410 if ( rtl_str_compare (pImplName, SVG_FILTER_IMPL_NAME) == 0 )
412 return sdecl::component_getFactoryHelper( pImplName, {&svgFilter} );
414 else if ( rtl_str_compare (pImplName, SVG_WRITER_IMPL_NAME) == 0 )
416 return sdecl::component_getFactoryHelper( pImplName, {&svgWriter} );
418 return nullptr;
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */