LibreOffice Module filter (master) 1
svgfilter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#include <cstdio>
22
23#include <comphelper/lok.hxx>
25#include <com/sun/star/drawing/XDrawPage.hpp>
26#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
27#include <com/sun/star/drawing/XDrawView.hpp>
28#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
29#include <com/sun/star/drawing/XMasterPageTarget.hpp>
30#include <com/sun/star/frame/Desktop.hpp>
31#include <com/sun/star/frame/XController.hpp>
32#include <com/sun/star/io/IOException.hpp>
33#include <com/sun/star/view/XSelectionSupplier.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
43#include <tools/debug.hxx>
45#include <tools/zcodec.hxx>
46
50
51#include "svgfilter.hxx"
52
53#include <svx/unopage.hxx>
54#include <vcl/graphicfilter.hxx>
55#include <vcl/window.hxx>
56#include <svx/svdmodel.hxx>
57#include <svx/svdpage.hxx>
58#include <svx/svdograf.hxx>
59#include <svl/itempool.hxx>
60
61#include <memory>
62
63using namespace ::com::sun::star;
64
65namespace
66{
67 constexpr OUStringLiteral constFilterNameDraw = u"svg_Scalable_Vector_Graphics_Draw";
68 constexpr OUStringLiteral constFilterName = u"svg_Scalable_Vector_Graphics";
69}
70
71SVGFilter::SVGFilter( const Reference< XComponentContext >& rxCtx ) :
72 mxContext( rxCtx ),
73 mpSVGDoc( nullptr ),
74 mpSVGFontExport( nullptr ),
75 mpSVGWriter( nullptr ),
76 mbSinglePage( false ),
77 mnVisiblePage( -1 ),
78 mpObjects( nullptr ),
79 mbExportShapeSelection(false),
80 mbIsPreview(false),
81 mbShouldCompress(false),
82 mbWriterFilter(false),
83 mbCalcFilter(false),
84 mbImpressFilter(false),
85 mpDefaultSdrPage( nullptr ),
86 mbPresentation( false )
87{
88}
89
91{
92 DBG_ASSERT( mpSVGDoc == nullptr, "mpSVGDoc not destroyed" );
93 DBG_ASSERT( mpSVGExport == nullptr, "mpSVGExport not destroyed" );
94 DBG_ASSERT( mpSVGFontExport == nullptr, "mpSVGFontExport not destroyed" );
95 DBG_ASSERT( mpSVGWriter == nullptr, "mpSVGWriter not destroyed" );
96 DBG_ASSERT( mpObjects == nullptr, "mpObjects not destroyed" );
97}
98
99sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescriptor )
100{
101 mbWriterFilter = false;
102 mbCalcFilter = false;
103 mbImpressFilter = false;
104 mbShouldCompress = false;
105
106 if(mxDstDoc.is()) // Import works for Impress / draw only
107 return filterImpressOrDraw(rDescriptor);
108
109 if(!mxSrcDoc)
110 return false;
111
112 for (const PropertyValue& rProp : rDescriptor)
113 {
114 if (rProp.Name == "IsPreview")
115 {
116 rProp.Value >>= mbIsPreview;
117 break;
118 }
119 }
120
121 for (const PropertyValue& rProp : rDescriptor)
122 {
123 if (rProp.Name == "FilterName")
124 {
125 OUString sFilterName;
126 rProp.Value >>= sFilterName;
127 if(sFilterName == "impress_svg_Export")
128 {
129 mbImpressFilter = true;
130 return filterImpressOrDraw(rDescriptor);
131 }
132 else if(sFilterName == "writer_svg_Export")
133 {
134 mbWriterFilter = true;
135 return filterWriterOrCalc(rDescriptor);
136 }
137 else if(sFilterName == "calc_svg_Export")
138 {
139 mbCalcFilter = true;
140 return filterWriterOrCalc(rDescriptor);
141 }
142 else if(sFilterName == "draw_svgz_Export")
143 {
144 mbShouldCompress = true;
145 }
146 break;
147 }
148 }
149 return filterImpressOrDraw(rDescriptor);
150}
151
152bool SVGFilter::filterImpressOrDraw( const Sequence< PropertyValue >& rDescriptor )
153{
154 SolarMutexGuard aGuard;
156 bool bRet(false);
157
158 if(pFocusWindow)
159 {
160 pFocusWindow->EnterWait();
161 }
162
163 if(mxDstDoc.is())
164 {
165 // Import. Use an endless loop to have easy exits for error handling
166 while(true)
167 {
168 // use MediaDescriptor to get needed data out of Sequence< PropertyValue >
169 utl::MediaDescriptor aMediaDescriptor(rDescriptor);
170 uno::Reference<io::XInputStream> xInputStream;
171
172 xInputStream.set(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], UNO_QUERY);
173
174 if(!xInputStream.is())
175 {
176 // we need the InputStream
177 break;
178 }
179
180 // get the DrawPagesSupplier
181 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxDstDoc, uno::UNO_QUERY );
182
183 if(!xDrawPagesSupplier.is())
184 {
185 // we need the DrawPagesSupplier
186 break;
187 }
188
189 // get the DrawPages
190 uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
191
192 if(!xDrawPages.is())
193 {
194 // we need the DrawPages
195 break;
196 }
197
198 // check DrawPageCount (there should be one by default)
199 sal_Int32 nDrawPageCount(xDrawPages->getCount());
200
201 if(0 == nDrawPageCount)
202 {
203 // at least one DrawPage should be there - we need that
204 break;
205 }
206
207 // get that DrawPage
208 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
209
210 if(!xDrawPage.is())
211 {
212 // we need that DrawPage
213 break;
214 }
215
216 // get that DrawPage's UNO API implementation
217 SvxDrawPage* pSvxDrawPage(comphelper::getFromUnoTunnel<SvxDrawPage>(xDrawPage));
218
219 if(nullptr == pSvxDrawPage || nullptr == pSvxDrawPage->GetSdrPage())
220 {
221 // we need a SvxDrawPage
222 break;
223 }
224
225 // get the SvStream to work with
226 std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
227
228 if (!aStream)
229 {
230 // we need the SvStream
231 break;
232 }
233
234 // create a GraphicFilter and load the SVG (format already known, thus *could*
235 // be handed over to ImportGraphic - but detection is fast).
236 // As a bonus, zipped data is already detected and handled there
237 GraphicFilter aGraphicFilter;
238 Graphic aGraphic;
239 const ErrCode nGraphicFilterErrorCode(
240 aGraphicFilter.ImportGraphic(aGraphic, u"", *aStream));
241
242 if(ERRCODE_NONE != nGraphicFilterErrorCode)
243 {
244 // SVG import error, cannot continue
245 break;
246 }
247
248 // get the GraphicPrefSize early to check if we have any content
249 // (the SVG may contain nothing and/or just <g visibility="hidden"> stuff...)
250 const Size aGraphicPrefSize(aGraphic.GetPrefSize());
251
252 if(0 == aGraphicPrefSize.Width() || 0 == aGraphicPrefSize.Height())
253 {
254 // SVG has no displayable content, stop import.
255 // Also possible would be to get the sequence< Primitives >
256 // from aGraphic and check if it is empty.
257 // Possibility to set some error message here to tell
258 // the user what/why loading went wrong, but I do not
259 // know how this could be done here
260 break;
261 }
262
263 // tdf#118232 Get the sequence of primitives and check if geometry is completely
264 // hidden. If so, there is no need to add a SdrObject at all
265 auto const & rVectorGraphicData(aGraphic.getVectorGraphicData());
266 bool bContainsNoGeometry(false);
267
268 if(bool(rVectorGraphicData) && VectorGraphicDataType::Svg == rVectorGraphicData->getType())
269 {
270 const drawinglayer::primitive2d::Primitive2DContainer aContainer(rVectorGraphicData->getPrimitive2DSequence());
271
272 if(!aContainer.empty())
273 {
274 bool bAllAreHiddenGeometry(true);
275
276 for(const auto& rCandidate : aContainer)
277 {
278 if(rCandidate && PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D != rCandidate->getPrimitive2DID())
279 {
280 bAllAreHiddenGeometry = false;
281 break;
282 }
283 }
284
285 if(bAllAreHiddenGeometry)
286 {
287 bContainsNoGeometry = true;
288 }
289 }
290 }
291
292 // create a SdrModel-GraphicObject to insert to page
293 SdrPage* pTargetSdrPage(pSvxDrawPage->GetSdrPage());
294 rtl::Reference< SdrGrafObj > aNewSdrGrafObj;
295
296 // tdf#118232 only add an SdrGrafObj when we have Geometry
297 if(!bContainsNoGeometry)
298 {
299 aNewSdrGrafObj =
300 new SdrGrafObj(
301 pTargetSdrPage->getSdrModelFromSdrPage(),
302 aGraphic);
303 }
304
305 // Evtl. adapt the GraphicPrefSize to target-MapMode of target-Model
306 // (should be 100thmm here, but just stay safe by doing the conversion)
307 const MapMode aGraphicPrefMapMode(aGraphic.GetPrefMapMode());
308 const MapUnit eDestUnit(pTargetSdrPage->getSdrModelFromSdrPage().GetItemPool().GetMetric(0));
309 const MapUnit eSrcUnit(aGraphicPrefMapMode.GetMapUnit());
310 Size aGraphicSize(aGraphicPrefSize);
311
312 if (eDestUnit != eSrcUnit)
313 {
314 aGraphicSize = Size(
315 OutputDevice::LogicToLogic(aGraphicSize.Width(), eSrcUnit, eDestUnit),
316 OutputDevice::LogicToLogic(aGraphicSize.Height(), eSrcUnit, eDestUnit));
317 }
318
319 // Based on GraphicSize, set size of Page. Do not forget to adapt PageBorders,
320 // but interpret them relative to PageSize so that they do not 'explode/shrink'
321 // in comparison. Use a common scaling factor for hor/ver to not get
322 // asynchronous border distances, though. All in all this will adapt borders
323 // nicely and is based on office-defaults for standard-page-border-sizes.
324 const Size aPageSize(pTargetSdrPage->GetSize());
325 const double fBorderRelation((
326 static_cast< double >(pTargetSdrPage->GetLeftBorder()) / aPageSize.Width() +
327 static_cast< double >(pTargetSdrPage->GetRightBorder()) / aPageSize.Width() +
328 static_cast< double >(pTargetSdrPage->GetUpperBorder()) / aPageSize.Height() +
329 static_cast< double >(pTargetSdrPage->GetLowerBorder()) / aPageSize.Height()) / 4.0);
330 const tools::Long nAllBorder(basegfx::fround((aGraphicSize.Width() + aGraphicSize.Height()) * fBorderRelation * 0.5));
331
332 // Adapt PageSize and Border stuff. To get all MasterPages and PresObjs
333 // correctly adapted, do not just use
334 // pTargetSdrPage->SetBorder(...) and
335 // pTargetSdrPage->SetSize(...),
336 // but ::adaptSizeAndBorderForAllPages
337 // Do use original Size and borders to get as close to original
338 // as possible for better turn-arounds.
340 Size(
341 aGraphicSize.Width(),
342 aGraphicSize.Height()),
343 nAllBorder,
344 nAllBorder,
345 nAllBorder,
346 nAllBorder);
347
348 // tdf#118232 set pos/size at SdrGraphicObj - use zero position for
349 // better turn-around results
350 if(!bContainsNoGeometry)
351 {
352 aNewSdrGrafObj->SetSnapRect(
354 Point(0, 0),
355 aGraphicSize));
356
357 // insert to page (owner change of SdrGrafObj)
358 pTargetSdrPage->InsertObject(aNewSdrGrafObj.get());
359 }
360
361 // done - set positive result now
362 bRet = true;
363
364 // always leave helper endless loop
365 break;
366 }
367 }
368 else if( mxSrcDoc.is() )
369 {
370 // #i124608# detect selection
371 bool bSelectionOnly = false;
372 bool bGotSelection = false;
373
374 // when using LibreOfficeKit, default to exporting everything (-1)
375 bool bPageProvided = comphelper::LibreOfficeKit::isActive();
376 sal_Int32 nPageToExport = -1;
377
378 for( const PropertyValue& rProp : rDescriptor )
379 {
380 if (rProp.Name == "SelectionOnly")
381 {
382 // #i124608# extract single selection wanted from dialog return values
383 rProp.Value >>= bSelectionOnly;
384 bPageProvided = false;
385 }
386 else if (rProp.Name == "PagePos")
387 {
388 rProp.Value >>= nPageToExport;
389 bPageProvided = true;
390 }
391 }
392
393 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
394 uno::Reference<frame::XController > xController;
395 if (xDesktop->getCurrentFrame().is() && !bPageProvided) // Manage headless case
396 {
397 uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
398 xController.set(xFrame->getController(), uno::UNO_SET_THROW);
399 uno::Reference<drawing::XDrawView> xDrawView(xController, uno::UNO_QUERY_THROW);
400 uno::Reference<drawing::framework::XControllerManager> xManager(xController, uno::UNO_QUERY_THROW);
401 uno::Reference<drawing::framework::XConfigurationController> xConfigController(xManager->getConfigurationController());
402
403 // which view configuration are we in?
404 //
405 // * traverse Impress resources to find slide preview pane, grab selection from there
406 // * otherwise, fallback to current slide
407 //
408 const uno::Sequence<uno::Reference<drawing::framework::XResourceId> > aResIds(
409 xConfigController->getCurrentConfiguration()->getResources(
410 uno::Reference<drawing::framework::XResourceId>(),
411 "",
412 drawing::framework::AnchorBindingMode_INDIRECT));
413
414 for( const uno::Reference<drawing::framework::XResourceId>& rResId : aResIds )
415 {
416 // can we somehow obtain the slidesorter from the Impress framework?
417 if( rResId->getResourceURL() == "private:resource/view/SlideSorter" )
418 {
419 // got it, grab current selection from there
420 uno::Reference<drawing::framework::XResource> xRes(
421 xConfigController->getResource(rResId));
422
423 uno::Reference< view::XSelectionSupplier > xSelectionSupplier(
424 xRes,
425 uno::UNO_QUERY );
426 if( xSelectionSupplier.is() )
427 {
428 uno::Any aSelection = xSelectionSupplier->getSelection();
429 if( aSelection.hasValue() )
430 {
431 Sequence< Reference< XInterface > > aSelectedPageSequence;
432 aSelection >>= aSelectedPageSequence;
433 mSelectedPages.resize( aSelectedPageSequence.getLength() );
434 for( size_t j=0; j<mSelectedPages.size(); ++j )
435 {
436 uno::Reference< drawing::XDrawPage > xDrawPage( aSelectedPageSequence[j],
437 uno::UNO_QUERY );
438 mSelectedPages[j] = xDrawPage;
439 }
440
441 // and stop looping. It is likely not getting better
442 break;
443 }
444 }
445 }
446 }
447
448 if( mSelectedPages.empty() )
449 {
450 // apparently failed to clean selection - fallback to current page
451 mSelectedPages.resize( 1 );
452 mSelectedPages[0] = xDrawView->getCurrentPage();
453 }
454 }
455
456 /*
457 * Export all slides, or requested "PagePos"
458 */
459 if( mSelectedPages.empty() )
460 {
461 uno::Reference< drawing::XMasterPagesSupplier > xMasterPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
462 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
463
464 if( xMasterPagesSupplier.is() && xDrawPagesSupplier.is() )
465 {
466 uno::Reference< drawing::XDrawPages > xMasterPages = xMasterPagesSupplier->getMasterPages();
467 uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
468 if( xMasterPages.is() && xMasterPages->getCount() &&
469 xDrawPages.is() && xDrawPages->getCount() )
470 {
471 sal_Int32 nDPCount = xDrawPages->getCount();
472
473 mSelectedPages.resize( nPageToExport != -1 ? 1 : nDPCount );
474 sal_Int32 i;
475 for( i = 0; i < nDPCount; ++i )
476 {
477 if( nPageToExport != -1 && nPageToExport == i )
478 {
479 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
480 mSelectedPages[0] = xDrawPage;
481 }
482 else
483 {
484 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
485 mSelectedPages[i] = xDrawPage;
486 }
487 }
488 }
489 }
490 }
491
492 if (bSelectionOnly)
493 {
494 // #i124608# when selection only is wanted, get the current object selection
495 // from the DrawView
496 Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
497
498 if (xSelection.is())
499 {
500 bGotSelection
501 = ( xSelection->getSelection() >>= maShapeSelection );
502 }
503 }
504
505 if(bSelectionOnly && bGotSelection && 0 == maShapeSelection->getCount())
506 {
507 // #i124608# export selection, got maShapeSelection but no shape selected -> nothing
508 // to export, we are done (maybe return true, but a hint that nothing was done
509 // may be useful; it may have happened by error)
510 bRet = false;
511 }
512 else
513 {
514 /*
515 * We get all master page that are targeted by at least one draw page.
516 * The master page are put in an unordered set.
517 */
518 ObjectSet aMasterPageTargetSet;
519 for(const uno::Reference<drawing::XDrawPage> & mSelectedPage : mSelectedPages)
520 {
521 uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mSelectedPage, uno::UNO_QUERY );
522 if( xMasterPageTarget.is() )
523 {
524 aMasterPageTargetSet.insert( xMasterPageTarget->getMasterPage() );
525 }
526 }
527 // Later we move them to an uno::Sequence so we can get them by index
528 mMasterPageTargets.resize( aMasterPageTargetSet.size() );
529 sal_Int32 i = 0;
530 for (auto const& masterPageTarget : aMasterPageTargetSet)
531 {
532 uno::Reference< drawing::XDrawPage > xMasterPage( masterPageTarget, uno::UNO_QUERY );
533 mMasterPageTargets[i++] = xMasterPage;
534 }
535
536 bRet = implExport( rDescriptor );
537 }
538 }
539 else
540 bRet = false;
541
542 if( pFocusWindow )
543 pFocusWindow->LeaveWait();
544
545 return bRet;
546}
547
548bool SVGFilter::filterWriterOrCalc( const Sequence< PropertyValue >& rDescriptor )
549{
550 bool bSelectionOnly = false;
551
552 for (const PropertyValue& rProp : rDescriptor)
553 {
554 if (rProp.Name == "SelectionOnly")
555 {
556 rProp.Value >>= bSelectionOnly;
557 break;
558 }
559 }
560
561 if(!bSelectionOnly) // For Writer only the selection-only mode is supported
562 return false;
563
564 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
565 uno::Reference<frame::XController > xController;
566 if (xDesktop->getCurrentFrame().is())
567 {
568 uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
569 xController.set(xFrame->getController(), uno::UNO_SET_THROW);
570 }
571
572 Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
573 if (!xSelection.is())
574 return false;
575
576 // Select only one draw page
577 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
578 uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
579 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
580 mSelectedPages.resize( 1 );
581 mSelectedPages[0] = xDrawPage;
582
583 bool bGotSelection = xSelection->getSelection() >>= maShapeSelection;
584
585 if (!bGotSelection)
586 {
587 if (mbWriterFilter)
588 {
589 // For Writer we might have a non-shape graphic
590 bGotSelection = implExportWriterTextGraphic(xSelection);
591 }
592 if (!bGotSelection)
593 return false;
594 }
595
596 return implExport( rDescriptor );
597}
598
599void SAL_CALL SVGFilter::cancel( )
600{
601}
602
603void SAL_CALL SVGFilter::setSourceDocument( const Reference< XComponent >& xDoc )
604{
605 mxSrcDoc = xDoc;
606}
607
608void SAL_CALL SVGFilter::setTargetDocument( const Reference< XComponent >& xDoc )
609{
610 mxDstDoc = xDoc;
611}
612
613namespace {
614
615// There is already another SVG-Type_Detector, see
616// vcl/source/filter/graphicfilter.cxx ("DOCTYPE svg"),
617// but since these start from different preconditions it is not
618// easy to unify these. For now, use this local helper.
619class SVGFileInfo
620{
621private:
622 const uno::Reference<io::XInputStream>& mxInput;
623 uno::Sequence< sal_Int8 > mnFirstBytes;
624 sal_Int32 mnFirstBytesSize;
625 sal_Int32 mnFirstRead;
626 bool mbProcessed;
627 bool mbIsSVG;
628
629 bool impCheckForMagic(
630 const sal_Int8* pMagic,
631 const sal_Int32 nMagicSize)
632 {
633 const sal_Int8* pBuffer(mnFirstBytes.getConstArray());
634 return std::search(
635 pBuffer,
636 pBuffer + mnFirstRead,
637 pMagic,
638 pMagic + nMagicSize) != pBuffer + mnFirstRead;
639 }
640
641 void impEnsureProcessed()
642 {
643 if(mbProcessed)
644 {
645 return;
646 }
647
648 mbProcessed = true;
649
650 if(!mxInput.is())
651 {
652 return;
653 }
654
655 if(0 == mnFirstBytesSize)
656 {
657 return;
658 }
659
660 mnFirstBytes.realloc(mnFirstBytesSize);
661
662 if(mnFirstBytesSize != mnFirstBytes.getLength())
663 {
664 return;
665 }
666
667 std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(mxInput, true));
668
669 if (!aStream)
670 {
671 return;
672 }
673
674 const sal_uInt64 nStreamLen(aStream->remainingSize());
675
676 if(aStream->GetError())
677 {
678 return;
679 }
680
681 mnFirstRead = aStream->ReadBytes(
682 &mnFirstBytes.getArray()[0],
683 std::min(nStreamLen, static_cast<sal_uInt64>(mnFirstBytesSize)));
684
685 if(aStream->GetError())
686 {
687 return;
688 }
689
690 // check if it is gzipped -> svgz
691 if (mnFirstBytes[0] == 0x1F && static_cast<sal_uInt8>(mnFirstBytes[1]) == 0x8B)
692 {
693 ZCodec aCodec;
694
695 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
696 mnFirstRead = aCodec.Read(
697 *aStream,
698 reinterpret_cast< sal_uInt8* >(mnFirstBytes.getArray()),
699 mnFirstBytesSize);
700 aCodec.EndCompression();
701
702 if (mnFirstRead < 0)
703 return;
704 }
705
706 if(!mbIsSVG)
707 {
708 const sal_Int8 aMagic[] = {'<', 's', 'v', 'g'};
709 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
710
711 mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
712 }
713
714 if(!mbIsSVG)
715 {
716 const sal_Int8 aMagic[] = {'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 's', 'v', 'g'};
717 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
718
719 mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
720 }
721
722 return;
723 }
724
725public:
726 SVGFileInfo(
727 const uno::Reference<io::XInputStream>& xInput)
728 : mxInput(xInput),
729 mnFirstBytesSize(2048),
730 mnFirstRead(0),
731 mbProcessed(false),
732 mbIsSVG(false)
733 {
734 // For the default buffer size: Use not too big
735 // (not more than 16K), but also not too small
736 // (not less than 1/2K), see comments at
737 // ImpPeekGraphicFormat, SVG section.
738 // I remember these cases and it *can* happen
739 // that SVGs have quite massive comments in their
740 // headings (!)
741 // Limit to plausible sizes, also for security reasons
742 mnFirstBytesSize = std::min(sal_Int32(512), mnFirstBytesSize);
743 mnFirstBytesSize = std::max(sal_Int32(16384), mnFirstBytesSize);
744 }
745
746 bool isSVG()
747 {
748 impEnsureProcessed();
749
750 return mbIsSVG;
751 }
752
753 bool isOwnFormat()
754 {
755 impEnsureProcessed();
756
757 if(mbIsSVG)
758 {
759 // xmlns:ooo
760 const sal_Int8 aMagic[] = {'x', 'm', 'l', 'n', 's', ':', 'o', 'o', 'o'};
761 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
762
763 return impCheckForMagic(aMagic, nMagicSize);
764 }
765
766 return false;
767 }
768
769 bool isImpress()
770 {
771 impEnsureProcessed();
772
773 if(mbIsSVG)
774 {
775 // ooo:meta_slides
776 const sal_Int8 aMagic[] = {'o', 'o', 'o', ':', 'm', 'e', 't', 'a', '_', 's', 'l', 'i', 'd', 'e', 's'};
777 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
778
779 return impCheckForMagic(aMagic, nMagicSize);
780 }
781
782 return false;
783 }
784};
785
786}
787
788OUString SAL_CALL SVGFilter::detect(Sequence<PropertyValue>& rDescriptor)
789{
790 utl::MediaDescriptor aMediaDescriptor(rDescriptor);
791 uno::Reference<io::XInputStream> xInput(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], UNO_QUERY);
792 OUString aRetval;
793
794 if (!xInput.is())
795 {
796 return aRetval;
797 }
798
799 try
800 {
801 SVGFileInfo aSVGFileInfo(xInput);
802
803 if(aSVGFileInfo.isSVG())
804 {
805 // We have SVG - set default document format to Draw
806 aRetval = OUString(constFilterNameDraw);
807
808 if(aSVGFileInfo.isOwnFormat())
809 {
810 // it's a file that was written/exported by LO
811 if(aSVGFileInfo.isImpress())
812 {
813 // it was written by Impress export. Set document
814 // format for import to Impress
815 aRetval = OUString(constFilterName);
816 }
817 }
818 }
819 }
820 catch (css::io::IOException &)
821 {
822 TOOLS_WARN_EXCEPTION("filter.svg", "");
823 }
824
825 return aRetval;
826}
827
828// XServiceInfo
829sal_Bool SVGFilter::supportsService(const OUString& sServiceName)
830{
832}
834{
835 return "com.sun.star.comp.Draw.SVGFilter";
836}
837css::uno::Sequence< OUString > SVGFilter::getSupportedServiceNames()
838{
839 return { "com.sun.star.document.ImportFilter",
840 "com.sun.star.document.ExportFilter",
841 "com.sun.star.document.ExtendedTypeDetection" };
842}
843
844
845extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
847 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
848{
849 return cppu::acquire(new SVGFilter(context));
850}
851
852
853/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr OUStringLiteral sServiceName
static vcl::Window * GetFocusWindow()
ErrCode ImportGraphic(Graphic &rGraphic, const INetURLObject &rPath, sal_uInt16 nFormat=GRFILTER_FORMAT_DONTKNOW, sal_uInt16 *pDeterminedFormat=nullptr, GraphicFilterImportFlags nImportFlags=GraphicFilterImportFlags::NONE)
Size GetPrefSize() const
MapMode GetPrefMapMode() const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
MapUnit GetMapUnit() const
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
bool filterImpressOrDraw(const Sequence< PropertyValue > &rDescriptor)
Definition: svgfilter.cxx:152
bool mbShouldCompress
Definition: svgfilter.hxx:213
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
Definition: svgfilter.cxx:837
SVGFontExport * mpSVGFontExport
Definition: svgfilter.hxx:199
bool mbWriterFilter
Definition: svgfilter.hxx:215
bool filterWriterOrCalc(const Sequence< PropertyValue > &rDescriptor)
Definition: svgfilter.cxx:548
virtual void SAL_CALL cancel() override
Definition: svgfilter.cxx:599
SVGActionWriter * mpSVGWriter
Definition: svgfilter.hxx:200
std::vector< Reference< css::drawing::XDrawPage > > mMasterPageTargets
Definition: svgfilter.hxx:234
virtual void SAL_CALL setTargetDocument(const Reference< XComponent > &xDoc) override
Definition: svgfilter.cxx:608
SVGFilter(const Reference< XComponentContext > &rxCtx)
Definition: svgfilter.cxx:71
bool implExport(const Sequence< PropertyValue > &rDescriptor)
Definition: svgexport.cxx:629
virtual void SAL_CALL setSourceDocument(const Reference< XComponent > &xDoc) override
Definition: svgfilter.cxx:603
SvXMLElementExport * mpSVGDoc
Definition: svgfilter.hxx:197
virtual ~SVGFilter() override
Definition: svgfilter.cxx:90
bool implExportWriterTextGraphic(const Reference< view::XSelectionSupplier > &xSelectionSupplier)
Definition: svgexport.cxx:828
ObjectMap * mpObjects
Definition: svgfilter.hxx:203
virtual OUString SAL_CALL detect(Sequence< PropertyValue > &io_rDescriptor) override
Definition: svgfilter.cxx:788
virtual OUString SAL_CALL getImplementationName() override
Definition: svgfilter.cxx:833
Reference< XComponentContext > mxContext
Generally use members.
Definition: svgfilter.hxx:196
bool mbCalcFilter
Definition: svgfilter.hxx:216
rtl::Reference< SVGExport > mpSVGExport
Definition: svgfilter.hxx:198
virtual sal_Bool SAL_CALL filter(const Sequence< PropertyValue > &rDescriptor) override
Definition: svgfilter.cxx:99
std::unordered_set< Reference< XInterface > > ObjectSet
Definition: svgfilter.hxx:184
std::vector< Reference< css::drawing::XDrawPage > > mSelectedPages
Definition: svgfilter.hxx:211
Reference< XComponent > mxSrcDoc
Definition: svgfilter.hxx:204
Reference< XComponent > mxDstDoc
Definition: svgfilter.hxx:205
bool mbImpressFilter
Definition: svgfilter.hxx:217
Reference< css::drawing::XShapes > maShapeSelection
Definition: svgfilter.hxx:207
bool mbIsPreview
Definition: svgfilter.hxx:212
virtual sal_Bool SAL_CALL supportsService(const OUString &sServiceName) override
Definition: svgfilter.cxx:829
virtual void adaptSizeAndBorderForAllPages(const Size &rNewSize, tools::Long nLeft=0, tools::Long nRight=0, tools::Long nUpper=0, tools::Long nLower=0)
const SfxItemPool & GetItemPool() const
virtual void InsertObject(SdrObject *pObj, size_t nPos=SAL_MAX_SIZE)
Size GetSize() const
sal_Int32 GetUpperBorder() const
sal_Int32 GetRightBorder() const
sal_Int32 GetLeftBorder() const
SdrModel & getSdrModelFromSdrPage() const
sal_Int32 GetLowerBorder() const
virtual MapUnit GetMetric(sal_uInt16 nWhich) const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
SdrPage * GetSdrPage() const
tools::Long Read(SvStream &rIStm, sal_uInt8 *pData, sal_uInt32 nSize)
tools::Long EndCompression()
void BeginCompression(int nCompressLevel=ZCODEC_DEFAULT_COMPRESSION, bool gzLib=false)
static constexpr OUStringLiteral PROP_INPUTSTREAM
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
void LeaveWait()
void EnterWait()
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
uno::Reference< uno::XComponentContext > mxContext
#define PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D
float u
#define ERRCODE_NONE
#define SAL_N_ELEMENTS(arr)
MapUnit
B2IRange fround(const B2DRange &rRange)
Shape IDs per cluster in DGG atom.
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
int i
Definition: gentoken.py:48
long Long
bool hasValue()
Reference< XController > xController
Reference< XFrame > xFrame
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * filter_SVGFilter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
Definition: svgfilter.cxx:846
unsigned char sal_uInt8
unsigned char sal_Bool
signed char sal_Int8