Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / filter / source / svg / svgfilter.cxx
blob5677d63fcc6cafb5b4405c55064b3aa10ff63641
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 <sal/log.hxx>
24 #include <comphelper/lok.hxx>
25 #include <comphelper/servicedecl.hxx>
26 #include <uno/environment.h>
27 #include <com/sun/star/text/XTextDocument.hpp>
28 #include <com/sun/star/drawing/XDrawPage.hpp>
29 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
30 #include <com/sun/star/drawing/XDrawView.hpp>
31 #include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
32 #include <com/sun/star/drawing/XMasterPageTarget.hpp>
33 #include <com/sun/star/frame/Desktop.hpp>
34 #include <com/sun/star/frame/XController.hpp>
35 #include <com/sun/star/io/IOException.hpp>
36 #include <com/sun/star/view/XSelectionSupplier.hpp>
37 #include <com/sun/star/drawing/XDrawSubController.hpp>
38 #include <com/sun/star/container/XNamed.hpp>
39 #include <com/sun/star/uno/XComponentContext.hpp>
40 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
41 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
42 #include <com/sun/star/drawing/framework/XConfiguration.hpp>
43 #include <com/sun/star/drawing/framework/AnchorBindingMode.hpp>
44 #include <com/sun/star/drawing/framework/XResourceId.hpp>
45 #include <com/sun/star/drawing/framework/XResource.hpp>
46 #include <com/sun/star/drawing/framework/XView.hpp>
47 #include <com/sun/star/drawing/framework/ResourceId.hpp>
49 #include <unotools/mediadescriptor.hxx>
50 #include <unotools/ucbstreamhelper.hxx>
51 #include <unotools/streamwrap.hxx>
52 #include <tools/debug.hxx>
53 #include <tools/diagnose_ex.h>
54 #include <tools/zcodec.hxx>
56 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
57 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
59 #include "svgfilter.hxx"
60 #include "svgwriter.hxx"
62 #include <svx/unopage.hxx>
63 #include <vcl/graphicfilter.hxx>
64 #include <svx/svdpage.hxx>
65 #include <svx/svdograf.hxx>
66 #include <svl/itempool.hxx>
68 #include <memory>
70 using namespace ::com::sun::star;
72 namespace
74 static const char constFilterNameDraw[] = "svg_Scalable_Vector_Graphics_Draw";
75 static const char constFilterName[] = "svg_Scalable_Vector_Graphics";
78 SVGFilter::SVGFilter( const Reference< XComponentContext >& rxCtx ) :
79 mxContext( rxCtx ),
80 mpSVGDoc( nullptr ),
81 mpSVGExport( nullptr ),
82 mpSVGFontExport( nullptr ),
83 mpSVGWriter( nullptr ),
84 mbSinglePage( false ),
85 mnVisiblePage( -1 ),
86 mpObjects( nullptr ),
87 mxSrcDoc(),
88 mxDstDoc(),
89 maShapeSelection(),
90 mbExportShapeSelection(false),
91 maFilterData(),
92 mxDefaultPage(),
93 mbWriterFilter(false),
94 mbCalcFilter(false),
95 mbImpressFilter(false),
96 mpDefaultSdrPage( nullptr ),
97 mbPresentation( false ),
98 maOldFieldHdl()
102 SVGFilter::~SVGFilter()
104 DBG_ASSERT( mpSVGDoc == nullptr, "mpSVGDoc not destroyed" );
105 DBG_ASSERT( mpSVGExport == nullptr, "mpSVGExport not destroyed" );
106 DBG_ASSERT( mpSVGFontExport == nullptr, "mpSVGFontExport not destroyed" );
107 DBG_ASSERT( mpSVGWriter == nullptr, "mpSVGWriter not destroyed" );
108 DBG_ASSERT( mpObjects == nullptr, "mpObjects not destroyed" );
111 sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescriptor )
113 mbWriterFilter = false;
114 mbCalcFilter = false;
115 mbImpressFilter = false;
117 if(mxDstDoc.is()) // Import works for Impress / draw only
118 return filterImpressOrDraw(rDescriptor);
120 if(mxSrcDoc.is())
122 for (sal_Int32 nInd = 0; nInd < rDescriptor.getLength(); nInd++)
124 if (rDescriptor[nInd].Name == "FilterName")
126 OUString sFilterName;
127 rDescriptor[nInd].Value >>= sFilterName;
128 if(sFilterName == "impress_svg_Export")
130 mbImpressFilter = true;
131 return filterImpressOrDraw(rDescriptor);
133 else if(sFilterName == "writer_svg_Export")
135 mbWriterFilter = true;
136 return filterWriterOrCalc(rDescriptor);
138 else if(sFilterName == "calc_svg_Export")
140 mbCalcFilter = true;
141 return filterWriterOrCalc(rDescriptor);
143 break;
146 return filterImpressOrDraw(rDescriptor);
148 return false;
151 bool SVGFilter::filterImpressOrDraw( const Sequence< PropertyValue >& rDescriptor )
153 SolarMutexGuard aGuard;
154 vcl::Window* pFocusWindow(Application::GetFocusWindow());
155 bool bRet(false);
157 if(pFocusWindow)
159 pFocusWindow->EnterWait();
162 if(mxDstDoc.is())
164 // Import. Use an endless loop to have easy exits for error handling
165 while(true)
167 // use MediaDescriptor to get needed data out of Sequence< PropertyValue >
168 utl::MediaDescriptor aMediaDescriptor(rDescriptor);
169 uno::Reference<io::XInputStream> xInputStream;
171 xInputStream.set(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM()], UNO_QUERY);
173 if(!xInputStream.is())
175 // we need the InputStream
176 break;
179 // get the DrawPagesSupplier
180 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxDstDoc, uno::UNO_QUERY );
182 if(!xDrawPagesSupplier.is())
184 // we need the DrawPagesSupplier
185 break;
188 // get the DrawPages
189 uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
191 if(!xDrawPages.is())
193 // we need the DrawPages
194 break;
197 // check DrawPageCount (there should be one by default)
198 sal_Int32 nDrawPageCount(xDrawPages->getCount());
200 if(0 == nDrawPageCount)
202 // at least one DrawPage should be there - we need that
203 break;
206 // get that DrawPage
207 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
209 if(!xDrawPage.is())
211 // we need that DrawPage
212 break;
215 // get that DrawPage's UNO API implementation
216 SvxDrawPage* pSvxDrawPage(comphelper::getUnoTunnelImplementation<SvxDrawPage>(xDrawPage));
218 if(nullptr == pSvxDrawPage || nullptr == pSvxDrawPage->GetSdrPage())
220 // we need a SvxDrawPage
221 break;
224 // get the SvStream to work with
225 std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
227 if (!aStream)
229 // we need the SvStream
230 break;
233 // create a GraphicFilter and load the SVG (format already known, thus *could*
234 // be handed over to ImportGraphic - but detection is fast).
235 // As a bonus, zipped data is already detected and handled there
236 GraphicFilter aGraphicFilter;
237 Graphic aGraphic;
238 const ErrCode nGraphicFilterErrorCode(
239 aGraphicFilter.ImportGraphic(aGraphic, OUString(), *aStream));
241 if(ERRCODE_NONE != nGraphicFilterErrorCode)
243 // SVG import error, cannot continue
244 break;
247 // get the GraphicPrefSize early to check if we have any content
248 // (the SVG may contain nothing and/or just <g visibility="hidden"> stuff...)
249 const Size aGraphicPrefSize(aGraphic.GetPrefSize());
251 if(0 == aGraphicPrefSize.Width() || 0 == aGraphicPrefSize.Height())
253 // SVG has no displayable content, stop import.
254 // Also possible would be to get the sequence< Primitives >
255 // from aGraphic and check if it is empty.
256 // Possibility to set some error message here to tell
257 // the user what/why loading went wrong, but I do not
258 // know how this could be done here
259 break;
262 // tdf#118232 Get the sequence of primitives and check if geometry is completely
263 // hidden. If so, there is no need to add a SdrObject at all
264 const VectorGraphicDataPtr& rVectorGraphicData(aGraphic.getVectorGraphicData());
265 bool bContainsNoGeometry(false);
267 if(bool(rVectorGraphicData) && VectorGraphicDataType::Svg == rVectorGraphicData->getVectorGraphicDataType())
269 const drawinglayer::primitive2d::Primitive2DContainer aContainer(rVectorGraphicData->getPrimitive2DSequence());
271 if(!aContainer.empty())
273 bool bAllAreHiddenGeometry(true);
275 for(const auto& rCandidate : aContainer)
277 if(rCandidate.is())
279 // try to cast to BasePrimitive2D implementation
280 const drawinglayer::primitive2d::BasePrimitive2D* pBasePrimitive(
281 dynamic_cast< const drawinglayer::primitive2d::BasePrimitive2D* >(rCandidate.get()));
283 if(pBasePrimitive && PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D != pBasePrimitive->getPrimitive2DID())
285 bAllAreHiddenGeometry = false;
286 break;
291 if(bAllAreHiddenGeometry)
293 bContainsNoGeometry = true;
298 // create a SdrModel-GraphicObject to insert to page
299 SdrPage* pTargetSdrPage(pSvxDrawPage->GetSdrPage());
300 std::unique_ptr< SdrGrafObj, SdrObjectFreeOp > aNewSdrGrafObj;
302 // tdf#118232 only add an SdrGrafObj when we have Geometry
303 if(!bContainsNoGeometry)
305 aNewSdrGrafObj.reset(
306 new SdrGrafObj(
307 pTargetSdrPage->getSdrModelFromSdrPage(),
308 aGraphic));
311 // Evtl. adapt the GraphicPrefSize to target-MapMode of target-Model
312 // (should be 100thmm here, but just stay safe by doing the conversion)
313 const MapMode aGraphicPrefMapMode(aGraphic.GetPrefMapMode());
314 const MapUnit eDestUnit(pTargetSdrPage->getSdrModelFromSdrPage().GetItemPool().GetMetric(0));
315 const MapUnit eSrcUnit(aGraphicPrefMapMode.GetMapUnit());
316 Size aGraphicSize(aGraphicPrefSize);
318 if (eDestUnit != eSrcUnit)
320 aGraphicSize = Size(
321 OutputDevice::LogicToLogic(aGraphicSize.Width(), eSrcUnit, eDestUnit),
322 OutputDevice::LogicToLogic(aGraphicSize.Height(), eSrcUnit, eDestUnit));
325 // Based on GraphicSize, set size of Page. Do not forget to adapt PageBorders,
326 // but interpret them relative to PageSize so that they do not 'explode/shrink'
327 // in comparison. Use a common scaling factor for hor/ver to not get
328 // asynchronous border distances, though. All in all this will adapt borders
329 // nicely and is based on office-defaults for standard-page-border-sizes.
330 const Size aPageSize(pTargetSdrPage->GetSize());
331 const double fBorderRelation((
332 static_cast< double >(pTargetSdrPage->GetLeftBorder()) / aPageSize.Width() +
333 static_cast< double >(pTargetSdrPage->GetRightBorder()) / aPageSize.Width() +
334 static_cast< double >(pTargetSdrPage->GetUpperBorder()) / aPageSize.Height() +
335 static_cast< double >(pTargetSdrPage->GetLowerBorder()) / aPageSize.Height()) / 4.0);
336 const long nAllBorder(basegfx::fround((aGraphicSize.Width() + aGraphicSize.Height()) * fBorderRelation * 0.5));
338 // Adapt PageSize and Border stuff. To get all MasterPages and PresObjs
339 // correctly adapted, do not just use
340 // pTargetSdrPage->SetBorder(...) and
341 // pTargetSdrPage->SetSize(...),
342 // but ::adaptSizeAndBorderForAllPages
343 // Do use original Size and borders to get as close to original
344 // as possible for better turn-arounds.
345 pTargetSdrPage->getSdrModelFromSdrPage().adaptSizeAndBorderForAllPages(
346 Size(
347 aGraphicSize.Width(),
348 aGraphicSize.Height()),
349 nAllBorder,
350 nAllBorder,
351 nAllBorder,
352 nAllBorder);
354 // tdf#118232 set pos/size at SdrGraphicObj - use zero position for
355 // better turn-around results
356 if(!bContainsNoGeometry)
358 aNewSdrGrafObj->SetSnapRect(
359 tools::Rectangle(
360 Point(0, 0),
361 aGraphicSize));
363 // insert to page (owner change of SdrGrafObj)
364 pTargetSdrPage->InsertObject(aNewSdrGrafObj.release());
367 // done - set positive result now
368 bRet = true;
370 // always leave helper endless loop
371 break;
374 else if( mxSrcDoc.is() )
376 // #i124608# detect selection
377 bool bSelectionOnly = false;
378 bool bGotSelection = false;
380 // when using LibreOfficeKit, default to exporting everything (-1)
381 bool bPageProvided = comphelper::LibreOfficeKit::isActive();
382 sal_Int32 nPageToExport = -1;
384 for (sal_Int32 nInd = 0; nInd < rDescriptor.getLength(); nInd++)
386 if (rDescriptor[nInd].Name == "SelectionOnly")
388 // #i124608# extract single selection wanted from dialog return values
389 rDescriptor[nInd].Value >>= bSelectionOnly;
390 bPageProvided = false;
392 else if (rDescriptor[nInd].Name == "PagePos")
394 rDescriptor[nInd].Value >>= nPageToExport;
395 bPageProvided = true;
399 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
400 uno::Reference<frame::XController > xController;
401 if (xDesktop->getCurrentFrame().is() && !bPageProvided) // Manage headless case
403 uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
404 xController.set(xFrame->getController(), uno::UNO_SET_THROW);
405 uno::Reference<drawing::XDrawView> xDrawView(xController, uno::UNO_QUERY_THROW);
406 uno::Reference<drawing::framework::XControllerManager> xManager(xController, uno::UNO_QUERY_THROW);
407 uno::Reference<drawing::framework::XConfigurationController> xConfigController(xManager->getConfigurationController());
409 // which view configuration are we in?
411 // * traverse Impress resources to find slide preview pane, grab selection from there
412 // * otherwise, fallback to current slide
414 uno::Sequence<uno::Reference<drawing::framework::XResourceId> > aResIds(
415 xConfigController->getCurrentConfiguration()->getResources(
416 uno::Reference<drawing::framework::XResourceId>(),
418 drawing::framework::AnchorBindingMode_INDIRECT));
420 for( sal_Int32 i=0; i<aResIds.getLength(); ++i )
422 // can we somehow obtain the slidesorter from the Impress framework?
423 if( aResIds[i]->getResourceURL() == "private:resource/view/SlideSorter" )
425 // got it, grab current selection from there
426 uno::Reference<drawing::framework::XResource> xRes(
427 xConfigController->getResource(aResIds[i]));
429 uno::Reference< view::XSelectionSupplier > xSelectionSupplier(
430 xRes,
431 uno::UNO_QUERY );
432 if( xSelectionSupplier.is() )
434 uno::Any aSelection = xSelectionSupplier->getSelection();
435 if( aSelection.hasValue() )
437 Sequence< Reference< XInterface > > aSelectedPageSequence;
438 aSelection >>= aSelectedPageSequence;
439 mSelectedPages.resize( aSelectedPageSequence.getLength() );
440 for( size_t j=0; j<mSelectedPages.size(); ++j )
442 uno::Reference< drawing::XDrawPage > xDrawPage( aSelectedPageSequence[j],
443 uno::UNO_QUERY );
444 mSelectedPages[j] = xDrawPage;
447 // and stop looping. It is likely not getting better
448 break;
454 if( mSelectedPages.empty() )
456 // apparently failed to clean selection - fallback to current page
457 mSelectedPages.resize( 1 );
458 mSelectedPages[0] = xDrawView->getCurrentPage();
463 * Export all slides, or requested "PagePos"
465 if( mSelectedPages.empty() )
467 uno::Reference< drawing::XMasterPagesSupplier > xMasterPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
468 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
470 if( xMasterPagesSupplier.is() && xDrawPagesSupplier.is() )
472 uno::Reference< drawing::XDrawPages > xMasterPages = xMasterPagesSupplier->getMasterPages();
473 uno::Reference< drawing::XDrawPages > xDrawPages = xDrawPagesSupplier->getDrawPages();
474 if( xMasterPages.is() && xMasterPages->getCount() &&
475 xDrawPages.is() && xDrawPages->getCount() )
477 sal_Int32 nDPCount = xDrawPages->getCount();
479 mSelectedPages.resize( nPageToExport != -1 ? 1 : nDPCount );
480 sal_Int32 i;
481 for( i = 0; i < nDPCount; ++i )
483 if( nPageToExport != -1 && nPageToExport == i )
485 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
486 mSelectedPages[0] = xDrawPage;
488 else
490 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex( i ), uno::UNO_QUERY );
491 mSelectedPages[i] = xDrawPage;
498 if (bSelectionOnly)
500 // #i124608# when selection only is wanted, get the current object selection
501 // from the DrawView
502 Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
504 if (xSelection.is())
506 bGotSelection
507 = ( xSelection->getSelection() >>= maShapeSelection );
511 if(bSelectionOnly && bGotSelection && 0 == maShapeSelection->getCount())
513 // #i124608# export selection, got maShapeSelection but no shape selected -> nothing
514 // to export, we are done (maybe return true, but a hint that nothing was done
515 // may be useful; it may have happened by error)
516 bRet = false;
518 else
521 * We get all master page that are targeted by at least one draw page.
522 * The master page are put in an unordered set.
524 ObjectSet aMasterPageTargetSet;
525 for(const uno::Reference<drawing::XDrawPage> & mSelectedPage : mSelectedPages)
527 uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mSelectedPage, uno::UNO_QUERY );
528 if( xMasterPageTarget.is() )
530 aMasterPageTargetSet.insert( xMasterPageTarget->getMasterPage() );
533 // Later we move them to an uno::Sequence so we can get them by index
534 mMasterPageTargets.resize( aMasterPageTargetSet.size() );
535 sal_Int32 i = 0;
536 for (auto const& masterPageTarget : aMasterPageTargetSet)
538 uno::Reference< drawing::XDrawPage > xMasterPage( masterPageTarget, uno::UNO_QUERY );
539 mMasterPageTargets[i++] = xMasterPage;
542 bRet = implExport( rDescriptor );
545 else
546 bRet = false;
548 if( pFocusWindow )
549 pFocusWindow->LeaveWait();
551 return bRet;
554 bool SVGFilter::filterWriterOrCalc( const Sequence< PropertyValue >& rDescriptor )
556 bool bSelectionOnly = false;
558 for (sal_Int32 nInd = 0; nInd < rDescriptor.getLength(); nInd++)
560 if (rDescriptor[nInd].Name == "SelectionOnly")
562 rDescriptor[nInd].Value >>= bSelectionOnly;
563 break;
567 if(!bSelectionOnly) // For Writer only the selection-only mode is supported
568 return false;
570 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(mxContext));
571 uno::Reference<frame::XController > xController;
572 if (xDesktop->getCurrentFrame().is())
574 uno::Reference<frame::XFrame> xFrame(xDesktop->getCurrentFrame(), uno::UNO_SET_THROW);
575 xController.set(xFrame->getController(), uno::UNO_SET_THROW);
578 Reference< view::XSelectionSupplier > xSelection (xController, UNO_QUERY);
579 if (!xSelection.is())
580 return false;
582 // Select only one draw page
583 uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxSrcDoc, uno::UNO_QUERY );
584 uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
585 uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );
586 mSelectedPages.resize( 1 );
587 mSelectedPages[0] = xDrawPage;
589 bool bGotSelection = xSelection->getSelection() >>= maShapeSelection;
591 if (!bGotSelection)
593 if (mbWriterFilter)
595 // For Writer we might have a non-shape graphic
596 bGotSelection = implExportWriterTextGraphic(xSelection);
598 if (!bGotSelection)
599 return false;
602 return implExport( rDescriptor );
605 void SAL_CALL SVGFilter::cancel( )
609 void SAL_CALL SVGFilter::setSourceDocument( const Reference< XComponent >& xDoc )
611 mxSrcDoc = xDoc;
614 void SAL_CALL SVGFilter::setTargetDocument( const Reference< XComponent >& xDoc )
616 mxDstDoc = xDoc;
619 // There is already another SVG-Type_Detector, see
620 // vcl/source/filter/graphicfilter.cxx ("DOCTYPE svg"),
621 // but since these start from different preconditions it is not
622 // easy to unify these. For now, use this local helper.
623 class SVGFileInfo
625 private:
626 const uno::Reference<io::XInputStream>& mxInput;
627 uno::Sequence< sal_Int8 > mnFirstBytes;
628 sal_Int32 mnFirstBytesSize;
629 sal_Int32 mnFirstRead;
630 bool mbProcessed;
631 bool mbIsSVG;
633 bool impCheckForMagic(
634 const sal_Int8* pMagic,
635 const sal_Int32 nMagicSize)
637 const sal_Int8* pBuffer(mnFirstBytes.getConstArray());
638 return std::search(
639 pBuffer,
640 pBuffer + mnFirstRead,
641 pMagic,
642 pMagic + nMagicSize) != pBuffer + mnFirstRead;
645 void impEnsureProcessed()
647 if(mbProcessed)
649 return;
652 mbProcessed = true;
654 if(!mxInput.is())
656 return;
659 if(0 == mnFirstBytesSize)
661 return;
664 mnFirstBytes.realloc(mnFirstBytesSize);
666 if(mnFirstBytesSize != mnFirstBytes.getLength())
668 return;
671 std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(mxInput, true));
673 if (!aStream)
675 return;
678 const sal_uLong nStreamLen(aStream->remainingSize());
680 if(aStream->GetError())
682 return;
685 mnFirstRead = aStream->ReadBytes(
686 &mnFirstBytes[0],
687 std::min(nStreamLen, sal_uLong(mnFirstBytesSize)));
689 if(aStream->GetError())
691 return;
694 // check if it is gzipped -> svgz
695 if (mnFirstBytes[0] == 0x1F && static_cast<sal_uInt8>(mnFirstBytes[1]) == 0x8B)
697 ZCodec aCodec;
699 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
700 mnFirstRead = aCodec.Read(
701 *aStream,
702 reinterpret_cast< sal_uInt8* >(mnFirstBytes.getArray()),
703 mnFirstBytesSize);
704 aCodec.EndCompression();
706 if (mnFirstRead < 0)
707 return;
710 if(!mbIsSVG)
712 const sal_Int8 aMagic[] = {'<', 's', 'v', 'g'};
713 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
715 mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
718 if(!mbIsSVG)
720 const sal_Int8 aMagic[] = {'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 's', 'v', 'g'};
721 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
723 mbIsSVG = impCheckForMagic(aMagic, nMagicSize);
726 return;
729 public:
730 SVGFileInfo(
731 const uno::Reference<io::XInputStream>& xInput)
732 : mxInput(xInput),
733 mnFirstBytes(),
734 mnFirstBytesSize(2048),
735 mnFirstRead(0),
736 mbProcessed(false),
737 mbIsSVG(false)
739 // For the default buffer size: Use not too big
740 // (not more than 16K), but also not too small
741 // (not less than 1/2K), see comments at
742 // ImpPeekGraphicFormat, SVG section.
743 // I remember these cases and it *can* happen
744 // that SVGs have quite massive comments in their
745 // headings (!)
746 // Limit to plausible sizes, also for security reasons
747 mnFirstBytesSize = std::min(sal_Int32(512), mnFirstBytesSize);
748 mnFirstBytesSize = std::max(sal_Int32(16384), mnFirstBytesSize);
751 bool isSVG()
753 impEnsureProcessed();
755 return mbIsSVG;
758 bool isOwnFormat()
760 impEnsureProcessed();
762 if(mbIsSVG)
764 // xmlns:ooo
765 const sal_Int8 aMagic[] = {'x', 'm', 'l', 'n', 's', ':', 'o', 'o', 'o'};
766 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
768 return impCheckForMagic(aMagic, nMagicSize);
771 return false;
774 bool isImpress()
776 impEnsureProcessed();
778 if(mbIsSVG)
780 // ooo:meta_slides
781 const sal_Int8 aMagic[] = {'o', 'o', 'o', ':', 'm', 'e', 't', 'a', '_', 's', 'l', 'i', 'd', 'e', 's'};
782 const sal_Int32 nMagicSize(SAL_N_ELEMENTS(aMagic));
784 return impCheckForMagic(aMagic, nMagicSize);
787 return false;
791 OUString SAL_CALL SVGFilter::detect(Sequence<PropertyValue>& rDescriptor)
793 utl::MediaDescriptor aMediaDescriptor(rDescriptor);
794 uno::Reference<io::XInputStream> xInput(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM()], UNO_QUERY);
795 OUString aRetval;
797 if (!xInput.is())
799 return aRetval;
804 SVGFileInfo aSVGFileInfo(xInput);
806 if(aSVGFileInfo.isSVG())
808 // We have SVG - set default document format to Draw
809 aRetval = OUString(constFilterNameDraw);
811 if(aSVGFileInfo.isOwnFormat())
813 // it's a file that was written/exported by LO
814 if(aSVGFileInfo.isImpress())
816 // it was written by Impress export. Set document
817 // format for import to Impress
818 aRetval = OUString(constFilterName);
823 catch (css::io::IOException &)
825 TOOLS_WARN_EXCEPTION("filter.svg", "");
828 return aRetval;
831 #define SVG_FILTER_IMPL_NAME "com.sun.star.comp.Draw.SVGFilter"
832 #define SVG_WRITER_IMPL_NAME "com.sun.star.comp.Draw.SVGWriter"
834 namespace sdecl = comphelper::service_decl;
835 sdecl::class_<SVGFilter> const serviceFilterImpl;
836 const sdecl::ServiceDecl svgFilter(
837 serviceFilterImpl,
838 SVG_FILTER_IMPL_NAME,
839 "com.sun.star.document.ImportFilter;"
840 "com.sun.star.document.ExportFilter;"
841 "com.sun.star.document.ExtendedTypeDetection" );
843 sdecl::class_<SVGWriter, sdecl::with_args<true> > const serviceWriterImpl;
844 const sdecl::ServiceDecl svgWriter(
845 serviceWriterImpl,
846 SVG_WRITER_IMPL_NAME,
847 "com.sun.star.svg.SVGWriter" );
849 // The C shared lib entry points
850 extern "C" SAL_DLLPUBLIC_EXPORT void* svgfilter_component_getFactory(
851 sal_Char const* pImplName, void*, void*)
853 if ( rtl_str_compare (pImplName, SVG_FILTER_IMPL_NAME) == 0 )
855 return sdecl::component_getFactoryHelper( pImplName, {&svgFilter} );
857 else if ( rtl_str_compare (pImplName, SVG_WRITER_IMPL_NAME) == 0 )
859 return sdecl::component_getFactoryHelper( pImplName, {&svgWriter} );
861 return nullptr;
864 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */