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 .
20 #include "sal/config.h"
22 #include <comphelper/processfactory.hxx>
23 #include <tools/fract.hxx>
24 #include <tools/vcompat.hxx>
25 #include <tools/urlobj.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/helpers.hxx>
28 #include <ucbhelper/content.hxx>
29 #include <unotools/ucbstreamhelper.hxx>
30 #include <unotools/tempfile.hxx>
31 #include <vcl/outdev.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/gfxlink.hxx>
34 #include <vcl/cvtgrf.hxx>
35 #include <vcl/graph.hxx>
36 #include <vcl/metaact.hxx>
37 #include <impgraph.hxx>
38 #include <com/sun/star/ucb/CommandAbortedException.hpp>
39 #include <vcl/dibtools.hxx>
41 #include <o3tl/make_unique.hxx>
43 #define GRAPHIC_MTFTOBMP_MAXEXT 2048
44 #define GRAPHIC_STREAMBUFSIZE 8192UL
46 #define SYS_WINMETAFILE 0x00000003L
47 #define SYS_WNTMETAFILE 0x00000004L
48 #define SYS_OS2METAFILE 0x00000005L
49 #define SYS_MACMETAFILE 0x00000006L
51 #define GRAPHIC_FORMAT_50 COMPAT_FORMAT( 'G', 'R', 'F', '5' )
52 #define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' )
54 const sal_uInt32
nPdfMagic((sal_uInt32('p') << 24) | (sal_uInt32('d') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
56 using namespace com::sun::star
;
60 INetURLObject aSwapURL
;
70 GraphicReader::GraphicReader()
74 GraphicReader::~GraphicReader()
78 void GraphicReader::DisablePreviewMode()
81 mpReaderData
->maPreviewSize
= Size( 0, 0 );
84 void GraphicReader::SetPreviewSize( const Size
& rSize
)
87 mpReaderData
.reset( new ReaderData
);
88 mpReaderData
->maPreviewSize
= rSize
;
91 Size
GraphicReader::GetPreviewSize() const
95 aSize
= mpReaderData
->maPreviewSize
;
99 ImpGraphic::ImpGraphic() :
100 meType ( GraphicType::NONE
),
103 mbDummyContext ( false )
107 ImpGraphic::ImpGraphic(const ImpGraphic
& rImpGraphic
)
108 : maMetaFile(rImpGraphic
.maMetaFile
)
109 , maEx(rImpGraphic
.maEx
)
110 , maSwapInfo(rImpGraphic
.maSwapInfo
)
111 , mpContext(rImpGraphic
.mpContext
)
112 , mpSwapFile(rImpGraphic
.mpSwapFile
)
113 , meType(rImpGraphic
.meType
)
114 , mnSizeBytes(rImpGraphic
.mnSizeBytes
)
115 , mbSwapOut(rImpGraphic
.mbSwapOut
)
116 , mbDummyContext(rImpGraphic
.mbDummyContext
)
117 , maSvgData(rImpGraphic
.maSvgData
)
118 , maPdfData(rImpGraphic
.maPdfData
)
120 if( rImpGraphic
.mpGfxLink
)
121 mpGfxLink
= o3tl::make_unique
<GfxLink
>( *rImpGraphic
.mpGfxLink
);
123 if( rImpGraphic
.mpAnimation
)
125 mpAnimation
= o3tl::make_unique
<Animation
>( *rImpGraphic
.mpAnimation
);
126 maEx
= mpAnimation
->GetBitmapEx();
130 ImpGraphic::ImpGraphic(ImpGraphic
&& rImpGraphic
)
131 : maMetaFile(std::move(rImpGraphic
.maMetaFile
))
132 , maEx(std::move(rImpGraphic
.maEx
))
133 , maSwapInfo(std::move(rImpGraphic
.maSwapInfo
))
134 , mpAnimation(std::move(rImpGraphic
.mpAnimation
))
135 , mpContext(std::move(rImpGraphic
.mpContext
))
136 , mpSwapFile(std::move(rImpGraphic
.mpSwapFile
))
137 , mpGfxLink(std::move(rImpGraphic
.mpGfxLink
))
138 , meType(rImpGraphic
.meType
)
139 , mnSizeBytes(rImpGraphic
.mnSizeBytes
)
140 , mbSwapOut(rImpGraphic
.mbSwapOut
)
141 , mbDummyContext(rImpGraphic
.mbDummyContext
)
142 , maSvgData(std::move(rImpGraphic
.maSvgData
))
143 , maPdfData(std::move(rImpGraphic
.maPdfData
))
145 rImpGraphic
.ImplClear();
146 rImpGraphic
.mbDummyContext
= false;
149 ImpGraphic::ImpGraphic( const Bitmap
& rBitmap
) :
151 meType ( !rBitmap
.IsEmpty() ? GraphicType::Bitmap
: GraphicType::NONE
),
154 mbDummyContext ( false )
158 ImpGraphic::ImpGraphic( const BitmapEx
& rBitmapEx
) :
160 meType ( !rBitmapEx
.IsEmpty() ? GraphicType::Bitmap
: GraphicType::NONE
),
163 mbDummyContext ( false )
167 ImpGraphic::ImpGraphic(const SvgDataPtr
& rSvgDataPtr
)
168 : meType( rSvgDataPtr
.get() ? GraphicType::Bitmap
: GraphicType::NONE
),
171 mbDummyContext ( false ),
172 maSvgData(rSvgDataPtr
)
176 ImpGraphic::ImpGraphic( const Animation
& rAnimation
) :
177 maEx ( rAnimation
.GetBitmapEx() ),
178 mpAnimation ( o3tl::make_unique
<Animation
>( rAnimation
) ),
179 meType ( GraphicType::Bitmap
),
182 mbDummyContext ( false )
186 ImpGraphic::ImpGraphic( const GDIMetaFile
& rMtf
) :
188 meType ( GraphicType::GdiMetafile
),
191 mbDummyContext ( false )
195 ImpGraphic::~ImpGraphic()
199 ImpGraphic
& ImpGraphic::operator=( const ImpGraphic
& rImpGraphic
)
201 if( &rImpGraphic
!= this )
203 maMetaFile
= rImpGraphic
.maMetaFile
;
204 meType
= rImpGraphic
.meType
;
205 mnSizeBytes
= rImpGraphic
.mnSizeBytes
;
207 maSwapInfo
= rImpGraphic
.maSwapInfo
;
208 mpContext
= rImpGraphic
.mpContext
;
209 mbDummyContext
= rImpGraphic
.mbDummyContext
;
213 if ( rImpGraphic
.mpAnimation
)
215 mpAnimation
= o3tl::make_unique
<Animation
>( *rImpGraphic
.mpAnimation
);
216 maEx
= mpAnimation
->GetBitmapEx();
220 maEx
= rImpGraphic
.maEx
;
223 mbSwapOut
= rImpGraphic
.mbSwapOut
;
224 mpSwapFile
= rImpGraphic
.mpSwapFile
;
228 if( rImpGraphic
.mpGfxLink
)
229 mpGfxLink
= o3tl::make_unique
<GfxLink
>( *rImpGraphic
.mpGfxLink
);
231 maSvgData
= rImpGraphic
.maSvgData
;
232 maPdfData
= rImpGraphic
.maPdfData
;
238 ImpGraphic
& ImpGraphic::operator=(ImpGraphic
&& rImpGraphic
)
240 maMetaFile
= std::move(rImpGraphic
.maMetaFile
);
241 meType
= rImpGraphic
.meType
;
242 mnSizeBytes
= rImpGraphic
.mnSizeBytes
;
243 maSwapInfo
= std::move(rImpGraphic
.maSwapInfo
);
244 mpContext
= std::move(rImpGraphic
.mpContext
);
245 mbDummyContext
= rImpGraphic
.mbDummyContext
;
246 mpAnimation
= std::move(rImpGraphic
.mpAnimation
);
247 maEx
= std::move(rImpGraphic
.maEx
);
248 mbSwapOut
= rImpGraphic
.mbSwapOut
;
249 mpSwapFile
= std::move(rImpGraphic
.mpSwapFile
);
250 mpGfxLink
= std::move(rImpGraphic
.mpGfxLink
);
251 maSvgData
= std::move(rImpGraphic
.maSvgData
);
252 maPdfData
= std::move(rImpGraphic
.maPdfData
);
254 rImpGraphic
.ImplClear();
255 rImpGraphic
.mbDummyContext
= false;
260 bool ImpGraphic::operator==( const ImpGraphic
& rImpGraphic
) const
264 if( this == &rImpGraphic
)
266 else if( !ImplIsSwapOut() && ( rImpGraphic
.meType
== meType
) )
270 case GraphicType::NONE
:
274 case GraphicType::GdiMetafile
:
276 if( rImpGraphic
.maMetaFile
== maMetaFile
)
281 case GraphicType::Bitmap
:
285 if(maSvgData
== rImpGraphic
.maSvgData
)
289 else if(rImpGraphic
.maSvgData
)
291 if(maSvgData
->getSvgDataArrayLength() == rImpGraphic
.maSvgData
->getSvgDataArrayLength())
294 maSvgData
->getSvgDataArray().getConstArray(),
295 rImpGraphic
.maSvgData
->getSvgDataArray().getConstArray(),
296 maSvgData
->getSvgDataArrayLength()))
303 else if (maPdfData
.hasElements())
305 bRet
= maPdfData
== rImpGraphic
.maPdfData
;
307 else if( mpAnimation
)
309 if( rImpGraphic
.mpAnimation
&& ( *rImpGraphic
.mpAnimation
== *mpAnimation
) )
312 else if( !rImpGraphic
.mpAnimation
&& ( rImpGraphic
.maEx
== maEx
) )
327 void ImpGraphic::ImplCreateSwapInfo()
329 if (!ImplIsSwapOut())
331 maSwapInfo
.maPrefMapMode
= ImplGetPrefMapMode();
332 maSwapInfo
.maPrefSize
= ImplGetPrefSize();
336 void ImpGraphic::ImplClearGraphics()
343 maPdfData
= uno::Sequence
<sal_Int8
>();
346 ImpSwapFile::~ImpSwapFile()
350 ::ucbhelper::Content
aCnt( aSwapURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
351 css::uno::Reference
< css::ucb::XCommandEnvironment
>(),
352 comphelper::getProcessComponentContext() );
354 aCnt
.executeCommand( "delete", css::uno::makeAny( true ) );
356 catch( const css::ucb::ContentCreationException
& )
359 catch( const css::uno::RuntimeException
& )
362 catch( const css::ucb::CommandAbortedException
& )
365 catch( const css::uno::Exception
& )
370 void ImpGraphic::ImplClear()
377 meType
= GraphicType::NONE
;
381 void ImpGraphic::ImplSetDefaultType()
384 meType
= GraphicType::Default
;
387 bool ImpGraphic::ImplIsSupportedGraphic() const
389 return( meType
!= GraphicType::NONE
);
392 bool ImpGraphic::ImplIsTransparent() const
396 if( meType
== GraphicType::Bitmap
&& !maSvgData
.get())
398 bRet
= ( mpAnimation
? mpAnimation
->IsTransparent() : maEx
.IsTransparent() );
404 bool ImpGraphic::ImplIsAlpha() const
412 else if( meType
== GraphicType::Bitmap
)
414 bRet
= ( nullptr == mpAnimation
) && maEx
.IsAlpha();
420 bool ImpGraphic::ImplIsAnimated() const
422 return( mpAnimation
!= nullptr );
425 bool ImpGraphic::ImplIsEPS() const
427 return( ( meType
== GraphicType::GdiMetafile
) &&
428 ( maMetaFile
.GetActionSize() > 0 ) &&
429 ( maMetaFile
.GetAction( 0 )->GetType() == MetaActionType::EPS
) );
432 Bitmap
ImpGraphic::ImplGetBitmap(const GraphicConversionParameters
& rParameters
) const
436 if( meType
== GraphicType::Bitmap
)
438 if(maSvgData
.get() && maEx
.IsEmpty())
440 // use maEx as local buffer for rendered svg
441 const_cast< ImpGraphic
* >(this)->maEx
= maSvgData
->getReplacement();
444 const BitmapEx
& rRetBmpEx
= ( mpAnimation
? mpAnimation
->GetBitmapEx() : maEx
);
445 const Color
aReplaceColor( COL_WHITE
);
447 aRetBmp
= rRetBmpEx
.GetBitmap( &aReplaceColor
);
449 if(rParameters
.getSizePixel().Width() || rParameters
.getSizePixel().Height())
450 aRetBmp
.Scale(rParameters
.getSizePixel());
452 else if( ( meType
!= GraphicType::Default
) && ImplIsSupportedGraphic() )
457 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
458 Size
aDrawSize(aVDev
->LogicToPixel(maMetaFile
.GetPrefSize(), maMetaFile
.GetPrefMapMode()));
460 if(rParameters
.getSizePixel().Width() && rParameters
.getSizePixel().Height())
462 // apply given size if exists
463 aDrawSize
= rParameters
.getSizePixel();
466 if(aDrawSize
.Width() && aDrawSize
.Height() && !rParameters
.getUnlimitedSize()
467 && (aDrawSize
.Width() > GRAPHIC_MTFTOBMP_MAXEXT
|| aDrawSize
.Height() > GRAPHIC_MTFTOBMP_MAXEXT
))
469 // limit bitmap size to a maximum of GRAPHIC_MTFTOBMP_MAXEXT x GRAPHIC_MTFTOBMP_MAXEXT
470 double fWH((double)aDrawSize
.Width() / (double)aDrawSize
.Height());
474 aDrawSize
.setWidth(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT
* fWH
));
475 aDrawSize
.setHeight(GRAPHIC_MTFTOBMP_MAXEXT
);
479 aDrawSize
.setWidth(GRAPHIC_MTFTOBMP_MAXEXT
);
480 aDrawSize
.setHeight(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT
/ fWH
));
484 // calculate pixel size. Normally, it's the same as aDrawSize, but may
485 // need to be extended when hairlines are on the right or bottom edge
486 Size
aPixelSize(aDrawSize
);
488 if(GraphicType::GdiMetafile
== ImplGetType())
490 // get hairline and full bound rect
491 tools::Rectangle aHairlineRect
;
492 const tools::Rectangle
aRect(maMetaFile
.GetBoundRect(*aVDev
.get(), &aHairlineRect
));
494 if(!aRect
.IsEmpty() && !aHairlineRect
.IsEmpty())
496 // expand if needed to allow bottom and right hairlines to be added
497 if(aRect
.Right() == aHairlineRect
.Right())
499 aPixelSize
.setWidth(aPixelSize
.getWidth() + 1);
502 if(aRect
.Bottom() == aHairlineRect
.Bottom())
504 aPixelSize
.setHeight(aPixelSize
.getHeight() + 1);
509 if(aVDev
->SetOutputSizePixel(aPixelSize
))
511 if(rParameters
.getAntiAliase())
513 aVDev
->SetAntialiasing(aVDev
->GetAntialiasing() | AntialiasingFlags::EnableB2dDraw
);
516 if(rParameters
.getSnapHorVerLines())
518 aVDev
->SetAntialiasing(aVDev
->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline
);
521 ImplDraw( aVDev
.get(), Point(), aDrawSize
);
523 // use maEx as local buffer for rendered metafile
524 const_cast< ImpGraphic
* >(this)->maEx
= aVDev
->GetBitmap( Point(), aVDev
->GetOutputSizePixel() );
528 aRetBmp
= maEx
.GetBitmap();
533 aRetBmp
.SetPrefMapMode( ImplGetPrefMapMode() );
534 aRetBmp
.SetPrefSize( ImplGetPrefSize() );
540 BitmapEx
ImpGraphic::ImplGetBitmapEx(const GraphicConversionParameters
& rParameters
) const
544 if( meType
== GraphicType::Bitmap
)
546 if(maSvgData
.get() && maEx
.IsEmpty())
548 // use maEx as local buffer for rendered svg
549 const_cast< ImpGraphic
* >(this)->maEx
= maSvgData
->getReplacement();
552 aRetBmpEx
= ( mpAnimation
? mpAnimation
->GetBitmapEx() : maEx
);
554 if(rParameters
.getSizePixel().Width() || rParameters
.getSizePixel().Height())
557 rParameters
.getSizePixel(),
561 else if( ( meType
!= GraphicType::Default
) && ImplIsSupportedGraphic() )
565 const ImpGraphic
aMonoMask( maMetaFile
.GetMonochromeMtf( COL_BLACK
) );
567 // use maEx as local buffer for rendered metafile
568 const_cast< ImpGraphic
* >(this)->maEx
= BitmapEx(ImplGetBitmap(rParameters
), aMonoMask
.ImplGetBitmap(rParameters
));
577 Animation
ImpGraphic::ImplGetAnimation() const
579 Animation aAnimation
;
582 aAnimation
= *mpAnimation
;
587 const BitmapEx
& ImpGraphic::ImplGetBitmapExRef() const
592 const GDIMetaFile
& ImpGraphic::ImplGetGDIMetaFile() const
594 if (GraphicType::Bitmap
== meType
&& !maMetaFile
.GetActionSize())
597 // Use the local maMetaFile as container for a metafile-representation
598 // of the bitmap graphic. This will be done only once, thus be buffered.
599 // I checked all usages of maMetaFile, it is only used when type is not
600 // GraphicType::Bitmap. In operator= it will get copied, thus buffering will
601 // survive copying (change this if not wanted)
602 ImpGraphic
* pThat
= const_cast< ImpGraphic
* >(this);
604 if(maSvgData
.get() && !maEx
)
606 // use maEx as local buffer for rendered svg
607 pThat
->maEx
= maSvgData
->getReplacement();
610 // #123983# directly create a metafile with the same PrefSize and PrefMapMode
611 // the bitmap has, this will be an always correct metafile
612 if(maEx
.IsTransparent())
614 pThat
->maMetaFile
.AddAction(new MetaBmpExScaleAction(Point(), maEx
.GetPrefSize(), maEx
));
618 pThat
->maMetaFile
.AddAction(new MetaBmpScaleAction(Point(), maEx
.GetPrefSize(), maEx
.GetBitmap()));
621 pThat
->maMetaFile
.Stop();
622 pThat
->maMetaFile
.WindStart();
623 pThat
->maMetaFile
.SetPrefSize(maEx
.GetPrefSize());
624 pThat
->maMetaFile
.SetPrefMapMode(maEx
.GetPrefMapMode());
630 Size
ImpGraphic::ImplGetPrefSize() const
634 if( ImplIsSwapOut() )
635 aSize
= maSwapInfo
.maPrefSize
;
640 case GraphicType::NONE
:
641 case GraphicType::Default
:
644 case GraphicType::Bitmap
:
646 if(maSvgData
.get() && maEx
.IsEmpty())
648 // svg not yet buffered in maEx, return size derived from range
649 const basegfx::B2DRange
& rRange
= maSvgData
->getRange();
651 aSize
= Size(basegfx::fround(rRange
.getWidth()), basegfx::fround(rRange
.getHeight()));
655 aSize
= maEx
.GetPrefSize();
657 if( !aSize
.Width() || !aSize
.Height() )
659 aSize
= maEx
.GetSizePixel();
667 if( ImplIsSupportedGraphic() )
668 aSize
= maMetaFile
.GetPrefSize();
677 void ImpGraphic::ImplSetPrefSize( const Size
& rPrefSize
)
681 case GraphicType::NONE
:
682 case GraphicType::Default
:
685 case GraphicType::Bitmap
:
687 // used when importing a writer FlyFrame with SVG as graphic, added conversion
688 // to allow setting the PrefSize at the BitmapEx to hold it
689 if(maSvgData
.get() && maEx
.IsEmpty())
691 // use maEx as local buffer for rendered svg
692 maEx
= maSvgData
->getReplacement();
695 // #108077# Push through pref size to animation object,
696 // will be lost on copy otherwise
697 if( ImplIsAnimated() )
699 const_cast< BitmapEx
& >(mpAnimation
->GetBitmapEx()).SetPrefSize( rPrefSize
);
702 maEx
.SetPrefSize( rPrefSize
);
708 if( ImplIsSupportedGraphic() )
709 maMetaFile
.SetPrefSize( rPrefSize
);
715 MapMode
ImpGraphic::ImplGetPrefMapMode() const
719 if( ImplIsSwapOut() )
720 aMapMode
= maSwapInfo
.maPrefMapMode
;
725 case GraphicType::NONE
:
726 case GraphicType::Default
:
729 case GraphicType::Bitmap
:
731 if(maSvgData
.get() && maEx
.IsEmpty())
733 // svg not yet buffered in maEx, return default PrefMapMode
734 aMapMode
= MapMode(MapUnit::Map100thMM
);
738 const Size
aSize( maEx
.GetPrefSize() );
740 if ( aSize
.Width() && aSize
.Height() )
741 aMapMode
= maEx
.GetPrefMapMode();
748 if( ImplIsSupportedGraphic() )
749 return maMetaFile
.GetPrefMapMode();
758 void ImpGraphic::ImplSetPrefMapMode( const MapMode
& rPrefMapMode
)
762 case GraphicType::NONE
:
763 case GraphicType::Default
:
766 case GraphicType::Bitmap
:
770 // ignore for Svg. If this is really used (except the grfcache)
771 // it can be extended by using maEx as buffer for maSvgData->getReplacement()
775 // #108077# Push through pref mapmode to animation object,
776 // will be lost on copy otherwise
777 if( ImplIsAnimated() )
779 const_cast< BitmapEx
& >(mpAnimation
->GetBitmapEx()).SetPrefMapMode( rPrefMapMode
);
782 maEx
.SetPrefMapMode( rPrefMapMode
);
789 if( ImplIsSupportedGraphic() )
790 maMetaFile
.SetPrefMapMode( rPrefMapMode
);
796 sal_uLong
ImpGraphic::ImplGetSizeBytes() const
798 if( 0 == mnSizeBytes
)
800 if( meType
== GraphicType::Bitmap
)
804 std::pair
<SvgData::State
, size_t> tmp(maSvgData
->getSizeBytes());
805 if (SvgData::State::UNPARSED
== tmp
.first
)
807 return tmp
.second
; // don't cache it until SVG is parsed
809 mnSizeBytes
= tmp
.second
;
813 mnSizeBytes
= mpAnimation
? mpAnimation
->GetSizeBytes() : maEx
.GetSizeBytes();
816 else if( meType
== GraphicType::GdiMetafile
)
818 mnSizeBytes
= maMetaFile
.GetSizeBytes();
825 void ImpGraphic::ImplDraw( OutputDevice
* pOutDev
, const Point
& rDestPt
) const
827 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
831 case GraphicType::Default
:
834 case GraphicType::Bitmap
:
836 if(maSvgData
.get() && !maEx
)
838 // use maEx as local buffer for rendered svg
839 const_cast< ImpGraphic
* >(this)->maEx
= maSvgData
->getReplacement();
844 mpAnimation
->Draw( pOutDev
, rDestPt
);
848 maEx
.Draw( pOutDev
, rDestPt
);
854 ImplDraw( pOutDev
, rDestPt
, maMetaFile
.GetPrefSize() );
860 void ImpGraphic::ImplDraw( OutputDevice
* pOutDev
,
861 const Point
& rDestPt
, const Size
& rDestSize
) const
863 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
867 case GraphicType::Default
:
870 case GraphicType::Bitmap
:
872 if(maSvgData
.get() && maEx
.IsEmpty())
874 // use maEx as local buffer for rendered svg
875 const_cast< ImpGraphic
* >(this)->maEx
= maSvgData
->getReplacement();
880 mpAnimation
->Draw( pOutDev
, rDestPt
, rDestSize
);
884 maEx
.Draw( pOutDev
, rDestPt
, rDestSize
);
891 const_cast<ImpGraphic
*>(this)->maMetaFile
.WindStart();
892 const_cast<ImpGraphic
*>(this)->maMetaFile
.Play( pOutDev
, rDestPt
, rDestSize
);
893 const_cast<ImpGraphic
*>(this)->maMetaFile
.WindStart();
900 void ImpGraphic::ImplStartAnimation( OutputDevice
* pOutDev
, const Point
& rDestPt
,
901 const Size
& rDestSize
, long nExtraData
,
902 OutputDevice
* pFirstFrameOutDev
)
904 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation
)
905 mpAnimation
->Start( pOutDev
, rDestPt
, rDestSize
, nExtraData
, pFirstFrameOutDev
);
908 void ImpGraphic::ImplStopAnimation( OutputDevice
* pOutDev
, long nExtraData
)
910 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation
)
911 mpAnimation
->Stop( pOutDev
, nExtraData
);
914 void ImpGraphic::ImplSetAnimationNotifyHdl( const Link
<Animation
*,void>& rLink
)
917 mpAnimation
->SetNotifyHdl( rLink
);
920 Link
<Animation
*,void> ImpGraphic::ImplGetAnimationNotifyHdl() const
922 Link
<Animation
*,void> aLink
;
925 aLink
= mpAnimation
->GetNotifyHdl();
930 sal_uLong
ImpGraphic::ImplGetAnimationLoopCount() const
932 return( mpAnimation
? mpAnimation
->GetLoopCount() : 0UL );
936 void ImpGraphic::ImplSetContext( const std::shared_ptr
<GraphicReader
>& pReader
)
939 mbDummyContext
= false;
942 bool ImpGraphic::ImplReadEmbedded( SvStream
& rIStm
)
948 const SvStreamEndian nOldFormat
= rIStm
.GetEndian();
951 rIStm
.SetEndian( SvStreamEndian::LITTLE
);
952 rIStm
.ReadUInt32( nId
);
955 if( GRAPHIC_FORMAT_50
== nId
)
957 // read new style header
958 std::unique_ptr
<VersionCompat
> pCompat( new VersionCompat( rIStm
, StreamMode::READ
) );
960 rIStm
.ReadInt32( nType
);
962 rIStm
.ReadInt32( nLen
);
963 ReadPair( rIStm
, aSize
);
964 ReadMapMode( rIStm
, aMapMode
);
968 // read old style header
969 sal_Int32 nWidth
, nHeight
;
970 sal_Int32 nMapMode
, nScaleNumX
, nScaleDenomX
;
971 sal_Int32 nScaleNumY
, nScaleDenomY
, nOffsX
, nOffsY
;
976 rIStm
.ReadInt32( nType
).ReadInt32( nLen
).ReadInt32( nWidth
).ReadInt32( nHeight
);
977 rIStm
.ReadInt32( nMapMode
).ReadInt32( nScaleNumX
).ReadInt32( nScaleDenomX
).ReadInt32( nScaleNumY
);
978 rIStm
.ReadInt32( nScaleDenomY
).ReadInt32( nOffsX
).ReadInt32( nOffsY
);
983 nType
= OSL_SWAPDWORD( nType
);
984 nWidth
= OSL_SWAPDWORD( nWidth
);
985 nHeight
= OSL_SWAPDWORD( nHeight
);
986 nMapMode
= OSL_SWAPDWORD( nMapMode
);
987 nScaleNumX
= OSL_SWAPDWORD( nScaleNumX
);
988 nScaleDenomX
= OSL_SWAPDWORD( nScaleDenomX
);
989 nScaleNumY
= OSL_SWAPDWORD( nScaleNumY
);
990 nScaleDenomY
= OSL_SWAPDWORD( nScaleDenomY
);
991 nOffsX
= OSL_SWAPDWORD( nOffsX
);
992 nOffsY
= OSL_SWAPDWORD( nOffsY
);
995 aSize
= Size( nWidth
, nHeight
);
996 aMapMode
= MapMode( (MapUnit
) nMapMode
, Point( nOffsX
, nOffsY
),
997 Fraction( nScaleNumX
, nScaleDenomX
),
998 Fraction( nScaleNumY
, nScaleDenomY
) );
1001 meType
= (GraphicType
) nType
;
1003 if( meType
!= GraphicType::NONE
)
1005 if( meType
== GraphicType::Bitmap
)
1007 if(maSvgData
.get() && maEx
.IsEmpty())
1009 // use maEx as local buffer for rendered svg
1010 maEx
= maSvgData
->getReplacement();
1013 maEx
.aBitmapSize
= aSize
;
1015 if( aMapMode
!= MapMode() )
1017 maEx
.SetPrefMapMode( aMapMode
);
1018 maEx
.SetPrefSize( aSize
);
1023 maMetaFile
.SetPrefMapMode( aMapMode
);
1024 maMetaFile
.SetPrefSize( aSize
);
1027 if( meType
== GraphicType::Bitmap
|| meType
== GraphicType::GdiMetafile
)
1029 ReadImpGraphic( rIStm
, *this );
1030 bRet
= ( rIStm
.GetError() == 0UL );
1032 else if( sal::static_int_cast
<sal_uLong
>(meType
) >= SYS_WINMETAFILE
1033 && sal::static_int_cast
<sal_uLong
>(meType
) <= SYS_MACMETAFILE
)
1035 Graphic aSysGraphic
;
1036 ConvertDataFormat nCvtType
;
1038 switch( sal::static_int_cast
<sal_uLong
>(meType
) )
1040 case SYS_WINMETAFILE
:
1041 case SYS_WNTMETAFILE
: nCvtType
= ConvertDataFormat::WMF
; break;
1042 case SYS_OS2METAFILE
: nCvtType
= ConvertDataFormat::MET
; break;
1043 case SYS_MACMETAFILE
: nCvtType
= ConvertDataFormat::PCT
; break;
1046 nCvtType
= ConvertDataFormat::Unknown
;
1050 if( nType
&& GraphicConverter::Import( rIStm
, aSysGraphic
, nCvtType
) == ERRCODE_NONE
)
1052 *this = ImpGraphic( aSysGraphic
.GetGDIMetaFile() );
1053 bRet
= ( rIStm
.GetError() == 0UL );
1056 meType
= GraphicType::Default
;
1061 ImplSetPrefMapMode( aMapMode
);
1062 ImplSetPrefSize( aSize
);
1068 rIStm
.SetEndian( nOldFormat
);
1073 bool ImpGraphic::ImplWriteEmbedded( SvStream
& rOStm
)
1077 if( ( meType
!= GraphicType::NONE
) && ( meType
!= GraphicType::Default
) && !ImplIsSwapOut() )
1079 const MapMode
aMapMode( ImplGetPrefMapMode() );
1080 const Size
aSize( ImplGetPrefSize() );
1081 const SvStreamEndian nOldFormat
= rOStm
.GetEndian();
1082 sal_uLong nDataFieldPos
;
1084 rOStm
.SetEndian( SvStreamEndian::LITTLE
);
1086 // write correct version ( old style/new style header )
1087 if( rOStm
.GetVersion() >= SOFFICE_FILEFORMAT_50
)
1089 // write ID for new format (5.0)
1090 rOStm
.WriteUInt32( GRAPHIC_FORMAT_50
);
1092 // write new style header
1093 std::unique_ptr
<VersionCompat
> pCompat( new VersionCompat( rOStm
, StreamMode::WRITE
, 1 ) );
1095 rOStm
.WriteInt32( (sal_Int32
)meType
);
1097 // data size is updated later
1098 nDataFieldPos
= rOStm
.Tell();
1099 rOStm
.WriteInt32( 0 );
1101 WritePair( rOStm
, aSize
);
1102 WriteMapMode( rOStm
, aMapMode
);
1106 // write old style (<=4.0) header
1107 rOStm
.WriteInt32( (sal_Int32
)meType
);
1109 // data size is updated later
1110 nDataFieldPos
= rOStm
.Tell();
1111 rOStm
.WriteInt32( 0 );
1112 rOStm
.WriteInt32( aSize
.Width() );
1113 rOStm
.WriteInt32( aSize
.Height() );
1114 rOStm
.WriteInt32( (sal_uInt16
)aMapMode
.GetMapUnit() );
1115 rOStm
.WriteInt32( aMapMode
.GetScaleX().GetNumerator() );
1116 rOStm
.WriteInt32( aMapMode
.GetScaleX().GetDenominator() );
1117 rOStm
.WriteInt32( aMapMode
.GetScaleY().GetNumerator() );
1118 rOStm
.WriteInt32( aMapMode
.GetScaleY().GetDenominator() );
1119 rOStm
.WriteInt32( aMapMode
.GetOrigin().X() );
1120 rOStm
.WriteInt32( aMapMode
.GetOrigin().Y() );
1124 if( !rOStm
.GetError() )
1126 const sal_uLong nDataStart
= rOStm
.Tell();
1128 if( ImplIsSupportedGraphic() )
1129 WriteImpGraphic( rOStm
, *this );
1131 if( !rOStm
.GetError() )
1133 const sal_uLong nStmPos2
= rOStm
.Tell();
1134 rOStm
.Seek( nDataFieldPos
);
1135 rOStm
.WriteInt32( nStmPos2
- nDataStart
);
1136 rOStm
.Seek( nStmPos2
);
1141 rOStm
.SetEndian( nOldFormat
);
1147 bool ImpGraphic::ImplSwapOut()
1151 if( !ImplIsSwapOut() )
1153 ::utl::TempFile aTempFile
;
1154 const INetURLObject
aTmpURL( aTempFile
.GetURL() );
1156 if( !aTmpURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
).isEmpty() )
1158 std::unique_ptr
<SvStream
> xOStm
;
1161 xOStm
.reset(::utl::UcbStreamHelper::CreateStream( aTmpURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::READWRITE
| StreamMode::SHARE_DENYWRITE
));
1163 catch( const css::uno::Exception
& )
1168 xOStm
->SetVersion( SOFFICE_FILEFORMAT_50
);
1169 xOStm
->SetCompressMode( SvStreamCompressFlags::NATIVE
);
1171 bRet
= ImplSwapOut( xOStm
.get() );
1174 mpSwapFile
= o3tl::make_unique
<ImpSwapFile
>();
1175 mpSwapFile
->aSwapURL
= aTmpURL
;
1183 ::ucbhelper::Content
aCnt( aTmpURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
1184 css::uno::Reference
< css::ucb::XCommandEnvironment
>(),
1185 comphelper::getProcessComponentContext() );
1187 aCnt
.executeCommand( "delete", css::uno::makeAny( true ) );
1189 catch( const css::ucb::ContentCreationException
& )
1192 catch( const css::uno::RuntimeException
& )
1195 catch( const css::ucb::CommandAbortedException
& )
1198 catch( const css::uno::Exception
& )
1209 void ImpGraphic::ImplSwapOutAsLink()
1211 ImplCreateSwapInfo();
1212 ImplClearGraphics();
1216 bool ImpGraphic::ImplSwapOut( SvStream
* xOStm
)
1222 xOStm
->SetBufferSize( GRAPHIC_STREAMBUFSIZE
);
1224 if( !xOStm
->GetError() && ImplWriteEmbedded( *xOStm
) )
1228 if( !xOStm
->GetError() )
1230 ImplCreateSwapInfo();
1231 ImplClearGraphics();
1232 bRet
= mbSwapOut
= true;
1238 SAL_WARN("vcl.gdi", "Graphic SwapOut: No stream for swap out!");
1244 bool ImpGraphic::ImplSwapIn()
1248 if( ImplIsSwapOut() )
1253 aSwapURL
= mpSwapFile
->aSwapURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1255 if( !aSwapURL
.isEmpty() )
1257 std::unique_ptr
<SvStream
> xIStm
;
1260 xIStm
.reset(::utl::UcbStreamHelper::CreateStream( aSwapURL
, StreamMode::READWRITE
| StreamMode::SHARE_DENYWRITE
));
1262 catch( const css::uno::Exception
& )
1268 xIStm
->SetVersion( SOFFICE_FILEFORMAT_50
);
1269 xIStm
->SetCompressMode( SvStreamCompressFlags::NATIVE
);
1271 bRet
= ImplSwapIn( xIStm
.get() );
1282 bool ImpGraphic::ImplSwapIn( SvStream
* xIStm
)
1288 xIStm
->SetBufferSize( GRAPHIC_STREAMBUFSIZE
);
1290 if( !xIStm
->GetError() )
1292 //keep the swap file alive, because its quite possibly the backing storage
1294 std::shared_ptr
<ImpSwapFile
> xSwapFile(std::move(mpSwapFile
));
1295 assert(!mpSwapFile
);
1297 std::shared_ptr
<GraphicReader
> xContext(std::move(mpContext
));
1300 bool bDummyContext
= mbDummyContext
;
1301 mbDummyContext
= false;
1303 bRet
= ImplReadEmbedded( *xIStm
);
1305 //restore ownership of the swap file and context
1306 mpSwapFile
= std::move(xSwapFile
);
1307 mpContext
= std::move(xContext
);
1308 mbDummyContext
= bDummyContext
;
1312 //throw away swapfile, etc.
1323 void ImpGraphic::ImplSetLink( const GfxLink
& rGfxLink
)
1325 mpGfxLink
= o3tl::make_unique
<GfxLink
>( rGfxLink
);
1327 if( mpGfxLink
->IsNative() )
1328 mpGfxLink
->SwapOut();
1331 GfxLink
ImpGraphic::ImplGetLink()
1333 return( mpGfxLink
? *mpGfxLink
: GfxLink() );
1336 bool ImpGraphic::ImplIsLink() const
1338 return ( bool(mpGfxLink
) );
1341 BitmapChecksum
ImpGraphic::ImplGetChecksum() const
1343 BitmapChecksum nRet
= 0;
1345 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
1349 case GraphicType::Default
:
1352 case GraphicType::Bitmap
:
1354 if(maSvgData
.get() && maEx
.IsEmpty())
1356 // use maEx as local buffer for rendered svg
1357 const_cast< ImpGraphic
* >(this)->maEx
= maSvgData
->getReplacement();
1362 nRet
= mpAnimation
->GetChecksum();
1366 nRet
= maEx
.GetChecksum();
1369 if (maPdfData
.hasElements())
1370 // Include the PDF data in the checksum, so a metafile with
1371 // and without PDF data is considered to be different.
1372 nRet
= vcl_get_checksum(nRet
, maPdfData
.getConstArray(), maPdfData
.getLength());
1377 nRet
= maMetaFile
.GetChecksum();
1385 bool ImpGraphic::ImplExportNative( SvStream
& rOStm
) const
1387 bool bResult
= false;
1389 if( !rOStm
.GetError() )
1391 if( !ImplIsSwapOut() )
1393 if( mpGfxLink
&& mpGfxLink
->IsNative() )
1394 bResult
= mpGfxLink
->ExportNative( rOStm
);
1397 WriteImpGraphic( rOStm
, *this );
1398 bResult
= ( rOStm
.GetError() == ERRCODE_NONE
);
1402 rOStm
.SetError( SVSTREAM_GENERALERROR
);
1409 void ReadImpGraphic( SvStream
& rIStm
, ImpGraphic
& rImpGraphic
)
1411 if (rIStm
.GetError())
1414 const sal_uLong nStmPos1
= rIStm
.Tell();
1417 rImpGraphic
.ImplClear();
1420 rIStm
.ReadUInt32( nTmp
);
1422 // if there is no more data, avoid further expensive
1423 // reading which will create VDevs and other stuff, just to
1424 // read nothing. CAUTION: Eof is only true AFTER reading another
1425 // byte, a speciality of SvMemoryStream (!)
1426 if (rIStm
.GetError() || rIStm
.IsEof())
1429 if (NATIVE_FORMAT_50
== nTmp
)
1435 std::unique_ptr
<VersionCompat
> pCompat(new VersionCompat( rIStm
, StreamMode::READ
));
1436 pCompat
.reset(); // destructor writes stuff into the header
1438 ReadGfxLink( rIStm
, aLink
);
1440 // set dummy link to avoid creation of additional link after filtering;
1441 // we set a default link to avoid unnecessary swapping of native data
1442 aGraphic
.SetLink( GfxLink() );
1444 if( !rIStm
.GetError() && aLink
.LoadNative( aGraphic
) )
1446 // set link only, if no other link was set
1447 const bool bSetLink
= ( !rImpGraphic
.mpGfxLink
);
1450 rImpGraphic
= *aGraphic
.ImplGetImpGraphic();
1452 if( aLink
.IsPrefMapModeValid() )
1453 rImpGraphic
.ImplSetPrefMapMode( aLink
.GetPrefMapMode() );
1455 if( aLink
.IsPrefSizeValid() )
1456 rImpGraphic
.ImplSetPrefSize( aLink
.GetPrefSize() );
1459 rImpGraphic
.ImplSetLink( aLink
);
1463 rIStm
.Seek( nStmPos1
);
1464 rIStm
.SetError( ERRCODE_IO_WRONGFORMAT
);
1470 const SvStreamEndian nOldFormat
= rIStm
.GetEndian();
1472 rIStm
.SeekRel( -4 );
1473 rIStm
.SetEndian( SvStreamEndian::LITTLE
);
1474 ReadDIBBitmapEx(aBmpEx
, rIStm
);
1476 if( !rIStm
.GetError() )
1478 sal_uInt32
nMagic1(0), nMagic2(0);
1479 sal_uLong nActPos
= rIStm
.Tell();
1481 rIStm
.ReadUInt32( nMagic1
).ReadUInt32( nMagic2
);
1482 rIStm
.Seek( nActPos
);
1484 rImpGraphic
= ImpGraphic( aBmpEx
);
1486 if( !rIStm
.GetError() && ( 0x5344414e == nMagic1
) && ( 0x494d4931 == nMagic2
) )
1488 rImpGraphic
.mpAnimation
= o3tl::make_unique
<Animation
>();
1489 ReadAnimation( rIStm
, *rImpGraphic
.mpAnimation
);
1491 // #108077# manually set loaded BmpEx to Animation
1492 // (which skips loading its BmpEx if already done)
1493 rImpGraphic
.mpAnimation
->SetBitmapEx(aBmpEx
);
1502 rIStm
.Seek( nStmPos1
);
1504 ReadGDIMetaFile( rIStm
, aMtf
);
1506 if( !rIStm
.GetError() )
1512 sal_uInt32 nOrigError
= rIStm
.GetErrorCode();
1513 // try to stream in Svg defining data (length, byte array and evtl. path)
1514 // See below (operator<<) for more information
1515 const sal_uInt32
nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
1517 rIStm
.Seek(nStmPos1
);
1519 rIStm
.ReadUInt32( nMagic
);
1521 if (nSvgMagic
== nMagic
)
1523 sal_uInt32
nSvgDataArrayLength(0);
1524 rIStm
.ReadUInt32(nSvgDataArrayLength
);
1526 if (nSvgDataArrayLength
)
1528 SvgDataArray
aNewData(nSvgDataArrayLength
);
1530 rIStm
.ReadBytes(aNewData
.getArray(), nSvgDataArrayLength
);
1531 OUString aPath
= rIStm
.ReadUniOrByteString(rIStm
.GetStreamCharSet());
1533 if (!rIStm
.GetError())
1535 SvgDataPtr
aSvgDataPtr(new SvgData(aNewData
, aPath
));
1536 rImpGraphic
= aSvgDataPtr
;
1540 else if (nMagic
== nPdfMagic
)
1542 // Stream in PDF data.
1543 sal_uInt32 nPdfDataLength
= 0;
1544 rIStm
.ReadUInt32(nPdfDataLength
);
1548 uno::Sequence
<sal_Int8
> aPdfData(nPdfDataLength
);
1549 rIStm
.ReadBytes(aPdfData
.getArray(), nPdfDataLength
);
1550 if (!rIStm
.GetError())
1551 rImpGraphic
.maPdfData
= aPdfData
;
1556 rIStm
.SetError(nOrigError
);
1559 rIStm
.Seek(nStmPos1
);
1563 rIStm
.SetEndian( nOldFormat
);
1566 void WriteImpGraphic(SvStream
& rOStm
, const ImpGraphic
& rImpGraphic
)
1568 if (rOStm
.GetError())
1571 if (rImpGraphic
.ImplIsSwapOut())
1573 rOStm
.SetError( SVSTREAM_GENERALERROR
);
1577 if( ( rOStm
.GetVersion() >= SOFFICE_FILEFORMAT_50
) &&
1578 ( rOStm
.GetCompressMode() & SvStreamCompressFlags::NATIVE
) &&
1579 rImpGraphic
.mpGfxLink
&& rImpGraphic
.mpGfxLink
->IsNative() &&
1580 !rImpGraphic
.maPdfData
.hasElements())
1583 rOStm
.WriteUInt32( NATIVE_FORMAT_50
);
1585 // write compat info
1586 std::unique_ptr
<VersionCompat
> pCompat(new VersionCompat( rOStm
, StreamMode::WRITE
, 1 ));
1587 pCompat
.reset(); // destructor writes stuff into the header
1589 rImpGraphic
.mpGfxLink
->SetPrefMapMode( rImpGraphic
.ImplGetPrefMapMode() );
1590 rImpGraphic
.mpGfxLink
->SetPrefSize( rImpGraphic
.ImplGetPrefSize() );
1591 WriteGfxLink( rOStm
, *rImpGraphic
.mpGfxLink
);
1596 const SvStreamEndian nOldFormat
= rOStm
.GetEndian();
1597 rOStm
.SetEndian( SvStreamEndian::LITTLE
);
1599 switch( rImpGraphic
.ImplGetType() )
1601 case GraphicType::NONE
:
1602 case GraphicType::Default
:
1605 case GraphicType::Bitmap
:
1607 if(rImpGraphic
.getSvgData().get())
1609 // stream out Svg defining data (length, byte array and evtl. path)
1610 // this is used e.g. in swapping out graphic data and in transporting it over UNO API
1611 // as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
1612 // no problem to extend it; only used at runtime
1613 const sal_uInt32
nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
1615 rOStm
.WriteUInt32( nSvgMagic
);
1616 rOStm
.WriteUInt32( rImpGraphic
.getSvgData()->getSvgDataArrayLength() );
1617 rOStm
.WriteBytes(rImpGraphic
.getSvgData()->getSvgDataArray().getConstArray(),
1618 rImpGraphic
.getSvgData()->getSvgDataArrayLength());
1619 rOStm
.WriteUniOrByteString(rImpGraphic
.getSvgData()->getPath(),
1620 rOStm
.GetStreamCharSet());
1622 else if (rImpGraphic
.maPdfData
.hasElements())
1624 // Stream out PDF data.
1625 rOStm
.WriteUInt32(nPdfMagic
);
1626 rOStm
.WriteUInt32(rImpGraphic
.maPdfData
.getLength());
1627 rOStm
.WriteBytes(rImpGraphic
.maPdfData
.getConstArray(), rImpGraphic
.maPdfData
.getLength());
1629 else if( rImpGraphic
.ImplIsAnimated())
1631 WriteAnimation( rOStm
, *rImpGraphic
.mpAnimation
);
1635 WriteDIBBitmapEx(rImpGraphic
.maEx
, rOStm
);
1642 if( rImpGraphic
.ImplIsSupportedGraphic() )
1643 WriteGDIMetaFile( rOStm
, rImpGraphic
.maMetaFile
);
1648 rOStm
.SetEndian( nOldFormat
);
1652 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */