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 <tools/poly.hxx>
27 #include <vcl/bitmapaccess.hxx>
28 #include <vcl/virdev.hxx>
29 #include <svl/solar.hrc>
30 #include <sfx2/docfile.hxx>
31 #include <sfx2/app.hxx>
32 #include <svx/xoutbmp.hxx>
33 #include <vcl/dibtools.hxx>
34 #include <vcl/FilterConfigItem.hxx>
35 #include <vcl/graphicfilter.hxx>
36 #include <vcl/cvtgrf.hxx>
37 #include <sax/tools/converter.hxx>
38 #include <memory>
39 
40 #define FORMAT_BMP "bmp"
41 #define FORMAT_GIF "gif"
42 #define FORMAT_JPG "jpg"
43 #define FORMAT_PNG "png"
44 
45 using namespace com::sun::star;
46 
48 
49 Animation 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  AnimationBitmap aAnimationBitmap( aNewAnim.Get( i ) );
67 
68  // mirror the BitmapEx
69  aAnimationBitmap.maBitmapEx.Mirror( nMirrorFlags );
70 
71  // Adjust the positions inside the whole bitmap
72  if( bHMirr )
73  aAnimationBitmap.maPositionPixel.setX(rGlobalSize.Width() - aAnimationBitmap.maPositionPixel.X() -
74  aAnimationBitmap.maSizePixel.Width());
75 
76  if( bVMirr )
77  aAnimationBitmap.maPositionPixel.setY(rGlobalSize.Height() - aAnimationBitmap.maPositionPixel.Y() -
78  aAnimationBitmap.maSizePixel.Height());
79 
80  aNewAnim.Replace(aAnimationBitmap, i);
81  }
82  }
83 
84  return aNewAnim;
85 }
86 
87 Graphic 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 
112 ErrCode XOutBitmap::WriteGraphic( const Graphic& rGraphic, OUString& rFileName,
113  const OUString& rFilterName, const XOutFlags nFlags,
114  const Size* pMtfSize_100TH_MM,
115  const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
116 {
117  if( rGraphic.GetType() != GraphicType::NONE )
118  {
119  INetURLObject aURL( rFileName );
120  Graphic aGraphic;
121  OUString aExt;
124  sal_uInt16 nFilter = GRFILTER_FORMAT_NOTFOUND;
125  bool bTransparent = rGraphic.IsTransparent(), bAnimated = rGraphic.IsAnimated();
126 
127  DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::WriteGraphic(...): invalid URL" );
128 
129  // calculate correct file name
130  if( !( nFlags & XOutFlags::DontExpandFilename ) )
131  {
132  OUString aStr( OUString::number( rGraphic.GetChecksum(), 16 ) );
133  if ( aStr[0] == '-' )
134  aStr = "m" + aStr.copy(1);
135  OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_" + aStr;
136  aURL.setBase( aName );
137  }
138 
139  // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible)
140  const VectorGraphicDataPtr& aVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
141 
142  if(aVectorGraphicDataPtr.get()
143  && aVectorGraphicDataPtr->getVectorGraphicDataArrayLength())
144  {
145  const bool bIsSvg(rFilterName.equalsIgnoreAsciiCase("svg") && VectorGraphicDataType::Svg == aVectorGraphicDataPtr->getVectorGraphicDataType());
146  const bool bIsWmf(rFilterName.equalsIgnoreAsciiCase("wmf") && VectorGraphicDataType::Wmf == aVectorGraphicDataPtr->getVectorGraphicDataType());
147  const bool bIsEmf(rFilterName.equalsIgnoreAsciiCase("emf") && VectorGraphicDataType::Emf == aVectorGraphicDataPtr->getVectorGraphicDataType());
148 
149  if (bIsSvg || bIsWmf || bIsEmf)
150  {
151  if (!(nFlags & XOutFlags::DontAddExtension))
152  {
153  aURL.setExtension(rFilterName);
154  }
155 
157  SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
158  SvStream* pOStm = aMedium.GetOutStream();
159 
160  if (pOStm)
161  {
162  pOStm->WriteBytes(aVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), aVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
163  aMedium.Commit();
164 
165  if (!aMedium.GetError())
166  {
167  nErr = ERRCODE_NONE;
168  }
169  }
170  }
171  }
172 
173  // Write PDF data in original form if possible.
174  if (rGraphic.hasPdfData() && rFilterName.equalsIgnoreAsciiCase("pdf"))
175  {
176  if (!(nFlags & XOutFlags::DontAddExtension))
177  aURL.setExtension(rFilterName);
178 
180  SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE|StreamMode::SHARE_DENYNONE|StreamMode::TRUNC);
181  if (SvStream* pOutStream = aMedium.GetOutStream())
182  {
183  const std::shared_ptr<std::vector<sal_Int8>> rPdfData(rGraphic.getPdfData());
184  pOutStream->WriteBytes(rPdfData->data(), rPdfData->size());
185  aMedium.Commit();
186  if (!aMedium.GetError())
187  nErr = ERRCODE_NONE;
188  }
189  }
190 
191  if( ERRCODE_NONE != nErr )
192  {
193  if( ( nFlags & XOutFlags::UseNativeIfPossible ) &&
194  !( nFlags & XOutFlags::MirrorHorz ) &&
195  !( nFlags & XOutFlags::MirrorVert ) &&
196  ( rGraphic.GetType() != GraphicType::GdiMetafile ) && rGraphic.IsGfxLink() )
197  {
198  // try to write native link
199  const GfxLink aGfxLink( rGraphic.GetGfxLink() );
200 
201  switch( aGfxLink.GetType() )
202  {
203  case GfxLinkType::NativeGif: aExt = FORMAT_GIF; break;
204 
205  // #i15508# added BMP type for better exports (no call/trigger found, prob used in HTML export)
206  case GfxLinkType::NativeBmp: aExt = FORMAT_BMP; break;
207 
208  case GfxLinkType::NativeJpg: aExt = FORMAT_JPG; break;
209  case GfxLinkType::NativePng: aExt = FORMAT_PNG; break;
210 
211  default:
212  break;
213  }
214 
215  if( !aExt.isEmpty() )
216  {
217  if( !(nFlags & XOutFlags::DontAddExtension) )
218  aURL.setExtension( aExt );
220 
221  SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
222  SvStream* pOStm = aMedium.GetOutStream();
223 
224  if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() )
225  {
226  pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
227  aMedium.Commit();
228 
229  if( !aMedium.GetError() )
230  nErr = ERRCODE_NONE;
231  }
232  }
233  }
234  }
235 
236  if( ERRCODE_NONE != nErr )
237  {
238  OUString aFilter( rFilterName );
239  bool bWriteTransGrf = ( aFilter.equalsIgnoreAsciiCase( "transgrf" ) ) ||
240  ( aFilter.equalsIgnoreAsciiCase( "gif" ) ) ||
241  ( nFlags & XOutFlags::UseGifIfPossible ) ||
242  ( ( nFlags & XOutFlags::UseGifIfSensible ) && ( bAnimated || bTransparent ) );
243 
244  // get filter and extension
245  if( bWriteTransGrf )
246  aFilter = FORMAT_GIF;
247 
248  nFilter = rFilter.GetExportFormatNumberForShortName( aFilter );
249 
250  if( GRFILTER_FORMAT_NOTFOUND == nFilter )
251  {
252  nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_PNG );
253 
254  if( GRFILTER_FORMAT_NOTFOUND == nFilter )
255  nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_BMP );
256  }
257 
258  if( GRFILTER_FORMAT_NOTFOUND != nFilter )
259  {
260  aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase();
261 
262  if( bWriteTransGrf )
263  {
264  if( bAnimated )
265  aGraphic = rGraphic;
266  else
267  {
268  if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
269  {
271  const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
272 
273  if( pVDev->SetOutputSizePixel( aSize ) )
274  {
275  const Wallpaper aWallpaper( pVDev->GetBackground() );
276  const Point aPt;
277 
278  pVDev->SetBackground( Wallpaper( COL_BLACK ) );
279  pVDev->Erase();
280  rGraphic.Draw( pVDev.get(), aPt, aSize );
281 
282  const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) );
283 
284  pVDev->SetBackground( aWallpaper );
285  pVDev->Erase();
286  rGraphic.Draw( pVDev.get(), aPt, aSize );
287 
288  pVDev->SetRasterOp( RasterOp::Xor );
289  pVDev->DrawBitmap( aPt, aSize, aBitmap );
290  aGraphic = BitmapEx( aBitmap, pVDev->GetBitmap( aPt, aSize ) );
291  }
292  else
293  aGraphic = rGraphic.GetBitmapEx();
294  }
295  else
296  aGraphic = rGraphic.GetBitmapEx();
297  }
298  }
299  else
300  {
301  if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
302  {
304  const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
305 
306  if( pVDev->SetOutputSizePixel( aSize ) )
307  {
308  rGraphic.Draw( pVDev.get(), Point(), aSize );
309  aGraphic = pVDev->GetBitmap( Point(), aSize );
310  }
311  else
312  aGraphic = rGraphic.GetBitmapEx();
313  }
314  else
315  aGraphic = rGraphic.GetBitmapEx();
316  }
317 
318  // mirror?
319  if( ( nFlags & XOutFlags::MirrorHorz ) || ( nFlags & XOutFlags::MirrorVert ) )
320  {
321  BmpMirrorFlags nBmpMirrorFlags = BmpMirrorFlags::NONE;
322  if( nFlags & XOutFlags::MirrorHorz )
323  nBmpMirrorFlags |= BmpMirrorFlags::Horizontal;
324  if( nFlags & XOutFlags::MirrorVert )
325  nBmpMirrorFlags |= BmpMirrorFlags::Vertical;
326  aGraphic = MirrorGraphic( aGraphic, nBmpMirrorFlags );
327  }
328 
329  if (aGraphic.GetType() != GraphicType::NONE)
330  {
331  if( !(nFlags & XOutFlags::DontAddExtension) )
332  aURL.setExtension( aExt );
334  nErr = ExportGraphic( aGraphic, aURL, rFilter, nFilter, pFilterData );
335  }
336  }
337  }
338 
339  return nErr;
340  }
341  else
342  {
343  return ERRCODE_NONE;
344  }
345 }
346 
347 bool XOutBitmap::GraphicToBase64(const Graphic& rGraphic, OUString& rOUString, bool bAddPrefix,
348  ConvertDataFormat aTargetFormat)
349 {
350  SvMemoryStream aOStm;
351  GfxLink aLink = rGraphic.GetGfxLink();
352 
353  if (aTargetFormat == ConvertDataFormat::Unknown)
354  {
355  switch (aLink.GetType())
356  {
357  case GfxLinkType::NativeJpg:
358  aTargetFormat = ConvertDataFormat::JPG;
359  break;
360  case GfxLinkType::NativePng:
361  aTargetFormat = ConvertDataFormat::PNG;
362  break;
363  case GfxLinkType::NativeSvg:
364  aTargetFormat = ConvertDataFormat::SVG;
365  break;
366  default:
367  // save everything else (including gif) into png
368  aTargetFormat = ConvertDataFormat::PNG;
369  break;
370  }
371  }
372 
373  ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat);
374  if ( nErr )
375  {
376  SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr );
377  return false;
378  }
379  css::uno::Sequence<sal_Int8> aOStmSeq( static_cast<sal_Int8 const *>(aOStm.GetData()),aOStm.TellEnd() );
380  OUStringBuffer aStrBuffer;
381  ::comphelper::Base64::encode(aStrBuffer,aOStmSeq);
382  rOUString = aStrBuffer.makeStringAndClear();
383 
384  if (bAddPrefix)
385  {
386  OUString aMimeType
388  rOUString = aMimeType + ";base64," + rOUString;
389  }
390 
391  return true;
392 }
393 
395  GraphicFilter& rFilter, const sal_uInt16 nFormat,
396  const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
397 {
398  DBG_ASSERT( rURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::ExportGraphic(...): invalid URL" );
399 
400  SfxMedium aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC );
401  SvStream* pOStm = aMedium.GetOutStream();
403 
404  if( pOStm )
405  {
406  pGrfFilter = &rFilter;
407 
408  nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData );
409 
410  pGrfFilter = nullptr;
411  aMedium.Commit();
412 
413  if( aMedium.GetError() && ( ERRCODE_NONE == nRet ) )
415  }
416 
417  return nRet;
418 }
419 
420 
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define FORMAT_PNG
Definition: _xoutbmp.cxx:43
long Width() const
static OUString GetMimeTypeForConvertDataFormat(ConvertDataFormat convertDataFormat)
URL aURL
const VectorGraphicDataPtr & getVectorGraphicData() const
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
static bool GraphicToBase64(const Graphic &rGraphic, OUString &rOUString, bool bAddPrefix=true, ConvertDataFormat aTargetFormat=ConvertDataFormat::Unknown)
Definition: _xoutbmp.cxx:347
long Height() const
BitmapChecksum GetChecksum() const
OUString getBase(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool Commit()
#define FORMAT_BMP
Definition: _xoutbmp.cxx:40
bool IsTransparent() const
BitmapEx maBitmapEx
#define ERRCODE_GRFILTER_IOERROR
bool IsGfxLink() const
virtual sal_uInt64 TellEnd() override
const std::shared_ptr< std::vector< sal_Int8 > > & getPdfData() const
bool setExtension(OUString const &rTheExtension, sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
int nCount
sal_uInt16 GetExportFormatNumberForShortName(const OUString &rShortName)
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)
Definition: _xoutbmp.cxx:112
static void encode(OUStringBuffer &aStrBuffer, const css::uno::Sequence< sal_Int8 > &aPass)
bool IsAnimated() const
Animation GetAnimation() const
bool Mirror(BmpMirrorFlags nMirrorFlags)
void Draw(OutputDevice *pOutDev, const Point &rDestPt) const
#define DBG_ASSERT(sCon, aError)
void Replace(const AnimationBitmap &rNewAnimationBmp, sal_uInt16 nAnimation)
GraphicType GetType() const
ConvertDataFormat
int i
const char *const aMimeType[]
std::size_t WriteBytes(const void *pData, std::size_t nSize)
static Graphic MirrorGraphic(const Graphic &rGraphic, const BmpMirrorFlags nMirrorFlags)
Definition: _xoutbmp.cxx:87
BmpMirrorFlags
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
const Size & GetDisplaySizePixel() const
GfxLink GetGfxLink() const
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:394
ErrCode ExportGraphic(const Graphic &rGraphic, const INetURLObject &rPath, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData=nullptr)
const AnimationBitmap & Get(sal_uInt16 nAnimation) const
bool setBase(OUString const &rTheBase, sal_Int32 nIndex=LAST_SEGMENT, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
std::shared_ptr< VectorGraphicData > VectorGraphicDataPtr
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetExportFormatShortName(sal_uInt16 nFormat)
#define ERRCODE_GRFILTER_FILTERERROR
static ErrCode Export(SvStream &rOStm, const Graphic &rGraphic, ConvertDataFormat nFormat)
XOutFlags
Definition: xoutbmp.hxx:32
#define ERRCODE_NONE
static GraphicFilter * pGrfFilter
Definition: xoutbmp.hxx:57
OUString aName
INetProtocol GetProtocol() const
static Animation MirrorAnimation(const Animation &rAnimation, bool bHMirr, bool bVMirr)
Definition: _xoutbmp.cxx:49
#define FORMAT_GIF
Definition: _xoutbmp.cxx:41
bool hasPdfData() const
static GraphicFilter & GetGraphicFilter()
size_t Count() const
reference_type * get() const
#define SAL_WARN(area, stream)
#define FORMAT_JPG
Definition: _xoutbmp.cxx:42
aStr
OUString getExtension(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
const void * GetData()
#define GRFILTER_FORMAT_NOTFOUND