/* -*- 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 <o3tl/make_shared.hxx>
#include <tools/fract.hxx>
#include <tools/vcompat.hxx>
#include <tools/urlobj.hxx>
#include <tools/stream.hxx>
#include <unotools/ucbhelper.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/tempfile.hxx>
#include <utility>
#include <vcl/filter/SvmReader.hxx>
#include <vcl/filter/SvmWriter.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/graphic/XPrimitive2D.hpp>
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <vcl/dibtools.hxx>
#include <map>
#include <memory>
#include <vcl/gdimetafiletools.hxx>
#include <vcl/TypeSerializer.hxx>
#include <vcl/pdfread.hxx>
#include <graphic/VectorGraphicLoader.hxx>
 
#define GRAPHIC_MTFTOBMP_MAXEXT     2048
#define GRAPHIC_STREAMBUFSIZE       8192UL
 
#define SWAP_FORMAT_ID COMPAT_FORMAT( 'S', 'W', 'A', 'P' )
 
using namespace com::sun::star;
 
 
class ImpSwapFile
{
private:
    utl::TempFileFast maTempFile;
    OUString maOriginURL;
 
public:
    ImpSwapFile(OUString aOriginURL)
        : maOriginURL(std::move(aOriginURL))
    {
    }
 
    SvStream* getStream() { return maTempFile.GetStream(StreamMode::READWRITE); }
    OUString const & getOriginURL() const { return maOriginURL; }
};
 
SvStream* ImpGraphic::getSwapFileStream() const
{
    if (mpSwapFile)
        return mpSwapFile->getStream();
    return nullptr;
}
 
ImpGraphic::ImpGraphic(bool bDefault)
    : MemoryManaged(false)
    , meType(bDefault ? GraphicType::Default : GraphicType::NONE)
{
}
 
ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
    : MemoryManaged(rImpGraphic)
    , maMetaFile(rImpGraphic.maMetaFile)
    , maBitmapEx(rImpGraphic.maBitmapEx)
    , maSwapInfo(rImpGraphic.maSwapInfo)
    , mpSwapFile(rImpGraphic.mpSwapFile)
    , mpGfxLink(rImpGraphic.mpGfxLink)
    , maVectorGraphicData(rImpGraphic.maVectorGraphicData)
    , meType(rImpGraphic.meType)
    , mnSizeBytes(rImpGraphic.mnSizeBytes)
    , mbSwapOut(rImpGraphic.mbSwapOut)
    , mbDummyContext(rImpGraphic.mbDummyContext)
    , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
    , mbPrepared(rImpGraphic.mbPrepared)
{
    updateCurrentSizeInBytes(mnSizeBytes);
 
    // Special case for animations
    if (rImpGraphic.mpAnimation)
    {
        mpAnimation = std::make_unique<Animation>(*rImpGraphic.mpAnimation);
        maBitmapEx = mpAnimation->GetBitmapEx();
    }
}
 
ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
    : MemoryManaged(rImpGraphic)
    , maMetaFile(std::move(rImpGraphic.maMetaFile))
    , maBitmapEx(std::move(rImpGraphic.maBitmapEx))
    , maSwapInfo(std::move(rImpGraphic.maSwapInfo))
    , mpAnimation(std::move(rImpGraphic.mpAnimation))
    , mpSwapFile(std::move(rImpGraphic.mpSwapFile))
    , mpGfxLink(std::move(rImpGraphic.mpGfxLink))
    , maVectorGraphicData(std::move(rImpGraphic.maVectorGraphicData))
    , meType(rImpGraphic.meType)
    , mnSizeBytes(rImpGraphic.mnSizeBytes)
    , mbSwapOut(rImpGraphic.mbSwapOut)
    , mbDummyContext(rImpGraphic.mbDummyContext)
    , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
    , mbPrepared (rImpGraphic.mbPrepared)
{
    updateCurrentSizeInBytes(mnSizeBytes);
 
    rImpGraphic.clear();
    rImpGraphic.mbDummyContext = false;
}
 
ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, sal_Int32 nPageIndex)
    : MemoryManaged(true)
    , mpGfxLink(std::move(xGfxLink))
    , meType(GraphicType::Bitmap)
    , mbSwapOut(true)
{
    maSwapInfo.mbIsTransparent = true;
    maSwapInfo.mbIsAlpha = true;
    maSwapInfo.mbIsEPS = false;
    maSwapInfo.mbIsAnimated = false;
    maSwapInfo.mnAnimationLoopCount = 0;
    maSwapInfo.mnPageIndex = nPageIndex;
 
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(GraphicExternalLink aGraphicExternalLink)
    : MemoryManaged(true)
    , meType(GraphicType::Default)
    , maGraphicExternalLink(std::move(aGraphicExternalLink))
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const BitmapEx& rBitmapEx)
    : MemoryManaged(!rBitmapEx.IsEmpty())
    , maBitmapEx(rBitmapEx)
    , meType(rBitmapEx.IsEmpty() ? GraphicType::NONE : GraphicType::Bitmap)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
    : MemoryManaged(bool(rVectorGraphicDataPtr))
    , maVectorGraphicData(rVectorGraphicDataPtr)
    , meType(rVectorGraphicDataPtr ? GraphicType::Bitmap : GraphicType::NONE)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const Animation& rAnimation)
    : MemoryManaged(true)
    , maBitmapEx(rAnimation.GetBitmapEx())
    , mpAnimation(std::make_unique<Animation>(rAnimation))
    , meType(GraphicType::Bitmap)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const GDIMetaFile& rMetafile)
    : MemoryManaged(true)
    , maMetaFile(rMetafile)
    , meType(GraphicType::GdiMetafile)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::~ImpGraphic()
{
}
 
ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
{
    if( &rImpGraphic != this )
    {
        maMetaFile = rImpGraphic.maMetaFile;
        meType = rImpGraphic.meType;
        mnSizeBytes = rImpGraphic.mnSizeBytes;
        updateCurrentSizeInBytes(mnSizeBytes);
 
        maSwapInfo = rImpGraphic.maSwapInfo;
        mbDummyContext = rImpGraphic.mbDummyContext;
        maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
 
        mpAnimation.reset();
 
        if ( rImpGraphic.mpAnimation )
        {
            mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
            maBitmapEx = mpAnimation->GetBitmapEx();
        }
        else
        {
            maBitmapEx = rImpGraphic.maBitmapEx;
        }
 
        mbSwapOut = rImpGraphic.mbSwapOut;
        mpSwapFile = rImpGraphic.mpSwapFile;
        mbPrepared = rImpGraphic.mbPrepared;
 
        mpGfxLink = rImpGraphic.mpGfxLink;
 
        maVectorGraphicData = rImpGraphic.maVectorGraphicData;
        resetLastUsed();
 
        changeExisting(mnSizeBytes);
    }
 
    return *this;
}
 
ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
{
    maMetaFile = std::move(rImpGraphic.maMetaFile);
    meType = rImpGraphic.meType;
    mnSizeBytes = rImpGraphic.mnSizeBytes;
    maSwapInfo = std::move(rImpGraphic.maSwapInfo);
    mbDummyContext = rImpGraphic.mbDummyContext;
    mpAnimation = std::move(rImpGraphic.mpAnimation);
    maBitmapEx = std::move(rImpGraphic.maBitmapEx);
    mbSwapOut = rImpGraphic.mbSwapOut;
    mpSwapFile = std::move(rImpGraphic.mpSwapFile);
    mpGfxLink = std::move(rImpGraphic.mpGfxLink);
    maVectorGraphicData = std::move(rImpGraphic.maVectorGraphicData);
    maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
    mbPrepared = rImpGraphic.mbPrepared;
 
    rImpGraphic.clear();
    rImpGraphic.mbDummyContext = false;
    resetLastUsed();
 
    changeExisting(mnSizeBytes);
 
    return *this;
}
 
bool ImpGraphic::operator==( const ImpGraphic& rOther ) const
{
    if( this == &rOther )
        return true;
 
    if (mbPrepared && rOther.mbPrepared)
        return (*mpGfxLink == *rOther.mpGfxLink);
 
    if (!isAvailable() || !rOther.isAvailable())
        return false;
 
    if ( meType != rOther.meType )
        return false;
 
    bool bRet = false;
    switch( meType )
    {
        case GraphicType::NONE:
        case GraphicType::Default:
            return true;
 
        case GraphicType::GdiMetafile:
            return ( rOther.maMetaFile == maMetaFile );
 
        case GraphicType::Bitmap:
        {
            if(maVectorGraphicData)
            {
                if(maVectorGraphicData == rOther.maVectorGraphicData)
                {
                    // equal instances
                    bRet = true;
                }
                else if(rOther.maVectorGraphicData)
                {
                    // equal content
                    bRet = (*maVectorGraphicData) == (*rOther.maVectorGraphicData);
                }
            }
            else if( mpAnimation )
            {
                if( rOther.mpAnimation && ( *rOther.mpAnimation == *mpAnimation ) )
                    bRet = true;
            }
            else if( !rOther.mpAnimation && ( rOther.maBitmapEx == maBitmapEx ) )
            {
                bRet = true;
            }
        }
        break;
    }
 
    return bRet;
}
 
const std::shared_ptr<VectorGraphicData>& ImpGraphic::getVectorGraphicData() const
{
    ensureAvailable();
 
    return maVectorGraphicData;
}
 
void ImpGraphic::createSwapInfo()
{
    if (isSwappedOut())
        return;
 
    if (!maBitmapEx.IsEmpty())
        maSwapInfo.maSizePixel = maBitmapEx.GetSizePixel();
    else
        maSwapInfo.maSizePixel = Size();
 
    maSwapInfo.maPrefMapMode = getPrefMapMode();
    maSwapInfo.maPrefSize = getPrefSize();
    maSwapInfo.mbIsAnimated = isAnimated();
    maSwapInfo.mbIsEPS = isEPS();
    maSwapInfo.mbIsTransparent = isTransparent();
    maSwapInfo.mbIsAlpha = isAlpha();
    maSwapInfo.mnAnimationLoopCount = getAnimationLoopCount();
    maSwapInfo.mnPageIndex = getPageNumber();
}
 
void ImpGraphic::clearGraphics()
{
    maBitmapEx.Clear();
    maMetaFile.Clear();
    mpAnimation.reset();
    maVectorGraphicData.reset();
}
 
void ImpGraphic::setPrepared(bool bAnimated, const Size* pSizeHint)
{
    mbPrepared = true;
    mbSwapOut = true;
    meType = GraphicType::Bitmap;
 
    SvMemoryStream aMemoryStream(const_cast<sal_uInt8*>(mpGfxLink->GetData()), mpGfxLink->GetDataSize(), StreamMode::READ | StreamMode::WRITE);
 
    if (pSizeHint)
    {
        maSwapInfo.maPrefSize = *pSizeHint;
        maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
    }
 
    GraphicDescriptor aDescriptor(aMemoryStream, nullptr);
    if (aDescriptor.Detect(true))
    {
        if (!pSizeHint)
        {
            // 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 (aDescriptor.GetPreferredLogSize() && aDescriptor.GetPreferredMapMode())
            {
                maSwapInfo.maPrefSize = *aDescriptor.GetPreferredLogSize();
                maSwapInfo.maPrefMapMode = *aDescriptor.GetPreferredMapMode();
            }
            else 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.mbIsTransparent = aDescriptor.IsTransparent();
        maSwapInfo.mbIsAlpha = aDescriptor.IsAlpha();
    } else {
        maSwapInfo.mbIsTransparent = false;
        maSwapInfo.mbIsAlpha = false;
    }
 
    maSwapInfo.mnAnimationLoopCount = 0;
    maSwapInfo.mbIsEPS = false;
    maSwapInfo.mbIsAnimated = bAnimated;
 
    if (maVectorGraphicData)
        maSwapInfo.mnPageIndex = maVectorGraphicData->getPageIndex();
}
 
void ImpGraphic::clear()
{
    mpSwapFile.reset();
    mbSwapOut = false;
    mbPrepared = false;
 
    // cleanup
    clearGraphics();
    meType = GraphicType::NONE;
    mnSizeBytes = 0;
 
    changeExisting(mnSizeBytes);
    maGraphicExternalLink.msURL.clear();
}
 
bool ImpGraphic::isSupportedGraphic() const
{
    return meType != GraphicType::NONE;
}
 
bool ImpGraphic::isTransparent() const
{
    bool bRet(true);
 
    if (mbSwapOut)
    {
        bRet = maSwapInfo.mbIsTransparent;
    }
    else if (meType == GraphicType::Bitmap && !maVectorGraphicData)
    {
        bRet = mpAnimation ? mpAnimation->IsTransparent() : maBitmapEx.IsAlpha();
    }
 
    return bRet;
}
 
bool ImpGraphic::isAlpha() const
{
    bool bRet(false);
 
    if (mbSwapOut)
    {
        bRet = maSwapInfo.mbIsAlpha;
    }
    else if (maVectorGraphicData)
    {
        bRet = true;
    }
    else if (meType == GraphicType::Bitmap)
    {
        bRet = (nullptr == mpAnimation && maBitmapEx.IsAlpha());
    }
 
    return bRet;
}
 
bool ImpGraphic::isAnimated() const
{
    return mbSwapOut ? maSwapInfo.mbIsAnimated : mpAnimation != nullptr;
}
 
bool ImpGraphic::isEPS() 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();
}
 
void ImpGraphic::updateBitmapFromVectorGraphic(const Size& pixelSize) const
{
    assert (maVectorGraphicData);
 
    // use maBitmapEx as local buffer for rendered vector image
    if (pixelSize.Width() && pixelSize.Height())
    {
        if (maBitmapEx.IsEmpty() || maBitmapEx.GetSizePixel() != pixelSize)
            const_cast<ImpGraphic*>(this)->maBitmapEx = maVectorGraphicData->getBitmap(pixelSize);
    }
    else // maVectorGraphicData caches the replacement, so updating unconditionally is cheap
    {
        const_cast<ImpGraphic*>(this)->maBitmapEx = maVectorGraphicData->getReplacement();
    }
 
    if (maExPrefSize.getWidth() && maExPrefSize.getHeight())
        const_cast<ImpGraphic*>(this)->maBitmapEx.SetPrefSize(maExPrefSize);
}
 
Bitmap ImpGraphic::getBitmap(const GraphicConversionParameters& rParameters) const
{
    Bitmap aRetBmp;
 
    ensureAvailable();
 
    if( meType == GraphicType::Bitmap )
    {
        if (!mpAnimation && maVectorGraphicData)
            updateBitmapFromVectorGraphic(rParameters.getSizePixel());
 
        const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maBitmapEx );
 
        aRetBmp = rRetBmpEx.GetBitmap( COL_WHITE );
 
        if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
            aRetBmp.Scale(rParameters.getSizePixel());
    }
    else if( ( meType != GraphicType::Default ) && isSupportedGraphic() )
    {
        if(maBitmapEx.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<tools::Long>(GRAPHIC_MTFTOBMP_MAXEXT * fWH));
                    aDrawSize.setHeight(GRAPHIC_MTFTOBMP_MAXEXT);
                }
                else
                {
                    aDrawSize.setWidth(GRAPHIC_MTFTOBMP_MAXEXT);
                    aDrawSize.setHeight(basegfx::fround<tools::Long>(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 == getType())
            {
                // tdf#126319 Removed correction based on hairline-at-the-extremes of
                // the metafile. The task shows that this is no longer sufficient since
                // less hairlines get used in general - what is good, but breaks that
                // old fix. Anyways, hairlines are a left-over from non-AA times
                // when it was not possible to paint lines taller than one pixel.
                // This might need to be corrected further using primitives and
                // the possibility to get better-quality ranges for correction. For
                // now, always add that one pixel.
                aPixelSize.setWidth(aPixelSize.getWidth() + 1);
                aPixelSize.setHeight(aPixelSize.getHeight() + 1);
            }
 
            if(aVDev->SetOutputSizePixel(aPixelSize))
            {
                if(rParameters.getAntiAliase())
                {
                    aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::Enable);
                }
 
                if(rParameters.getSnapHorVerLines())
                {
                    aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline);
                }
 
                draw(*aVDev, Point(), aDrawSize);
 
                // use maBitmapEx as local buffer for rendered metafile
                const_cast< ImpGraphic* >(this)->maBitmapEx = aVDev->GetBitmapEx( Point(), aVDev->GetOutputSizePixel() );
            }
        }
 
        aRetBmp = maBitmapEx.GetBitmap();
    }
 
    if( !aRetBmp.IsEmpty() )
    {
        aRetBmp.SetPrefMapMode(getPrefMapMode());
        aRetBmp.SetPrefSize(getPrefSize());
    }
 
    return aRetBmp;
}
 
BitmapEx ImpGraphic::getBitmapEx(const GraphicConversionParameters& rParameters) const
{
    BitmapEx aRetBmpEx;
 
    ensureAvailable();
 
    if( meType == GraphicType::Bitmap )
    {
        if (!mpAnimation && maVectorGraphicData)
            updateBitmapFromVectorGraphic(rParameters.getSizePixel());
 
        aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maBitmapEx );
 
        if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
        {
            aRetBmpEx.Scale(
                rParameters.getSizePixel(),
                BmpScaleFlag::Fast);
        }
    }
    else if( ( meType != GraphicType::Default ) && isSupportedGraphic() )
    {
        if(maBitmapEx.IsEmpty())
        {
            const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) );
 
            // use maBitmapEx as local buffer for rendered metafile
            const_cast< ImpGraphic* >(this)->maBitmapEx = BitmapEx(getBitmap(rParameters), aMonoMask.getBitmap(rParameters));
        }
 
        aRetBmpEx = maBitmapEx;
    }
 
    return aRetBmpEx;
}
 
Animation ImpGraphic::getAnimation() const
{
    Animation aAnimation;
 
    ensureAvailable();
    if( mpAnimation )
        aAnimation = *mpAnimation;
 
    return aAnimation;
}
 
const BitmapEx& ImpGraphic::getBitmapExRef() const
{
    ensureAvailable();
    return maBitmapEx;
}
 
const GDIMetaFile& ImpGraphic::getGDIMetaFile() const
{
    ensureAvailable();
    if (!maMetaFile.GetActionSize()
        && maVectorGraphicData
        && (VectorGraphicDataType::Emf == maVectorGraphicData->getType()
            || VectorGraphicDataType::Wmf == maVectorGraphicData->getType()))
    {
        // 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]);
            auto pUnoPrimitive = static_cast< const drawinglayer::primitive2d::UnoPrimitive2D* >(xReference.get());
            if (pUnoPrimitive)
            {
                const MetafileAccessor* pMetafileAccessor = dynamic_cast< const MetafileAccessor* >(pUnoPrimitive->getBasePrimitive2D().get());
 
                if (pMetafileAccessor)
                {
                    // it is a MetafileAccessor implementation, get Metafile
                    pMetafileAccessor->accessMetafile(const_cast< ImpGraphic* >(this)->maMetaFile);
                }
            }
        }
    }
 
    if (GraphicType::Bitmap == meType && !maMetaFile.GetActionSize())
    {
        if (maVectorGraphicData)
            updateBitmapFromVectorGraphic();
 
        // #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);
 
        // #123983# directly create a metafile with the same PrefSize and PrefMapMode
        // the bitmap has, this will be an always correct metafile
        if(maBitmapEx.IsAlpha())
        {
            pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), maBitmapEx.GetPrefSize(), maBitmapEx));
        }
        else
        {
            pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), maBitmapEx.GetPrefSize(), maBitmapEx.GetBitmap()));
        }
 
        pThat->maMetaFile.Stop();
        pThat->maMetaFile.WindStart();
        pThat->maMetaFile.SetPrefSize(maBitmapEx.GetPrefSize());
        pThat->maMetaFile.SetPrefMapMode(maBitmapEx.GetPrefMapMode());
    }
 
    return maMetaFile;
}
 
Size ImpGraphic::getSizePixel() const
{
    Size aSize;
 
    if (isSwappedOut())
        aSize = maSwapInfo.maSizePixel;
    else
        aSize = getBitmapEx(GraphicConversionParameters()).GetSizePixel();
 
    return aSize;
}
 
Size ImpGraphic::getPrefSize() const
{
    Size aSize;
 
    if (isSwappedOut())
    {
        aSize = maSwapInfo.maPrefSize;
    }
    else
    {
        switch (meType)
        {
            case GraphicType::Bitmap:
            {
                if (maVectorGraphicData && maBitmapEx.IsEmpty())
                {
                    if (!maExPrefSize.getWidth() || !maExPrefSize.getHeight())
                    {
                        // svg not yet buffered in maBitmapEx, return size derived from range
                        const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
 
#ifdef MACOSX
                        // tdf#157680 scale down estimated size of embedded PDF
                        // For some unknown reason, the embedded PDF sizes
                        // are 20x larger than expected. This only occurs on
                        // macOS so possibly there is some special conversion
                        // from MapUnit::MapPoint to MapUnit::MapTwip elsewhere
                        // in the code.
                        if (maVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
                           aSize = Size(basegfx::fround(rRange.getWidth() / 20.0f), basegfx::fround(rRange.getHeight() / 20.0f));
                        else
#endif
                            aSize = Size(basegfx::fround<tools::Long>(rRange.getWidth()), basegfx::fround<tools::Long>(rRange.getHeight()));
                    }
                    else
                    {
                        aSize = maExPrefSize;
                    }
                }
                else
                {
                    aSize = maBitmapEx.GetPrefSize();
 
                    if( !aSize.Width() || !aSize.Height() )
                    {
                        aSize = maBitmapEx.GetSizePixel();
                    }
                }
            }
            break;
 
            case GraphicType::GdiMetafile:
            {
                aSize = maMetaFile.GetPrefSize();
            }
            break;
 
            case GraphicType::NONE:
            case GraphicType::Default:
                break;
        }
    }
 
    return aSize;
}
 
void ImpGraphic::setValuesForPrefSize(const Size& rPrefSize)
{
    switch (meType)
    {
        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)
            {
                maExPrefSize = rPrefSize;
            }
 
            // #108077# Push through pref size to animation object,
            // will be lost on copy otherwise
            if (mpAnimation)
            {
                const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize(rPrefSize);
            }
 
            maBitmapEx.SetPrefSize(rPrefSize);
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            if (isSupportedGraphic())
                maMetaFile.SetPrefSize(rPrefSize);
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
}
 
void ImpGraphic::setPrefSize(const Size& rPrefSize)
{
    ensureAvailable();
    setValuesForPrefSize(rPrefSize);
}
 
MapMode ImpGraphic::getPrefMapMode() const
{
    MapMode aMapMode;
 
    if (isSwappedOut())
    {
        aMapMode = maSwapInfo.maPrefMapMode;
    }
    else
    {
        switch (meType)
        {
            case GraphicType::Bitmap:
            {
                if (maVectorGraphicData && maBitmapEx.IsEmpty())
                {
                    // svg not yet buffered in maBitmapEx, return default PrefMapMode
                    aMapMode = MapMode(MapUnit::Map100thMM);
                }
                else
                {
                    const Size aSize(maBitmapEx.GetPrefSize());
 
                    if (aSize.Width() && aSize.Height())
                        aMapMode = maBitmapEx.GetPrefMapMode();
                }
            }
            break;
 
            case GraphicType::GdiMetafile:
            {
                return maMetaFile.GetPrefMapMode();
            }
            break;
 
            case GraphicType::NONE:
            case GraphicType::Default:
                break;
        }
    }
 
    return aMapMode;
}
 
void ImpGraphic::setValuesForPrefMapMod(const MapMode& rPrefMapMode)
{
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                // ignore for Vector Graphic Data. If this is really used (except the grfcache)
                // it can be extended by using maBitmapEx as buffer for updateBitmapFromVectorGraphic()
            }
            else
            {
                // #108077# Push through pref mapmode to animation object,
                // will be lost on copy otherwise
                if (mpAnimation)
                {
                    const_cast<BitmapEx&>(mpAnimation->GetBitmapEx()).SetPrefMapMode(rPrefMapMode);
                }
 
                maBitmapEx.SetPrefMapMode(rPrefMapMode);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            maMetaFile.SetPrefMapMode(rPrefMapMode);
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
}
 
void ImpGraphic::setPrefMapMode(const MapMode& rPrefMapMode)
{
    ensureAvailable();
    setValuesForPrefMapMod(rPrefMapMode);
}
 
void ImpGraphic::ensureCurrentSizeInBytes()
{
    if (isAvailable())
        changeExisting(getSizeBytes());
    else
        changeExisting(0);
}
 
sal_uLong ImpGraphic::getSizeBytes() const
{
    if (mnSizeBytes > 0)
        return mnSizeBytes;
 
    if (mbPrepared)
        ensureAvailable();
 
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                std::pair<VectorGraphicData::State, size_t> aPair(maVectorGraphicData->getSizeBytes());
                if (VectorGraphicData::State::UNPARSED == aPair.first)
                {
                    return aPair.second; // don't cache it until Vector Graphic Data is parsed
                }
                mnSizeBytes = aPair.second;
            }
            else
            {
                mnSizeBytes = mpAnimation ? mpAnimation->GetSizeBytes() : maBitmapEx.GetSizeBytes();
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            mnSizeBytes = maMetaFile.GetSizeBytes();
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
 
    return mnSizeBytes;
}
 
void ImpGraphic::draw(OutputDevice& rOutDev, const Point& rDestPt) const
{
    ensureAvailable();
 
    if (isSwappedOut())
        return;
 
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (mpAnimation)
            {
                mpAnimation->Draw(rOutDev, rDestPt);
            }
            else
            {
                if (maVectorGraphicData)
                    updateBitmapFromVectorGraphic();
                maBitmapEx.Draw(&rOutDev, rDestPt);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            draw(rOutDev, rDestPt, maMetaFile.GetPrefSize());
        }
        break;
 
        case GraphicType::Default:
        case GraphicType::NONE:
            break;
    }
}
 
void ImpGraphic::draw(OutputDevice& rOutDev,
                      const Point& rDestPt, const Size& rDestSize) const
{
    ensureAvailable();
 
    if (isSwappedOut())
        return;
 
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (mpAnimation)
            {
                mpAnimation->Draw(rOutDev, rDestPt, rDestSize);
            }
            else
            {
                if (maVectorGraphicData)
                    updateBitmapFromVectorGraphic(rOutDev.LogicToPixel(rDestSize));
                maBitmapEx.Draw(&rOutDev, rDestPt, rDestSize);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
            const_cast<ImpGraphic*>(this)->maMetaFile.Play(rOutDev, rDestPt, rDestSize);
            const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
        }
        break;
 
        case GraphicType::Default:
        case GraphicType::NONE:
            break;
    }
}
 
void ImpGraphic::startAnimation(OutputDevice& rOutDev, const Point& rDestPt,
                                const Size& rDestSize, tools::Long nRendererId,
                                OutputDevice* pFirstFrameOutDev )
{
    ensureAvailable();
 
    if( isSupportedGraphic() && !isSwappedOut() && mpAnimation )
        mpAnimation->Start(rOutDev, rDestPt, rDestSize, nRendererId, pFirstFrameOutDev);
}
 
void ImpGraphic::stopAnimation( const OutputDevice* pOutDev, tools::Long nRendererId )
{
    ensureAvailable();
 
    if( isSupportedGraphic() && !isSwappedOut() && mpAnimation )
        mpAnimation->Stop( pOutDev, nRendererId );
}
 
void ImpGraphic::setAnimationNotifyHdl( const Link<Animation*,void>& rLink )
{
    ensureAvailable();
 
    if( mpAnimation )
        mpAnimation->SetNotifyHdl( rLink );
}
 
Link<Animation*,void> ImpGraphic::getAnimationNotifyHdl() const
{
    Link<Animation*,void> aLink;
 
    ensureAvailable();
 
    if( mpAnimation )
        aLink = mpAnimation->GetNotifyHdl();
 
    return aLink;
}
 
sal_uInt32 ImpGraphic::getAnimationLoopCount() const
{
    if (mbSwapOut)
        return maSwapInfo.mnAnimationLoopCount;
 
    return mpAnimation ? mpAnimation->GetLoopCount() : 0;
}
 
bool ImpGraphic::swapInContent(SvStream& rStream)
{
    bool bRet = false;
 
    sal_uInt32 nId;
    sal_Int32 nType;
    sal_Int32 nLength;
 
    rStream.ReadUInt32(nId);
 
    // check version
    if (SWAP_FORMAT_ID != nId)
    {
        SAL_WARN("vcl", "Incompatible swap file!");
        return false;
    }
 
    rStream.ReadInt32(nType);
    rStream.ReadInt32(nLength);
 
    meType = static_cast<GraphicType>(nType);
 
    if (meType == GraphicType::NONE  || meType == GraphicType::Default)
    {
        return true;
    }
    else
    {
        bRet = swapInGraphic(rStream);
    }
 
    return bRet;
}
 
bool ImpGraphic::swapOutGraphic(SvStream& rStream)
{
    if (rStream.GetError())
        return false;
 
    ensureAvailable();
 
    if (isSwappedOut())
    {
        rStream.SetError(SVSTREAM_GENERALERROR);
        return false;
    }
 
    switch (meType)
    {
        case GraphicType::GdiMetafile:
        {
            if(!rStream.GetError())
            {
                SvmWriter aWriter(rStream);
                aWriter.Write(maMetaFile);
            }
        }
        break;
 
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Vector));
                // 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 (maVectorGraphicData->getType())
                {
                    case VectorGraphicDataType::Wmf:
                    {
                        rStream.WriteUInt32(constWmfMagic);
                        break;
                    }
                    case VectorGraphicDataType::Emf:
                    {
                        rStream.WriteUInt32(constEmfMagic);
                        break;
                    }
                    case VectorGraphicDataType::Svg:
                    {
                        rStream.WriteUInt32(constSvgMagic);
                        break;
                    }
                    case VectorGraphicDataType::Pdf:
                    {
                        rStream.WriteUInt32(constPdfMagic);
                        break;
                    }
                }
 
                rStream.WriteUInt32(maVectorGraphicData->getBinaryDataContainer().getSize());
                maVectorGraphicData->getBinaryDataContainer().writeToStream(rStream);
            }
            else if (mpAnimation)
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Animation));
                WriteAnimation(rStream, *mpAnimation);
            }
            else
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Bitmap));
                WriteDIBBitmapEx(maBitmapEx, rStream);
            }
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
 
    if (mpGfxLink)
        mpGfxLink->getDataContainer().swapOut();
 
    return true;
}
 
bool ImpGraphic::swapOutContent(SvStream& rStream)
{
    ensureAvailable();
 
    bool bRet = false;
 
    if (meType == GraphicType::NONE || meType == GraphicType::Default || isSwappedOut())
        return false;
 
    sal_uLong nDataFieldPos;
 
    // Write the SWAP ID
    rStream.WriteUInt32(SWAP_FORMAT_ID);
 
    rStream.WriteInt32(static_cast<sal_Int32>(meType));
 
    // data size is updated later
    nDataFieldPos = rStream.Tell();
    rStream.WriteInt32(0);
 
    // write data block
    const sal_uInt64 nDataStart = rStream.Tell();
 
    swapOutGraphic(rStream);
 
    if (!rStream.GetError())
    {
        // Write the written length th the header
        const sal_uInt64 nCurrentPosition = rStream.Tell();
        rStream.Seek(nDataFieldPos);
        rStream.WriteInt32(nCurrentPosition - nDataStart);
        rStream.Seek(nCurrentPosition);
        bRet = true;
    }
 
    return bRet;
}
 
bool ImpGraphic::swapOut()
{
    if (isSwappedOut())
        return false;
 
    bool bResult = false;
 
    // We have GfxLink so we have the source available
    if (mpGfxLink && mpGfxLink->IsNative())
    {
        createSwapInfo();
 
        clearGraphics();
 
        // reset the swap file
        mpSwapFile.reset();
 
        mpGfxLink->getDataContainer().swapOut();
 
        // mark as swapped out
        mbSwapOut = true;
 
        bResult = true;
    }
    else
    {
        // Create a swap file
        auto pSwapFile = o3tl::make_shared<ImpSwapFile>(getOriginURL());
 
        // Open a stream to write the swap file to
        {
            SvStream* pOutputStream = pSwapFile->getStream();
 
            if (!pOutputStream)
                return false;
 
            // Write to stream
            pOutputStream->SetVersion(SOFFICE_FILEFORMAT_50);
            pOutputStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
            pOutputStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
 
            if (!pOutputStream->GetError() && swapOutContent(*pOutputStream))
            {
                pOutputStream->FlushBuffer();
                bResult = !pOutputStream->GetError();
            }
        }
 
        // Check if writing was successful
        if (bResult)
        {
            // We have swapped out, so can clean memory and prepare swap info
            createSwapInfo();
            clearGraphics();
 
            mpSwapFile = std::move(pSwapFile);
            mbSwapOut = true;
        }
    }
 
    if (bResult)
    {
        // Signal to manager that we have swapped out
        swappedOut(0);
    }
 
    return bResult;
}
 
bool ImpGraphic::ensureAvailable() const
{
    bool bResult = true;
 
    if (isSwappedOut())
    {
        auto pThis = const_cast<ImpGraphic*>(this);
        pThis->registerIntoManager();
 
        bResult = pThis->swapIn();
    }
 
    resetLastUsed();
    return bResult;
}
 
void ImpGraphic::updateFromLoadedGraphic(const ImpGraphic* pGraphic)
{
    if (mbPrepared)
    {
        GraphicExternalLink aLink = maGraphicExternalLink;
        Size aPrefSize = maSwapInfo.maPrefSize;
        MapMode aPrefMapMode = maSwapInfo.maPrefMapMode;
        *this = *pGraphic;
        if (aPrefSize.getWidth() && aPrefSize.getHeight() && aPrefMapMode == getPrefMapMode())
        {
            // Use custom preferred size if it was set when the graphic was still unloaded.
            // Only set the size in case the unloaded and loaded unit matches.
            setPrefSize(aPrefSize);
        }
        maGraphicExternalLink = std::move(aLink);
    }
    else
    {
        // Move over only graphic content
        mpAnimation.reset();
        if (pGraphic->mpAnimation)
        {
            mpAnimation = std::make_unique<Animation>(*pGraphic->mpAnimation);
            maBitmapEx = mpAnimation->GetBitmapEx();
        }
        else
        {
            maBitmapEx = pGraphic->maBitmapEx;
        }
 
        maMetaFile = pGraphic->maMetaFile;
        maVectorGraphicData = pGraphic->maVectorGraphicData;
 
        // Set to 0, to force recalculation
        mnSizeBytes = 0;
        mnChecksum = 0;
 
        restoreFromSwapInfo();
 
        mbSwapOut = false;
    }
}
 
void ImpGraphic::dumpState(rtl::OStringBuffer &rState)
{
    if (meType == GraphicType::NONE && mnSizeBytes == 0)
        return; // uninteresting.
 
    rState.append("\n\t");
 
    if (mbSwapOut)
        rState.append("swapped\t");
    else
        rState.append("loaded\t");
 
    rState.append(static_cast<sal_Int32>(meType));
    rState.append("\tsize:\t");
    rState.append(static_cast<sal_Int64>(mnSizeBytes));
    rState.append("\tgfxl:\t");
    rState.append(static_cast<sal_Int64>(mpGfxLink ? mpGfxLink->getSizeBytes() : -1));
    rState.append("\t");
    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Width()));
    rState.append("x");
    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Height()));
    rState.append("\t");
    rState.append(static_cast<sal_Int32>(maExPrefSize.Width()));
    rState.append("x");
    rState.append(static_cast<sal_Int32>(maExPrefSize.Height()));
}
 
void ImpGraphic::restoreFromSwapInfo()
{
    setValuesForPrefMapMod(maSwapInfo.maPrefMapMode);
    setValuesForPrefSize(maSwapInfo.maPrefSize);
 
    if (maVectorGraphicData)
    {
        maVectorGraphicData->setPageIndex(maSwapInfo.mnPageIndex);
    }
}
 
namespace
{
 
std::optional<VectorGraphicDataType> lclConvertToVectorGraphicType(GfxLink const & rLink)
{
    switch(rLink.GetType())
    {
        case GfxLinkType::NativePdf:
            return VectorGraphicDataType::Pdf;
 
        case GfxLinkType::NativeWmf:
            if (rLink.IsEMF())
                return VectorGraphicDataType::Emf;
            else
                return VectorGraphicDataType::Wmf;
 
        case GfxLinkType::NativeSvg:
            return VectorGraphicDataType::Svg;
 
        default:
            break;
    }
    return std::optional<VectorGraphicDataType>();
}
 
} // end namespace
 
bool ImpGraphic::swapIn()
{
    if (!isSwappedOut())
        return false;
 
    bool bReturn = false;
 
    if (mbPrepared)
    {
        Graphic aGraphic;
        if (!mpGfxLink->LoadNative(aGraphic))
            return false;
 
        updateFromLoadedGraphic(aGraphic.ImplGetImpGraphic());
 
        resetLastUsed();
        bReturn = true;
    }
    else if (mpGfxLink && mpGfxLink->IsNative())
    {
        std::optional<VectorGraphicDataType> oType = lclConvertToVectorGraphicType(*mpGfxLink);
        if (oType)
        {
            maVectorGraphicData = vcl::loadVectorGraphic(mpGfxLink->getDataContainer(), *oType);
 
            // Set to 0, to force recalculation
            mnSizeBytes = 0;
            mnChecksum = 0;
 
            restoreFromSwapInfo();
 
            mbSwapOut = false;
        }
        else
        {
            Graphic aGraphic;
            if (!mpGfxLink->LoadNative(aGraphic))
                return false;
 
            ImpGraphic* pImpGraphic = aGraphic.ImplGetImpGraphic();
            if (meType != pImpGraphic->meType)
                return false;
 
            updateFromLoadedGraphic(pImpGraphic);
        }
 
        resetLastUsed();
        bReturn = true;
    }
    else
    {
        SvStream* pStream = nullptr;
 
        if (mpSwapFile)
            pStream = mpSwapFile->getStream();
 
        if (pStream)
        {
            pStream->SetVersion(SOFFICE_FILEFORMAT_50);
            pStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
            pStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
            pStream->Seek(STREAM_SEEK_TO_BEGIN);
 
            bReturn = swapInFromStream(*pStream);
 
            restoreFromSwapInfo();
 
            setOriginURL(mpSwapFile->getOriginURL());
 
            mpSwapFile.reset();
        }
    }
 
    if (bReturn)
    {
        swappedIn(getSizeBytes());
    }
 
    return bReturn;
}
 
bool ImpGraphic::swapInFromStream(SvStream& rStream)
{
    bool bRet = false;
 
    if (rStream.GetError())
        return false;
 
    clearGraphics();
    mnSizeBytes = 0;
    mnChecksum = 0;
 
    bRet = swapInContent(rStream);
 
    if (!bRet)
    {
        //throw away swapfile, etc.
        clear();
    }
 
    mbSwapOut = false;
 
    return bRet;
}
 
bool ImpGraphic::swapInGraphic(SvStream& rStream)
{
    bool bReturn = false;
 
    if (rStream.GetError())
        return bReturn;
 
    if (meType == GraphicType::Bitmap)
    {
        sal_Int32 nContentType = -1;
        rStream.ReadInt32(nContentType);
        if (nContentType < 0)
            return false;
 
        auto eContentType = static_cast<GraphicContentType>(nContentType);
 
        switch (eContentType)
        {
            case GraphicContentType::Bitmap:
            {
                BitmapEx aBitmapEx;
                ReadDIBBitmapEx(aBitmapEx, rStream);
                if (!rStream.GetError())
                {
                    maBitmapEx = aBitmapEx;
                    bReturn = true;
                }
            }
            break;
 
            case GraphicContentType::Animation:
            {
                auto pAnimation = std::make_unique<Animation>();
                ReadAnimation(rStream, *pAnimation);
                if (!rStream.GetError())
                {
                    mpAnimation = std::move(pAnimation);
                    maBitmapEx = mpAnimation->GetBitmapEx();
                    bReturn = true;
                }
            }
            break;
 
            case GraphicContentType::Vector:
            {
                // try to stream in Svg defining data (length, byte array and evtl. path)
                // See below (operator<<) for more information
                sal_uInt32 nMagic;
                rStream.ReadUInt32(nMagic);
 
                if (constSvgMagic == nMagic || constWmfMagic == nMagic || constEmfMagic == nMagic || constPdfMagic == nMagic)
                {
                    sal_uInt32 nVectorGraphicDataSize(0);
                    rStream.ReadUInt32(nVectorGraphicDataSize);
 
                    if (nVectorGraphicDataSize)
                    {
                        BinaryDataContainer aDataContainer(rStream, nVectorGraphicDataSize);
 
                        if (rStream.GetError())
                            return false;
 
                        VectorGraphicDataType aDataType;
 
                        switch (nMagic)
                        {
                            case constSvgMagic:
                                aDataType = VectorGraphicDataType::Svg;
                                break;
                            case constWmfMagic:
                                aDataType = VectorGraphicDataType::Wmf;
                                break;
                            case constEmfMagic:
                                aDataType = VectorGraphicDataType::Emf;
                                break;
                            case constPdfMagic:
                                aDataType = VectorGraphicDataType::Pdf;
                                break;
                            default:
                                return false;
                        }
 
                        auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, aDataType);
 
                        if (!rStream.GetError())
                        {
                            maVectorGraphicData = std::move(aVectorGraphicDataPtr);
                            bReturn = true;
                        }
                    }
                }
            }
            break;
        }
    }
    else if (meType == GraphicType::GdiMetafile)
    {
        GDIMetaFile aMetaFile;
        SvmReader aReader(rStream);
        aReader.Read(aMetaFile);
        if (!rStream.GetError())
        {
            maMetaFile = aMetaFile;
            bReturn = true;
        }
    }
    return bReturn;
}
 
void ImpGraphic::setGfxLink(const std::shared_ptr<GfxLink>& rGfxLink)
{
    ensureAvailable();
 
    mpGfxLink = rGfxLink;
}
 
const std::shared_ptr<GfxLink> & ImpGraphic::getSharedGfxLink() const
{
    return mpGfxLink;
}
 
GfxLink ImpGraphic::getGfxLink() const
{
    ensureAvailable();
 
    return( mpGfxLink ? *mpGfxLink : GfxLink() );
}
 
bool ImpGraphic::isGfxLink() const
{
    return ( bool(mpGfxLink) );
}
 
BitmapChecksum ImpGraphic::getChecksum() const
{
    if (mnChecksum != 0)
        return mnChecksum;
 
    ensureAvailable();
 
    switch (meType)
    {
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
 
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
                mnChecksum = maVectorGraphicData->GetChecksum();
            else if (mpAnimation)
                mnChecksum = mpAnimation->GetChecksum();
            else
                mnChecksum = maBitmapEx.GetChecksum();
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            mnChecksum = SvmWriter::GetChecksum(maMetaFile);
        }
        break;
    }
    return mnChecksum;
}
 
sal_Int32 ImpGraphic::getPageNumber() const
{
    if (isSwappedOut())
        return maSwapInfo.mnPageIndex;
 
    if (maVectorGraphicData)
        return maVectorGraphicData->getPageIndex();
    return -1;
}
 
bool ImpGraphic::canReduceMemory() const
{
    return !isSwappedOut();
}
 
bool ImpGraphic::reduceMemory()
{
    return swapOut();
}
 
std::chrono::high_resolution_clock::time_point ImpGraphic::getLastUsed() const
{
    return maLastUsed;
}
 
void ImpGraphic::resetLastUsed() const
{
    maLastUsed = std::chrono::high_resolution_clock::now();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: maSwapInfo.