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>
21 #include <sal/log.hxx>
23 #include <comphelper/base64.hxx>
24 #include <comphelper/graphicmimetype.hxx>
25 #include <tools/debug.hxx>
26 #include <vcl/virdev.hxx>
27 #include <sfx2/docfile.hxx>
28 #include <svx/xoutbmp.hxx>
29 #include <vcl/graphicfilter.hxx>
30 #include <vcl/cvtgrf.hxx>
33 #include <com/sun/star/beans/XPropertySet.hpp>
35 constexpr OUStringLiteral FORMAT_SVG
= u
"svg";
36 constexpr OUStringLiteral FORMAT_WMF
= u
"wmf";
37 constexpr OUStringLiteral FORMAT_EMF
= u
"emf";
38 constexpr OUStringLiteral FORMAT_PDF
= u
"pdf";
40 constexpr OUStringLiteral FORMAT_BMP
= u
"bmp";
41 constexpr OUStringLiteral FORMAT_GIF
= u
"gif";
42 constexpr OUStringLiteral FORMAT_JPG
= u
"jpg";
43 constexpr OUStringLiteral FORMAT_PNG
= u
"png";
44 constexpr OUStringLiteral FORMAT_TIF
= u
"tif";
45 constexpr OUStringLiteral FORMAT_WEBP
= u
"webp";
47 using namespace com::sun::star
;
49 Animation
XOutBitmap::MirrorAnimation( const Animation
& rAnimation
, bool bHMirr
, bool bVMirr
)
51 Animation
aNewAnim( rAnimation
);
53 if( bHMirr
|| bVMirr
)
55 const Size
& rGlobalSize
= aNewAnim
.GetDisplaySizePixel();
56 BmpMirrorFlags nMirrorFlags
= BmpMirrorFlags::NONE
;
59 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
62 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
64 for( sal_uInt16 i
= 0, nCount
= aNewAnim
.Count(); i
< nCount
; i
++ )
66 AnimationFrame
aAnimationFrame( aNewAnim
.Get( i
) );
68 // mirror the BitmapEx
69 aAnimationFrame
.maBitmapEx
.Mirror( nMirrorFlags
);
71 // Adjust the positions inside the whole bitmap
73 aAnimationFrame
.maPositionPixel
.setX(rGlobalSize
.Width() - aAnimationFrame
.maPositionPixel
.X() -
74 aAnimationFrame
.maSizePixel
.Width());
77 aAnimationFrame
.maPositionPixel
.setY(rGlobalSize
.Height() - aAnimationFrame
.maPositionPixel
.Y() -
78 aAnimationFrame
.maSizePixel
.Height());
80 aNewAnim
.Replace(aAnimationFrame
, i
);
87 Graphic
XOutBitmap::MirrorGraphic( const Graphic
& rGraphic
, const BmpMirrorFlags nMirrorFlags
)
91 if( nMirrorFlags
!= BmpMirrorFlags::NONE
)
93 if( rGraphic
.IsAnimated() )
95 aRetGraphic
= MirrorAnimation( rGraphic
.GetAnimation(),
96 bool( nMirrorFlags
& BmpMirrorFlags::Horizontal
),
97 bool( nMirrorFlags
& BmpMirrorFlags::Vertical
) );
101 BitmapEx
aBmp( rGraphic
.GetBitmapEx() );
102 aBmp
.Mirror( nMirrorFlags
);
107 aRetGraphic
= rGraphic
;
112 static OUString
match(std::u16string_view filter
, const OUString
& expected
, bool matchEmpty
= true)
114 return (matchEmpty
&& filter
.empty()) || expected
.equalsIgnoreAsciiCase(filter
) ? expected
118 static OUString
isKnownVectorFormat(const Graphic
& rGraphic
, std::u16string_view rFilter
)
120 const auto& pData(rGraphic
.getVectorGraphicData());
121 if (!pData
|| pData
->getBinaryDataContainer().getSize() == 0)
124 // Does the filter name match the original format?
125 switch (pData
->getType())
127 case VectorGraphicDataType::Svg
:
128 return match(rFilter
, FORMAT_SVG
, false);
129 case VectorGraphicDataType::Wmf
:
130 return match(rFilter
, FORMAT_WMF
, false);
131 case VectorGraphicDataType::Emf
:
132 return match(rFilter
, FORMAT_EMF
, false);
133 case VectorGraphicDataType::Pdf
:
134 return match(rFilter
, FORMAT_PDF
, false);
137 if (rGraphic
.GetGfxLink().IsEMF())
138 return match(rFilter
, FORMAT_EMF
, false);
143 static OUString
isKnownRasterFormat(const GfxLink
& rLink
, std::u16string_view rFilter
)
145 // tdf#60684: use native format if possible but it must correspond to filter name
146 // or no specific format has been required
147 // without this, you may save for example file with png extension but jpg content
148 switch (rLink
.GetType())
150 case GfxLinkType::NativeGif
:
151 return match(rFilter
, FORMAT_GIF
);
153 // #i15508# added BMP type for better exports (no call/trigger found, prob used in HTML export)
154 case GfxLinkType::NativeBmp
:
155 return match(rFilter
, FORMAT_BMP
);
157 case GfxLinkType::NativeJpg
:
158 return match(rFilter
, FORMAT_JPG
);
159 case GfxLinkType::NativePng
:
160 return match(rFilter
, FORMAT_PNG
);
161 case GfxLinkType::NativeTif
:
162 return match(rFilter
, FORMAT_TIF
);
163 case GfxLinkType::NativeWebp
:
164 return match(rFilter
, FORMAT_WEBP
);
170 ErrCode
XOutBitmap::WriteGraphic( const Graphic
& rGraphic
, OUString
& rFileName
,
171 const OUString
& rFilterName
, const XOutFlags nFlags
,
172 const Size
* pMtfSize_100TH_MM
,
173 const css::uno::Sequence
< css::beans::PropertyValue
>* pFilterData
,
174 OUString
* pMediaType
)
176 if( rGraphic
.GetType() == GraphicType::NONE
)
179 INetURLObject
aURL( rFileName
);
180 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
182 DBG_ASSERT( aURL
.GetProtocol() != INetProtocol::NotValid
, "XOutBitmap::WriteGraphic(...): invalid URL" );
184 // calculate correct file name
185 if( !( nFlags
& XOutFlags::DontExpandFilename
) )
187 OUString
aStr( OUString::number( rGraphic
.GetChecksum(), 16 ) );
188 if ( aStr
[0] == '-' )
189 aStr
= OUString::Concat("m") + aStr
.subView(1);
190 OUString aName
= aURL
.getBase() + "_" + aURL
.getExtension() + "_" + aStr
;
191 aURL
.setBase( aName
);
194 // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible)
195 if (OUString aExt
= isKnownVectorFormat(rGraphic
, rFilterName
); !aExt
.isEmpty())
197 if (!(nFlags
& XOutFlags::DontAddExtension
))
198 aURL
.setExtension(aExt
);
200 rFileName
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
202 if (auto xGraphic
= rGraphic
.GetXGraphic().query
<css::beans::XPropertySet
>())
203 xGraphic
->getPropertyValue("MimeType") >>= *pMediaType
;
205 SfxMedium
aMedium(aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
), StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
| StreamMode::TRUNC
);
206 SvStream
* pOStm
= aMedium
.GetOutStream();
210 rGraphic
.getVectorGraphicData()->getBinaryDataContainer().writeToStream(*pOStm
);
213 if (!aMedium
.GetError())
218 if( ( nFlags
& XOutFlags::UseNativeIfPossible
) &&
219 !( nFlags
& XOutFlags::MirrorHorz
) &&
220 !( nFlags
& XOutFlags::MirrorVert
) &&
221 ( rGraphic
.GetType() != GraphicType::GdiMetafile
) && rGraphic
.IsGfxLink() )
223 // try to write native link
224 const GfxLink
aGfxLink( rGraphic
.GetGfxLink() );
225 if (OUString aExt
= isKnownRasterFormat(aGfxLink
, rFilterName
); !aExt
.isEmpty())
227 if( !(nFlags
& XOutFlags::DontAddExtension
) )
228 aURL
.setExtension( aExt
);
229 rFileName
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
231 if (auto xGraphic
= rGraphic
.GetXGraphic().query
<css::beans::XPropertySet
>())
232 xGraphic
->getPropertyValue("MimeType") >>= *pMediaType
;
234 SfxMedium
aMedium(aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
), StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
| StreamMode::TRUNC
);
235 SvStream
* pOStm
= aMedium
.GetOutStream();
237 if( pOStm
&& aGfxLink
.GetDataSize() && aGfxLink
.GetData() )
239 pOStm
->WriteBytes(aGfxLink
.GetData(), aGfxLink
.GetDataSize());
242 if( !aMedium
.GetError() )
248 OUString
aFilter( rFilterName
);
249 bool bTransparent
= rGraphic
.IsTransparent(), bAnimated
= rGraphic
.IsAnimated();
250 bool bWriteTransGrf
= ( aFilter
.equalsIgnoreAsciiCase( "transgrf" ) ) ||
251 ( aFilter
.equalsIgnoreAsciiCase( "gif" ) ) ||
252 ( nFlags
& XOutFlags::UseGifIfPossible
) ||
253 ( ( nFlags
& XOutFlags::UseGifIfSensible
) && ( bAnimated
|| bTransparent
) );
255 // get filter and extension
257 aFilter
= FORMAT_GIF
;
259 sal_uInt16 nFilter
= rFilter
.GetExportFormatNumberForShortName( aFilter
);
261 if( GRFILTER_FORMAT_NOTFOUND
== nFilter
)
263 nFilter
= rFilter
.GetExportFormatNumberForShortName( FORMAT_PNG
);
265 if( GRFILTER_FORMAT_NOTFOUND
== nFilter
)
266 nFilter
= rFilter
.GetExportFormatNumberForShortName( FORMAT_BMP
);
269 if( GRFILTER_FORMAT_NOTFOUND
!= nFilter
)
272 OUString aExt
= rFilter
.GetExportFormatShortName( nFilter
).toAsciiLowerCase();
280 if( pMtfSize_100TH_MM
&& ( rGraphic
.GetType() != GraphicType::Bitmap
) )
282 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
283 const Size
aSize(pVDev
->LogicToPixel(*pMtfSize_100TH_MM
, MapMode(MapUnit::Map100thMM
)));
285 if( pVDev
->SetOutputSizePixel( aSize
) )
287 const Wallpaper
aWallpaper( pVDev
->GetBackground() );
290 pVDev
->SetBackground( Wallpaper( COL_BLACK
) );
292 rGraphic
.Draw(*pVDev
, aPt
, aSize
);
294 const Bitmap
aBitmap( pVDev
->GetBitmap( aPt
, aSize
) );
296 pVDev
->SetBackground( aWallpaper
);
298 rGraphic
.Draw(*pVDev
, aPt
, aSize
);
300 pVDev
->SetRasterOp( RasterOp::Xor
);
301 pVDev
->DrawBitmap( aPt
, aSize
, aBitmap
);
302 aGraphic
= BitmapEx( aBitmap
, pVDev
->GetBitmap( aPt
, aSize
) );
305 aGraphic
= rGraphic
.GetBitmapEx();
308 aGraphic
= rGraphic
.GetBitmapEx();
313 if( pMtfSize_100TH_MM
&& ( rGraphic
.GetType() != GraphicType::Bitmap
) )
315 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
316 const Size
aSize(pVDev
->LogicToPixel(*pMtfSize_100TH_MM
, MapMode(MapUnit::Map100thMM
)));
318 if( pVDev
->SetOutputSizePixel( aSize
) )
320 rGraphic
.Draw(*pVDev
, Point(), aSize
);
321 aGraphic
= BitmapEx(pVDev
->GetBitmap(Point(), aSize
));
324 aGraphic
= rGraphic
.GetBitmapEx();
327 aGraphic
= rGraphic
.GetBitmapEx();
331 if( ( nFlags
& XOutFlags::MirrorHorz
) || ( nFlags
& XOutFlags::MirrorVert
) )
333 BmpMirrorFlags nBmpMirrorFlags
= BmpMirrorFlags::NONE
;
334 if( nFlags
& XOutFlags::MirrorHorz
)
335 nBmpMirrorFlags
|= BmpMirrorFlags::Horizontal
;
336 if( nFlags
& XOutFlags::MirrorVert
)
337 nBmpMirrorFlags
|= BmpMirrorFlags::Vertical
;
338 aGraphic
= MirrorGraphic( aGraphic
, nBmpMirrorFlags
);
341 if (aGraphic
.GetType() != GraphicType::NONE
)
343 if( !(nFlags
& XOutFlags::DontAddExtension
) )
344 aURL
.setExtension( aExt
);
345 rFileName
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
347 *pMediaType
= rFilter
.GetExportFormatMediaType(nFilter
);
348 return ExportGraphic( aGraphic
, aURL
, rFilter
, nFilter
, pFilterData
);
352 return ERRCODE_GRFILTER_FILTERERROR
;
355 bool XOutBitmap::GraphicToBase64(const Graphic
& rGraphic
, OUString
& rOUString
, bool bAddPrefix
,
356 ConvertDataFormat aTargetFormat
)
358 SvMemoryStream aOStm
;
359 GfxLink aLink
= rGraphic
.GetGfxLink();
361 if (aTargetFormat
== ConvertDataFormat::Unknown
)
363 switch (aLink
.GetType())
365 case GfxLinkType::NativeJpg
:
366 aTargetFormat
= ConvertDataFormat::JPG
;
368 case GfxLinkType::NativePng
:
369 aTargetFormat
= ConvertDataFormat::PNG
;
371 case GfxLinkType::NativeSvg
:
372 aTargetFormat
= ConvertDataFormat::SVG
;
375 // save everything else (including gif) into png
376 aTargetFormat
= ConvertDataFormat::PNG
;
381 ErrCode nErr
= GraphicConverter::Export(aOStm
,rGraphic
,aTargetFormat
);
384 SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr
);
387 css::uno::Sequence
<sal_Int8
> aOStmSeq( static_cast<sal_Int8
const *>(aOStm
.GetData()),aOStm
.TellEnd() );
388 OUStringBuffer aStrBuffer
;
389 ::comphelper::Base64::encode(aStrBuffer
,aOStmSeq
);
390 rOUString
= aStrBuffer
.makeStringAndClear();
395 = comphelper::GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(aTargetFormat
);
396 rOUString
= aMimeType
+ ";base64," + rOUString
;
402 ErrCode
XOutBitmap::ExportGraphic( const Graphic
& rGraphic
, const INetURLObject
& rURL
,
403 GraphicFilter
& rFilter
, const sal_uInt16 nFormat
,
404 const css::uno::Sequence
< css::beans::PropertyValue
>* pFilterData
)
406 DBG_ASSERT( rURL
.GetProtocol() != INetProtocol::NotValid
, "XOutBitmap::ExportGraphic(...): invalid URL" );
408 SfxMedium
aMedium( rURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
| StreamMode::TRUNC
);
409 SvStream
* pOStm
= aMedium
.GetOutStream();
410 ErrCode nRet
= ERRCODE_GRFILTER_IOERROR
;
414 nRet
= rFilter
.ExportGraphic( rGraphic
, rURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), *pOStm
, nFormat
, pFilterData
);
418 if( aMedium
.GetError() && ( ERRCODE_NONE
== nRet
) )
419 nRet
= ERRCODE_GRFILTER_IOERROR
;
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */