LibreOffice Module svx (master) 1
_xoutbmp.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <comphelper/base64.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>
31#include <memory>
32
33#include <com/sun/star/beans/XPropertySet.hpp>
34
35constexpr OUStringLiteral FORMAT_SVG = u"svg";
36constexpr OUStringLiteral FORMAT_WMF = u"wmf";
37constexpr OUStringLiteral FORMAT_EMF = u"emf";
38constexpr OUStringLiteral FORMAT_PDF = u"pdf";
39
40constexpr OUStringLiteral FORMAT_BMP = u"bmp";
41constexpr OUStringLiteral FORMAT_GIF = u"gif";
42constexpr OUStringLiteral FORMAT_JPG = u"jpg";
43constexpr OUStringLiteral FORMAT_PNG = u"png";
44constexpr OUStringLiteral FORMAT_TIF = u"tif";
45constexpr OUStringLiteral FORMAT_WEBP = u"webp";
46
47using namespace com::sun::star;
48
49Animation XOutBitmap::MirrorAnimation( const Animation& rAnimation, bool bHMirr, bool bVMirr )
50{
51 Animation aNewAnim( rAnimation );
52
53 if( bHMirr || bVMirr )
54 {
55 const Size& rGlobalSize = aNewAnim.GetDisplaySizePixel();
56 BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;
57
58 if( bHMirr )
59 nMirrorFlags |= BmpMirrorFlags::Horizontal;
60
61 if( bVMirr )
62 nMirrorFlags |= BmpMirrorFlags::Vertical;
63
64 for( sal_uInt16 i = 0, nCount = aNewAnim.Count(); i < nCount; i++ )
65 {
66 AnimationFrame aAnimationFrame( aNewAnim.Get( i ) );
67
68 // mirror the BitmapEx
69 aAnimationFrame.maBitmapEx.Mirror( nMirrorFlags );
70
71 // Adjust the positions inside the whole bitmap
72 if( bHMirr )
73 aAnimationFrame.maPositionPixel.setX(rGlobalSize.Width() - aAnimationFrame.maPositionPixel.X() -
74 aAnimationFrame.maSizePixel.Width());
75
76 if( bVMirr )
77 aAnimationFrame.maPositionPixel.setY(rGlobalSize.Height() - aAnimationFrame.maPositionPixel.Y() -
78 aAnimationFrame.maSizePixel.Height());
79
80 aNewAnim.Replace(aAnimationFrame, i);
81 }
82 }
83
84 return aNewAnim;
85}
86
87Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const BmpMirrorFlags nMirrorFlags )
88{
89 Graphic aRetGraphic;
90
91 if( nMirrorFlags != BmpMirrorFlags::NONE )
92 {
93 if( rGraphic.IsAnimated() )
94 {
95 aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(),
96 bool( nMirrorFlags & BmpMirrorFlags::Horizontal ),
97 bool( nMirrorFlags & BmpMirrorFlags::Vertical ) );
98 }
99 else
100 {
101 BitmapEx aBmp( rGraphic.GetBitmapEx() );
102 aBmp.Mirror( nMirrorFlags );
103 aRetGraphic = aBmp;
104 }
105 }
106 else
107 aRetGraphic = rGraphic;
108
109 return aRetGraphic;
110}
111
112static OUString match(std::u16string_view filter, const OUString& expected, bool matchEmpty = true)
113{
114 return (matchEmpty && filter.empty()) || expected.equalsIgnoreAsciiCase(filter) ? expected
115 : OUString();
116}
117
118static OUString isKnownVectorFormat(const Graphic& rGraphic, std::u16string_view rFilter)
119{
120 const auto& pData(rGraphic.getVectorGraphicData());
121 if (!pData || pData->getBinaryDataContainer().getSize() == 0)
122 return {};
123
124 // Does the filter name match the original format?
125 switch (pData->getType())
126 {
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);
135 }
136
137 if (rGraphic.GetGfxLink().IsEMF())
138 return match(rFilter, FORMAT_EMF, false);
139
140 return {};
141}
142
143static OUString isKnownRasterFormat(const GfxLink& rLink, std::u16string_view rFilter)
144{
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())
149 {
150 case GfxLinkType::NativeGif:
151 return match(rFilter, FORMAT_GIF);
152
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);
156
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);
165 default:
166 return {};
167 }
168}
169
170ErrCode 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 )
175{
176 if( rGraphic.GetType() == GraphicType::NONE )
177 return ERRCODE_NONE;
178
179 INetURLObject aURL( rFileName );
181
182 DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::WriteGraphic(...): invalid URL" );
183
184 // calculate correct file name
185 if( !( nFlags & XOutFlags::DontExpandFilename ) )
186 {
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 );
192 }
193
194 // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible)
195 if (OUString aExt = isKnownVectorFormat(rGraphic, rFilterName); !aExt.isEmpty())
196 {
197 if (!(nFlags & XOutFlags::DontAddExtension))
198 aURL.setExtension(aExt);
199
200 rFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
201 if (pMediaType)
202 if (auto xGraphic = rGraphic.GetXGraphic().query<css::beans::XPropertySet>())
203 xGraphic->getPropertyValue("MimeType") >>= *pMediaType;
204
205 SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
206 SvStream* pOStm = aMedium.GetOutStream();
207
208 if (pOStm)
209 {
210 rGraphic.getVectorGraphicData()->getBinaryDataContainer().writeToStream(*pOStm);
211 aMedium.Commit();
212
213 if (!aMedium.GetError())
214 return ERRCODE_NONE;
215 }
216 }
217
218 if( ( nFlags & XOutFlags::UseNativeIfPossible ) &&
219 !( nFlags & XOutFlags::MirrorHorz ) &&
220 !( nFlags & XOutFlags::MirrorVert ) &&
221 ( rGraphic.GetType() != GraphicType::GdiMetafile ) && rGraphic.IsGfxLink() )
222 {
223 // try to write native link
224 const GfxLink aGfxLink( rGraphic.GetGfxLink() );
225 if (OUString aExt = isKnownRasterFormat(aGfxLink, rFilterName); !aExt.isEmpty())
226 {
227 if( !(nFlags & XOutFlags::DontAddExtension) )
228 aURL.setExtension( aExt );
229 rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
230 if (pMediaType)
231 if (auto xGraphic = rGraphic.GetXGraphic().query<css::beans::XPropertySet>())
232 xGraphic->getPropertyValue("MimeType") >>= *pMediaType;
233
234 SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
235 SvStream* pOStm = aMedium.GetOutStream();
236
237 if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() )
238 {
239 pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
240 aMedium.Commit();
241
242 if( !aMedium.GetError() )
243 return ERRCODE_NONE;
244 }
245 }
246 }
247
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 ) );
254
255 // get filter and extension
256 if( bWriteTransGrf )
257 aFilter = FORMAT_GIF;
258
259 sal_uInt16 nFilter = rFilter.GetExportFormatNumberForShortName( aFilter );
260
261 if( GRFILTER_FORMAT_NOTFOUND == nFilter )
262 {
264
265 if( GRFILTER_FORMAT_NOTFOUND == nFilter )
267 }
268
269 if( GRFILTER_FORMAT_NOTFOUND != nFilter )
270 {
271 Graphic aGraphic;
272 OUString aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase();
273
274 if( bWriteTransGrf )
275 {
276 if( bAnimated )
277 aGraphic = rGraphic;
278 else
279 {
280 if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
281 {
283 const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
284
285 if( pVDev->SetOutputSizePixel( aSize ) )
286 {
287 const Wallpaper aWallpaper( pVDev->GetBackground() );
288 const Point aPt;
289
290 pVDev->SetBackground( Wallpaper( COL_BLACK ) );
291 pVDev->Erase();
292 rGraphic.Draw(*pVDev, aPt, aSize);
293
294 const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) );
295
296 pVDev->SetBackground( aWallpaper );
297 pVDev->Erase();
298 rGraphic.Draw(*pVDev, aPt, aSize);
299
300 pVDev->SetRasterOp( RasterOp::Xor );
301 pVDev->DrawBitmap( aPt, aSize, aBitmap );
302 aGraphic = BitmapEx( aBitmap, pVDev->GetBitmap( aPt, aSize ) );
303 }
304 else
305 aGraphic = rGraphic.GetBitmapEx();
306 }
307 else
308 aGraphic = rGraphic.GetBitmapEx();
309 }
310 }
311 else
312 {
313 if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
314 {
316 const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
317
318 if( pVDev->SetOutputSizePixel( aSize ) )
319 {
320 rGraphic.Draw(*pVDev, Point(), aSize);
321 aGraphic = BitmapEx(pVDev->GetBitmap(Point(), aSize));
322 }
323 else
324 aGraphic = rGraphic.GetBitmapEx();
325 }
326 else
327 aGraphic = rGraphic.GetBitmapEx();
328 }
329
330 // mirror?
331 if( ( nFlags & XOutFlags::MirrorHorz ) || ( nFlags & XOutFlags::MirrorVert ) )
332 {
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 );
339 }
340
341 if (aGraphic.GetType() != GraphicType::NONE)
342 {
343 if( !(nFlags & XOutFlags::DontAddExtension) )
344 aURL.setExtension( aExt );
345 rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
346 if (pMediaType)
347 *pMediaType = rFilter.GetExportFormatMediaType(nFilter);
348 return ExportGraphic( aGraphic, aURL, rFilter, nFilter, pFilterData );
349 }
350 }
351
353}
354
355bool XOutBitmap::GraphicToBase64(const Graphic& rGraphic, OUString& rOUString, bool bAddPrefix,
356 ConvertDataFormat aTargetFormat)
357{
358 SvMemoryStream aOStm;
359 GfxLink aLink = rGraphic.GetGfxLink();
360
361 if (aTargetFormat == ConvertDataFormat::Unknown)
362 {
363 switch (aLink.GetType())
364 {
365 case GfxLinkType::NativeJpg:
366 aTargetFormat = ConvertDataFormat::JPG;
367 break;
368 case GfxLinkType::NativePng:
369 aTargetFormat = ConvertDataFormat::PNG;
370 break;
371 case GfxLinkType::NativeSvg:
372 aTargetFormat = ConvertDataFormat::SVG;
373 break;
374 default:
375 // save everything else (including gif) into png
376 aTargetFormat = ConvertDataFormat::PNG;
377 break;
378 }
379 }
380
381 ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat);
382 if ( nErr )
383 {
384 SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr );
385 return false;
386 }
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();
391
392 if (bAddPrefix)
393 {
394 OUString aMimeType
396 rOUString = aMimeType + ";base64," + rOUString;
397 }
398
399 return true;
400}
401
403 GraphicFilter& rFilter, const sal_uInt16 nFormat,
404 const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
405{
406 DBG_ASSERT( rURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::ExportGraphic(...): invalid URL" );
407
408 SfxMedium aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC );
409 SvStream* pOStm = aMedium.GetOutStream();
411
412 if( pOStm )
413 {
414 nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData );
415
416 aMedium.Commit();
417
418 if( aMedium.GetError() && ( ERRCODE_NONE == nRet ) )
420 }
421
422 return nRet;
423}
424
425
426/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr OUStringLiteral FORMAT_BMP
Definition: _xoutbmp.cxx:40
constexpr OUStringLiteral FORMAT_SVG
Definition: _xoutbmp.cxx:35
static OUString match(std::u16string_view filter, const OUString &expected, bool matchEmpty=true)
Definition: _xoutbmp.cxx:112
constexpr OUStringLiteral FORMAT_PDF
Definition: _xoutbmp.cxx:38
constexpr OUStringLiteral FORMAT_EMF
Definition: _xoutbmp.cxx:37
static OUString isKnownVectorFormat(const Graphic &rGraphic, std::u16string_view rFilter)
Definition: _xoutbmp.cxx:118
constexpr OUStringLiteral FORMAT_PNG
Definition: _xoutbmp.cxx:43
constexpr OUStringLiteral FORMAT_GIF
Definition: _xoutbmp.cxx:41
constexpr OUStringLiteral FORMAT_TIF
Definition: _xoutbmp.cxx:44
constexpr OUStringLiteral FORMAT_WMF
Definition: _xoutbmp.cxx:36
constexpr OUStringLiteral FORMAT_WEBP
Definition: _xoutbmp.cxx:45
static OUString isKnownRasterFormat(const GfxLink &rLink, std::u16string_view rFilter)
Definition: _xoutbmp.cxx:143
constexpr OUStringLiteral FORMAT_JPG
Definition: _xoutbmp.cxx:42
void Replace(const AnimationFrame &rNewAnimationBmp, sal_uInt16 nAnimation)
size_t Count() const
const Size & GetDisplaySizePixel() const
const AnimationFrame & Get(sal_uInt16 nAnimation) const
bool Mirror(BmpMirrorFlags nMirrorFlags)
static ErrCode Export(SvStream &rOStm, const Graphic &rGraphic, ConvertDataFormat nFormat)
sal_uInt16 GetExportFormatNumberForShortName(std::u16string_view rShortName)
static GraphicFilter & GetGraphicFilter()
ErrCode ExportGraphic(const Graphic &rGraphic, const INetURLObject &rPath, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData=nullptr)
OUString GetExportFormatShortName(sal_uInt16 nFormat)
OUString GetExportFormatMediaType(sal_uInt16 nFormat)
css::uno::Reference< css::graphic::XGraphic > GetXGraphic() const
Animation GetAnimation() const
GraphicType GetType() const
GfxLink GetGfxLink() const
bool IsAnimated() const
BitmapChecksum GetChecksum() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
void Draw(OutputDevice &rOutDev, const Point &rDestPt) const
bool IsGfxLink() const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
bool IsTransparent() const
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
INetProtocol GetProtocol() const
constexpr tools::Long Y() const
void setX(tools::Long nX)
void setY(tools::Long nY)
constexpr tools::Long X() const
ErrCode GetError() const
SvStream * GetOutStream()
bool Commit()
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const void * GetData()
virtual sal_uInt64 TellEnd() override
std::size_t WriteBytes(const void *pData, std::size_t nSize)
static ErrCode WriteGraphic(const Graphic &rGraphic, OUString &rFileName, const OUString &rFilterName, const XOutFlags nFlags, const Size *pMtfSize_100TH_MM=nullptr, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData=nullptr, OUString *pMediaType=nullptr)
Definition: _xoutbmp.cxx:170
static bool GraphicToBase64(const Graphic &rGraphic, OUString &rOUString, bool bAddPrefix=true, ConvertDataFormat aTargetFormat=ConvertDataFormat::Unknown)
Definition: _xoutbmp.cxx:355
static Animation MirrorAnimation(const Animation &rAnimation, bool bHMirr, bool bVMirr)
Definition: _xoutbmp.cxx:49
static Graphic MirrorGraphic(const Graphic &rGraphic, const BmpMirrorFlags nMirrorFlags)
Definition: _xoutbmp.cxx:87
static ErrCode ExportGraphic(const Graphic &rGraphic, const INetURLObject &rURL, GraphicFilter &rFilter, const sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData)
Definition: _xoutbmp.cxx:402
static void encode(OUStringBuffer &aStrBuffer, const css::uno::Sequence< sal_Int8 > &aPass)
static OUString GetMimeTypeForConvertDataFormat(ConvertDataFormat convertDataFormat)
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
int nCount
#define DBG_ASSERT(sCon, aError)
URL aURL
float u
#define ERRCODE_NONE
#define GRFILTER_FORMAT_NOTFOUND
#define ERRCODE_GRFILTER_FILTERERROR
#define ERRCODE_GRFILTER_IOERROR
BmpMirrorFlags
OUString aName
#define SAL_WARN(area, stream)
aStr
std::unique_ptr< sal_Int32[]> pData
class SAL_NO_VTABLE XPropertySet
Definition: xmlexchg.hxx:29
int i
ConvertDataFormat
const sal_Unicode *const aMimeType[]
Point maPositionPixel
BitmapEx maBitmapEx
signed char sal_Int8
XOutFlags
Definition: xoutbmp.hxx:32
@ DontAddExtension
@ DontExpandFilename
@ UseNativeIfPossible
@ UseGifIfSensible
@ UseGifIfPossible