diff options
Diffstat (limited to 'svx/source/xoutdev/_xoutbmp.cxx')
-rw-r--r-- | svx/source/xoutdev/_xoutbmp.cxx | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/svx/source/xoutdev/_xoutbmp.cxx b/svx/source/xoutdev/_xoutbmp.cxx new file mode 100644 index 000000000..cb690df32 --- /dev/null +++ b/svx/source/xoutdev/_xoutbmp.cxx @@ -0,0 +1,389 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <comphelper/base64.hxx> +#include <comphelper/graphicmimetype.hxx> +#include <tools/debug.hxx> +#include <vcl/virdev.hxx> +#include <sfx2/docfile.hxx> +#include <svx/xoutbmp.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/cvtgrf.hxx> +#include <memory> + +constexpr OUStringLiteral FORMAT_BMP = u"bmp"; +constexpr OUStringLiteral FORMAT_GIF = u"gif"; +constexpr OUStringLiteral FORMAT_JPG = u"jpg"; +constexpr OUStringLiteral FORMAT_PNG = u"png"; +constexpr OUStringLiteral FORMAT_WEBP = u"webp"; + +using namespace com::sun::star; + +Animation XOutBitmap::MirrorAnimation( const Animation& rAnimation, bool bHMirr, bool bVMirr ) +{ + Animation aNewAnim( rAnimation ); + + if( bHMirr || bVMirr ) + { + const Size& rGlobalSize = aNewAnim.GetDisplaySizePixel(); + BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE; + + if( bHMirr ) + nMirrorFlags |= BmpMirrorFlags::Horizontal; + + if( bVMirr ) + nMirrorFlags |= BmpMirrorFlags::Vertical; + + for( sal_uInt16 i = 0, nCount = aNewAnim.Count(); i < nCount; i++ ) + { + AnimationBitmap aAnimationBitmap( aNewAnim.Get( i ) ); + + // mirror the BitmapEx + aAnimationBitmap.maBitmapEx.Mirror( nMirrorFlags ); + + // Adjust the positions inside the whole bitmap + if( bHMirr ) + aAnimationBitmap.maPositionPixel.setX(rGlobalSize.Width() - aAnimationBitmap.maPositionPixel.X() - + aAnimationBitmap.maSizePixel.Width()); + + if( bVMirr ) + aAnimationBitmap.maPositionPixel.setY(rGlobalSize.Height() - aAnimationBitmap.maPositionPixel.Y() - + aAnimationBitmap.maSizePixel.Height()); + + aNewAnim.Replace(aAnimationBitmap, i); + } + } + + return aNewAnim; +} + +Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const BmpMirrorFlags nMirrorFlags ) +{ + Graphic aRetGraphic; + + if( nMirrorFlags != BmpMirrorFlags::NONE ) + { + if( rGraphic.IsAnimated() ) + { + aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(), + bool( nMirrorFlags & BmpMirrorFlags::Horizontal ), + bool( nMirrorFlags & BmpMirrorFlags::Vertical ) ); + } + else + { + BitmapEx aBmp( rGraphic.GetBitmapEx() ); + aBmp.Mirror( nMirrorFlags ); + aRetGraphic = aBmp; + } + } + else + aRetGraphic = rGraphic; + + return aRetGraphic; +} + +ErrCode XOutBitmap::WriteGraphic( const Graphic& rGraphic, OUString& rFileName, + const OUString& rFilterName, const XOutFlags nFlags, + const Size* pMtfSize_100TH_MM, + const css::uno::Sequence< css::beans::PropertyValue >* pFilterData ) +{ + if( rGraphic.GetType() == GraphicType::NONE ) + return ERRCODE_NONE; + + INetURLObject aURL( rFileName ); + Graphic aGraphic; + OUString aExt; + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + ErrCode nErr = ERRCODE_GRFILTER_FILTERERROR; + bool bTransparent = rGraphic.IsTransparent(), bAnimated = rGraphic.IsAnimated(); + + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::WriteGraphic(...): invalid URL" ); + + // calculate correct file name + if( !( nFlags & XOutFlags::DontExpandFilename ) ) + { + OUString aStr( OUString::number( rGraphic.GetChecksum(), 16 ) ); + if ( aStr[0] == '-' ) + aStr = OUString::Concat("m") + aStr.subView(1); + OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_" + aStr; + aURL.setBase( aName ); + } + + // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible) + auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData()); + + if (rVectorGraphicDataPtr && rVectorGraphicDataPtr->getBinaryDataContainer().getSize()) + { + // Does the filter name match the original format? + const bool bIsSvg(rFilterName.equalsIgnoreAsciiCase("svg") && VectorGraphicDataType::Svg == rVectorGraphicDataPtr->getType()); + const bool bIsWmf(rFilterName.equalsIgnoreAsciiCase("wmf") && VectorGraphicDataType::Wmf == rVectorGraphicDataPtr->getType()); + bool bIsEmf(rFilterName.equalsIgnoreAsciiCase("emf") && VectorGraphicDataType::Emf == rVectorGraphicDataPtr->getType()); + if (!bIsEmf) + { + bIsEmf = rFilterName.equalsIgnoreAsciiCase("emf") && rGraphic.GetGfxLink().IsEMF(); + } + const bool bIsPdf(rFilterName.equalsIgnoreAsciiCase("pdf") && VectorGraphicDataType::Pdf == rVectorGraphicDataPtr->getType()); + + if (bIsSvg || bIsWmf || bIsEmf || bIsPdf) + { + if (!(nFlags & XOutFlags::DontAddExtension)) + { + aURL.setExtension(rFilterName); + } + + rFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC); + SvStream* pOStm = aMedium.GetOutStream(); + + if (pOStm) + { + auto & rDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer(); + pOStm->WriteBytes(rDataContainer.getData(), rDataContainer.getSize()); + aMedium.Commit(); + + if (!aMedium.GetError()) + return ERRCODE_NONE; + } + } + } + + if( ( nFlags & XOutFlags::UseNativeIfPossible ) && + !( nFlags & XOutFlags::MirrorHorz ) && + !( nFlags & XOutFlags::MirrorVert ) && + ( rGraphic.GetType() != GraphicType::GdiMetafile ) && rGraphic.IsGfxLink() ) + { + // try to write native link + const GfxLink aGfxLink( rGraphic.GetGfxLink() ); + + switch( aGfxLink.GetType() ) + { + case GfxLinkType::NativeGif: aExt = FORMAT_GIF; break; + + // #i15508# added BMP type for better exports (no call/trigger found, prob used in HTML export) + case GfxLinkType::NativeBmp: aExt = FORMAT_BMP; break; + + case GfxLinkType::NativeJpg: aExt = FORMAT_JPG; break; + case GfxLinkType::NativePng: aExt = FORMAT_PNG; break; + case GfxLinkType::NativeWebp: aExt = FORMAT_WEBP; break; + + default: + break; + } + + // tdf#60684: use native format if possible but it must correspond to filter name + // or no specific format has been required + // without this, you may save for example file with png extension but jpg content + if( !aExt.isEmpty() && (aExt == rFilterName || rFilterName.isEmpty()) ) + { + if( !(nFlags & XOutFlags::DontAddExtension) ) + aURL.setExtension( aExt ); + rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC); + SvStream* pOStm = aMedium.GetOutStream(); + + if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() ) + { + pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize()); + aMedium.Commit(); + + if( !aMedium.GetError() ) + return ERRCODE_NONE; + } + } + } + + OUString aFilter( rFilterName ); + bool bWriteTransGrf = ( aFilter.equalsIgnoreAsciiCase( "transgrf" ) ) || + ( aFilter.equalsIgnoreAsciiCase( "gif" ) ) || + ( nFlags & XOutFlags::UseGifIfPossible ) || + ( ( nFlags & XOutFlags::UseGifIfSensible ) && ( bAnimated || bTransparent ) ); + + // get filter and extension + if( bWriteTransGrf ) + aFilter = FORMAT_GIF; + + sal_uInt16 nFilter = rFilter.GetExportFormatNumberForShortName( aFilter ); + + if( GRFILTER_FORMAT_NOTFOUND == nFilter ) + { + nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_PNG ); + + if( GRFILTER_FORMAT_NOTFOUND == nFilter ) + nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_BMP ); + } + + if( GRFILTER_FORMAT_NOTFOUND != nFilter ) + { + aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase(); + + if( bWriteTransGrf ) + { + if( bAnimated ) + aGraphic = rGraphic; + else + { + if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) ) + { + ScopedVclPtrInstance< VirtualDevice > pVDev; + const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM))); + + if( pVDev->SetOutputSizePixel( aSize ) ) + { + const Wallpaper aWallpaper( pVDev->GetBackground() ); + const Point aPt; + + pVDev->SetBackground( Wallpaper( COL_BLACK ) ); + pVDev->Erase(); + rGraphic.Draw(*pVDev, aPt, aSize); + + const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) ); + + pVDev->SetBackground( aWallpaper ); + pVDev->Erase(); + rGraphic.Draw(*pVDev, aPt, aSize); + + pVDev->SetRasterOp( RasterOp::Xor ); + pVDev->DrawBitmap( aPt, aSize, aBitmap ); + aGraphic = BitmapEx( aBitmap, pVDev->GetBitmap( aPt, aSize ) ); + } + else + aGraphic = rGraphic.GetBitmapEx(); + } + else + aGraphic = rGraphic.GetBitmapEx(); + } + } + else + { + if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) ) + { + ScopedVclPtrInstance< VirtualDevice > pVDev; + const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM))); + + if( pVDev->SetOutputSizePixel( aSize ) ) + { + rGraphic.Draw(*pVDev, Point(), aSize); + aGraphic = BitmapEx(pVDev->GetBitmap(Point(), aSize)); + } + else + aGraphic = rGraphic.GetBitmapEx(); + } + else + aGraphic = rGraphic.GetBitmapEx(); + } + + // mirror? + if( ( nFlags & XOutFlags::MirrorHorz ) || ( nFlags & XOutFlags::MirrorVert ) ) + { + BmpMirrorFlags nBmpMirrorFlags = BmpMirrorFlags::NONE; + if( nFlags & XOutFlags::MirrorHorz ) + nBmpMirrorFlags |= BmpMirrorFlags::Horizontal; + if( nFlags & XOutFlags::MirrorVert ) + nBmpMirrorFlags |= BmpMirrorFlags::Vertical; + aGraphic = MirrorGraphic( aGraphic, nBmpMirrorFlags ); + } + + if (aGraphic.GetType() != GraphicType::NONE) + { + if( !(nFlags & XOutFlags::DontAddExtension) ) + aURL.setExtension( aExt ); + rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + nErr = ExportGraphic( aGraphic, aURL, rFilter, nFilter, pFilterData ); + } + } + + return nErr; +} + +bool XOutBitmap::GraphicToBase64(const Graphic& rGraphic, OUString& rOUString, bool bAddPrefix, + ConvertDataFormat aTargetFormat) +{ + SvMemoryStream aOStm; + GfxLink aLink = rGraphic.GetGfxLink(); + + if (aTargetFormat == ConvertDataFormat::Unknown) + { + switch (aLink.GetType()) + { + case GfxLinkType::NativeJpg: + aTargetFormat = ConvertDataFormat::JPG; + break; + case GfxLinkType::NativePng: + aTargetFormat = ConvertDataFormat::PNG; + break; + case GfxLinkType::NativeSvg: + aTargetFormat = ConvertDataFormat::SVG; + break; + default: + // save everything else (including gif) into png + aTargetFormat = ConvertDataFormat::PNG; + break; + } + } + + ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat); + if ( nErr ) + { + SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr ); + return false; + } + css::uno::Sequence<sal_Int8> aOStmSeq( static_cast<sal_Int8 const *>(aOStm.GetData()),aOStm.TellEnd() ); + OUStringBuffer aStrBuffer; + ::comphelper::Base64::encode(aStrBuffer,aOStmSeq); + rOUString = aStrBuffer.makeStringAndClear(); + + if (bAddPrefix) + { + OUString aMimeType + = comphelper::GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(aTargetFormat); + rOUString = aMimeType + ";base64," + rOUString; + } + + return true; +} + +ErrCode XOutBitmap::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rURL, + GraphicFilter& rFilter, const sal_uInt16 nFormat, + const css::uno::Sequence< css::beans::PropertyValue >* pFilterData ) +{ + DBG_ASSERT( rURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::ExportGraphic(...): invalid URL" ); + + SfxMedium aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC ); + SvStream* pOStm = aMedium.GetOutStream(); + ErrCode nRet = ERRCODE_GRFILTER_IOERROR; + + if( pOStm ) + { + nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData ); + + aMedium.Commit(); + + if( aMedium.GetError() && ( ERRCODE_NONE == nRet ) ) + nRet = ERRCODE_GRFILTER_IOERROR; + } + + return nRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |