/* -*- 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/fileformat.h>
#include <comphelper/processfactory.hxx>
#include <tools/fract.hxx>
#include <tools/vcompat.hxx>
#include <tools/urlobj.hxx>
#include <tools/stream.hxx>
#include <tools/helpers.hxx>
#include <ucbhelper/content.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/tempfile.hxx>
#include <vcl/outdev.hxx>
#include <vcl/graphicfilter.hxx>
#include <vcl/virdev.hxx>
#include <vcl/gfxlink.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/graph.hxx>
#include <vcl/metaact.hxx>
#include <impgraph.hxx>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <vcl/dibtools.hxx>
#include <memory>
#include <o3tl/make_unique.hxx>
#include <vcl/gdimetafiletools.hxx>
#include <vcl/pdfread.hxx>
#define GRAPHIC_MTFTOBMP_MAXEXT 2048
#define GRAPHIC_STREAMBUFSIZE 8192UL
#define SYS_WINMETAFILE 0x00000003L
#define SYS_WNTMETAFILE 0x00000004L
#define SYS_OS2METAFILE 0x00000005L
#define SYS_MACMETAFILE 0x00000006L
#define GRAPHIC_FORMAT_50 COMPAT_FORMAT( 'G', 'R', 'F', '5' )
#define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' )
const sal_uInt32 nPdfMagic((sal_uInt32('p') << 24) | (sal_uInt32('d') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
using namespace com::sun::star;
struct ImpSwapFile
{
INetURLObject aSwapURL;
OUString maOriginURL;
~ImpSwapFile();
};
class ReaderData
{
public:
Size maPreviewSize;
};
GraphicReader::GraphicReader()
{
}
GraphicReader::~GraphicReader()
{
}
void GraphicReader::DisablePreviewMode()
{
if( mpReaderData )
mpReaderData->maPreviewSize = Size( 0, 0 );
}
void GraphicReader::SetPreviewSize( const Size& rSize )
{
if( !mpReaderData )
mpReaderData.reset( new ReaderData );
mpReaderData->maPreviewSize = rSize;
}
Size GraphicReader::GetPreviewSize() const
{
Size aSize( 0, 0 );
if( mpReaderData )
aSize = mpReaderData->maPreviewSize;
return aSize;
}
GraphicID::GraphicID(ImpGraphic const & rGraphic)
{
rGraphic.ensureAvailable();
mnID1 = static_cast<sal_uLong>(rGraphic.ImplGetType()) << 28;
mnID2 = mnID3 = mnID4 = 0;
if (rGraphic.ImplGetType() == GraphicType::Bitmap)
{
if (rGraphic.getVectorGraphicData().get())
{
const VectorGraphicDataPtr& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData();
const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange();
mnID1 |= rVectorGraphicDataPtr->getVectorGraphicDataArrayLength();
mnID2 = basegfx::fround(rRange.getWidth());
mnID3 = basegfx::fround(rRange.getHeight());
mnID4 = vcl_get_checksum(0, rVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), rVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
}
else if (rGraphic.ImplIsAnimated())
{
const Animation aAnimation(rGraphic.ImplGetAnimation());
mnID1 |= ( aAnimation.Count() & 0x0fffffff );
mnID2 = aAnimation.GetDisplaySizePixel().Width();
mnID3 = aAnimation.GetDisplaySizePixel().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
else
{
const BitmapEx aBmpEx(rGraphic.ImplGetBitmapEx(GraphicConversionParameters()));
mnID1 |= ( ( ( static_cast<sal_uLong>(aBmpEx.GetTransparentType()) << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff );
mnID2 = aBmpEx.GetSizePixel().Width();
mnID3 = aBmpEx.GetSizePixel().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
}
else if (rGraphic.ImplGetType() == GraphicType::GdiMetafile)
{
const GDIMetaFile& rMtf = rGraphic.ImplGetGDIMetaFile();
mnID1 |= ( rMtf.GetActionSize() & 0x0fffffff );
mnID2 = rMtf.GetPrefSize().Width();
mnID3 = rMtf.GetPrefSize().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
}
OString GraphicID::getIDString() const
{
static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
OStringBuffer aHexStr;
sal_Int32 nShift, nIndex = 0;
aHexStr.setLength(24 + (2 * BITMAP_CHECKSUM_SIZE));
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID1 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID2 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID3 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = ( 8 * BITMAP_CHECKSUM_SIZE ) - 4; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID4 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
return aHexStr.makeStringAndClear();
}
ImpGraphic::ImpGraphic() :
meType ( GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared ( false ),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
: maMetaFile(rImpGraphic.maMetaFile)
, maEx(rImpGraphic.maEx)
, maSwapInfo(rImpGraphic.maSwapInfo)
, mpContext(rImpGraphic.mpContext)
, mpSwapFile(rImpGraphic.mpSwapFile)
, mpGfxLink(rImpGraphic.mpGfxLink)
, meType(rImpGraphic.meType)
, mnSizeBytes(rImpGraphic.mnSizeBytes)
, mbSwapOut(rImpGraphic.mbSwapOut)
, mbDummyContext(rImpGraphic.mbDummyContext)
, maVectorGraphicData(rImpGraphic.maVectorGraphicData)
, mpPdfData(rImpGraphic.mpPdfData)
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, maLastUsed (std::chrono::high_resolution_clock::now())
, mbPrepared (rImpGraphic.mbPrepared)
, mnPageNumber(rImpGraphic.mnPageNumber)
{
if( rImpGraphic.mpAnimation )
{
mpAnimation = o3tl::make_unique<Animation>( *rImpGraphic.mpAnimation );
maEx = mpAnimation->GetBitmapEx();
}
}
ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic)
: maMetaFile(std::move(rImpGraphic.maMetaFile))
, maEx(std::move(rImpGraphic.maEx))
, maSwapInfo(std::move(rImpGraphic.maSwapInfo))
, mpAnimation(std::move(rImpGraphic.mpAnimation))
, mpContext(std::move(rImpGraphic.mpContext))
, mpSwapFile(std::move(rImpGraphic.mpSwapFile))
, mpGfxLink(std::move(rImpGraphic.mpGfxLink))
, meType(rImpGraphic.meType)
, mnSizeBytes(rImpGraphic.mnSizeBytes)
, mbSwapOut(rImpGraphic.mbSwapOut)
, mbDummyContext(rImpGraphic.mbDummyContext)
, maVectorGraphicData(std::move(rImpGraphic.maVectorGraphicData))
, mpPdfData(std::move(rImpGraphic.mpPdfData))
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, maLastUsed (std::chrono::high_resolution_clock::now())
, mbPrepared (rImpGraphic.mbPrepared)
, mnPageNumber(rImpGraphic.mnPageNumber)
{
rImpGraphic.ImplClear();
rImpGraphic.mbDummyContext = false;
}
ImpGraphic::ImpGraphic(GraphicExternalLink const & rGraphicExternalLink) :
meType ( GraphicType::Default ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maGraphicExternalLink(rGraphicExternalLink),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const Bitmap& rBitmap ) :
maEx ( rBitmap ),
meType ( !rBitmap.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const BitmapEx& rBitmapEx ) :
maEx ( rBitmapEx ),
meType ( !rBitmapEx.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic(const VectorGraphicDataPtr& rVectorGraphicDataPtr)
: meType( rVectorGraphicDataPtr.get() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes( 0 ),
mbSwapOut( false ),
mbDummyContext ( false ),
maVectorGraphicData(rVectorGraphicDataPtr),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const Animation& rAnimation ) :
maEx ( rAnimation.GetBitmapEx() ),
mpAnimation ( o3tl::make_unique<Animation>( rAnimation ) ),
meType ( GraphicType::Bitmap ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const GDIMetaFile& rMtf ) :
maMetaFile ( rMtf ),
meType ( GraphicType::GdiMetafile ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::~ImpGraphic()
{
vcl::graphic::Manager::get().unregisterGraphic(this);
}
ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
{
if( &rImpGraphic != this )
{
sal_Int64 aOldSizeBytes = mnSizeBytes;
maMetaFile = rImpGraphic.maMetaFile;
meType = rImpGraphic.meType;
mnSizeBytes = rImpGraphic.mnSizeBytes;
maSwapInfo = rImpGraphic.maSwapInfo;
mpContext = rImpGraphic.mpContext;
mbDummyContext = rImpGraphic.mbDummyContext;
mnPageNumber = rImpGraphic.mnPageNumber;
maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
mpAnimation.reset();
if ( rImpGraphic.mpAnimation )
{
mpAnimation = o3tl::make_unique<Animation>( *rImpGraphic.mpAnimation );
maEx = mpAnimation->GetBitmapEx();
}
else
{
maEx = rImpGraphic.maEx;
}
mbSwapOut = rImpGraphic.mbSwapOut;
mpSwapFile = rImpGraphic.mpSwapFile;
mbPrepared = rImpGraphic.mbPrepared;
mpGfxLink = rImpGraphic.mpGfxLink;
maVectorGraphicData = rImpGraphic.maVectorGraphicData;
mpPdfData = rImpGraphic.mpPdfData;
maLastUsed = std::chrono::high_resolution_clock::now();
vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
}
return *this;
}
ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
{
sal_Int64 aOldSizeBytes = mnSizeBytes;
maMetaFile = std::move(rImpGraphic.maMetaFile);
meType = rImpGraphic.meType;
mnSizeBytes = rImpGraphic.mnSizeBytes;
maSwapInfo = std::move(rImpGraphic.maSwapInfo);
mpContext = std::move(rImpGraphic.mpContext);
mbDummyContext = rImpGraphic.mbDummyContext;
mnPageNumber = rImpGraphic.mnPageNumber;
mpAnimation = std::move(rImpGraphic.mpAnimation);
maEx = std::move(rImpGraphic.maEx);
mbSwapOut = rImpGraphic.mbSwapOut;
mpSwapFile = std::move(rImpGraphic.mpSwapFile);
mpGfxLink = std::move(rImpGraphic.mpGfxLink);
maVectorGraphicData = std::move(rImpGraphic.maVectorGraphicData);
mpPdfData = std::move(rImpGraphic.mpPdfData);
maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
mbPrepared = rImpGraphic.mbPrepared;
rImpGraphic.ImplClear();
rImpGraphic.mbDummyContext = false;
maLastUsed = std::chrono::high_resolution_clock::now();
vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
rImpGraphic.mnPageNumber = -1;
return *this;
}
bool ImpGraphic::operator==( const ImpGraphic& rImpGraphic ) const
{
bool bRet = false;
if( this == &rImpGraphic )
bRet = true;
else if (mbPrepared && rImpGraphic.mbPrepared)
{
bRet = (*mpGfxLink == *rImpGraphic.mpGfxLink);
}
else if (isAvailable() && rImpGraphic.isAvailable())
{
switch( meType )
{
case GraphicType::NONE:
bRet = true;
break;
case GraphicType::GdiMetafile:
{
if( rImpGraphic.maMetaFile == maMetaFile )
bRet = true;
}
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get())
{
if(maVectorGraphicData == rImpGraphic.maVectorGraphicData)
{
// equal instances
bRet = true;
}
else if(rImpGraphic.maVectorGraphicData)
{
// equal content
bRet = (*maVectorGraphicData) == (*rImpGraphic.maVectorGraphicData);
}
}
else if (mpPdfData && mpPdfData->hasElements())
{
bRet = (rImpGraphic.mpPdfData && *mpPdfData == *rImpGraphic.mpPdfData);
}
else if( mpAnimation )
{
if( rImpGraphic.mpAnimation && ( *rImpGraphic.mpAnimation == *mpAnimation ) )
bRet = true;
}
else if( !rImpGraphic.mpAnimation && ( rImpGraphic.maEx == maEx ) )
{
bRet = true;
}
}
break;
default:
break;
}
}
return bRet;
}
const VectorGraphicDataPtr& ImpGraphic::getVectorGraphicData() const
{
ensureAvailable();
return maVectorGraphicData;
}
void ImpGraphic::setPdfData(const std::shared_ptr<uno::Sequence<sal_Int8>>& rPdfData)
{
ensureAvailable();
mpPdfData = rPdfData;
}
const std::shared_ptr<uno::Sequence<sal_Int8>>& ImpGraphic::getPdfData() const
{
ensureAvailable();
return mpPdfData;
}
void ImpGraphic::ImplCreateSwapInfo()
{
if (!ImplIsSwapOut())
{
maSwapInfo.maPrefMapMode = ImplGetPrefMapMode();
maSwapInfo.maPrefSize = ImplGetPrefSize();
maSwapInfo.mbIsAnimated = ImplIsAnimated();
maSwapInfo.mbIsEPS = ImplIsEPS();
maSwapInfo.mbIsTransparent = ImplIsTransparent();
maSwapInfo.mbIsAlpha = ImplIsAlpha();
maSwapInfo.mnAnimationLoopCount = ImplGetAnimationLoopCount();
}
}
void ImpGraphic::ImplClearGraphics()
{
maEx.Clear();
maMetaFile.Clear();
mpAnimation.reset();
mpGfxLink.reset();
maVectorGraphicData.reset();
mpPdfData.reset();
}
ImpSwapFile::~ImpSwapFile()
{
try
{
::ucbhelper::Content aCnt( aSwapURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
css::uno::Reference< css::ucb::XCommandEnvironment >(),
comphelper::getProcessComponentContext() );
aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
}
catch( const css::ucb::ContentCreationException& )
{
}
catch( const css::uno::RuntimeException& )
{
}
catch( const css::ucb::CommandAbortedException& )
{
}
catch( const css::uno::Exception& )
{
}
}
void ImpGraphic::ImplSetPrepared(bool bAnimated)
{
mbPrepared = true;
mbSwapOut = true;
meType = GraphicType::Bitmap;
SvMemoryStream aMemoryStream(const_cast<sal_uInt8*>(mpGfxLink->GetData()), mpGfxLink->GetDataSize(), StreamMode::READ | StreamMode::WRITE);
GraphicDescriptor aDescriptor(aMemoryStream, nullptr);
if (aDescriptor.Detect(true))
{
// If we have logic size, work with that, as later pixel -> logic
// conversion will work with the output device DPI, not the graphic
// DPI.
Size aLogSize = aDescriptor.GetSize_100TH_MM();
if (aLogSize.getWidth() && aLogSize.getHeight())
{
maSwapInfo.maPrefSize = aLogSize;
maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
}
else
{
maSwapInfo.maPrefSize = aDescriptor.GetSizePixel();
maSwapInfo.maPrefMapMode = MapMode(MapUnit::MapPixel);
}
maSwapInfo.maSizePixel = aDescriptor.GetSizePixel();
}
maSwapInfo.mnAnimationLoopCount = 0;
maSwapInfo.mbIsAnimated = false;
maSwapInfo.mbIsEPS = false;
maSwapInfo.mbIsTransparent = false;
maSwapInfo.mbIsAlpha = false;
maSwapInfo.mbIsAnimated = bAnimated;
}
void ImpGraphic::ImplClear()
{
mpSwapFile.reset();
mbSwapOut = false;
mbPrepared = false;
// cleanup
ImplClearGraphics();
meType = GraphicType::NONE;
sal_Int64 nOldSize = mnSizeBytes;
mnSizeBytes = 0;
vcl::graphic::Manager::get().changeExisting(this, nOldSize);
maGraphicExternalLink.msURL.clear();
}
void ImpGraphic::ImplSetDefaultType()
{
ImplClear();
meType = GraphicType::Default;
}
bool ImpGraphic::ImplIsSupportedGraphic() const
{
return( meType != GraphicType::NONE );
}
bool ImpGraphic::ImplIsTransparent() const
{
bool bRet(true);
if (mbSwapOut)
{
bRet = maSwapInfo.mbIsTransparent;
}
else if (meType == GraphicType::Bitmap && !maVectorGraphicData.get())
{
bRet = mpAnimation ? mpAnimation->IsTransparent() : maEx.IsTransparent();
}
return bRet;
}
bool ImpGraphic::ImplIsAlpha() const
{
bool bRet(false);
if (mbSwapOut)
{
bRet = maSwapInfo.mbIsAlpha;
}
else if (maVectorGraphicData.get())
{
bRet = true;
}
else if (meType == GraphicType::Bitmap)
{
bRet = (nullptr == mpAnimation && maEx.IsAlpha());
}
return bRet;
}
bool ImpGraphic::ImplIsAnimated() const
{
return mbSwapOut ? maSwapInfo.mbIsAnimated : mpAnimation != nullptr;
}
bool ImpGraphic::ImplIsEPS() const
{
if (mbSwapOut)
return maSwapInfo.mbIsEPS;
return( ( meType == GraphicType::GdiMetafile ) &&
( maMetaFile.GetActionSize() > 0 ) &&
( maMetaFile.GetAction( 0 )->GetType() == MetaActionType::EPS ) );
}
bool ImpGraphic::isAvailable() const
{
return !mbPrepared && !mbSwapOut;
}
bool ImpGraphic::makeAvailable()
{
return ensureAvailable();
}
Bitmap ImpGraphic::ImplGetBitmap(const GraphicConversionParameters& rParameters) const
{
Bitmap aRetBmp;
ensureAvailable();
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
const Color aReplaceColor( COL_WHITE );
aRetBmp = rRetBmpEx.GetBitmap( &aReplaceColor );
if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
aRetBmp.Scale(rParameters.getSizePixel());
}
else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
{
if(maEx.IsEmpty())
{
// calculate size
ScopedVclPtrInstance< VirtualDevice > aVDev;
Size aDrawSize(aVDev->LogicToPixel(maMetaFile.GetPrefSize(), maMetaFile.GetPrefMapMode()));
if(rParameters.getSizePixel().Width() && rParameters.getSizePixel().Height())
{
// apply given size if exists
aDrawSize = rParameters.getSizePixel();
}
if(aDrawSize.Width() && aDrawSize.Height() && !rParameters.getUnlimitedSize()
&& (aDrawSize.Width() > GRAPHIC_MTFTOBMP_MAXEXT || aDrawSize.Height() > GRAPHIC_MTFTOBMP_MAXEXT))
{
// limit bitmap size to a maximum of GRAPHIC_MTFTOBMP_MAXEXT x GRAPHIC_MTFTOBMP_MAXEXT
double fWH(static_cast<double>(aDrawSize.Width()) / static_cast<double>(aDrawSize.Height()));
if(fWH <= 1.0)
{
aDrawSize.setWidth(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT * fWH));
aDrawSize.setHeight(GRAPHIC_MTFTOBMP_MAXEXT);
}
else
{
aDrawSize.setWidth(GRAPHIC_MTFTOBMP_MAXEXT);
aDrawSize.setHeight(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT / fWH));
}
}
// calculate pixel size. Normally, it's the same as aDrawSize, but may
// need to be extended when hairlines are on the right or bottom edge
Size aPixelSize(aDrawSize);
if(GraphicType::GdiMetafile == ImplGetType())
{
// get hairline and full bound rect
tools::Rectangle aHairlineRect;
const tools::Rectangle aRect(maMetaFile.GetBoundRect(*aVDev.get(), &aHairlineRect));
if(!aRect.IsEmpty() && !aHairlineRect.IsEmpty())
{
// expand if needed to allow bottom and right hairlines to be added
if(aRect.Right() == aHairlineRect.Right())
{
aPixelSize.setWidth(aPixelSize.getWidth() + 1);
}
if(aRect.Bottom() == aHairlineRect.Bottom())
{
aPixelSize.setHeight(aPixelSize.getHeight() + 1);
}
}
}
if(aVDev->SetOutputSizePixel(aPixelSize))
{
if(rParameters.getAntiAliase())
{
aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::EnableB2dDraw);
}
if(rParameters.getSnapHorVerLines())
{
aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline);
}
ImplDraw( aVDev.get(), Point(), aDrawSize );
// use maEx as local buffer for rendered metafile
const_cast< ImpGraphic* >(this)->maEx = aVDev->GetBitmapEx( Point(), aVDev->GetOutputSizePixel() );
}
}
aRetBmp = maEx.GetBitmap();
}
if( !!aRetBmp )
{
aRetBmp.SetPrefMapMode( ImplGetPrefMapMode() );
aRetBmp.SetPrefSize( ImplGetPrefSize() );
}
return aRetBmp;
}
BitmapEx ImpGraphic::ImplGetBitmapEx(const GraphicConversionParameters& rParameters) const
{
BitmapEx aRetBmpEx;
ensureAvailable();
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
{
aRetBmpEx.Scale(
rParameters.getSizePixel(),
BmpScaleFlag::Fast);
}
}
else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
{
if(maEx.IsEmpty())
{
const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) );
// use maEx as local buffer for rendered metafile
const_cast< ImpGraphic* >(this)->maEx = BitmapEx(ImplGetBitmap(rParameters), aMonoMask.ImplGetBitmap(rParameters));
}
aRetBmpEx = maEx;
}
return aRetBmpEx;
}
Animation ImpGraphic::ImplGetAnimation() const
{
Animation aAnimation;
ensureAvailable();
if( mpAnimation )
aAnimation = *mpAnimation;
return aAnimation;
}
const BitmapEx& ImpGraphic::ImplGetBitmapExRef() const
{
ensureAvailable();
return maEx;
}
const GDIMetaFile& ImpGraphic::ImplGetGDIMetaFile() const
{
ensureAvailable();
if (!maMetaFile.GetActionSize()
&& maVectorGraphicData.get()
&& (VectorGraphicDataType::Emf == maVectorGraphicData->getVectorGraphicDataType()
|| VectorGraphicDataType::Wmf == maVectorGraphicData->getVectorGraphicDataType()))
{
// If we have a Emf/Wmf VectorGraphic object, we
// need a way to get the Metafile data out of the primitive
// representation. Use a strict virtual hook (MetafileAccessor)
// to access the MetafilePrimitive2D directly. Also see comments in
// XEmfParser about this.
const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > aSequence(maVectorGraphicData->getPrimitive2DSequence());
if (1 == aSequence.size())
{
// try to cast to MetafileAccessor implementation
const css::uno::Reference< css::graphic::XPrimitive2D > xReference(aSequence[0]);
const MetafileAccessor* pMetafileAccessor = dynamic_cast< const MetafileAccessor* >(xReference.get());
if (pMetafileAccessor)
{
// it is a MetafileAccessor implementation, get Metafile
pMetafileAccessor->accessMetafile(const_cast< ImpGraphic* >(this)->maMetaFile);
}
}
}
if (GraphicType::Bitmap == meType && !maMetaFile.GetActionSize())
{
// #i119735#
// Use the local maMetaFile as container for a metafile-representation
// of the bitmap graphic. This will be done only once, thus be buffered.
// I checked all usages of maMetaFile, it is only used when type is not
// GraphicType::Bitmap. In operator= it will get copied, thus buffering will
// survive copying (change this if not wanted)
ImpGraphic* pThat = const_cast< ImpGraphic* >(this);
if(maVectorGraphicData.get() && !maEx)
{
// use maEx as local buffer for rendered svg
pThat->maEx = maVectorGraphicData->getReplacement();
}
// #123983# directly create a metafile with the same PrefSize and PrefMapMode
// the bitmap has, this will be an always correct metafile
if(maEx.IsTransparent())
{
pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), maEx.GetPrefSize(), maEx));
}
else
{
pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), maEx.GetPrefSize(), maEx.GetBitmap()));
}
pThat->maMetaFile.Stop();
pThat->maMetaFile.WindStart();
pThat->maMetaFile.SetPrefSize(maEx.GetPrefSize());
pThat->maMetaFile.SetPrefMapMode(maEx.GetPrefMapMode());
}
return maMetaFile;
}
Size ImpGraphic::ImplGetSizePixel() const
{
Size aSize;
if (ImplIsSwapOut())
aSize = maSwapInfo.maSizePixel;
else
aSize = ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel();
return aSize;
}
Size ImpGraphic::ImplGetPrefSize() const
{
Size aSize;
if (ImplIsSwapOut())
{
aSize = maSwapInfo.maPrefSize;
}
else
{
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// svg not yet buffered in maEx, return size derived from range
const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
aSize = Size(basegfx::fround(rRange.getWidth()), basegfx::fround(rRange.getHeight()));
}
else
{
aSize = maEx.GetPrefSize();
if( !aSize.Width() || !aSize.Height() )
{
aSize = maEx.GetSizePixel();
}
}
}
break;
default:
{
if( ImplIsSupportedGraphic() )
aSize = maMetaFile.GetPrefSize();
}
break;
}
}
return aSize;
}
void ImpGraphic::ImplSetPrefSize( const Size& rPrefSize )
{
ensureAvailable();
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
// used when importing a writer FlyFrame with SVG as graphic, added conversion
// to allow setting the PrefSize at the BitmapEx to hold it
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
maEx = maVectorGraphicData->getReplacement();
}
// #108077# Push through pref size to animation object,
// will be lost on copy otherwise
if( ImplIsAnimated() )
{
const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize( rPrefSize );
}
maEx.SetPrefSize( rPrefSize );
}
break;
default:
{
if( ImplIsSupportedGraphic() )
maMetaFile.SetPrefSize( rPrefSize );
}
break;
}
}
MapMode ImpGraphic::ImplGetPrefMapMode() const
{
MapMode aMapMode;
if (ImplIsSwapOut())
{
aMapMode = maSwapInfo.maPrefMapMode;
}
else
{
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// svg not yet buffered in maEx, return default PrefMapMode
aMapMode = MapMode(MapUnit::Map100thMM);
}
else
{
const Size aSize( maEx.GetPrefSize() );
if ( aSize.Width() && aSize.Height() )
aMapMode = maEx.GetPrefMapMode();
}
}
break;
default:
{
if( ImplIsSupportedGraphic() )
return maMetaFile.GetPrefMapMode();
}
break;
}
}
return aMapMode;
}
void ImpGraphic::ImplSetPrefMapMode( const MapMode& rPrefMapMode )
{
ensureAvailable();
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get())
{
// ignore for Vector Graphic Data. If this is really used (except the grfcache)
// it can be extended by using maEx as buffer for maVectorGraphicData->getReplacement()
}
else
{
// #108077# Push through pref mapmode to animation object,
// will be lost on copy otherwise
if( ImplIsAnimated() )
{
const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefMapMode( rPrefMapMode );
}
maEx.SetPrefMapMode( rPrefMapMode );
}
}
break;
default:
{
if( ImplIsSupportedGraphic() )
maMetaFile.SetPrefMapMode( rPrefMapMode );
}
break;
}
}
sal_uLong ImpGraphic::ImplGetSizeBytes() const
{
if( 0 == mnSizeBytes )
{
if (mbPrepared)
ensureAvailable();
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get())
{
std::pair<VectorGraphicData::State, size_t> tmp(maVectorGraphicData->getSizeBytes());
if (VectorGraphicData::State::UNPARSED == tmp.first)
{
return tmp.second; // don't cache it until Vector Graphic Data is parsed
}
mnSizeBytes = tmp.second;
}
else
{
mnSizeBytes = mpAnimation ? mpAnimation->GetSizeBytes() : maEx.GetSizeBytes();
}
}
else if( meType == GraphicType::GdiMetafile )
{
mnSizeBytes = maMetaFile.GetSizeBytes();
}
}
return mnSizeBytes;
}
void ImpGraphic::ImplDraw( OutputDevice* pOutDev, const Point& rDestPt ) const
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && !maEx)
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
if ( mpAnimation )
{
mpAnimation->Draw( pOutDev, rDestPt );
}
else
{
maEx.Draw( pOutDev, rDestPt );
}
}
break;
default:
ImplDraw( pOutDev, rDestPt, maMetaFile.GetPrefSize() );
break;
}
}
}
void ImpGraphic::ImplDraw( OutputDevice* pOutDev,
const Point& rDestPt, const Size& rDestSize ) const
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
if( mpAnimation )
{
mpAnimation->Draw( pOutDev, rDestPt, rDestSize );
}
else
{
maEx.Draw( pOutDev, rDestPt, rDestSize );
}
}
break;
default:
{
const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
const_cast<ImpGraphic*>(this)->maMetaFile.Play( pOutDev, rDestPt, rDestSize );
const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
}
break;
}
}
}
void ImpGraphic::ImplStartAnimation( OutputDevice* pOutDev, const Point& rDestPt,
const Size& rDestSize, long nExtraData,
OutputDevice* pFirstFrameOutDev )
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation )
mpAnimation->Start( pOutDev, rDestPt, rDestSize, nExtraData, pFirstFrameOutDev );
}
void ImpGraphic::ImplStopAnimation( OutputDevice* pOutDev, long nExtraData )
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation )
mpAnimation->Stop( pOutDev, nExtraData );
}
void ImpGraphic::ImplSetAnimationNotifyHdl( const Link<Animation*,void>& rLink )
{
ensureAvailable();
if( mpAnimation )
mpAnimation->SetNotifyHdl( rLink );
}
Link<Animation*,void> ImpGraphic::ImplGetAnimationNotifyHdl() const
{
Link<Animation*,void> aLink;
ensureAvailable();
if( mpAnimation )
aLink = mpAnimation->GetNotifyHdl();
return aLink;
}
sal_uInt32 ImpGraphic::ImplGetAnimationLoopCount() const
{
if (mbSwapOut)
return maSwapInfo.mnAnimationLoopCount;
return mpAnimation ? mpAnimation->GetLoopCount() : 0;
}
void ImpGraphic::ImplSetContext( const std::shared_ptr<GraphicReader>& pReader )
{
mpContext = pReader;
mbDummyContext = false;
}
bool ImpGraphic::ImplReadEmbedded( SvStream& rIStm )
{
ensureAvailable();
MapMode aMapMode;
Size aSize;
sal_uInt32 nId;
sal_Int32 nType;
const SvStreamEndian nOldFormat = rIStm.GetEndian();
bool bRet = false;
rIStm.SetEndian( SvStreamEndian::LITTLE );
rIStm.ReadUInt32( nId );
// check version
if( GRAPHIC_FORMAT_50 == nId )
{
// read new style header
std::unique_ptr<VersionCompat> pCompat( new VersionCompat( rIStm, StreamMode::READ ) );
rIStm.ReadInt32( nType );
sal_Int32 nLen;
rIStm.ReadInt32( nLen );
ReadPair( rIStm, aSize );
ReadMapMode( rIStm, aMapMode );
}
else
{
// read old style header
sal_Int32 nWidth, nHeight;
sal_Int32 nMapMode, nScaleNumX, nScaleDenomX;
sal_Int32 nScaleNumY, nScaleDenomY, nOffsX, nOffsY;
rIStm.SeekRel( -4 );
sal_Int32 nLen;
rIStm.ReadInt32( nType ).ReadInt32( nLen ).ReadInt32( nWidth ).ReadInt32( nHeight );
rIStm.ReadInt32( nMapMode ).ReadInt32( nScaleNumX ).ReadInt32( nScaleDenomX ).ReadInt32( nScaleNumY );
rIStm.ReadInt32( nScaleDenomY ).ReadInt32( nOffsX ).ReadInt32( nOffsY );
// swapped
if( nType > 100 )
{
nType = OSL_SWAPDWORD( nType );
nWidth = OSL_SWAPDWORD( nWidth );
nHeight = OSL_SWAPDWORD( nHeight );
nMapMode = OSL_SWAPDWORD( nMapMode );
nScaleNumX = OSL_SWAPDWORD( nScaleNumX );
nScaleDenomX = OSL_SWAPDWORD( nScaleDenomX );
nScaleNumY = OSL_SWAPDWORD( nScaleNumY );
nScaleDenomY = OSL_SWAPDWORD( nScaleDenomY );
nOffsX = OSL_SWAPDWORD( nOffsX );
nOffsY = OSL_SWAPDWORD( nOffsY );
}
aSize = Size( nWidth, nHeight );
aMapMode = MapMode( static_cast<MapUnit>(nMapMode), Point( nOffsX, nOffsY ),
Fraction( nScaleNumX, nScaleDenomX ),
Fraction( nScaleNumY, nScaleDenomY ) );
}
meType = static_cast<GraphicType>(nType);
if( meType != GraphicType::NONE )
{
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
maEx = maVectorGraphicData->getReplacement();
}
maEx.SetSizePixel(aSize);
if( aMapMode != MapMode() )
{
maEx.SetPrefMapMode( aMapMode );
maEx.SetPrefSize( aSize );
}
}
else
{
maMetaFile.SetPrefMapMode( aMapMode );
maMetaFile.SetPrefSize( aSize );
}
if( meType == GraphicType::Bitmap || meType == GraphicType::GdiMetafile )
{
ReadImpGraphic( rIStm, *this );
bRet = rIStm.GetError() == ERRCODE_NONE;
}
else if( sal::static_int_cast<sal_uLong>(meType) >= SYS_WINMETAFILE
&& sal::static_int_cast<sal_uLong>(meType) <= SYS_MACMETAFILE )
{
Graphic aSysGraphic;
ConvertDataFormat nCvtType;
switch( sal::static_int_cast<sal_uLong>(meType) )
{
case SYS_WINMETAFILE:
case SYS_WNTMETAFILE: nCvtType = ConvertDataFormat::WMF; break;
case SYS_OS2METAFILE: nCvtType = ConvertDataFormat::MET; break;
case SYS_MACMETAFILE: nCvtType = ConvertDataFormat::PCT; break;
default:
nCvtType = ConvertDataFormat::Unknown;
break;
}
if( nType && GraphicConverter::Import( rIStm, aSysGraphic, nCvtType ) == ERRCODE_NONE )
{
*this = ImpGraphic( aSysGraphic.GetGDIMetaFile() );
bRet = rIStm.GetError() == ERRCODE_NONE;
}
else
meType = GraphicType::Default;
}
if( bRet )
{
ImplSetPrefMapMode( aMapMode );
ImplSetPrefSize( aSize );
}
}
else
bRet = true;
rIStm.SetEndian( nOldFormat );
return bRet;
}
bool ImpGraphic::ImplWriteEmbedded( SvStream& rOStm )
{
bool bRet = false;
ensureAvailable();
if( ( meType != GraphicType::NONE ) && ( meType != GraphicType::Default ) && !ImplIsSwapOut() )
{
const MapMode aMapMode( ImplGetPrefMapMode() );
const Size aSize( ImplGetPrefSize() );
const SvStreamEndian nOldFormat = rOStm.GetEndian();
sal_uLong nDataFieldPos;
rOStm.SetEndian( SvStreamEndian::LITTLE );
// write correct version ( old style/new style header )
if( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 )
{
// write ID for new format (5.0)
rOStm.WriteUInt32( GRAPHIC_FORMAT_50 );
// write new style header
std::unique_ptr<VersionCompat> pCompat( new VersionCompat( rOStm, StreamMode::WRITE, 1 ) );
rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
// data size is updated later
nDataFieldPos = rOStm.Tell();
rOStm.WriteInt32( 0 );
WritePair( rOStm, aSize );
WriteMapMode( rOStm, aMapMode );
}
else
{
// write old style (<=4.0) header
rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
// data size is updated later
nDataFieldPos = rOStm.Tell();
rOStm.WriteInt32( 0 );
rOStm.WriteInt32( aSize.Width() );
rOStm.WriteInt32( aSize.Height() );
rOStm.WriteInt32( static_cast<sal_uInt16>(aMapMode.GetMapUnit()) );
rOStm.WriteInt32( aMapMode.GetScaleX().GetNumerator() );
rOStm.WriteInt32( aMapMode.GetScaleX().GetDenominator() );
rOStm.WriteInt32( aMapMode.GetScaleY().GetNumerator() );
rOStm.WriteInt32( aMapMode.GetScaleY().GetDenominator() );
rOStm.WriteInt32( aMapMode.GetOrigin().X() );
rOStm.WriteInt32( aMapMode.GetOrigin().Y() );
}
// write data block
if( !rOStm.GetError() )
{
const sal_uLong nDataStart = rOStm.Tell();
if( ImplIsSupportedGraphic() )
WriteImpGraphic( rOStm, *this );
if( !rOStm.GetError() )
{
const sal_uLong nStmPos2 = rOStm.Tell();
rOStm.Seek( nDataFieldPos );
rOStm.WriteInt32( nStmPos2 - nDataStart );
rOStm.Seek( nStmPos2 );
bRet = true;
}
}
rOStm.SetEndian( nOldFormat );
}
return bRet;
}
bool ImpGraphic::ImplSwapOut()
{
bool bRet = false;
if( !ImplIsSwapOut() )
{
::utl::TempFile aTempFile;
const INetURLObject aTmpURL( aTempFile.GetURL() );
if( !aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty() )
{
std::unique_ptr<SvStream> xOStm;
try
{
xOStm = ::utl::UcbStreamHelper::CreateStream( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
}
catch( const css::uno::Exception& )
{
}
if( xOStm )
{
xOStm->SetVersion( SOFFICE_FILEFORMAT_50 );
xOStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
bRet = ImplSwapOut( xOStm.get() );
if( bRet )
{
mpSwapFile = o3tl::make_unique<ImpSwapFile>();
mpSwapFile->aSwapURL = aTmpURL;
mpSwapFile->maOriginURL = getOriginURL();
}
else
{
xOStm.reset();
try
{
::ucbhelper::Content aCnt( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
css::uno::Reference< css::ucb::XCommandEnvironment >(),
comphelper::getProcessComponentContext() );
aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
}
catch( const css::ucb::ContentCreationException& )
{
}
catch( const css::uno::RuntimeException& )
{
}
catch( const css::ucb::CommandAbortedException& )
{
}
catch( const css::uno::Exception& )
{
}
}
}
}
}
if (bRet)
vcl::graphic::Manager::get().swappedOut(this);
return bRet;
}
bool ImpGraphic::ImplSwapOut( SvStream* xOStm )
{
bool bRet = false;
if( xOStm )
{
xOStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
if( !xOStm->GetError() && ImplWriteEmbedded( *xOStm ) )
{
xOStm->Flush();
if( !xOStm->GetError() )
{
ImplCreateSwapInfo();
ImplClearGraphics();
bRet = mbSwapOut = true;
}
}
}
else
{
SAL_WARN("vcl.gdi", "Graphic SwapOut: No stream for swap out!");
}
return bRet;
}
bool ImpGraphic::ensureAvailable() const
{
auto pThis = const_cast<ImpGraphic*>(this);
if (ImplIsSwapOut())
return pThis->ImplSwapIn();
pThis->maLastUsed = std::chrono::high_resolution_clock::now();
return true;
}
bool ImpGraphic::loadPrepared()
{
Graphic aGraphic;
if (mpGfxLink->LoadNative(aGraphic))
{
GraphicExternalLink aLink = maGraphicExternalLink;
*this = *aGraphic.ImplGetImpGraphic();
maGraphicExternalLink = aLink;
return true;
}
return false;
}
bool ImpGraphic::ImplSwapIn()
{
bool bRet = false;
if (!ImplIsSwapOut())
return bRet;
if (mbPrepared)
{
bRet = loadPrepared();
}
else
{
OUString aSwapURL;
if( mpSwapFile )
aSwapURL = mpSwapFile->aSwapURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
if( !aSwapURL.isEmpty() )
{
std::unique_ptr<SvStream> xIStm;
try
{
xIStm = ::utl::UcbStreamHelper::CreateStream( aSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
}
catch( const css::uno::Exception& )
{
}
if( xIStm )
{
xIStm->SetVersion( SOFFICE_FILEFORMAT_50 );
xIStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
bRet = ImplSwapIn( xIStm.get() );
xIStm.reset();
if (mpSwapFile)
setOriginURL(mpSwapFile->maOriginURL);
mpSwapFile.reset();
}
}
}
if (bRet)
vcl::graphic::Manager::get().swappedIn(this);
return bRet;
}
bool ImpGraphic::ImplSwapIn( SvStream* xIStm )
{
bool bRet = false;
if( xIStm )
{
xIStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
if( !xIStm->GetError() )
{
//keep the swap file alive, because its quite possibly the backing storage
//for xIStm
std::shared_ptr<ImpSwapFile> xSwapFile(std::move(mpSwapFile));
assert(!mpSwapFile);
std::shared_ptr<GraphicReader> xContext(std::move(mpContext));
assert(!mpContext);
bool bDummyContext = mbDummyContext;
mbDummyContext = false;
bRet = ImplReadEmbedded( *xIStm );
//restore ownership of the swap file and context
mpSwapFile = std::move(xSwapFile);
mpContext = std::move(xContext);
mbDummyContext = bDummyContext;
if (!bRet)
{
//throw away swapfile, etc.
ImplClear();
}
mbSwapOut = false;
}
}
return bRet;
}
void ImpGraphic::ImplSetLink(const std::shared_ptr<GfxLink>& rGfxLink)
{
ensureAvailable();
mpGfxLink = rGfxLink;
if (mpGfxLink && mpGfxLink->IsNative())
mpGfxLink->SwapOut();
}
GfxLink ImpGraphic::ImplGetLink()
{
ensureAvailable();
return( mpGfxLink ? *mpGfxLink : GfxLink() );
}
bool ImpGraphic::ImplIsLink() const
{
return ( bool(mpGfxLink) );
}
BitmapChecksum ImpGraphic::ImplGetChecksum() const
{
BitmapChecksum nRet = 0;
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
if( mpAnimation )
{
nRet = mpAnimation->GetChecksum();
}
else
{
nRet = maEx.GetChecksum();
}
if (mpPdfData && mpPdfData->hasElements())
// Include the PDF data in the checksum, so a metafile with
// and without PDF data is considered to be different.
nRet = vcl_get_checksum(nRet, mpPdfData->getConstArray(), mpPdfData->getLength());
}
break;
default:
nRet = maMetaFile.GetChecksum();
break;
}
}
return nRet;
}
bool ImpGraphic::ImplExportNative( SvStream& rOStm ) const
{
bool bResult = false;
ensureAvailable();
if( !rOStm.GetError() )
{
if( !ImplIsSwapOut() )
{
if( mpGfxLink && mpGfxLink->IsNative() )
bResult = mpGfxLink->ExportNative( rOStm );
else
{
WriteImpGraphic( rOStm, *this );
bResult = ( rOStm.GetError() == ERRCODE_NONE );
}
}
else
rOStm.SetError( SVSTREAM_GENERALERROR );
}
return bResult;
}
void ReadImpGraphic( SvStream& rIStm, ImpGraphic& rImpGraphic )
{
if (rIStm.GetError())
return;
const sal_uLong nStmPos1 = rIStm.Tell();
sal_uInt32 nTmp;
rImpGraphic.ImplClear();
// read Id
rIStm.ReadUInt32( nTmp );
// if there is no more data, avoid further expensive
// reading which will create VDevs and other stuff, just to
// read nothing. CAUTION: Eof is only true AFTER reading another
// byte, a speciality of SvMemoryStream (!)
if (!rIStm.good())
return;
if (NATIVE_FORMAT_50 == nTmp)
{
Graphic aGraphic;
GfxLink aLink;
// read compat info
std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rIStm, StreamMode::READ ));
pCompat.reset(); // destructor writes stuff into the header
ReadGfxLink( rIStm, aLink );
// set dummy link to avoid creation of additional link after filtering;
// we set a default link to avoid unnecessary swapping of native data
aGraphic.SetGfxLink(std::make_shared<GfxLink>());
if( !rIStm.GetError() && aLink.LoadNative( aGraphic ) )
{
// set link only, if no other link was set
const bool bSetLink = !rImpGraphic.mpGfxLink;
// assign graphic
rImpGraphic = *aGraphic.ImplGetImpGraphic();
if( aLink.IsPrefMapModeValid() )
rImpGraphic.ImplSetPrefMapMode( aLink.GetPrefMapMode() );
if( aLink.IsPrefSizeValid() )
rImpGraphic.ImplSetPrefSize( aLink.GetPrefSize() );
if( bSetLink )
rImpGraphic.ImplSetLink(std::make_shared<GfxLink>(aLink));
}
else
{
rIStm.Seek( nStmPos1 );
rIStm.SetError( ERRCODE_IO_WRONGFORMAT );
}
return;
}
BitmapEx aBmpEx;
const SvStreamEndian nOldFormat = rIStm.GetEndian();
rIStm.SeekRel( -4 );
rIStm.SetEndian( SvStreamEndian::LITTLE );
ReadDIBBitmapEx(aBmpEx, rIStm);
if( !rIStm.GetError() )
{
sal_uInt32 nMagic1(0), nMagic2(0);
sal_uLong nActPos = rIStm.Tell();
rIStm.ReadUInt32( nMagic1 ).ReadUInt32( nMagic2 );
rIStm.Seek( nActPos );
rImpGraphic = ImpGraphic( aBmpEx );
if( !rIStm.GetError() && ( 0x5344414e == nMagic1 ) && ( 0x494d4931 == nMagic2 ) )
{
rImpGraphic.mpAnimation = o3tl::make_unique<Animation>();
ReadAnimation( rIStm, *rImpGraphic.mpAnimation );
// #108077# manually set loaded BmpEx to Animation
// (which skips loading its BmpEx if already done)
rImpGraphic.mpAnimation->SetBitmapEx(aBmpEx);
}
else
rIStm.ResetError();
}
else
{
GDIMetaFile aMtf;
rIStm.Seek( nStmPos1 );
rIStm.ResetError();
ReadGDIMetaFile( rIStm, aMtf );
if( !rIStm.GetError() )
{
rImpGraphic = aMtf;
}
else
{
ErrCode nOrigError = rIStm.GetErrorCode();
// try to stream in Svg defining data (length, byte array and evtl. path)
// See below (operator<<) for more information
const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
sal_uInt32 nMagic;
rIStm.Seek(nStmPos1);
rIStm.ResetError();
rIStm.ReadUInt32( nMagic );
if (nSvgMagic == nMagic || nWmfMagic == nMagic || nEmfMagic == nMagic)
{
sal_uInt32 nVectorGraphicDataArrayLength(0);
rIStm.ReadUInt32(nVectorGraphicDataArrayLength);
if (nVectorGraphicDataArrayLength)
{
VectorGraphicDataArray aNewData(nVectorGraphicDataArrayLength);
rIStm.ReadBytes(aNewData.getArray(), nVectorGraphicDataArrayLength);
OUString aPath = rIStm.ReadUniOrByteString(rIStm.GetStreamCharSet());
if (!rIStm.GetError())
{
VectorGraphicDataType aDataType(VectorGraphicDataType::Svg);
if (nWmfMagic == nMagic)
{
aDataType = VectorGraphicDataType::Wmf;
}
else if (nEmfMagic == nMagic)
{
aDataType = VectorGraphicDataType::Emf;
}
VectorGraphicDataPtr aVectorGraphicDataPtr(new VectorGraphicData(aNewData, aPath, aDataType));
rImpGraphic = aVectorGraphicDataPtr;
}
}
}
else if (nMagic == nPdfMagic)
{
// Stream in PDF data.
sal_uInt32 nPdfDataLength = 0;
rIStm.ReadUInt32(nPdfDataLength);
Bitmap aBitmap;
if (nPdfDataLength && !rIStm.GetError())
{
if (!rImpGraphic.mpPdfData)
rImpGraphic.mpPdfData.reset(new uno::Sequence<sal_Int8>());
if (vcl::ImportPDF(rIStm, aBitmap, rImpGraphic.mnPageNumber,
*rImpGraphic.mpPdfData,
rIStm.Tell(), nPdfDataLength))
{
rImpGraphic.maEx = aBitmap;
rImpGraphic.meType = GraphicType::Bitmap;
}
}
}
else
{
rIStm.SetError(nOrigError);
}
rIStm.Seek(nStmPos1);
}
}
rIStm.SetEndian( nOldFormat );
}
void WriteImpGraphic(SvStream& rOStm, const ImpGraphic& rImpGraphic)
{
if (rOStm.GetError())
return;
rImpGraphic.ensureAvailable();
if (rImpGraphic.ImplIsSwapOut())
{
rOStm.SetError( SVSTREAM_GENERALERROR );
return;
}
if( ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) &&
( rOStm.GetCompressMode() & SvStreamCompressFlags::NATIVE ) &&
rImpGraphic.mpGfxLink && rImpGraphic.mpGfxLink->IsNative() &&
!rImpGraphic.hasPdfData())
{
// native format
rOStm.WriteUInt32( NATIVE_FORMAT_50 );
// write compat info
std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rOStm, StreamMode::WRITE, 1 ));
pCompat.reset(); // destructor writes stuff into the header
rImpGraphic.mpGfxLink->SetPrefMapMode( rImpGraphic.ImplGetPrefMapMode() );
rImpGraphic.mpGfxLink->SetPrefSize( rImpGraphic.ImplGetPrefSize() );
WriteGfxLink( rOStm, *rImpGraphic.mpGfxLink );
}
else
{
// own format
const SvStreamEndian nOldFormat = rOStm.GetEndian();
rOStm.SetEndian( SvStreamEndian::LITTLE );
switch( rImpGraphic.ImplGetType() )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(rImpGraphic.getVectorGraphicData().get())
{
// stream out Vector Graphic defining data (length, byte array and evtl. path)
// this is used e.g. in swapping out graphic data and in transporting it over UNO API
// as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
// no problem to extend it; only used at runtime
switch (rImpGraphic.getVectorGraphicData()->getVectorGraphicDataType())
{
case VectorGraphicDataType::Wmf:
{
const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nWmfMagic);
break;
}
case VectorGraphicDataType::Emf:
{
const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nEmfMagic);
break;
}
default: // case VectorGraphicDataType::Svg:
{
const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nSvgMagic);
break;
}
}
rOStm.WriteUInt32( rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength() );
rOStm.WriteBytes(rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArray().getConstArray(),
rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
rOStm.WriteUniOrByteString(rImpGraphic.getVectorGraphicData()->getPath(),
rOStm.GetStreamCharSet());
}
else if (rImpGraphic.hasPdfData())
{
// Stream out PDF data.
rOStm.WriteUInt32(nPdfMagic);
rOStm.WriteUInt32(rImpGraphic.mpPdfData->getLength());
rOStm.WriteBytes(rImpGraphic.mpPdfData->getConstArray(), rImpGraphic.mpPdfData->getLength());
}
else if( rImpGraphic.ImplIsAnimated())
{
WriteAnimation( rOStm, *rImpGraphic.mpAnimation );
}
else
{
WriteDIBBitmapEx(rImpGraphic.maEx, rOStm);
}
}
break;
default:
{
if( rImpGraphic.ImplIsSupportedGraphic() )
WriteGDIMetaFile( rOStm, rImpGraphic.maMetaFile );
}
break;
}
rOStm.SetEndian( nOldFormat );
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.
↑ V519 The 'maSwapInfo.mbIsAnimated' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 554, 558.