1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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"
56 using namespace ::com::sun::star
;
60 static const char constFilterName
[] = "svg_Scalable_Vector_Graphics";
63 SVGFilter::SVGFilter( const Reference
< XComponentContext
>& rxCtx
) :
66 mpSVGExport( nullptr ),
67 mpSVGFontExport( nullptr ),
68 mpSVGWriter( nullptr ),
69 mpDefaultSdrPage( nullptr ),
70 mpSdrModel( nullptr ),
71 mbPresentation( false ),
72 mbSinglePage( false ),
80 mbExportShapeSelection(false),
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();
102 pFocusWindow
->EnterWait();
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(
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
],
176 mSelectedPages
[j
] = xDrawPage
;
179 // and stop looping. it is likely not getting better
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
);
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
;
222 uno::Reference
< drawing::XDrawPage
> xDrawPage( xDrawPages
->getByIndex( i
), uno::UNO_QUERY
);
223 mSelectedPages
[i
] = xDrawPage
;
232 // #i124608# when selection only is wanted, get the current object selection
234 Reference
< view::XSelectionSupplier
> xSelection (xController
, UNO_QUERY
);
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)
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
);
282 pFocusWindow
->LeaveWait();
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
)
297 void SAL_CALL
SVGFilter::setTargetDocument( const Reference
< XComponent
>& xDoc
)
298 throw (css::lang::IllegalArgumentException
, RuntimeException
, std::exception
)
303 bool SVGFilter::isStreamGZip(const uno::Reference
<io::XInputStream
>& xInput
)
305 uno::Reference
<io::XSeekable
> xSeek(xInput
, uno::UNO_QUERY
);
309 uno::Sequence
<sal_Int8
> aBuffer(2);
310 const sal_uInt64 nBytes
= xInput
->readBytes(aBuffer
, 2);
313 const sal_Int8
* pBuffer
= aBuffer
.getConstArray();
314 if (pBuffer
[0] == 0x1F && static_cast<sal_uInt8
>(pBuffer
[1]) == 0x8B)
320 bool SVGFilter::isStreamSvg(const uno::Reference
<io::XInputStream
>& xInput
)
322 uno::Reference
<io::XSeekable
> xSeek(xInput
, uno::UNO_QUERY
);
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
)
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
)
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
);
355 if (isStreamGZip(xInput
))
357 std::unique_ptr
<SvStream
> aStream(utl::UcbStreamHelper::CreateStream(xInput
, true ));
361 SvStream
* pMemoryStream
= new SvMemoryStream
;
362 uno::Reference
<io::XSeekable
> xSeek(xInput
, uno::UNO_QUERY
);
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
);
379 if (isStreamSvg(xInput
))
380 return OUString(constFilterName
);
382 } catch (css::io::IOException
& e
) {
383 SAL_WARN("filter.svg", "caught IOException " + e
.Message
);
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(
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(
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
} );
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */