Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / xoutdev / _xoutbmp.cxx
blobe095f8e200f14cabc4aae518c6b6c272ea02f30a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
31 #include <memory>
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;
58 if( bHMirr )
59 nMirrorFlags |= BmpMirrorFlags::Horizontal;
61 if( bVMirr )
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
72 if( bHMirr )
73 aAnimationFrame.maPositionPixel.setX(rGlobalSize.Width() - aAnimationFrame.maPositionPixel.X() -
74 aAnimationFrame.maSizePixel.Width());
76 if( bVMirr )
77 aAnimationFrame.maPositionPixel.setY(rGlobalSize.Height() - aAnimationFrame.maPositionPixel.Y() -
78 aAnimationFrame.maSizePixel.Height());
80 aNewAnim.Replace(aAnimationFrame, i);
84 return aNewAnim;
87 Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const BmpMirrorFlags nMirrorFlags )
89 Graphic aRetGraphic;
91 if( nMirrorFlags != BmpMirrorFlags::NONE )
93 if( rGraphic.IsAnimated() )
95 aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(),
96 bool( nMirrorFlags & BmpMirrorFlags::Horizontal ),
97 bool( nMirrorFlags & BmpMirrorFlags::Vertical ) );
99 else
101 BitmapEx aBmp( rGraphic.GetBitmapEx() );
102 aBmp.Mirror( nMirrorFlags );
103 aRetGraphic = aBmp;
106 else
107 aRetGraphic = rGraphic;
109 return aRetGraphic;
112 static OUString match(std::u16string_view filter, const OUString& expected, bool matchEmpty = true)
114 return (matchEmpty && filter.empty()) || expected.equalsIgnoreAsciiCase(filter) ? expected
115 : OUString();
118 static OUString isKnownVectorFormat(const Graphic& rGraphic, std::u16string_view rFilter)
120 const auto& pData(rGraphic.getVectorGraphicData());
121 if (!pData || pData->getBinaryDataContainer().getSize() == 0)
122 return {};
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);
140 return {};
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);
165 default:
166 return {};
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 )
177 return ERRCODE_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);
201 if (pMediaType)
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();
208 if (pOStm)
210 rGraphic.getVectorGraphicData()->getBinaryDataContainer().writeToStream(*pOStm);
211 aMedium.Commit();
213 if (!aMedium.GetError())
214 return ERRCODE_NONE;
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 );
230 if (pMediaType)
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());
240 aMedium.Commit();
242 if( !aMedium.GetError() )
243 return ERRCODE_NONE;
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
256 if( bWriteTransGrf )
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 )
271 Graphic aGraphic;
272 OUString aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase();
274 if( bWriteTransGrf )
276 if( bAnimated )
277 aGraphic = rGraphic;
278 else
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() );
288 const Point aPt;
290 pVDev->SetBackground( Wallpaper( COL_BLACK ) );
291 pVDev->Erase();
292 rGraphic.Draw(*pVDev, aPt, aSize);
294 const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) );
296 pVDev->SetBackground( aWallpaper );
297 pVDev->Erase();
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 ) );
304 else
305 aGraphic = rGraphic.GetBitmapEx();
307 else
308 aGraphic = rGraphic.GetBitmapEx();
311 else
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));
323 else
324 aGraphic = rGraphic.GetBitmapEx();
326 else
327 aGraphic = rGraphic.GetBitmapEx();
330 // mirror?
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 );
346 if (pMediaType)
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;
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;
381 ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat);
382 if ( nErr )
384 SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr );
385 return false;
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();
392 if (bAddPrefix)
394 OUString aMimeType
395 = comphelper::GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(aTargetFormat);
396 rOUString = aMimeType + ";base64," + rOUString;
399 return true;
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;
412 if( pOStm )
414 nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData );
416 aMedium.Commit();
418 if( aMedium.GetError() && ( ERRCODE_NONE == nRet ) )
419 nRet = ERRCODE_GRFILTER_IOERROR;
422 return nRet;
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */