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 .
21 #include <com/sun/star/io/XOutputStream.hpp>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/container/XChild.hpp>
24 #include <com/sun/star/lang/XServiceInfo.hpp>
25 #include <com/sun/star/lang/XComponent.hpp>
26 #include <com/sun/star/drawing/XShape.hpp>
27 #include <com/sun/star/drawing/XDrawPage.hpp>
28 #include <com/sun/star/drawing/XGraphicExportFilter.hpp>
29 #include <com/sun/star/graphic/XGraphic.hpp>
30 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
31 #include <com/sun/star/task/XStatusIndicator.hpp>
32 #include <com/sun/star/task/XInteractionHandler.hpp>
33 #include <com/sun/star/task/XInteractionContinuation.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
36 #include <boost/property_tree/json_parser/error.hpp>
37 #include <tools/debug.hxx>
38 #include <comphelper/diagnose_ex.hxx>
39 #include <tools/urlobj.hxx>
40 #include <comphelper/interaction.hxx>
41 #include <framework/interaction.hxx>
42 #include <com/sun/star/drawing/GraphicFilterRequest.hpp>
43 #include <com/sun/star/util/URL.hpp>
44 #include <cppuhelper/implbase.hxx>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <vcl/metaact.hxx>
47 #include <vcl/svapp.hxx>
48 #include <vcl/virdev.hxx>
49 #include <svl/outstrm.hxx>
50 #include <sdr/contact/objectcontactofobjlistpainter.hxx>
51 #include <svx/sdr/contact/viewobjectcontact.hxx>
52 #include <svx/sdr/contact/viewcontact.hxx>
53 #include <svx/sdr/contact/displayinfo.hxx>
54 #include <editeng/numitem.hxx>
55 #include <svx/svdograf.hxx>
56 #include <svx/xoutbmp.hxx>
57 #include <vcl/graphicfilter.hxx>
58 #include <svx/svdpage.hxx>
59 #include <svx/svdmodel.hxx>
60 #include <svx/fmview.hxx>
61 #include <svx/fmmodel.hxx>
62 #include <svx/unopage.hxx>
63 #include <svx/svdoutl.hxx>
64 #include <svx/xlineit0.hxx>
65 #include <editeng/flditem.hxx>
66 #include <svtools/optionsdrawinglayer.hxx>
67 #include <comphelper/sequenceashashmap.hxx>
68 #include <comphelper/propertysequence.hxx>
69 #include <comphelper/sequence.hxx>
70 #include <UnoGraphicExporter.hxx>
73 #include <editeng/editstat.hxx>
75 #define MAX_EXT_PIX 2048
77 using namespace ::comphelper
;
78 using namespace ::cppu
;
79 using namespace ::com::sun::star
;
80 using namespace ::com::sun::star::uno
;
81 using namespace ::com::sun::star::util
;
82 using namespace ::com::sun::star::container
;
83 using namespace ::com::sun::star::drawing
;
84 using namespace ::com::sun::star::lang
;
85 using namespace ::com::sun::star::beans
;
86 using namespace ::com::sun::star::task
;
92 OUString maFilterName
;
95 css::uno::Reference
< css::io::XOutputStream
> mxOutputStream
;
96 css::uno::Reference
< css::graphic::XGraphicRenderer
> mxGraphicRenderer
;
97 css::uno::Reference
< css::task::XStatusIndicator
> mxStatusIndicator
;
98 css::uno::Reference
< css::task::XInteractionHandler
> mxInteractionHandler
;
102 bool mbExportOnlyBackground
;
104 bool mbUseHighContrast
;
107 Sequence
< PropertyValue
> maFilterData
;
112 TriState meAntiAliasing
= TRISTATE_INDET
;
114 explicit ExportSettings();
117 ExportSettings::ExportSettings()
120 ,mbExportOnlyBackground( false )
121 ,mbScrollText( false )
122 ,mbUseHighContrast( false )
123 ,mbTranslucent( false )
129 /** implements a component to export shapes or pages to external graphic formats.
131 @implements com.sun.star.drawing.GraphicExportFilter
133 class GraphicExporter
: public ::cppu::WeakImplHelper
< XGraphicExportFilter
, XServiceInfo
>
139 virtual sal_Bool SAL_CALL
filter( const Sequence
< PropertyValue
>& aDescriptor
) override
;
140 virtual void SAL_CALL
cancel( ) override
;
143 virtual void SAL_CALL
setSourceDocument( const Reference
< XComponent
>& xDoc
) override
;
146 virtual OUString SAL_CALL
getImplementationName( ) override
;
147 virtual sal_Bool SAL_CALL
supportsService( const OUString
& ServiceName
) override
;
148 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames( ) override
;
151 virtual sal_Bool SAL_CALL
supportsMimeType( const OUString
& MimeTypeName
) override
;
152 virtual Sequence
< OUString
> SAL_CALL
getSupportedMimeTypeNames( ) override
;
154 VclPtr
<VirtualDevice
> CreatePageVDev( SdrPage
* pPage
, tools::Long nWidthPixel
, tools::Long nHeightPixel
) const;
156 DECL_LINK( CalcFieldValueHdl
, EditFieldInfo
*, void );
158 void ParseSettings( const Sequence
< PropertyValue
>& aDescriptor
, ExportSettings
& rSettings
);
159 bool GetGraphic( ExportSettings
const & rSettings
, Graphic
& aGraphic
, bool bVectorType
);
162 Reference
< XShape
> mxShape
;
163 Reference
< XDrawPage
> mxPage
;
164 Reference
< XShapes
> mxShapes
;
167 SvxDrawPage
* mpUnoPage
;
169 Link
<EditFieldInfo
*,void> maOldCalcFieldValueHdl
;
170 sal_Int32 mnPageNumber
;
171 SdrPage
* mpCurrentPage
;
175 Size
* CalcSize( sal_Int32 nWidth
, sal_Int32 nHeight
, const Size
& aBoundSize
, Size
& aOutSize
)
177 if( (nWidth
== 0) && (nHeight
== 0) )
180 if( (nWidth
== 0) && (nHeight
!= 0) && (aBoundSize
.Height() != 0) )
182 nWidth
= ( nHeight
* aBoundSize
.Width() ) / aBoundSize
.Height();
184 else if( (nWidth
!= 0) && (nHeight
== 0) && (aBoundSize
.Width() != 0) )
186 nHeight
= ( nWidth
* aBoundSize
.Height() ) / aBoundSize
.Width();
189 aOutSize
.setWidth( nWidth
);
190 aOutSize
.setHeight( nHeight
);
195 class ImplExportCheckVisisbilityRedirector
: public sdr::contact::ViewObjectContactRedirector
198 explicit ImplExportCheckVisisbilityRedirector( SdrPage
* pCurrentPage
);
200 virtual void createRedirectedPrimitive2DSequence(
201 const sdr::contact::ViewObjectContact
& rOriginal
,
202 const sdr::contact::DisplayInfo
& rDisplayInfo
,
203 drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
) override
;
206 SdrPage
* mpCurrentPage
;
209 ImplExportCheckVisisbilityRedirector::ImplExportCheckVisisbilityRedirector( SdrPage
* pCurrentPage
)
210 : mpCurrentPage( pCurrentPage
)
214 void ImplExportCheckVisisbilityRedirector::createRedirectedPrimitive2DSequence(
215 const sdr::contact::ViewObjectContact
& rOriginal
,
216 const sdr::contact::DisplayInfo
& rDisplayInfo
,
217 drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
)
219 SdrObject
* pObject
= rOriginal
.GetViewContact().TryToGetSdrObject();
223 SdrPage
* pPage
= mpCurrentPage
;
227 pPage
= pObject
->getSdrPageFromSdrObject();
230 if( (pPage
== nullptr) || pPage
->checkVisibility(rOriginal
, rDisplayInfo
, false) )
232 return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal
, rDisplayInfo
, rVisitor
);
239 // not an object, maybe a page
240 sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal
, rDisplayInfo
, rVisitor
);
244 GraphicExporter::GraphicExporter()
245 : mpUnoPage( nullptr ), mnPageNumber(-1), mpCurrentPage(nullptr), mpDoc( nullptr )
249 IMPL_LINK(GraphicExporter
, CalcFieldValueHdl
, EditFieldInfo
*, pInfo
, void)
255 pInfo
->SetSdrPage( mpCurrentPage
);
257 else if( mnPageNumber
!= -1 )
259 const SvxFieldData
* pField
= pInfo
->GetField().GetField();
260 if( dynamic_cast<const SvxPageField
*>( pField
) )
262 OUString aPageNumValue
;
265 switch(mpDoc
->GetPageNumType())
267 case css::style::NumberingType::CHARS_UPPER_LETTER
:
268 aPageNumValue
+= OUStringChar( sal_Unicode((mnPageNumber
- 1) % 26 + 'A') );
270 case css::style::NumberingType::CHARS_LOWER_LETTER
:
271 aPageNumValue
+= OUStringChar( sal_Unicode((mnPageNumber
- 1) % 26 + 'a') );
273 case css::style::NumberingType::ROMAN_UPPER
:
276 case css::style::NumberingType::ROMAN_LOWER
:
277 aPageNumValue
+= SvxNumberFormat::CreateRomanString(mnPageNumber
, bUpper
);
279 case css::style::NumberingType::NUMBER_NONE
:
283 aPageNumValue
+= OUString::number( mnPageNumber
);
286 pInfo
->SetRepresentation( aPageNumValue
);
293 maOldCalcFieldValueHdl
.Call( pInfo
);
295 if( pInfo
&& mpCurrentPage
)
296 pInfo
->SetSdrPage( nullptr );
299 /** creates a virtual device for the given page
301 @return the returned VirtualDevice is owned by the caller
303 VclPtr
<VirtualDevice
> GraphicExporter::CreatePageVDev( SdrPage
* pPage
, tools::Long nWidthPixel
, tools::Long nHeightPixel
) const
305 VclPtr
<VirtualDevice
> pVDev
= VclPtr
<VirtualDevice
>::Create();
306 MapMode
aMM( MapUnit::Map100thMM
);
308 Point
aPoint( 0, 0 );
309 Size
aPageSize(pPage
->GetSize());
312 if( nWidthPixel
!= 0 )
314 const Fraction
aFrac( nWidthPixel
, pVDev
->LogicToPixel( aPageSize
, aMM
).Width() );
316 aMM
.SetScaleX( aFrac
);
318 if( nHeightPixel
== 0 )
319 aMM
.SetScaleY( aFrac
);
322 if( nHeightPixel
!= 0 )
324 const Fraction
aFrac( nHeightPixel
, pVDev
->LogicToPixel( aPageSize
, aMM
).Height() );
326 if( nWidthPixel
== 0 )
327 aMM
.SetScaleX( aFrac
);
329 aMM
.SetScaleY( aFrac
);
332 pVDev
->SetMapMode( aMM
);
333 bool bSuccess(false);
335 // #i122820# If available, use pixel size directly
336 if(nWidthPixel
&& nHeightPixel
)
338 bSuccess
= pVDev
->SetOutputSizePixel(Size(nWidthPixel
, nHeightPixel
));
342 bSuccess
= pVDev
->SetOutputSize(aPageSize
);
347 SdrView
aView(*mpDoc
, pVDev
);
349 aView
.SetPageVisible( false );
350 aView
.SetBordVisible( false );
351 aView
.SetGridVisible( false );
352 aView
.SetHlplVisible( false );
353 aView
.SetGlueVisible( false );
354 aView
.ShowSdrPage(pPage
);
356 vcl::Region
aRegion (tools::Rectangle( aPoint
, aPageSize
) );
358 ImplExportCheckVisisbilityRedirector
aRedirector( mpCurrentPage
);
360 aView
.CompleteRedraw(pVDev
, aRegion
, &aRedirector
);
364 OSL_ENSURE(false, "Could not get a VirtualDevice of requested size (!)");
370 void GraphicExporter::ParseSettings(const Sequence
<PropertyValue
>& rDescriptor
,
371 ExportSettings
& rSettings
)
373 Sequence
<PropertyValue
> aDescriptor
= rDescriptor
;
374 if (aDescriptor
.hasElements())
376 comphelper::SequenceAsHashMap
aMap(aDescriptor
);
377 Sequence
<PropertyValue
> aFilterData
;
378 OUString aFilterOptions
;
379 auto it
= aMap
.find(u
"FilterData"_ustr
);
380 if (it
!= aMap
.end())
382 it
->second
>>= aFilterData
;
384 it
= aMap
.find(u
"FilterOptions"_ustr
);
385 if (it
!= aMap
.end())
387 it
->second
>>= aFilterOptions
;
389 if (!aFilterData
.hasElements() && !aFilterOptions
.isEmpty())
391 // Allow setting filter data keys from the cmdline.
394 std::vector
<PropertyValue
> aData
395 = comphelper::JsonToPropertyValues(aFilterOptions
.toUtf8());
396 aFilterData
= comphelper::containerToSequence(aData
);
398 catch (const boost::property_tree::json_parser::json_parser_error
&)
400 // This wasn't a valid json; maybe came from import filter (tdf#162528)
402 if (aFilterData
.hasElements())
404 aMap
[u
"FilterData"_ustr
] <<= aFilterData
;
405 aDescriptor
= aMap
.getAsConstPropertyValueList();
410 for( const PropertyValue
& rValue
: aDescriptor
)
412 if ( rValue
.Name
== "FilterName" )
414 rValue
.Value
>>= rSettings
.maFilterName
;
416 else if ( rValue
.Name
== "MediaType" )
418 rValue
.Value
>>= rSettings
.maMediaType
;
420 else if ( rValue
.Name
== "URL" )
422 if( !( rValue
.Value
>>= rSettings
.maURL
) )
424 rValue
.Value
>>= rSettings
.maURL
.Complete
;
427 else if ( rValue
.Name
== "OutputStream" )
429 rValue
.Value
>>= rSettings
.mxOutputStream
;
431 else if ( rValue
.Name
== "GraphicRenderer" )
433 rValue
.Value
>>= rSettings
.mxGraphicRenderer
;
435 else if ( rValue
.Name
== "StatusIndicator" )
437 rValue
.Value
>>= rSettings
.mxStatusIndicator
;
439 else if ( rValue
.Name
== "InteractionHandler" )
441 rValue
.Value
>>= rSettings
.mxInteractionHandler
;
443 else if( rValue
.Name
== "Width" ) // for compatibility reasons, deprecated
445 rValue
.Value
>>= rSettings
.mnWidth
;
447 else if( rValue
.Name
== "Height" ) // for compatibility reasons, deprecated
449 rValue
.Value
>>= rSettings
.mnHeight
;
451 else if( rValue
.Name
== "ExportOnlyBackground" ) // for compatibility reasons, deprecated
453 rValue
.Value
>>= rSettings
.mbExportOnlyBackground
;
455 else if ( rValue
.Name
== "FilterData" )
457 rValue
.Value
>>= rSettings
.maFilterData
;
459 for( PropertyValue
& rDataValue
: asNonConstRange(rSettings
.maFilterData
) )
461 if ( rDataValue
.Name
== "Translucent" )
463 if ( !( rDataValue
.Value
>>= rSettings
.mbTranslucent
) ) // SJ: TODO: The GIF Transparency is stored as int32 in
464 { // configuration files, this has to be changed to boolean
465 sal_Int32 nTranslucent
= 0;
466 if ( rDataValue
.Value
>>= nTranslucent
)
467 rSettings
.mbTranslucent
= nTranslucent
!= 0;
470 else if ( rDataValue
.Name
== "PixelWidth" )
472 rDataValue
.Value
>>= rSettings
.mnWidth
;
474 else if ( rDataValue
.Name
== "PixelHeight" )
476 rDataValue
.Value
>>= rSettings
.mnHeight
;
478 else if( rDataValue
.Name
== "Width" ) // for compatibility reasons, deprecated
480 rDataValue
.Value
>>= rSettings
.mnWidth
;
481 rDataValue
.Name
= "PixelWidth";
483 else if( rDataValue
.Name
== "Height" ) // for compatibility reasons, deprecated
485 rDataValue
.Value
>>= rSettings
.mnHeight
;
486 rDataValue
.Name
= "PixelHeight";
488 else if ( rDataValue
.Name
== "ExportOnlyBackground" )
490 rDataValue
.Value
>>= rSettings
.mbExportOnlyBackground
;
492 else if ( rDataValue
.Name
== "HighContrast" )
494 rDataValue
.Value
>>= rSettings
.mbUseHighContrast
;
496 else if ( rDataValue
.Name
== "PageNumber" )
498 rDataValue
.Value
>>= mnPageNumber
;
500 else if ( rDataValue
.Name
== "ScrollText" )
502 // #110496# Read flag solitary scroll text metafile
503 rDataValue
.Value
>>= rSettings
.mbScrollText
;
505 else if ( rDataValue
.Name
== "CurrentPage" )
507 Reference
< XDrawPage
> xPage
;
508 rDataValue
.Value
>>= xPage
;
511 SvxDrawPage
* pUnoPage
= comphelper::getFromUnoTunnel
<SvxDrawPage
>( xPage
);
512 if( pUnoPage
&& pUnoPage
->GetSdrPage() )
513 mpCurrentPage
= pUnoPage
->GetSdrPage();
516 else if ( rDataValue
.Name
== "ScaleXNumerator" )
519 if( rDataValue
.Value
>>= nVal
)
520 rSettings
.maScaleX
= Fraction( nVal
, rSettings
.maScaleX
.GetDenominator() );
522 else if ( rDataValue
.Name
== "ScaleXDenominator" )
525 if( rDataValue
.Value
>>= nVal
)
526 rSettings
.maScaleX
= Fraction( rSettings
.maScaleX
.GetNumerator(), nVal
);
528 else if ( rDataValue
.Name
== "ScaleYNumerator" )
531 if( rDataValue
.Value
>>= nVal
)
532 rSettings
.maScaleY
= Fraction( nVal
, rSettings
.maScaleY
.GetDenominator() );
534 else if ( rDataValue
.Name
== "ScaleYDenominator" )
537 if( rDataValue
.Value
>>= nVal
)
538 rSettings
.maScaleY
= Fraction( rSettings
.maScaleY
.GetNumerator(), nVal
);
540 else if (rDataValue
.Name
== "AntiAliasing")
543 if (rDataValue
.Value
>>= bAntiAliasing
)
544 rSettings
.meAntiAliasing
= bAntiAliasing
? TRISTATE_TRUE
: TRISTATE_FALSE
;
550 // putting the StatusIndicator that we got from the MediaDescriptor into our local FilterData copy
551 if ( rSettings
.mxStatusIndicator
.is() )
553 int i
= rSettings
.maFilterData
.getLength();
554 rSettings
.maFilterData
.realloc( i
+ 1 );
555 auto pFilterData
= rSettings
.maFilterData
.getArray();
556 pFilterData
[ i
].Name
= "StatusIndicator";
557 pFilterData
[ i
].Value
<<= rSettings
.mxStatusIndicator
;
561 bool GraphicExporter::GetGraphic( ExportSettings
const & rSettings
, Graphic
& aGraphic
, bool bVectorType
)
563 if( !mpDoc
|| !mpUnoPage
)
566 SdrPage
* pPage
= mpUnoPage
->GetSdrPage();
570 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
571 const MapMode
aMap( mpDoc
->GetScaleUnit(), Point(), rSettings
.maScaleX
, rSettings
.maScaleY
);
573 SdrOutliner
& rOutl
=mpDoc
->GetDrawOutliner();
574 maOldCalcFieldValueHdl
= rOutl
.GetCalcFieldValueHdl();
575 rOutl
.SetCalcFieldValueHdl( LINK(this, GraphicExporter
, CalcFieldValueHdl
) );
576 ::Color
aOldBackColor(rOutl
.GetBackgroundColor());
577 rOutl
.SetBackgroundColor(pPage
->GetPageBackgroundColor());
580 const EEControlBits
nOldCntrl(rOutl
.GetControlWord());
581 EEControlBits nCntrl
= nOldCntrl
& ~EEControlBits::ONLINESPELLING
;
582 rOutl
.SetControlWord(nCntrl
);
584 rtl::Reference
<SdrObject
> pTempBackgroundShape
;
585 std::vector
< SdrObject
* > aShapes
;
588 // export complete page?
591 if( rSettings
.mbExportOnlyBackground
)
593 const SdrPageProperties
* pCorrectProperties
= pPage
->getCorrectSdrPageProperties();
595 if(pCorrectProperties
)
597 pTempBackgroundShape
= new SdrRectObj(
599 tools::Rectangle(Point(0,0), pPage
->GetSize()));
600 pTempBackgroundShape
->SetMergedItemSet(pCorrectProperties
->GetItemSet());
601 pTempBackgroundShape
->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE
));
602 pTempBackgroundShape
->NbcSetStyleSheet(pCorrectProperties
->GetStyleSheet(), true);
603 aShapes
.push_back(pTempBackgroundShape
.get());
608 const Size
aSize( pPage
->GetSize() );
610 // generate a bitmap to convert it to a pixel format.
611 // For gif pictures there can also be a vector format used (bTranslucent)
612 if ( !bVectorType
&& !rSettings
.mbTranslucent
)
614 tools::Long nWidthPix
= 0;
615 tools::Long nHeightPix
= 0;
616 if ( rSettings
.mnWidth
> 0 && rSettings
.mnHeight
> 0 )
618 nWidthPix
= rSettings
.mnWidth
;
619 nHeightPix
= rSettings
.mnHeight
;
623 const Size
aSizePix( Application::GetDefaultDevice()->LogicToPixel( aSize
, aMap
) );
624 if (aSizePix
.Width() > MAX_EXT_PIX
|| aSizePix
.Height() > MAX_EXT_PIX
)
626 if (aSizePix
.Width() > MAX_EXT_PIX
)
627 nWidthPix
= MAX_EXT_PIX
;
629 nWidthPix
= aSizePix
.Width();
630 if (aSizePix
.Height() > MAX_EXT_PIX
)
631 nHeightPix
= MAX_EXT_PIX
;
633 nHeightPix
= aSizePix
.Height();
635 double fWidthDif
= static_cast<double>(aSizePix
.Width()) / nWidthPix
;
636 double fHeightDif
= static_cast<double>(aSizePix
.Height()) / nHeightPix
;
638 if (fWidthDif
> fHeightDif
)
639 nHeightPix
= static_cast<tools::Long
>(aSizePix
.Height() / fWidthDif
);
641 nWidthPix
= static_cast<tools::Long
>(aSizePix
.Width() / fHeightDif
);
645 nWidthPix
= aSizePix
.Width();
646 nHeightPix
= aSizePix
.Height();
650 std::unique_ptr
<SdrView
> xLocalView
;
652 if (FmFormModel
* pFormModel
= dynamic_cast<FmFormModel
*>(mpDoc
))
654 xLocalView
.reset(new FmFormView(*pFormModel
, aVDev
));
658 xLocalView
.reset(new SdrView(*mpDoc
, aVDev
));
661 ScopedVclPtr
<VirtualDevice
> pVDev(CreatePageVDev( pPage
, nWidthPix
, nHeightPix
));
665 aGraphic
= pVDev
->GetBitmapEx( Point(), pVDev
->GetOutputSize() );
666 aGraphic
.SetPrefMapMode( aMap
);
667 aGraphic
.SetPrefSize( aSize
);
670 // create a metafile to export a vector format
675 aVDev
->SetMapMode( aMap
);
676 if( rSettings
.mbUseHighContrast
)
677 aVDev
->SetDrawMode( aVDev
->GetDrawMode() | DrawModeFlags::SettingsLine
| DrawModeFlags::SettingsFill
| DrawModeFlags::SettingsText
| DrawModeFlags::SettingsGradient
);
678 aVDev
->EnableOutput( false );
679 aMtf
.Record( aVDev
);
683 std::unique_ptr
< SdrView
> pView
;
685 if (FmFormModel
*pFormModel
= dynamic_cast<FmFormModel
*>(mpDoc
))
687 pView
.reset(new FmFormView(*pFormModel
, aVDev
));
691 pView
.reset(new SdrView(*mpDoc
, aVDev
));
694 pView
->SetBordVisible( false );
695 pView
->SetPageVisible( false );
696 pView
->ShowSdrPage( pPage
);
698 // tdf#96922 deactivate EditView PageVisualization, including PageBackground
699 // (formerly 'wiese'). Do *not* switch off MasterPageVisualizationAllowed, we
700 // want MasterPage content if a whole SdrPage is exported
701 pView
->SetPageDecorationAllowed(false);
703 const Point
aNewOrg( pPage
->GetLeftBorder(), pPage
->GetUpperBorder() );
704 aNewSize
= Size( aSize
.Width() - pPage
->GetLeftBorder() - pPage
->GetRightBorder(),
705 aSize
.Height() - pPage
->GetUpperBorder() - pPage
->GetLowerBorder() );
706 const tools::Rectangle
aClipRect( aNewOrg
, aNewSize
);
707 MapMode
aVMap( aMap
);
710 aVMap
.SetOrigin( Point( -aNewOrg
.X(), -aNewOrg
.Y() ) );
711 aVDev
->SetRelativeMapMode( aVMap
);
712 aVDev
->IntersectClipRegion( aClipRect
);
714 // Use new StandardCheckVisisbilityRedirector
715 ImplExportCheckVisisbilityRedirector
aRedirector( mpCurrentPage
);
717 pView
->CompleteRedraw(aVDev
, vcl::Region(tools::Rectangle(aNewOrg
, aNewSize
)), &aRedirector
);
723 aMtf
.SetPrefMapMode( aMap
);
724 aMtf
.SetPrefSize( aNewSize
);
726 // AW: Here the current version was filtering out the MetaActionType::CLIPREGIONs
727 // from the metafile. I asked some other developers why this was done, but no
728 // one knew a direct reason. Since it's in for long time, it may be an old
729 // piece of code. MetaFiles save and load ClipRegions with polygons with preserving
730 // the polygons, so a resolution-independent roundtrip is supported. Removed this
731 // code since it destroys some MetaFiles where ClipRegions are used. Anyways,
732 // just filtering them out is a hack, at least the encapsulated content would need
733 // to be clipped geometrically.
734 aGraphic
= Graphic(aMtf
);
736 pView
->HideSdrPage();
738 if( rSettings
.mbTranslucent
)
741 aGraphic
= GetBitmapFromMetaFile( aGraphic
.GetGDIMetaFile(), CalcSize( rSettings
.mnWidth
, rSettings
.mnHeight
, aNewSize
, aOutSize
) );
747 // export only single shape or shape collection
750 // build list of SdrObject
753 Reference
< XShape
> xShape
;
754 const sal_Int32 nCount
= mxShapes
->getCount();
756 for( sal_Int32 nIndex
= 0; nIndex
< nCount
; nIndex
++ )
758 mxShapes
->getByIndex( nIndex
) >>= xShape
;
759 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(xShape
);
761 aShapes
.push_back( pObj
);
767 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(mxShape
);
769 aShapes
.push_back( pObj
);
772 if( aShapes
.empty() )
776 if( bRet
&& !aShapes
.empty() )
778 // special treatment for only one SdrGrafObj that has text
779 bool bSingleGraphic
= false;
781 if( 1 == aShapes
.size() )
785 if( auto pGrafObj
= dynamic_cast<const SdrGrafObj
*>(aShapes
.front()) )
786 if (pGrafObj
->HasText() )
788 aGraphic
= pGrafObj
->GetTransformedGraphic();
789 if ( aGraphic
.GetType() == GraphicType::Bitmap
)
791 Size
aSizePixel( aGraphic
.GetSizePixel() );
792 if( rSettings
.mnWidth
&& rSettings
.mnHeight
&&
793 ( ( rSettings
.mnWidth
!= aSizePixel
.Width() ) ||
794 ( rSettings
.mnHeight
!= aSizePixel
.Height() ) ) )
796 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
797 // export: use highest quality
798 aBmpEx
.Scale( Size( rSettings
.mnWidth
, rSettings
.mnHeight
), BmpScaleFlag::Lanczos
);
802 // #118804# only accept for bitmap graphics, else the
803 // conversion to bitmap will happen anywhere without size control
804 // as evtl. defined in rSettings.mnWidth/mnHeight
805 bSingleGraphic
= true;
809 else if( rSettings
.mbScrollText
)
811 SdrObject
* pObj
= aShapes
.front();
812 auto pTextObj
= DynCastSdrTextObj( pObj
);
813 if( pTextObj
&& pTextObj
->HasText() )
815 tools::Rectangle aScrollRectangle
;
816 tools::Rectangle aPaintRectangle
;
818 const std::unique_ptr
< GDIMetaFile
> pMtf(
819 pTextObj
->GetTextScrollMetaFileAndRectangle(
820 aScrollRectangle
, aPaintRectangle
) );
822 // take the larger one of the two rectangles (that
823 // should be the bound rect of the retrieved
825 tools::Rectangle aTextRect
;
827 if( aScrollRectangle
.Contains( aPaintRectangle
) )
828 aTextRect
= aScrollRectangle
;
830 aTextRect
= aPaintRectangle
;
832 // setup pref size and mapmode
833 pMtf
->SetPrefSize( aTextRect
.GetSize() );
835 // set actual origin (mtf is at actual shape
837 MapMode
aLocalMapMode( aMap
);
838 aLocalMapMode
.SetOrigin(
839 Point( -aPaintRectangle
.Left(),
840 -aPaintRectangle
.Top() ) );
841 pMtf
->SetPrefMapMode( aLocalMapMode
);
843 pMtf
->AddAction( new MetaCommentAction(
844 "XTEXT_SCROLLRECT"_ostr
, 0,
845 reinterpret_cast<sal_uInt8
const*>(&aScrollRectangle
),
846 sizeof( tools::Rectangle
) ) );
847 pMtf
->AddAction( new MetaCommentAction(
848 "XTEXT_PAINTRECT"_ostr
, 0,
849 reinterpret_cast<sal_uInt8
const*>(&aPaintRectangle
),
850 sizeof( tools::Rectangle
) ) );
852 aGraphic
= Graphic( *pMtf
);
854 bSingleGraphic
= true;
859 if( !bSingleGraphic
)
861 // create a metafile for all shapes
862 ScopedVclPtrInstance
< VirtualDevice
> aOut
;
864 // calculate bound rect for all shapes
865 // tdf#126319 I did not convert all rendering to primitives,
866 // that would be too much for this fix. But I did so for the
867 // range calculation to get a valid high quality range.
868 // Based on that the conversion is reliable. With the BoundRect
869 // fetched from the Metafile it was just not possible to get the
870 // examples from the task handled in a way to fit all cases -
871 // due to bad-quality range data from it.
872 basegfx::B2DRange aBound
;
873 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
876 for( SdrObject
* pObj
: aShapes
)
878 drawinglayer::primitive2d::Primitive2DContainer aSequence
;
879 pObj
->GetViewContact().getViewIndependentPrimitive2DContainer(aSequence
);
880 aBound
.expand(aSequence
.getB2DRange(aViewInformation2D
));
884 aOut
->EnableOutput( false );
885 aOut
->SetMapMode( aMap
);
886 if( rSettings
.mbUseHighContrast
)
887 aOut
->SetDrawMode( aOut
->GetDrawMode() | DrawModeFlags::SettingsLine
| DrawModeFlags::SettingsFill
| DrawModeFlags::SettingsText
| DrawModeFlags::SettingsGradient
);
893 MapMode
aOutMap( aMap
);
894 const Size
aOnePixelInMtf(
895 Application::GetDefaultDevice()->PixelToLogic(
898 const Size
aHalfPixelInMtf(
899 (aOnePixelInMtf
.getWidth() + 1) / 2,
900 (aOnePixelInMtf
.getHeight() + 1) / 2);
902 // tdf#126319 Immediately add needed offset to create metafile,
903 // that avoids to do it later by Metafile::Move what would be expensive
906 basegfx::fround
<tools::Long
>(-aBound
.getMinX() - aHalfPixelInMtf
.getWidth()),
907 basegfx::fround
<tools::Long
>(-aBound
.getMinY() - aHalfPixelInMtf
.getHeight()) ) );
908 aOut
->SetRelativeMapMode( aOutMap
);
910 sdr::contact::DisplayInfo aDisplayInfo
;
914 if(mpCurrentPage
->TRG_HasMasterPage() && pPage
->IsMasterPage())
916 // MasterPage is processed as another page's SubContent
917 aDisplayInfo
.SetProcessLayers(mpCurrentPage
->TRG_GetMasterPageVisibleLayers());
918 aDisplayInfo
.SetSubContentActive(true);
924 // more effective way to paint a vector of SdrObjects. Hand over the processed page
926 ImplExportCheckVisisbilityRedirector
aCheckVisibilityRedirector(mpCurrentPage
);
927 sdr::contact::ObjectContactOfObjListPainter
aMultiObjectPainter(*aOut
, std::move(aShapes
), mpCurrentPage
);
928 aMultiObjectPainter
.SetViewObjectContactRedirector(&aCheckVisibilityRedirector
);
930 aMultiObjectPainter
.ProcessDisplay(aDisplayInfo
);
936 // tdf#126319 Immediately add needed size to target's PrefSize
937 // tdf#150102 Checked that in aBound is indeed the size - 1 (probably
938 // due to old integer stuff using Size()/Rectangle() and getWidth()/GetWidth()
939 // with the old one-less paradigm somewhere), so just correct to the
940 // correct size. Be aware that checking of tdf#126319 is needed, but
941 // looks good in my tests. Still: Changing the central UNO API Metafile
942 // export is always a risky thing, so it will have to show if this will
943 // not influence something else.
944 const Size
aBoundSize(
945 basegfx::fround
<tools::Long
>(aBound
.getWidth() + 1),
946 basegfx::fround
<tools::Long
>(aBound
.getHeight() + 1));
947 aMtf
.SetPrefMapMode( aMap
);
948 aMtf
.SetPrefSize( aBoundSize
);
953 aGraphic
= GetBitmapFromMetaFile( aMtf
, CalcSize( rSettings
.mnWidth
, rSettings
.mnHeight
, aBoundSize
, aOutSize
) );
962 pTempBackgroundShape
.clear();
964 rOutl
.SetCalcFieldValueHdl( maOldCalcFieldValueHdl
);
967 rOutl
.SetControlWord(nOldCntrl
);
969 rOutl
.SetBackgroundColor(aOldBackColor
);
976 sal_Bool SAL_CALL
GraphicExporter::filter( const Sequence
< PropertyValue
>& aDescriptor
)
978 ::SolarMutexGuard aGuard
;
980 if( maGraphic
.IsNone() && nullptr == mpUnoPage
)
983 if( maGraphic
.IsNone() && ( nullptr == mpUnoPage
->GetSdrPage() || nullptr == mpDoc
) )
986 GraphicFilter
&rFilter
= GraphicFilter::GetGraphicFilter();
988 // get the arguments from the descriptor
989 ExportSettings aSettings
;
990 ParseSettings(aDescriptor
, aSettings
);
992 const sal_uInt16 nFilter
= !aSettings
.maMediaType
.isEmpty()
993 ? rFilter
.GetExportFormatNumberForMediaType( aSettings
.maMediaType
)
994 : rFilter
.GetExportFormatNumberForShortName( aSettings
.maFilterName
);
995 bool bVectorType
= !rFilter
.IsExportPixelFormat( nFilter
);
997 // create the output stuff
998 Graphic aGraphic
= maGraphic
;
1000 ErrCode nStatus
= ERRCODE_NONE
;
1001 if (maGraphic
.IsNone())
1003 bool bAntiAliasing
= SvtOptionsDrawinglayer::IsAntiAliasing();
1004 AllSettings aAllSettings
= Application::GetSettings();
1005 StyleSettings aStyleSettings
= aAllSettings
.GetStyleSettings();
1006 bool bUseFontAAFromSystem
= aStyleSettings
.GetUseFontAAFromSystem();
1007 bool bUseSubpixelAA
= aStyleSettings
.GetUseSubpixelAA();
1008 aStyleSettings
.SetUseSubpixelAA(false);
1009 if (aSettings
.meAntiAliasing
!= TRISTATE_INDET
)
1011 // This is safe to do globally as we own the solar mutex.
1012 SvtOptionsDrawinglayer::SetAntiAliasing(aSettings
.meAntiAliasing
== TRISTATE_TRUE
, /*bTemporary*/true);
1013 // Opt in to have AA affect font rendering as well.
1014 aStyleSettings
.SetUseFontAAFromSystem(false);
1016 aAllSettings
.SetStyleSettings(aStyleSettings
);
1017 Application::SetSettings(aAllSettings
);
1018 nStatus
= GetGraphic( aSettings
, aGraphic
, bVectorType
) ? ERRCODE_NONE
: ERRCODE_GRFILTER_FILTERERROR
;
1019 if (aSettings
.meAntiAliasing
!= TRISTATE_INDET
)
1021 SvtOptionsDrawinglayer::SetAntiAliasing(bAntiAliasing
, /*bTemporary*/true);
1022 aStyleSettings
.SetUseFontAAFromSystem(bUseFontAAFromSystem
);
1024 aStyleSettings
.SetUseSubpixelAA(bUseSubpixelAA
);
1025 aAllSettings
.SetStyleSettings(aStyleSettings
);
1026 Application::SetSettings(aAllSettings
);
1029 if( nStatus
== ERRCODE_NONE
)
1031 // export graphic only if it has a size
1032 const Size
aGraphSize( aGraphic
.GetPrefSize() );
1033 if ( aGraphSize
.IsEmpty() )
1035 nStatus
= ERRCODE_GRFILTER_FILTERERROR
;
1039 // now we have a graphic, so export it
1040 if( aSettings
.mxGraphicRenderer
.is() )
1042 // render graphic directly into given renderer
1043 aSettings
.mxGraphicRenderer
->render( aGraphic
.GetXGraphic() );
1045 else if( aSettings
.mxOutputStream
.is() )
1047 // TODO: Either utilize optional XSeekable functionality for the
1048 // SvOutputStream, or adapt the graphic filter to not seek anymore.
1049 SvMemoryStream
aStream( 1024, 1024 );
1051 nStatus
= rFilter
.ExportGraphic( aGraphic
, u
"", aStream
, nFilter
, &aSettings
.maFilterData
);
1053 // copy temp stream to XOutputStream
1054 SvOutputStream
aOutputStream( aSettings
.mxOutputStream
);
1056 aOutputStream
.WriteStream( aStream
);
1060 INetURLObject
aURLObject( aSettings
.maURL
.Complete
);
1061 DBG_ASSERT( aURLObject
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
1063 nStatus
= XOutBitmap::ExportGraphic( aGraphic
, aURLObject
, rFilter
, nFilter
, &aSettings
.maFilterData
);
1068 if ( aSettings
.mxInteractionHandler
.is() && ( nStatus
!= ERRCODE_NONE
) )
1071 Sequence
< css::uno::Reference
< css::task::XInteractionContinuation
> > lContinuations
{
1072 new ::comphelper::OInteractionApprove()
1075 GraphicFilterRequest aErrorCode
;
1076 aErrorCode
.ErrCode
= sal_uInt32(nStatus
);
1077 aInteraction
<<= aErrorCode
;
1078 aSettings
.mxInteractionHandler
->handle( framework::InteractionRequest::CreateRequest( aInteraction
, lContinuations
) );
1080 return nStatus
== ERRCODE_NONE
;
1083 void SAL_CALL
GraphicExporter::cancel()
1089 /** the source 'document' could be a XDrawPage, a XShape or a generic XShapes */
1090 void SAL_CALL
GraphicExporter::setSourceDocument( const Reference
< lang::XComponent
>& xComponent
)
1092 ::SolarMutexGuard aGuard
;
1095 mpUnoPage
= nullptr;
1099 // any break inside this one loop while will throw an IllegalArgumentException
1102 mxPage
.set( xComponent
, UNO_QUERY
);
1103 mxShapes
.set( xComponent
, UNO_QUERY
);
1104 mxShape
.set( xComponent
, UNO_QUERY
);
1106 // Step 1: try a generic XShapes
1107 if( !mxPage
.is() && !mxShape
.is() && mxShapes
.is() )
1109 // we do not support empty shape collections
1110 if( 0 == mxShapes
->getCount() )
1113 // get first shape to detect corresponding page and model
1114 mxShapes
->getByIndex(0) >>= mxShape
;
1121 // Step 2: try a shape
1124 if (nullptr == SdrObject::getSdrObjectFromXShape(mxShape
))
1126 // This is not a Draw shape, let's see if it's a Writer one.
1127 uno::Reference
<beans::XPropertySet
> xPropertySet(mxShape
, uno::UNO_QUERY
);
1128 if (!xPropertySet
.is())
1130 uno::Reference
<graphic::XGraphic
> xGraphic(
1131 xPropertySet
->getPropertyValue(u
"Graphic"_ustr
), uno::UNO_QUERY
);
1135 maGraphic
= Graphic(xGraphic
);
1136 if (!maGraphic
.IsNone())
1142 // get page for this shape
1143 Reference
< XChild
> xChild( mxShape
, UNO_QUERY
);
1147 Reference
< XInterface
> xInt
;
1150 xInt
= xChild
->getParent();
1151 mxPage
.set( xInt
, UNO_QUERY
);
1153 xChild
.set( xInt
, UNO_QUERY
);
1155 while( !mxPage
.is() && xChild
.is() );
1161 // Step 3: check the page
1165 mpUnoPage
= comphelper::getFromUnoTunnel
<SvxDrawPage
>( mxPage
);
1167 if( nullptr == mpUnoPage
|| nullptr == mpUnoPage
->GetSdrPage() )
1170 mpDoc
= &mpUnoPage
->GetSdrPage()->getSdrModelFromSdrPage();
1172 // Step 4: If we got a generic XShapes test all contained shapes
1173 // if they belong to the same XDrawPage
1177 SdrPage
* pPage
= mpUnoPage
->GetSdrPage();
1179 Reference
< XShape
> xShape
;
1183 const sal_Int32 nCount
= mxShapes
->getCount();
1185 // test all but the first shape if they have the same page than
1187 for( sal_Int32 nIndex
= 1; bOk
&& ( nIndex
< nCount
); nIndex
++ )
1189 mxShapes
->getByIndex( nIndex
) >>= xShape
;
1190 pObj
= SdrObject::getSdrObjectFromXShape(xShape
);
1191 bOk
= pObj
&& pObj
->getSdrPageFromSdrObject() == pPage
;
1207 throw IllegalArgumentException();
1211 OUString SAL_CALL
GraphicExporter::getImplementationName( )
1213 return u
"com.sun.star.comp.Draw.GraphicExporter"_ustr
;
1216 sal_Bool SAL_CALL
GraphicExporter::supportsService( const OUString
& ServiceName
)
1218 return cppu::supportsService(this, ServiceName
);
1221 Sequence
< OUString
> SAL_CALL
GraphicExporter::getSupportedServiceNames( )
1223 Sequence
< OUString
> aSupportedServiceNames
{ u
"com.sun.star.drawing.GraphicExportFilter"_ustr
};
1224 return aSupportedServiceNames
;
1228 sal_Bool SAL_CALL
GraphicExporter::supportsMimeType( const OUString
& rMimeTypeName
)
1230 GraphicFilter
&rFilter
= GraphicFilter::GetGraphicFilter();
1231 sal_uInt16 nCount
= rFilter
.GetExportFormatCount();
1233 for( nFilter
= 0; nFilter
< nCount
; nFilter
++ )
1235 if( rMimeTypeName
== rFilter
.GetExportFormatMediaType( nFilter
) )
1244 Sequence
< OUString
> SAL_CALL
GraphicExporter::getSupportedMimeTypeNames( )
1246 GraphicFilter
&rFilter
= GraphicFilter::GetGraphicFilter();
1247 sal_uInt16 nCount
= rFilter
.GetExportFormatCount();
1249 sal_uInt16 nFound
= 0;
1251 Sequence
< OUString
> aSeq( nCount
);
1252 OUString
* pStr
= aSeq
.getArray();
1254 for( nFilter
= 0; nFilter
< nCount
; nFilter
++ )
1256 OUString
aMimeType( rFilter
.GetExportFormatMediaType( nFilter
) );
1257 if( !aMimeType
.isEmpty() )
1259 *pStr
++ = aMimeType
;
1264 if( nFound
< nCount
)
1265 aSeq
.realloc( nFound
);
1272 /** creates a bitmap that is optionally transparent from a metafile
1274 BitmapEx
GetBitmapFromMetaFile(const GDIMetaFile
& rMtf
, const Size
* pSize
)
1276 // use new primitive conversion tooling
1277 basegfx::B2DRange
aRange(basegfx::B2DPoint(0.0, 0.0));
1278 sal_uInt32 nMaximumQuadraticPixels
;
1282 // use 100th mm for primitive bitmap converter tool, input is pixel
1283 // use a real OutDev to get the correct DPI, the static LogicToLogic assumes 72dpi which is wrong (!)
1284 const Size
aSize100th(
1285 Application::GetDefaultDevice()->PixelToLogic(*pSize
, MapMode(MapUnit::Map100thMM
)));
1287 aRange
.expand(basegfx::B2DPoint(aSize100th
.Width(), aSize100th
.Height()));
1289 // when explicitly pixels are requested from the GraphicExporter, use a *very* high limit
1290 // of 16gb (4096x4096 pixels)
1291 nMaximumQuadraticPixels
= 4096 * 4096;
1295 // use 100th mm for primitive bitmap converter tool
1296 const Size
aSize100th(OutputDevice::LogicToLogic(rMtf
.GetPrefSize(), rMtf
.GetPrefMapMode(),
1297 MapMode(MapUnit::Map100thMM
)));
1299 aRange
.expand(basegfx::B2DPoint(aSize100th
.Width(), aSize100th
.Height()));
1301 // limit to 2048x2048 pixels, as in ImpGraphic::getBitmap (vcl/source/gdi/impgraph.cxx):
1302 nMaximumQuadraticPixels
= 2048 * 2048;
1305 return convertMetafileToBitmapEx(rMtf
, aRange
, nMaximumQuadraticPixels
);
1308 Graphic
SvxGetGraphicForShape( SdrObject
& rShape
)
1313 rtl::Reference
< GraphicExporter
> xExporter( new GraphicExporter() );
1314 Reference
< XComponent
> xComp( rShape
.getUnoShape(), UNO_QUERY_THROW
);
1315 xExporter
->setSourceDocument( xComp
);
1316 ExportSettings aSettings
;
1317 xExporter
->GetGraphic( aSettings
, aGraphic
, true/*bVector*/ );
1321 TOOLS_WARN_EXCEPTION("svx", "");
1326 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1327 com_sun_star_comp_Draw_GraphicExporter_get_implementation(
1328 css::uno::XComponentContext
*,
1329 css::uno::Sequence
<css::uno::Any
> const &)
1331 return cppu::acquire(new GraphicExporter
);
1334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */