summaryrefslogtreecommitdiffstats
path: root/vcl/source/graphic
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/graphic')
-rw-r--r--vcl/source/graphic/BinaryDataContainer.cxx189
-rw-r--r--vcl/source/graphic/BinaryDataContainerTools.cxx28
-rw-r--r--vcl/source/graphic/GraphicID.cxx101
-rw-r--r--vcl/source/graphic/GraphicLoader.cxx44
-rw-r--r--vcl/source/graphic/GraphicObject.cxx935
-rw-r--r--vcl/source/graphic/GraphicObject2.cxx503
-rw-r--r--vcl/source/graphic/GraphicReader.cxx29
-rw-r--r--vcl/source/graphic/Manager.cxx317
-rw-r--r--vcl/source/graphic/UnoBinaryDataContainer.cxx22
-rw-r--r--vcl/source/graphic/UnoGraphic.cxx261
-rw-r--r--vcl/source/graphic/UnoGraphicDescriptor.cxx421
-rw-r--r--vcl/source/graphic/UnoGraphicMapper.cxx87
-rw-r--r--vcl/source/graphic/UnoGraphicObject.cxx101
-rw-r--r--vcl/source/graphic/UnoGraphicProvider.cxx837
-rw-r--r--vcl/source/graphic/VectorGraphicLoader.cxx26
-rw-r--r--vcl/source/graphic/VectorGraphicSearch.cxx307
16 files changed, 4208 insertions, 0 deletions
diff --git a/vcl/source/graphic/BinaryDataContainer.cxx b/vcl/source/graphic/BinaryDataContainer.cxx
new file mode 100644
index 0000000000..89ae5eb8da
--- /dev/null
+++ b/vcl/source/graphic/BinaryDataContainer.cxx
@@ -0,0 +1,189 @@
+/* -*- 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/.
+ *
+ */
+
+#include <vcl/BinaryDataContainer.hxx>
+#include <o3tl/hash_combine.hxx>
+#include <unotools/tempfile.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/seqstream.hxx>
+#include <sal/log.hxx>
+
+struct BinaryDataContainer::Impl
+{
+ // temp file to store the data out of RAM if necessary
+ std::unique_ptr<utl::TempFileFast> mpFile;
+ // the binary data
+ std::shared_ptr<std::vector<sal_uInt8>> mpData;
+
+ Impl(SvStream& stream, size_t size) { readData(stream, size); }
+
+ /// Populate mpData from the stream
+ void readData(SvStream& stream, size_t size)
+ {
+ auto pData = std::make_shared<std::vector<sal_uInt8>>(size);
+ if (stream.ReadBytes(pData->data(), pData->size()) == size)
+ mpData = std::move(pData);
+ }
+
+ /// ensure the data is in-RAM
+ void ensureSwappedIn()
+ {
+ if (mpData || !mpFile)
+ return;
+
+ auto pStream = mpFile->GetStream(StreamMode::READ);
+ pStream->Seek(0);
+ readData(*pStream, pStream->remainingSize());
+
+ // Horrifying data loss ...
+ SAL_WARN_IF(pStream->GetError(), "vcl",
+ "Inconsistent system - failed to swap image back in");
+ }
+
+ void swapOut()
+ {
+ if (mpFile)
+ {
+ // we already have it swapped out.
+ mpData.reset();
+ return;
+ }
+
+ if (!mpData || mpData->empty())
+ return;
+
+ mpFile.reset(new utl::TempFileFast());
+ auto pStream = mpFile->GetStream(StreamMode::READWRITE);
+
+ pStream->WriteBytes(mpData->data(), mpData->size());
+
+ mpData.reset();
+ }
+};
+
+BinaryDataContainer::BinaryDataContainer(SvStream& stream, size_t size)
+ : mpImpl(new Impl(stream, size))
+{
+}
+
+size_t BinaryDataContainer::calculateHash() const
+{
+ size_t nSeed = 0;
+ if (mpImpl && mpImpl->mpData && !mpImpl->mpData->empty())
+ {
+ o3tl::hash_combine(nSeed, getSize());
+ for (sal_uInt8 const& rByte : *mpImpl->mpData)
+ o3tl::hash_combine(nSeed, rByte);
+ }
+ return nSeed;
+}
+
+css::uno::Sequence<sal_Int8> BinaryDataContainer::getCopyAsByteSequence() const
+{
+ if (isEmpty())
+ return css::uno::Sequence<sal_Int8>();
+ assert(mpImpl);
+
+ css::uno::Sequence<sal_Int8> aData(getSize());
+
+ std::copy(mpImpl->mpData->cbegin(), mpImpl->mpData->cend(), aData.getArray());
+
+ return aData;
+}
+
+namespace
+{
+/*
+ * Hold a reference on the internal state in case we swap out
+ * and free the vector while someone holds an SvStream pointer.
+ */
+class ReferencedMemoryStream : public SvMemoryStream
+{
+ std::shared_ptr<std::vector<sal_uInt8>> mpData;
+
+public:
+ ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData)
+ : SvMemoryStream(pData->data(), pData->size(), StreamMode::READ)
+ , mpData(pData)
+ {
+ }
+};
+
+class ReferencedXInputStream : public comphelper::MemoryInputStream
+{
+ std::shared_ptr<std::vector<sal_uInt8>> mpData;
+
+public:
+ ReferencedXInputStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData)
+ : comphelper::MemoryInputStream(reinterpret_cast<const sal_Int8*>(pData->data()),
+ pData->size())
+ , mpData(pData)
+ {
+ }
+};
+}
+
+std::shared_ptr<SvStream> BinaryDataContainer::getAsStream()
+{
+ ensureSwappedIn(); // TODO: transfer in streamed chunks
+ return std::make_shared<ReferencedMemoryStream>(mpImpl->mpData);
+}
+
+css::uno::Reference<css::io::XInputStream> BinaryDataContainer::getAsXInputStream()
+{
+ ensureSwappedIn(); // TODO: transfer in streamed chunks
+ return new ReferencedXInputStream(mpImpl->mpData);
+}
+
+std::size_t BinaryDataContainer::writeToStream(SvStream& rStream) const
+{
+ ensureSwappedIn(); // TODO: transfer in streamed chunks
+ return rStream.WriteBytes(getData(), getSize());
+}
+
+size_t BinaryDataContainer::getSize() const
+{
+ ensureSwappedIn();
+ return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0;
+}
+
+size_t BinaryDataContainer::getSizeBytes() const
+{
+ return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0;
+}
+
+bool BinaryDataContainer::isEmpty() const
+{
+ ensureSwappedIn();
+ return !mpImpl || !mpImpl->mpData || mpImpl->mpData->empty();
+}
+
+const sal_uInt8* BinaryDataContainer::getData() const
+{
+ ensureSwappedIn();
+ return mpImpl && mpImpl->mpData ? mpImpl->mpData->data() : nullptr;
+}
+
+void BinaryDataContainer::ensureSwappedIn() const
+{
+ if (mpImpl)
+ mpImpl->ensureSwappedIn();
+}
+
+void BinaryDataContainer::swapOut() const
+{
+ // Only bother reducing memory footprint in kit mode - for mobile/online etc.
+ if (!mpImpl || !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ mpImpl->swapOut();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/BinaryDataContainerTools.cxx b/vcl/source/graphic/BinaryDataContainerTools.cxx
new file mode 100644
index 0000000000..c643803313
--- /dev/null
+++ b/vcl/source/graphic/BinaryDataContainerTools.cxx
@@ -0,0 +1,28 @@
+/* -*- 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/.
+ *
+ */
+
+#include <vcl/BinaryDataContainerTools.hxx>
+#include <graphic/UnoBinaryDataContainer.hxx>
+
+namespace vcl
+{
+BinaryDataContainer convertUnoBinaryDataContainer(
+ const css::uno::Reference<css::util::XBinaryDataContainer>& rxBinaryDataContainer)
+{
+ BinaryDataContainer aBinaryDataContainer;
+ UnoBinaryDataContainer* pUnoBinaryDataContainer
+ = dynamic_cast<UnoBinaryDataContainer*>(rxBinaryDataContainer.get());
+ if (pUnoBinaryDataContainer)
+ aBinaryDataContainer = pUnoBinaryDataContainer->getBinaryDataContainer();
+ return aBinaryDataContainer;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/GraphicID.cxx b/vcl/source/graphic/GraphicID.cxx
new file mode 100644
index 0000000000..d78acea530
--- /dev/null
+++ b/vcl/source/graphic/GraphicID.cxx
@@ -0,0 +1,101 @@
+/* -*- 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 <graphic/GraphicID.hxx>
+
+#include <impgraph.hxx>
+#include <rtl/crc.h>
+#include <rtl/strbuf.hxx>
+
+GraphicID::GraphicID(ImpGraphic const& rGraphic)
+{
+ rGraphic.ensureAvailable();
+
+ mnID1 = static_cast<sal_uLong>(rGraphic.getType()) << 28;
+ mnID2 = mnID3 = mnID4 = 0;
+
+ if (rGraphic.getType() == GraphicType::Bitmap)
+ {
+ auto const& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData();
+ if (rVectorGraphicDataPtr)
+ {
+ const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange();
+
+ mnID1 |= rVectorGraphicDataPtr->getBinaryDataContainer().getSize();
+ mnID2 = basegfx::fround(rRange.getWidth());
+ mnID3 = basegfx::fround(rRange.getHeight());
+ mnID4 = rtl_crc32(0, rVectorGraphicDataPtr->getBinaryDataContainer().getData(),
+ rVectorGraphicDataPtr->getBinaryDataContainer().getSize());
+ }
+ else if (rGraphic.isAnimated())
+ {
+ const Animation aAnimation(rGraphic.getAnimation());
+
+ mnID1 |= (aAnimation.Count() & 0x0fffffff);
+ mnID2 = aAnimation.GetDisplaySizePixel().Width();
+ mnID3 = aAnimation.GetDisplaySizePixel().Height();
+ mnID4 = rGraphic.getChecksum();
+ }
+ else
+ {
+ const BitmapEx aBmpEx(rGraphic.getBitmapEx(GraphicConversionParameters()));
+
+ mnID1 |= aBmpEx.IsAlpha() ? 1 : 0;
+ mnID2 = aBmpEx.GetSizePixel().Width();
+ mnID3 = aBmpEx.GetSizePixel().Height();
+ mnID4 = rGraphic.getChecksum();
+ }
+ }
+ else if (rGraphic.getType() == GraphicType::GdiMetafile)
+ {
+ const GDIMetaFile& rMtf = rGraphic.getGDIMetaFile();
+
+ mnID1 |= (rMtf.GetActionSize() & 0x0fffffff);
+ mnID2 = rMtf.GetPrefSize().Width();
+ mnID3 = rMtf.GetPrefSize().Height();
+ mnID4 = rGraphic.getChecksum();
+ }
+}
+
+OString GraphicID::getIDString() const
+{
+ static const char aHexData[]
+ = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ sal_Int32 nShift, nIndex = 0;
+ sal_Int32 nLen = 24 + (2 * BITMAP_CHECKSUM_SIZE);
+ OStringBuffer aHexStr(nLen);
+ aHexStr.setLength(nLen);
+
+ 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();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/GraphicLoader.cxx b/vcl/source/graphic/GraphicLoader.cxx
new file mode 100644
index 0000000000..63d35efb68
--- /dev/null
+++ b/vcl/source/graphic/GraphicLoader.cxx
@@ -0,0 +1,44 @@
+/* -*- 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/.
+ *
+ */
+
+#include <vcl/GraphicLoader.hxx>
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/weld.hxx>
+
+using namespace css;
+
+namespace vcl::graphic
+{
+Graphic loadFromURL(OUString const& rURL, weld::Window* pParentWin)
+{
+ Graphic aGraphic;
+
+ std::unique_ptr<SvStream> pInputStream = utl::UcbStreamHelper::CreateStream(
+ rURL, StreamMode::READ, pParentWin ? pParentWin->GetXWindow() : nullptr);
+
+ if (pInputStream)
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+
+ ErrCode nError
+ = rFilter.ImportGraphic(aGraphic, rURL, *pInputStream, GRFILTER_FORMAT_DONTKNOW,
+ nullptr, GraphicFilterImportFlags::NONE);
+ if (nError != ERRCODE_NONE || aGraphic.GetType() == GraphicType::NONE)
+ return Graphic();
+ }
+
+ return aGraphic;
+}
+} // end vcl::graphic
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/GraphicObject.cxx b/vcl/source/graphic/GraphicObject.cxx
new file mode 100644
index 0000000000..7abf3b3d3b
--- /dev/null
+++ b/vcl/source/graphic/GraphicObject.cxx
@@ -0,0 +1,935 @@
+/* -*- 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 <algorithm>
+
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tools/fract.hxx>
+#include <tools/helpers.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <vcl/outdev.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <memory>
+
+
+using namespace css;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::Sequence;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::beans::XPropertySet;
+
+#define WATERMARK_LUM_OFFSET 50
+#define WATERMARK_CON_OFFSET -70
+
+namespace vcl::graphic
+{
+
+void SearchForGraphics(uno::Reference<uno::XInterface> const & xInterface,
+ std::vector<uno::Reference<css::graphic::XGraphic>> & raGraphicList)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(xInterface, UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ if (xPropertySet->getPropertySetInfo()->hasPropertyByName("ImageURL"))
+ {
+ OUString sURL;
+ xPropertySet->getPropertyValue("ImageURL") >>= sURL;
+ if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL))
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(sURL);
+ if (!aGraphic.IsNone())
+ {
+ raGraphicList.push_back(aGraphic.GetXGraphic());
+ }
+ }
+ } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName("Graphic"))
+ {
+ uno::Reference<css::graphic::XGraphic> xGraphic;
+ xPropertySet->getPropertyValue("Graphic") >>= xGraphic;
+ if (xGraphic.is())
+ {
+ raGraphicList.push_back(xGraphic);
+ }
+ }
+ }
+ Reference<XNameContainer> xContainer(xInterface, UNO_QUERY);
+ if (xContainer.is())
+ {
+ const css::uno::Sequence<OUString> aElementNames = xContainer->getElementNames();
+ for (OUString const & rName : aElementNames)
+ {
+ uno::Reference<XInterface> xInnerInterface;
+ xContainer->getByName(rName) >>= xInnerInterface;
+ SearchForGraphics(xInnerInterface, raGraphicList);
+ }
+ }
+}
+
+} // end namespace vcl::graphic
+
+namespace
+{
+
+bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz,
+ GraphicObject const & rObj, const GraphicAttr& rAttr)
+{
+ Point aPt( rPt );
+ Size aSz( rSz );
+ bool bRet = false;
+
+ if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) )
+ {
+ // simple output of transformed graphic
+ const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
+
+ if( aGraphic.IsSupportedGraphic() )
+ {
+ const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10;
+
+ if( nRot10 )
+ {
+ tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) );
+
+ aPoly.Rotate( aPt, nRot10 );
+ const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() );
+ aPt = aRotBoundRect.TopLeft();
+ aSz = aRotBoundRect.GetSize();
+ }
+
+ aGraphic.Draw(rOut, aPt, aSz);
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void lclImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
+{
+ GraphicAttr aAttr( rAttr );
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
+ {
+ switch( aAttr.GetDrawMode() )
+ {
+ case GraphicDrawMode::Mono:
+ rBmpEx.Convert( BmpConversion::N1BitThreshold );
+ break;
+
+ case GraphicDrawMode::Greys:
+ rBmpEx.Convert( BmpConversion::N8BitGreys );
+ break;
+
+ case GraphicDrawMode::Watermark:
+ {
+ aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
+ aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
+ {
+ rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
+ aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
+ aAttr.GetGamma(), aAttr.IsInvert() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
+ {
+ rBmpEx.Mirror( aAttr.GetMirrorFlags() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
+ {
+ rBmpEx.Rotate( aAttr.GetRotation(), COL_TRANSPARENT );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
+ {
+ rBmpEx.AdjustTransparency(255 - aAttr.GetAlpha());
+ }
+}
+
+void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
+{
+ GraphicAttr aAttr( rAttr );
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
+ {
+ switch( aAttr.GetDrawMode() )
+ {
+ case GraphicDrawMode::Mono:
+ rMtf.Convert( MtfConversion::N1BitThreshold );
+ break;
+
+ case GraphicDrawMode::Greys:
+ rMtf.Convert( MtfConversion::N8BitGreys );
+ break;
+
+ case GraphicDrawMode::Watermark:
+ {
+ aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
+ aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
+ {
+ rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
+ aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
+ aAttr.GetGamma(), aAttr.IsInvert() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
+ {
+ rMtf.Mirror( aAttr.GetMirrorFlags() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
+ {
+ rMtf.Rotate( aAttr.GetRotation() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
+ {
+ OSL_FAIL( "Missing implementation: Mtf-Transparency" );
+ }
+}
+
+void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
+{
+ GraphicAttr aAttr( rAttr );
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
+ {
+ switch( aAttr.GetDrawMode() )
+ {
+ case GraphicDrawMode::Mono:
+ rAnimation.Convert( BmpConversion::N1BitThreshold );
+ break;
+
+ case GraphicDrawMode::Greys:
+ rAnimation.Convert( BmpConversion::N8BitGreys );
+ break;
+
+ case GraphicDrawMode::Watermark:
+ {
+ aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
+ aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
+ {
+ rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
+ aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
+ aAttr.GetGamma(), aAttr.IsInvert() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
+ {
+ rAnimation.Mirror( aAttr.GetMirrorFlags() );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
+ {
+ OSL_FAIL( "Missing implementation: Animation-Rotation" );
+ }
+
+ if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
+ {
+ OSL_FAIL( "Missing implementation: Animation-Transparency" );
+ }
+}
+
+} // end anonymous namespace
+
+struct GrfSimpleCacheObj
+{
+ Graphic maGraphic;
+ GraphicAttr maAttr;
+
+ GrfSimpleCacheObj( Graphic aGraphic, const GraphicAttr& rAttr ) :
+ maGraphic(std::move( aGraphic )), maAttr( rAttr ) {}
+};
+
+GraphicObject::GraphicObject()
+{
+}
+
+GraphicObject::GraphicObject(Graphic aGraphic)
+ : maGraphic(std::move(aGraphic))
+{
+}
+
+GraphicObject::GraphicObject(const GraphicObject& rGraphicObj)
+ : maGraphic(rGraphicObj.GetGraphic())
+ , maAttr(rGraphicObj.maAttr)
+ , maUserData(rGraphicObj.maUserData)
+{
+}
+
+GraphicObject::~GraphicObject()
+{
+}
+
+GraphicType GraphicObject::GetType() const
+{
+ return maGraphic.GetType();
+}
+
+Size GraphicObject::GetPrefSize() const
+{
+ return maGraphic.GetPrefSize();
+}
+
+MapMode GraphicObject::GetPrefMapMode() const
+{
+ return maGraphic.GetPrefMapMode();
+}
+
+bool GraphicObject::IsTransparent() const
+{
+ return maGraphic.IsTransparent();
+}
+
+bool GraphicObject::IsAnimated() const
+{
+ return maGraphic.IsAnimated();
+}
+
+bool GraphicObject::IsEPS() const
+{
+ return maGraphic.IsEPS();
+}
+
+bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
+ tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const
+{
+ bool bRet = false;
+
+ if( GetType() != GraphicType::NONE )
+ {
+ tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) );
+ const Degree10 nRot10 = pAttr->GetRotation() % 3600_deg10;
+ const Point aOldOrigin( rPt );
+ const MapMode aMap100( MapUnit::Map100thMM );
+ Size aSize100;
+ tools::Long nTotalWidth, nTotalHeight;
+
+ if( nRot10 )
+ {
+ aClipPoly.Rotate( rPt, nRot10 );
+ bRectClipRegion = false;
+ }
+ else
+ bRectClipRegion = true;
+
+ rClipPolyPoly = tools::PolyPolygon(aClipPoly);
+
+ if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
+ else
+ {
+ MapMode m(maGraphic.GetPrefMapMode());
+ aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
+ }
+
+ nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
+ nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();
+
+ if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 )
+ {
+ double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth;
+ const tools::Long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale );
+ const tools::Long nNewRight = nNewLeft + FRound( aSize100.Width() * fScale ) - 1;
+
+ fScale = static_cast<double>(rSz.Width()) / aSize100.Width();
+ rPt.AdjustX(FRound( nNewLeft * fScale ) );
+ rSz.setWidth( FRound( ( nNewRight - nNewLeft + 1 ) * fScale ) );
+
+ fScale = static_cast<double>(aSize100.Height()) / nTotalHeight;
+ const tools::Long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale );
+ const tools::Long nNewBottom = nNewTop + FRound( aSize100.Height() * fScale ) - 1;
+
+ fScale = static_cast<double>(rSz.Height()) / aSize100.Height();
+ rPt.AdjustY(FRound( nNewTop * fScale ) );
+ rSz.setHeight( FRound( ( nNewBottom - nNewTop + 1 ) * fScale ) );
+
+ if( nRot10 )
+ {
+ tools::Polygon aOriginPoly( 1 );
+
+ aOriginPoly[ 0 ] = rPt;
+ aOriginPoly.Rotate( aOldOrigin, nRot10 );
+ rPt = aOriginPoly[ 0 ];
+ }
+
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj )
+{
+ if( &rGraphicObj != this )
+ {
+ mxSimpleCache.reset();
+ maGraphic = rGraphicObj.GetGraphic();
+ maAttr = rGraphicObj.maAttr;
+ maUserData = rGraphicObj.maUserData;
+ }
+
+ return *this;
+}
+
+bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
+{
+ return rGraphicObj.maGraphic == maGraphic
+ && rGraphicObj.maAttr == maAttr;
+}
+
+OString GraphicObject::GetUniqueID() const
+{
+ return GetGraphic().getUniqueID();
+}
+
+void GraphicObject::SetAttr( const GraphicAttr& rAttr )
+{
+ maAttr = rAttr;
+
+ if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr))
+ mxSimpleCache.reset();
+}
+
+void GraphicObject::SetUserData()
+{
+ maUserData.clear();
+}
+
+void GraphicObject::SetUserData( const OUString& rUserData )
+{
+ maUserData = rUserData;
+}
+
+bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz,
+ const GraphicAttr* pAttr) const
+{
+ GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
+ Point aPt( rPt );
+ Size aSz( rSz );
+ const DrawModeFlags nOldDrawMode = rOut.GetDrawMode();
+ bool bCropped = aAttr.IsCropped();
+ bool bRet;
+
+ rOut.SetDrawMode(nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ));
+
+ // mirrored horizontally
+ if( aSz.Width() < 0 )
+ {
+ aPt.AdjustX(aSz.Width() + 1 );
+ aSz.setWidth( -aSz.Width() );
+ aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal );
+ }
+
+ // mirrored vertically
+ if( aSz.Height() < 0 )
+ {
+ aPt.AdjustY(aSz.Height() + 1 );
+ aSz.setHeight( -aSz.Height() );
+ aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical );
+ }
+
+ if( bCropped )
+ {
+ tools::PolyPolygon aClipPolyPoly;
+ bool bRectClip;
+ const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
+
+ rOut.Push(vcl::PushFlags::CLIPREGION);
+
+ if( bCrop )
+ {
+ if( bRectClip )
+ {
+ // #i29534# Store crop rect for later forwarding to
+ // PDF writer
+ tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect();
+ rOut.IntersectClipRegion(aCropRect);
+ }
+ else
+ {
+ rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
+ }
+ }
+ }
+
+ bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr);
+
+ if( bCropped )
+ rOut.Pop();
+
+ rOut.SetDrawMode( nOldDrawMode );
+
+ return bRet;
+}
+
+void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize,
+ const Size& rOffset, int nTileCacheSize1D)
+{
+ if (rSize.IsEmpty())
+ return;
+
+ const MapMode aOutMapMode(rOut.GetMapMode());
+ // #106258# Clamp size to 1 for zero values. This is okay, since
+ // logical size of zero is handled above already
+ const Size aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ),
+ ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) );
+
+ //#i69780 clip final tile size to a sane max size
+ while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16)
+ nTileCacheSize1D /= 2;
+ while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16)
+ nTileCacheSize1D /= 2;
+
+ ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D);
+}
+
+bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz,
+ tools::Long nRendererId,
+ OutputDevice* pFirstFrameOutDev)
+{
+ bool bRet = false;
+
+ GetGraphic();
+
+ const GraphicAttr aAttr( GetAttr() );
+
+ if (IsAnimated())
+ {
+ Point aPt( rPt );
+ Size aSz( rSz );
+ bool bCropped = aAttr.IsCropped();
+
+ if( bCropped )
+ {
+ tools::PolyPolygon aClipPolyPoly;
+ bool bRectClip;
+ const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
+
+ rOut.Push(vcl::PushFlags::CLIPREGION);
+
+ if( bCrop )
+ {
+ if( bRectClip )
+ rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect());
+ else
+ rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
+ }
+ }
+
+ if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev)
+ {
+ mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr));
+ mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
+ }
+
+ mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nRendererId, pFirstFrameOutDev);
+
+ if( bCropped )
+ rOut.Pop();
+
+ bRet = true;
+ }
+ else
+ bRet = Draw(rOut, rPt, rSz, &aAttr);
+
+ return bRet;
+}
+
+void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId )
+{
+ if (mxSimpleCache)
+ mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId);
+}
+
+const Graphic& GraphicObject::GetGraphic() const
+{
+ return maGraphic;
+}
+
+void GraphicObject::SetGraphic( const Graphic& rGraphic)
+{
+ maGraphic = rGraphic;
+}
+
+Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
+{
+ // #104550# Extracted from svx/source/svdraw/svdograf.cxx
+ Graphic aTransGraphic( GetGraphic() );
+ const GraphicType eType = GetType();
+ const Size aSrcSize( aTransGraphic.GetPrefSize() );
+
+ // #104115# Convert the crop margins to graphic object mapmode
+ const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
+ const MapMode aMap100( MapUnit::Map100thMM );
+
+ Size aCropLeftTop;
+ Size aCropRightBottom;
+
+ if( GraphicType::GdiMetafile == eType )
+ {
+ GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
+
+ if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
+ aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
+ aMap100);
+ aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
+ aMap100);
+ }
+ else
+ {
+ // crops are in GraphicObject units -> to aMapGraph
+ aCropLeftTop = OutputDevice::LogicToLogic(
+ Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
+ aMap100,
+ aMapGraph);
+ aCropRightBottom = OutputDevice::LogicToLogic(
+ Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
+ aMap100,
+ aMapGraph);
+ }
+
+ // #104115# If the metafile is cropped, give it a special
+ // treatment: clip against the remaining area, scale up such
+ // that this area later fills the desired size, and move the
+ // origin to the upper left edge of that area.
+ if( rAttr.IsCropped() )
+ {
+ const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
+
+ tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
+ aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
+ aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
+ aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
+
+ // #104115# To correctly crop rotated metafiles, clip by view rectangle
+ aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
+
+ // #104115# To crop the metafile, scale larger than the output rectangle
+ aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
+ static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
+
+ // #104115# Adapt the pref size by hand (scale changes it
+ // proportionally, but we want it to be smaller than the
+ // former size, to crop the excess out)
+ aMtf.SetPrefSize( Size( static_cast<tools::Long>(static_cast<double>(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5),
+ static_cast<tools::Long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
+
+ // #104115# Adapt the origin of the new mapmode, such that it
+ // is shifted to the place where the cropped output starts
+ Point aNewOrigin( static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
+ static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
+ MapMode aNewMap( rDestMap );
+ aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
+ aMtf.SetPrefMapMode( aNewMap );
+ }
+ else
+ {
+ aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
+ aMtf.SetPrefMapMode( rDestMap );
+ }
+
+ aTransGraphic = aMtf;
+ }
+ else if( GraphicType::Bitmap == eType )
+ {
+ BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
+ tools::Rectangle aCropRect;
+
+ // convert crops to pixel
+ if(rAttr.IsCropped())
+ {
+ if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // crops are in 1/100th mm -> to MapUnit::MapPixel
+ aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
+ aMap100);
+ aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
+ aMap100);
+ }
+ else
+ {
+ // crops are in GraphicObject units -> to MapUnit::MapPixel
+ aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
+ aMapGraph);
+ aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
+ Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
+ aMapGraph);
+ }
+
+ // convert from prefmapmode to pixel
+ Size aSrcSizePixel(
+ Application::GetDefaultDevice()->LogicToPixel(
+ aSrcSize,
+ aMapGraph));
+
+ if(rAttr.IsCropped()
+ && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
+ && aSrcSizePixel.Width())
+ {
+ // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
+ // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
+ // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
+ // existing cropping is calculated based on this logic values already.
+ // aBitmapEx.Scale(aSrcSizePixel);
+
+ // another possibility is to adapt the values created so far with a factor; this
+ // will keep the original Bitmap untouched and thus quality will not change
+ // caution: convert to double first, else pretty big errors may occur
+ const double fFactorX(static_cast<double>(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width());
+ const double fFactorY(static_cast<double>(aBitmapEx.GetSizePixel().Height()) / aSrcSizePixel.Height());
+
+ aCropLeftTop.setWidth( basegfx::fround(aCropLeftTop.Width() * fFactorX) );
+ aCropLeftTop.setHeight( basegfx::fround(aCropLeftTop.Height() * fFactorY) );
+ aCropRightBottom.setWidth( basegfx::fround(aCropRightBottom.Width() * fFactorX) );
+ aCropRightBottom.setHeight( basegfx::fround(aCropRightBottom.Height() * fFactorY) );
+
+ aSrcSizePixel = aBitmapEx.GetSizePixel();
+ }
+
+ // setup crop rectangle in pixel
+ aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
+ aSrcSizePixel.Width() - aCropRightBottom.Width(),
+ aSrcSizePixel.Height() - aCropRightBottom.Height() );
+ }
+
+ // #105641# Also crop animations
+ if( aTransGraphic.IsAnimated() )
+ {
+ Animation aAnim( aTransGraphic.GetAnimation() );
+
+ for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
+ {
+ AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );
+
+ if( !aCropRect.Contains( tools::Rectangle(aAnimationFrame.maPositionPixel, aAnimationFrame.maSizePixel) ) )
+ {
+ // setup actual cropping (relative to frame position)
+ tools::Rectangle aCropRectRel( aCropRect );
+ aCropRectRel.Move( -aAnimationFrame.maPositionPixel.X(),
+ -aAnimationFrame.maPositionPixel.Y() );
+
+ // cropping affects this frame, apply it then
+ // do _not_ apply enlargement, this is done below
+ ImplTransformBitmap( aAnimationFrame.maBitmapEx, rAttr, Size(), Size(),
+ aCropRectRel, rDestSize, false );
+
+ aAnim.Replace( aAnimationFrame, nFrame );
+ }
+ // else: bitmap completely within crop area,
+ // i.e. nothing is cropped away
+ }
+
+ // now, apply enlargement (if any) through global animation size
+ if( aCropLeftTop.Width() < 0 ||
+ aCropLeftTop.Height() < 0 ||
+ aCropRightBottom.Width() < 0 ||
+ aCropRightBottom.Height() < 0 )
+ {
+ Size aNewSize( aAnim.GetDisplaySizePixel() );
+ aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 );
+ aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 );
+ aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 );
+ aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
+ aAnim.SetDisplaySizePixel( aNewSize );
+ }
+
+ // if topleft has changed, we must move all frames to the
+ // right and bottom, resp.
+ if( aCropLeftTop.Width() < 0 ||
+ aCropLeftTop.Height() < 0 )
+ {
+ Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
+ aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
+
+ for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
+ {
+ AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );
+
+ aAnimationFrame.maPositionPixel += aPosOffset;
+
+ aAnim.Replace( aAnimationFrame, nFrame );
+ }
+ }
+
+ aTransGraphic = aAnim;
+ }
+ else
+ {
+ ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
+ aCropRect, rDestSize, true );
+
+ aTransGraphic = aBitmapEx;
+ }
+
+ aTransGraphic.SetPrefSize( rDestSize );
+ aTransGraphic.SetPrefMapMode( rDestMap );
+ }
+
+ GraphicObject aGrfObj( aTransGraphic );
+ aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
+
+ return aTransGraphic;
+}
+
+Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const
+{
+ GetGraphic();
+
+ Graphic aGraphic;
+ GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
+
+ if (maGraphic.IsSupportedGraphic())
+ {
+ if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
+ {
+ if( GetType() == GraphicType::Bitmap )
+ {
+ if( IsAnimated() )
+ {
+ Animation aAnimation( maGraphic.GetAnimation() );
+ lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
+ aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
+ lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
+ aGraphic = aBmpEx;
+ }
+ }
+ else
+ {
+ GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
+ lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
+ aGraphic = aMtf;
+ }
+ }
+ else
+ {
+ if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
+ {
+ Animation aAnimation( maGraphic.GetAnimation() );
+ aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
+ aGraphic = aAnimation;
+ }
+ else
+ aGraphic = maGraphic;
+ }
+ }
+
+ return aGraphic;
+}
+
+bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL)
+{
+ return o3tl::starts_with(rURL, u"vnd.sun.star.GraphicObject:");
+}
+
+// calculate scalings between real image size and logic object size. This
+// is necessary since the crop values are relative to original bitmap size
+basegfx::B2DVector GraphicObject::calculateCropScaling(
+ double fWidth,
+ double fHeight,
+ double fLeftCrop,
+ double fTopCrop,
+ double fRightCrop,
+ double fBottomCrop) const
+{
+ const MapMode aMapMode100thmm(MapUnit::Map100thMM);
+ Size aBitmapSize(GetPrefSize());
+ double fFactorX(1.0);
+ double fFactorY(1.0);
+
+ if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit())
+ {
+ aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
+ }
+ else
+ {
+ aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
+ }
+
+ const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
+ const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
+
+ if(!basegfx::fTools::equalZero(fDivX))
+ {
+ fFactorX = fabs(fWidth) / fDivX;
+ }
+
+ if(!basegfx::fTools::equalZero(fDivY))
+ {
+ fFactorY = fabs(fHeight) / fDivY;
+ }
+
+ return basegfx::B2DVector(fFactorX,fFactorY);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/GraphicObject2.cxx b/vcl/source/graphic/GraphicObject2.cxx
new file mode 100644
index 0000000000..a8dd186a3d
--- /dev/null
+++ b/vcl/source/graphic/GraphicObject2.cxx
@@ -0,0 +1,503 @@
+/* -*- 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 <tools/gen.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <memory>
+
+struct ImplTileInfo
+{
+ ImplTileInfo() : nTilesEmptyX(0), nTilesEmptyY(0) {}
+
+ Point aTileTopLeft; // top, left position of the rendered tile
+ Point aNextTileTopLeft; // top, left position for next recursion
+ // level's tile
+ Size aTileSizePixel; // size of the generated tile (might
+ // differ from
+ // aNextTileTopLeft-aTileTopLeft, because
+ // this is nExponent*prevTileSize. The
+ // generated tile is always nExponent
+ // times the previous tile, such that it
+ // can be used in the next stage. The
+ // required area coverage is often
+ // less. The extraneous area covered is
+ // later overwritten by the next stage)
+ int nTilesEmptyX; // number of original tiles empty right of
+ // this tile. This counts from
+ // aNextTileTopLeft, i.e. the additional
+ // area covered by aTileSizePixel is not
+ // considered here. This is for
+ // unification purposes, as the iterative
+ // calculation of the next level's empty
+ // tiles has to be based on this value.
+ int nTilesEmptyY; // as above, for Y
+};
+
+
+bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev,
+ int nNumTilesX, int nNumTilesY,
+ const Size& rTileSizePixel,
+ const GraphicAttr* pAttr )
+{
+ // how many tiles to generate per recursion step
+ const int nExponent = 2;
+
+ // determine MSB factor
+ int nMSBFactor( 1 );
+ while( nNumTilesX / nMSBFactor != 0 ||
+ nNumTilesY / nMSBFactor != 0 )
+ {
+ nMSBFactor *= nExponent;
+ }
+
+ // one less
+ if(nMSBFactor > 1)
+ {
+ nMSBFactor /= nExponent;
+ }
+ ImplTileInfo aTileInfo;
+
+ // #105229# Switch off mapping (converting to logic and back to
+ // pixel might cause roundoff errors)
+ bool bOldMap( rVDev.IsMapModeEnabled() );
+ rVDev.EnableMapMode( false );
+
+ bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY,
+ nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, aTileInfo ) );
+
+ rVDev.EnableMapMode( bOldMap );
+
+ return bRet;
+}
+
+// define for debug drawings
+//#define DBG_TEST
+
+// see header comment. this works similar to base conversion of a
+// number, i.e. if the exponent is 10, then the number for every tile
+// size is given by the decimal place of the corresponding decimal
+// representation.
+bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor,
+ int nNumOrigTilesX, int nNumOrigTilesY,
+ int nRemainderTilesX, int nRemainderTilesY,
+ const Size& rTileSizePixel, const GraphicAttr* pAttr,
+ ImplTileInfo& rTileInfo )
+{
+ // gets loaded with our tile bitmap
+ std::unique_ptr<GraphicObject> xTmpGraphic;
+ GraphicObject* pTileGraphic;
+
+ // stores a flag that renders the zero'th tile position
+ // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the
+ // recursion stack. All other position already have that tile
+ // rendered, because the lower levels painted their generated tile
+ // there.
+ bool bNoFirstTileDraw( false );
+
+ // what's left when we're done with our tile size
+ const int nNewRemainderX( nRemainderTilesX % nMSBFactor );
+ const int nNewRemainderY( nRemainderTilesY % nMSBFactor );
+
+ // gets filled out from the recursive call with info of what's
+ // been generated
+ ImplTileInfo aTileInfo;
+
+ // check for recursion's end condition: LSB place reached?
+ if( nMSBFactor == 1 )
+ {
+ pTileGraphic = this;
+
+ // set initial tile size -> orig size
+ aTileInfo.aTileSizePixel = rTileSizePixel;
+ aTileInfo.nTilesEmptyX = nNumOrigTilesX;
+ aTileInfo.nTilesEmptyY = nNumOrigTilesY;
+ }
+ else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent,
+ nNumOrigTilesX, nNumOrigTilesY,
+ nNewRemainderX, nNewRemainderY,
+ rTileSizePixel, pAttr, aTileInfo ) )
+ {
+ // extract generated tile -> see comment on the first loop below
+ BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) );
+
+ xTmpGraphic.reset(new GraphicObject(std::move(aTileBitmap)));
+ pTileGraphic = xTmpGraphic.get();
+
+ // fill stripes left over from upstream levels:
+
+ // x0000
+ // 0
+ // 0
+ // 0
+ // 0
+
+ // where x denotes the place filled by our recursive predecessors
+
+ // check whether we have to fill stripes here. Although not
+ // obvious, there is one case where we can skip this step: if
+ // the previous recursion level (the one who filled our
+ // aTileInfo) had zero area to fill, then there are no white
+ // stripes left, naturally. This happens if the digit
+ // associated to that level has a zero, and can be checked via
+ // aTileTopLeft==aNextTileTopLeft.
+ if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft )
+ {
+ // now fill one row from aTileInfo.aNextTileTopLeft.X() all
+ // the way to the right
+ // current output position while drawing
+ Point aCurrPos(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y());
+ for (int nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor)
+ {
+ if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
+ return false;
+
+ aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() );
+ }
+
+#ifdef DBG_TEST
+// rVDev.SetFillCOL_WHITE );
+ rVDev.SetFillColor();
+ rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
+ rVDev.DrawEllipse( tools::Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(),
+ aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(),
+ aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) );
+#endif
+
+ // now fill one column from aTileInfo.aNextTileTopLeft.Y() all
+ // the way to the bottom
+ aCurrPos.setX( aTileInfo.aTileTopLeft.X() );
+ aCurrPos.setY( aTileInfo.aNextTileTopLeft.Y() );
+ for (int nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor)
+ {
+ if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
+ return false;
+
+ aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() );
+ }
+
+#ifdef DBG_TEST
+ rVDev.DrawEllipse( tools::Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(),
+ aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1,
+ aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) );
+#endif
+ }
+ else
+ {
+ // Thought that aTileInfo.aNextTileTopLeft tile has always
+ // been drawn already, but that's wrong: typically,
+ // _parts_ of that tile have been drawn, since the
+ // previous level generated the tile there. But when
+ // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the
+ // difference between these two values is missing in the
+ // lower right corner of this first tile. So, can do that
+ // only here.
+ bNoFirstTileDraw = true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ // calc number of original tiles in our drawing area without
+ // remainder
+ nRemainderTilesX -= nNewRemainderX;
+ nRemainderTilesY -= nNewRemainderY;
+
+ // fill tile info for calling method
+ rTileInfo.aTileTopLeft = aTileInfo.aNextTileTopLeft;
+ rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX,
+ rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY );
+ rTileInfo.aTileSizePixel = Size( rTileSizePixel.Width()*nMSBFactor*nExponent,
+ rTileSizePixel.Height()*nMSBFactor*nExponent );
+ rTileInfo.nTilesEmptyX = aTileInfo.nTilesEmptyX - nRemainderTilesX;
+ rTileInfo.nTilesEmptyY = aTileInfo.nTilesEmptyY - nRemainderTilesY;
+
+ // init output position
+ Point aCurrPos = aTileInfo.aNextTileTopLeft;
+
+ // fill our drawing area. Fill possibly more, to create the next
+ // bigger tile size -> see bitmap extraction above. This does no
+ // harm, since everything right or below our actual area is
+ // overdrawn by our caller. Just in case we're in the last level,
+ // we don't draw beyond the right or bottom border.
+ for (int nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor)
+ {
+ aCurrPos.setX( aTileInfo.aNextTileTopLeft.X() );
+
+ for (int nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor)
+ {
+ if( bNoFirstTileDraw )
+ bNoFirstTileDraw = false; // don't draw first tile position
+ else if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
+ return false;
+
+ aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() );
+ }
+
+ aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() );
+ }
+
+#ifdef DBG_TEST
+// rVDev.SetFillCOL_WHITE );
+ rVDev.SetFillColor();
+ rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
+ rVDev.DrawRect( tools::Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(),
+ (rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(),
+ (rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1,
+ (rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) );
+#endif
+
+ return true;
+}
+
+bool GraphicObject::ImplDrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSizePixel,
+ const Size& rOffset, const GraphicAttr* pAttr, int nTileCacheSize1D)
+{
+ const MapMode aOutMapMode(rOut.GetMapMode());
+ const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
+ bool bRet( false );
+
+ // #i42643# Casting to Int64, to avoid integer overflow for
+ // huge-DPI output devices
+ if( GetGraphic().GetType() == GraphicType::Bitmap &&
+ static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() <
+ static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D )
+ {
+ // First combine very small bitmaps into a larger tile
+
+
+ ScopedVclPtrInstance< VirtualDevice > aVDev;
+ const int nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() );
+ const int nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() );
+
+ aVDev->SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(),
+ nNumTilesInCacheY*rSizePixel.Height() ) );
+ aVDev->SetMapMode( aMapMode );
+
+ // draw bitmap content
+ if( ImplRenderTempTile( *aVDev, nNumTilesInCacheX,
+ nNumTilesInCacheY, rSizePixel, pAttr ) )
+ {
+ BitmapEx aTileBitmap( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) );
+
+ // draw alpha content, if any
+ if( IsTransparent() )
+ {
+ GraphicObject aAlphaGraphic;
+
+ if( GetGraphic().IsAlpha() )
+ aAlphaGraphic.SetGraphic(BitmapEx(GetGraphic().GetBitmapEx().GetAlphaMask().GetBitmap()));
+ else
+ aAlphaGraphic.SetGraphic(BitmapEx(Bitmap()));
+
+ if( aAlphaGraphic.ImplRenderTempTile( *aVDev, nNumTilesInCacheX,
+ nNumTilesInCacheY, rSizePixel, pAttr ) )
+ {
+ // Combine bitmap and alpha/mask
+ if( GetGraphic().IsAlpha() )
+ aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
+ AlphaMask( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) ) );
+ else
+ aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
+ aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ).CreateAlphaMask( COL_WHITE ) );
+ }
+ }
+
+ // paint generated tile
+ GraphicObject aTmpGraphic( aTileBitmap );
+ bRet = aTmpGraphic.ImplDrawTiled(rOut, rArea,
+ aTileBitmap.GetSizePixel(),
+ rOffset, pAttr, nTileCacheSize1D);
+ }
+ }
+ else
+ {
+ const Size aOutOffset( rOut.LogicToPixel( rOffset, aOutMapMode ) );
+ const tools::Rectangle aOutArea( rOut.LogicToPixel( rArea, aOutMapMode ) );
+
+ // number of invisible (because out-of-area) tiles
+ int nInvisibleTilesX;
+ int nInvisibleTilesY;
+
+ // round towards -infty for negative offset
+ if( aOutOffset.Width() < 0 )
+ nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width();
+ else
+ nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width();
+
+ // round towards -infty for negative offset
+ if( aOutOffset.Height() < 0 )
+ nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height();
+ else
+ nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height();
+
+ // origin from where to 'virtually' start drawing in pixel
+ const Point aOutOrigin( rOut.LogicToPixel( Point( rArea.Left() - rOffset.Width(),
+ rArea.Top() - rOffset.Height() ) ) );
+ // position in pixel from where to really start output
+ const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(),
+ aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() );
+
+ rOut.Push( vcl::PushFlags::CLIPREGION );
+ rOut.IntersectClipRegion( rArea );
+
+ // Paint all tiles
+
+
+ bRet = ImplDrawTiled(rOut, aOutStart,
+ (aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(),
+ (aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(),
+ rSizePixel, pAttr);
+
+ rOut.Pop();
+ }
+
+ return bRet;
+}
+
+bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel,
+ int nNumTilesX, int nNumTilesY,
+ const Size& rTileSizePixel, const GraphicAttr* pAttr ) const
+{
+ Point aCurrPos( rPosPixel );
+ Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) );
+ int nX, nY;
+
+ // #107607# Use logical coordinates for metafile playing, too
+ bool bDrawInPixel( rOut.GetConnectMetaFile() == nullptr && GraphicType::Bitmap == GetType() );
+ bool bRet = false;
+
+ // #105229# Switch off mapping (converting to logic and back to
+ // pixel might cause roundoff errors)
+ bool bOldMap( rOut.IsMapModeEnabled() );
+
+ if( bDrawInPixel )
+ rOut.EnableMapMode( false );
+
+ for( nY=0; nY < nNumTilesY; ++nY )
+ {
+ aCurrPos.setX( rPosPixel.X() );
+
+ for( nX=0; nX < nNumTilesX; ++nX )
+ {
+ // #105229# work with pixel coordinates here, mapping is disabled!
+ // #104004# don't disable mapping for metafile recordings
+ // #108412# don't quit the loop if one draw fails
+
+ // update return value. This method should return true, if
+ // at least one of the looped Draws succeeded.
+ bRet |= Draw(rOut,
+ bDrawInPixel ? aCurrPos : rOut.PixelToLogic(aCurrPos),
+ bDrawInPixel ? rTileSizePixel : aTileSizeLogic,
+ pAttr);
+
+ aCurrPos.AdjustX(rTileSizePixel.Width() );
+ }
+
+ aCurrPos.AdjustY(rTileSizePixel.Height() );
+ }
+
+ if( bDrawInPixel )
+ rOut.EnableMapMode( bOldMap );
+
+ return bRet;
+}
+
+void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx,
+ const GraphicAttr& rAttr,
+ const Size& rCropLeftTop,
+ const Size& rCropRightBottom,
+ const tools::Rectangle& rCropRect,
+ const Size& rDstSize,
+ bool bEnlarge ) const
+{
+ // #107947# Extracted from svdograf.cxx
+
+ // #104115# Crop the bitmap
+ if( rAttr.IsCropped() )
+ {
+ rBmpEx.Crop( rCropRect );
+
+ // #104115# Negative crop sizes mean: enlarge bitmap and pad
+ if( bEnlarge && (
+ rCropLeftTop.Width() < 0 ||
+ rCropLeftTop.Height() < 0 ||
+ rCropRightBottom.Width() < 0 ||
+ rCropRightBottom.Height() < 0 ) )
+ {
+ Size aBmpSize( rBmpEx.GetSizePixel() );
+ sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 );
+ sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 );
+ sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) );
+ sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) );
+
+ BitmapEx aBmpEx2;
+
+ if( rBmpEx.IsAlpha() )
+ {
+ aBmpEx2 = rBmpEx;
+ }
+ else
+ {
+ // #104115# Generate mask bitmap and init to zero
+ AlphaMask aMask(aBmpSize);
+ aMask.Erase(255);
+
+ // #104115# Always generate transparent bitmap, we need the border transparent
+ aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask );
+
+ // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent
+ rBmpEx = aBmpEx2;
+ }
+
+ aBmpEx2.Scale(Size(nPadTotalWidth, nPadTotalHeight));
+ aBmpEx2.Erase( Color(ColorAlpha,0,0,0,0) );
+ aBmpEx2.CopyPixel( tools::Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), tools::Rectangle( Point(0, 0), aBmpSize ), rBmpEx );
+ rBmpEx = aBmpEx2;
+ }
+ }
+
+ const Size aSizePixel( rBmpEx.GetSizePixel() );
+
+ if( rAttr.GetRotation() == 0_deg10 || IsAnimated() )
+ return;
+
+ if( !(aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height()) )
+ return;
+
+ double fSrcWH = static_cast<double>(aSizePixel.Width()) / aSizePixel.Height();
+ double fDstWH = static_cast<double>(rDstSize.Width()) / rDstSize.Height();
+ double fScaleX = 1.0, fScaleY = 1.0;
+
+ // always choose scaling to shrink bitmap
+ if( fSrcWH < fDstWH )
+ fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() );
+ else
+ fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width();
+
+ rBmpEx.Scale( fScaleX, fScaleY );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/GraphicReader.cxx b/vcl/source/graphic/GraphicReader.cxx
new file mode 100644
index 0000000000..9137ebd8a2
--- /dev/null
+++ b/vcl/source/graphic/GraphicReader.cxx
@@ -0,0 +1,29 @@
+/* -*- 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 <graphic/GraphicReader.hxx>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+GraphicReader::GraphicReader() {}
+
+GraphicReader::~GraphicReader() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx
new file mode 100644
index 0000000000..f77e3e3c40
--- /dev/null
+++ b/vcl/source/graphic/Manager.cxx
@@ -0,0 +1,317 @@
+/* -*- 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 <graphic/Manager.hxx>
+#include <impgraph.hxx>
+#include <sal/log.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <unotools/configmgr.hxx>
+
+using namespace css;
+
+namespace vcl::graphic
+{
+namespace
+{
+void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit,
+ std::chrono::seconds& rAllowedIdleTime, bool& bSwapEnabled)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ try
+ {
+ using officecfg::Office::Common::Cache;
+
+ rMemoryLimit = Cache::GraphicManager::GraphicMemoryLimit::get();
+ rAllowedIdleTime
+ = std::chrono::seconds(Cache::GraphicManager::GraphicAllowedIdleTime::get());
+ bSwapEnabled = Cache::GraphicManager::GraphicSwappingEnabled::get();
+ }
+ catch (...)
+ {
+ }
+}
+}
+
+Manager& Manager::get()
+{
+ static Manager gStaticManager;
+ return gStaticManager;
+}
+
+Manager::Manager()
+ : mnAllowedIdleTime(10)
+ , mbSwapEnabled(true)
+ , mbReducingGraphicMemory(false)
+ , mnMemoryLimit(300000000)
+ , mnUsedSize(0)
+ , maSwapOutTimer("graphic::Manager maSwapOutTimer")
+{
+ setupConfigurationValuesIfPossible(mnMemoryLimit, mnAllowedIdleTime, mbSwapEnabled);
+
+ if (mbSwapEnabled)
+ {
+ maSwapOutTimer.SetInvokeHandler(LINK(this, Manager, SwapOutTimerHandler));
+ maSwapOutTimer.SetTimeout(10000);
+ maSwapOutTimer.Start();
+ }
+}
+
+void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
+{
+ // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
+ // filter may create more temp Graphics which are auto-added to
+ // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
+ // reexport of tdf118346-1.odg
+ o3tl::sorted_vector<ImpGraphic*> aImpGraphicList = m_pImpGraphicList;
+
+ for (ImpGraphic* pEachImpGraphic : aImpGraphicList)
+ {
+ if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7) && !bDropAll)
+ return;
+
+ if (pEachImpGraphic->isSwappedOut())
+ continue;
+
+ sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic);
+ if (nCurrentGraphicSize > 100000 || bDropAll)
+ {
+ if (!pEachImpGraphic->mpContext)
+ {
+ auto aCurrent = std::chrono::high_resolution_clock::now();
+ auto aDeltaTime = aCurrent - pEachImpGraphic->maLastUsed;
+ auto aSeconds = std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
+
+ if (aSeconds > mnAllowedIdleTime)
+ {
+ // unlock because svgio can call back into us
+ rGuard.unlock();
+ pEachImpGraphic->swapOut();
+ rGuard.lock();
+ }
+ }
+ }
+ }
+}
+
+void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
+{
+ // maMutex is locked in callers
+
+ if (!mbSwapEnabled)
+ return;
+
+ if (mnUsedSize < mnMemoryLimit && !bDropAll)
+ return;
+
+ // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg
+ if (mbReducingGraphicMemory)
+ return;
+
+ mbReducingGraphicMemory = true;
+
+ loopGraphicsAndSwapOut(rGuard, bDropAll);
+
+ sal_Int64 calculatedSize = 0;
+ for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
+ {
+ if (!pEachImpGraphic->isSwappedOut())
+ {
+ calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
+ }
+ }
+
+ if (calculatedSize != mnUsedSize)
+ {
+ assert(rGuard.owns_lock() && rGuard.mutex() == &maMutex);
+ // coverity[missing_lock: FALSE] - as above assert
+ mnUsedSize = calculatedSize;
+ }
+
+ mbReducingGraphicMemory = false;
+}
+
+void Manager::dropCache()
+{
+ std::unique_lock aGuard(maMutex);
+
+ reduceGraphicMemory(aGuard, true);
+}
+
+void Manager::dumpState(rtl::OStringBuffer& rState)
+{
+ std::unique_lock aGuard(maMutex);
+
+ rState.append("\nImage Manager items:\t");
+ rState.append(static_cast<sal_Int32>(m_pImpGraphicList.size()));
+ rState.append("\tsize:\t");
+ rState.append(static_cast<sal_Int64>(mnUsedSize / 1024));
+ rState.append("\tkb");
+
+ for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
+ {
+ pEachImpGraphic->dumpState(rState);
+ }
+}
+
+sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
+{
+ if (!pImpGraphic->isAvailable())
+ return 0;
+ return pImpGraphic->getSizeBytes();
+}
+
+IMPL_LINK(Manager, SwapOutTimerHandler, Timer*, pTimer, void)
+{
+ std::unique_lock aGuard(maMutex);
+
+ pTimer->Stop();
+ reduceGraphicMemory(aGuard);
+ pTimer->Start();
+}
+
+void Manager::registerGraphic(const std::shared_ptr<ImpGraphic>& pImpGraphic)
+{
+ std::unique_lock aGuard(maMutex);
+
+ // make some space first
+ if (mnUsedSize > mnMemoryLimit)
+ reduceGraphicMemory(aGuard);
+
+ // Insert and update the used size (bytes)
+ assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
+ // coverity[missing_lock: FALSE] - as above assert
+ mnUsedSize += getGraphicSizeBytes(pImpGraphic.get());
+ m_pImpGraphicList.insert(pImpGraphic.get());
+
+ // calculate size of the graphic set
+ sal_Int64 calculatedSize = 0;
+ for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
+ {
+ if (!pEachImpGraphic->isSwappedOut())
+ {
+ calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
+ }
+ }
+
+ if (calculatedSize != mnUsedSize)
+ {
+ SAL_INFO_IF(calculatedSize != mnUsedSize, "vcl.gdi",
+ "Calculated size mismatch. Variable size is '"
+ << mnUsedSize << "' but calculated size is '" << calculatedSize << "'");
+ mnUsedSize = calculatedSize;
+ }
+}
+
+void Manager::unregisterGraphic(ImpGraphic* pImpGraphic)
+{
+ std::scoped_lock aGuard(maMutex);
+
+ mnUsedSize -= getGraphicSizeBytes(pImpGraphic);
+ m_pImpGraphicList.erase(pImpGraphic);
+}
+
+std::shared_ptr<ImpGraphic> Manager::copy(std::shared_ptr<ImpGraphic> const& rImpGraphicPtr)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(*rImpGraphicPtr);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance()
+{
+ auto pReturn = std::make_shared<ImpGraphic>();
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance(std::shared_ptr<GfxLink> const& rGfxLink,
+ sal_Int32 nPageIndex)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rGfxLink, nPageIndex);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance(const BitmapEx& rBitmapEx)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rBitmapEx);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance(const Animation& rAnimation)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rAnimation);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic>
+Manager::newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance(const GDIMetaFile& rMetaFile)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rMetaFile);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+std::shared_ptr<ImpGraphic> Manager::newInstance(const GraphicExternalLink& rGraphicLink)
+{
+ auto pReturn = std::make_shared<ImpGraphic>(rGraphicLink);
+ registerGraphic(pReturn);
+ return pReturn;
+}
+
+void Manager::swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
+{
+ std::scoped_lock aGuard(maMutex);
+ if (pImpGraphic)
+ {
+ mnUsedSize += nSizeBytes;
+ }
+}
+
+void Manager::swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
+{
+ std::scoped_lock aGuard(maMutex);
+ if (pImpGraphic)
+ {
+ mnUsedSize -= nSizeBytes;
+ }
+}
+
+void Manager::changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSizeBytes)
+{
+ std::scoped_lock aGuard(maMutex);
+
+ mnUsedSize -= nOldSizeBytes;
+ mnUsedSize += getGraphicSizeBytes(pImpGraphic);
+}
+} // end vcl::graphic
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoBinaryDataContainer.cxx b/vcl/source/graphic/UnoBinaryDataContainer.cxx
new file mode 100644
index 0000000000..3fe277024a
--- /dev/null
+++ b/vcl/source/graphic/UnoBinaryDataContainer.cxx
@@ -0,0 +1,22 @@
+/* -*- 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/.
+ *
+ */
+
+#include <graphic/UnoBinaryDataContainer.hxx>
+
+#include <cppuhelper/queryinterface.hxx>
+
+using namespace css;
+
+css::uno::Sequence<sal_Int8> SAL_CALL UnoBinaryDataContainer::getCopyAsByteSequence()
+{
+ return maBinaryDataContainer.getCopyAsByteSequence();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoGraphic.cxx b/vcl/source/graphic/UnoGraphic.cxx
new file mode 100644
index 0000000000..9036f54452
--- /dev/null
+++ b/vcl/source/graphic/UnoGraphic.cxx
@@ -0,0 +1,261 @@
+/* -*- 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 <graphic/UnoGraphic.hxx>
+
+#include <tools/stream.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/graphic/GraphicType.hpp>
+#include <com/sun/star/graphic/XGraphicTransformer.hpp>
+#include <vcl/dibtools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapColor.hxx>
+#include <vcl/BitmapDuoToneFilter.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+using namespace com::sun::star;
+
+namespace unographic {
+
+Graphic::Graphic()
+{
+}
+
+Graphic::~Graphic() noexcept
+{
+}
+
+void Graphic::init(const ::Graphic& rGraphic)
+{
+ maGraphic = rGraphic;
+ unographic::GraphicDescriptor::init(maGraphic);
+}
+
+uno::Any SAL_CALL Graphic::queryInterface( const uno::Type & rType )
+{
+ uno::Any aAny;
+ if( rType == cppu::UnoType<graphic::XGraphic>::get())
+ aAny <<= uno::Reference< graphic::XGraphic >( this );
+ else if( rType == cppu::UnoType<awt::XBitmap>::get())
+ aAny <<= uno::Reference< awt::XBitmap >( this );
+ else if( rType == cppu::UnoType<graphic::XGraphicTransformer>::get())
+ aAny <<= uno::Reference< graphic::XGraphicTransformer >(this);
+ else
+ aAny = ::unographic::GraphicDescriptor::queryInterface( rType );
+
+ return aAny;
+}
+
+void SAL_CALL Graphic::acquire()
+ noexcept
+{
+ unographic::GraphicDescriptor::acquire();
+}
+
+void SAL_CALL Graphic::release() noexcept
+{
+ unographic::GraphicDescriptor::release();
+}
+
+OUString SAL_CALL Graphic::getImplementationName()
+{
+ return "com.sun.star.comp.graphic.Graphic";
+}
+
+sal_Bool SAL_CALL Graphic::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL Graphic::getSupportedServiceNames()
+{
+ uno::Sequence< OUString > aRet( ::unographic::GraphicDescriptor::getSupportedServiceNames() );
+ const uno::Sequence< OUString > aNew { "com.sun.star.graphic.Graphic" };
+ sal_Int32 nOldCount = aRet.getLength();
+
+ aRet.realloc( nOldCount + aNew.getLength() );
+
+ std::copy(aNew.begin(), aNew.end(), std::next(aRet.getArray(), nOldCount));
+
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SAL_CALL Graphic::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<graphic::XGraphic>::get(),
+ cppu::UnoType<awt::XBitmap>::get(),
+ ::unographic::GraphicDescriptor::getTypes()
+ ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL Graphic::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Int8 SAL_CALL Graphic::getType()
+{
+ sal_Int8 cRet = graphic::GraphicType::EMPTY;
+
+ if (!maGraphic.IsNone())
+ {
+ cRet = (maGraphic.GetType() == ::GraphicType::Bitmap) ? graphic::GraphicType::PIXEL
+ : graphic::GraphicType::VECTOR;
+ }
+
+ return cRet;
+}
+
+// XBitmap
+
+awt::Size SAL_CALL Graphic::getSize()
+{
+ SolarMutexGuard aGuard;
+
+ Size aVclSize;
+ if (!maGraphic.IsNone())
+ {
+ aVclSize = maGraphic.GetSizePixel();
+ }
+ return awt::Size(aVclSize.Width(), aVclSize.Height());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL Graphic::getDIB()
+{
+ SolarMutexGuard aGuard;
+
+ if (!maGraphic.IsNone())
+ {
+ SvMemoryStream aMemoryStream;
+
+ WriteDIB(maGraphic.GetBitmapEx().GetBitmap(), aMemoryStream, false, true);
+ return css::uno::Sequence<sal_Int8>(static_cast<sal_Int8 const *>(aMemoryStream.GetData()), aMemoryStream.Tell());
+ }
+ else
+ {
+ return uno::Sequence<sal_Int8>();
+ }
+}
+
+uno::Sequence<sal_Int8> SAL_CALL Graphic::getMaskDIB()
+{
+ SolarMutexGuard aGuard;
+
+ if (!maGraphic.IsNone())
+ {
+ SvMemoryStream aMemoryStream;
+
+ WriteDIB(maGraphic.GetBitmapEx().GetAlphaMask().GetBitmap(), aMemoryStream, false, true);
+ return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMemoryStream.GetData()), aMemoryStream.Tell() );
+ }
+ else
+ {
+ return uno::Sequence<sal_Int8>();
+ }
+}
+
+// XGraphicTransformer
+uno::Reference< graphic::XGraphic > SAL_CALL Graphic::colorChange(
+ const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nColorFrom, sal_Int8 nTolerance, sal_Int32 nColorTo, sal_Int8 nAlphaTo )
+{
+ ::Graphic aGraphic(rxGraphic);
+ ::Graphic aReturnGraphic;
+
+ BitmapColor aBmpColorFrom(Color(ColorTransparency, static_cast<sal_uInt32>(nColorFrom)));
+ BitmapColor aBmpColorTo(Color(ColorTransparency, static_cast<sal_uInt32>(nColorTo)));
+
+ Color aColorFrom(aBmpColorFrom);
+ Color aColorTo(aBmpColorTo);
+
+ const sal_uInt8 cIndexFrom = aBmpColorFrom.GetIndex();
+
+ //TODO This code convert GdiMetafile(vector graphic) to Bitmap, which cause to information lost
+ if (aGraphic.GetType() == GraphicType::Bitmap ||
+ aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ BitmapEx aBitmapEx(aGraphic.GetBitmapEx());
+
+ if (aBitmapEx.IsAlpha())
+ {
+ aBitmapEx.ChangeColorAlpha( cIndexFrom, nAlphaTo );
+ aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance);
+ aReturnGraphic = ::Graphic(aBitmapEx);
+ }
+ else
+ {
+ if ((nAlphaTo == 0) || (nAlphaTo == sal::static_int_cast< sal_Int8 >(0xff)))
+ {
+ Bitmap aBitmap(aBitmapEx.GetBitmap());
+ AlphaMask aMask(aBitmap.CreateAlphaMask(aColorFrom, nTolerance));
+ aBitmap.Replace(aColorFrom, aColorTo, nTolerance);
+ aReturnGraphic = ::Graphic(BitmapEx(aBitmap, aMask));
+ }
+ else
+ {
+ aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance);
+ aReturnGraphic = ::Graphic(aBitmapEx);
+ }
+ }
+ }
+
+ aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
+ return aReturnGraphic.GetXGraphic();
+}
+
+uno::Reference< graphic::XGraphic > SAL_CALL Graphic::applyDuotone(
+ const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nColorOne, sal_Int32 nColorTwo )
+{
+ ::Graphic aGraphic(rxGraphic);
+ ::Graphic aReturnGraphic;
+
+ BitmapEx aBitmapEx( aGraphic.GetBitmapEx() );
+ AlphaMask aMask( aBitmapEx.GetAlphaMask() );
+
+ BitmapEx aTmpBmpEx(aBitmapEx.GetBitmap());
+ BitmapFilter::Filter(aTmpBmpEx,
+ BitmapDuoToneFilter(
+ Color(ColorTransparency, nColorOne),
+ Color(ColorTransparency, nColorTwo)));
+
+ aReturnGraphic = ::Graphic( BitmapEx( aTmpBmpEx.GetBitmap(), aMask ) );
+ aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
+ return aReturnGraphic.GetXGraphic();
+}
+
+uno::Reference< graphic::XGraphic > SAL_CALL Graphic::applyBrightnessContrast(
+ const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nBrightness, sal_Int32 nContrast, sal_Bool mso )
+{
+ ::Graphic aGraphic(rxGraphic);
+ ::Graphic aReturnGraphic;
+
+ BitmapEx aBitmapEx(aGraphic.GetBitmapEx());
+ aBitmapEx.Adjust(nBrightness, nContrast, 0, 0, 0, 0, false, mso);
+ aReturnGraphic = ::Graphic(aBitmapEx);
+ aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
+ return aReturnGraphic.GetXGraphic();
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoGraphicDescriptor.cxx b/vcl/source/graphic/UnoGraphicDescriptor.cxx
new file mode 100644
index 0000000000..e83ac98959
--- /dev/null
+++ b/vcl/source/graphic/UnoGraphicDescriptor.cxx
@@ -0,0 +1,421 @@
+/* -*- 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 <graphic/UnoGraphicDescriptor.hxx>
+
+#include <cppuhelper/weakagg.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/graphic/GraphicType.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <vcl/outdev.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+
+namespace {
+
+enum class UnoGraphicProperty
+{
+ GraphicType = 1
+ , MimeType = 2
+ , SizePixel = 3
+ , Size100thMM = 4
+ , BitsPerPixel = 5
+ , Transparent = 6
+ , Alpha = 7
+ , Animated = 8
+ , Linked = 9
+ , OriginURL = 10
+};
+
+}
+
+using namespace ::com::sun::star;
+
+namespace unographic {
+
+
+GraphicDescriptor::GraphicDescriptor() :
+ ::comphelper::PropertySetHelper( createPropertySetInfo() ),
+ mpGraphic( nullptr ),
+ meType( GraphicType::NONE ),
+ mnBitsPerPixel ( 0 ),
+ mbTransparent ( false )
+{
+}
+
+GraphicDescriptor::~GraphicDescriptor()
+ noexcept
+{
+}
+
+void GraphicDescriptor::init( const ::Graphic& rGraphic )
+{
+ mpGraphic = &rGraphic;
+}
+
+void GraphicDescriptor::init( const OUString& rURL )
+{
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( rURL, StreamMode::READ ));
+
+ if( pIStm )
+ implCreate( *pIStm, &rURL );
+}
+
+void GraphicDescriptor::init( const uno::Reference< io::XInputStream >& rxIStm, const OUString& rURL )
+{
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( rxIStm ));
+
+ if( pIStm )
+ implCreate( *pIStm, &rURL );
+}
+
+void GraphicDescriptor::implCreate( SvStream& rIStm, const OUString* pURL )
+{
+ OUString aURL;
+ if( pURL )
+ aURL = *pURL;
+ ::GraphicDescriptor aDescriptor( rIStm, &aURL );
+
+ mpGraphic = nullptr;
+ maMimeType.clear();
+ meType = GraphicType::NONE;
+ mnBitsPerPixel = 0;
+ mbTransparent = false;
+
+ if( !(aDescriptor.Detect( true ) && aDescriptor.GetFileFormat() != GraphicFileFormat::NOT) )
+ return;
+
+ OUString aMimeType;
+ sal_uInt8 cType = graphic::GraphicType::EMPTY;
+
+ switch( aDescriptor.GetFileFormat() )
+ {
+ case GraphicFileFormat::BMP: aMimeType = MIMETYPE_BMP; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::GIF: aMimeType = MIMETYPE_GIF; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::JPG: aMimeType = MIMETYPE_JPG; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PCD: aMimeType = MIMETYPE_PCD; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PCX: aMimeType = MIMETYPE_PCX; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PNG: aMimeType = MIMETYPE_PNG; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::TIF: aMimeType = MIMETYPE_TIF; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::XBM: aMimeType = MIMETYPE_XBM; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::XPM: aMimeType = MIMETYPE_XPM; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PBM: aMimeType = MIMETYPE_PBM; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PGM: aMimeType = MIMETYPE_PGM; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PPM: aMimeType = MIMETYPE_PPM; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::RAS: aMimeType = MIMETYPE_RAS; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::TGA: aMimeType = MIMETYPE_TGA; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::PSD: aMimeType = MIMETYPE_PSD; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::WEBP: aMimeType = MIMETYPE_WEBP; cType = graphic::GraphicType::PIXEL; break;
+
+ case GraphicFileFormat::EPS: aMimeType = MIMETYPE_EPS; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::DXF: aMimeType = MIMETYPE_DXF; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::MET: aMimeType = MIMETYPE_MET; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::PCT: aMimeType = MIMETYPE_PCT; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::SVM: aMimeType = MIMETYPE_SVM; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::WMF: aMimeType = MIMETYPE_WMF; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::WMZ: aMimeType = MIMETYPE_WMF; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::EMF: aMimeType = MIMETYPE_EMF; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::EMZ: aMimeType = MIMETYPE_EMF; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::SVG: aMimeType = MIMETYPE_SVG; cType = graphic::GraphicType::VECTOR; break;
+ case GraphicFileFormat::SVGZ: aMimeType = MIMETYPE_SVG; cType = graphic::GraphicType::VECTOR; break;
+
+ default:
+ break;
+ }
+
+ if( graphic::GraphicType::EMPTY != cType )
+ {
+ meType = ( ( graphic::GraphicType::PIXEL == cType ) ? GraphicType::Bitmap : GraphicType::GdiMetafile );
+ maMimeType = aMimeType;
+ maSizePixel = aDescriptor.GetSizePixel();
+ maSize100thMM = aDescriptor.GetSize_100TH_MM();
+ mnBitsPerPixel = aDescriptor.GetBitsPerPixel();
+ mbTransparent = ( graphic::GraphicType::VECTOR == cType );
+ }
+}
+
+
+uno::Any SAL_CALL GraphicDescriptor::queryInterface( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ if( rType == cppu::UnoType<lang::XServiceInfo>::get())
+ aAny <<= uno::Reference< lang::XServiceInfo >(this);
+ else if( rType == cppu::UnoType<lang::XTypeProvider>::get())
+ aAny <<= uno::Reference< lang::XTypeProvider >(this);
+ else if( rType == cppu::UnoType<beans::XPropertySet>::get())
+ aAny <<= uno::Reference< beans::XPropertySet >(this);
+ else if( rType == cppu::UnoType<beans::XPropertyState>::get())
+ aAny <<= uno::Reference< beans::XPropertyState >(this);
+ else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get())
+ aAny <<= uno::Reference< beans::XMultiPropertySet >(this);
+ else
+ aAny = OWeakObject::queryInterface( rType );
+
+ return aAny;
+}
+
+
+void SAL_CALL GraphicDescriptor::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL GraphicDescriptor::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+OUString SAL_CALL GraphicDescriptor::getImplementationName()
+{
+ return "com.sun.star.comp.graphic.GraphicDescriptor";
+}
+
+sal_Bool SAL_CALL GraphicDescriptor::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL GraphicDescriptor::getSupportedServiceNames()
+{
+ return { "com.sun.star.graphic.GraphicDescriptor" };
+}
+
+
+uno::Sequence< uno::Type > SAL_CALL GraphicDescriptor::getTypes()
+{
+ static const uno::Sequence< uno::Type > aTypes {
+ cppu::UnoType<uno::XAggregation>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get() };
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GraphicDescriptor::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+rtl::Reference<::comphelper::PropertySetInfo> GraphicDescriptor::createPropertySetInfo()
+{
+ static ::comphelper::PropertyMapEntry const aEntries[] =
+ {
+ { OUString( "GraphicType" ), static_cast< sal_Int32 >( UnoGraphicProperty::GraphicType ), cppu::UnoType< sal_Int8 >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "MimeType" ), static_cast< sal_Int32 >( UnoGraphicProperty::MimeType ), cppu::UnoType< OUString >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "SizePixel" ), static_cast< sal_Int32 >( UnoGraphicProperty::SizePixel ), cppu::UnoType< awt::Size >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "Size100thMM" ), static_cast< sal_Int32 >( UnoGraphicProperty::Size100thMM ), cppu::UnoType< awt::Size >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "BitsPerPixel" ), static_cast< sal_Int32 >( UnoGraphicProperty::BitsPerPixel ), cppu::UnoType< sal_uInt8 >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "Transparent" ), static_cast< sal_Int32 >( UnoGraphicProperty::Transparent ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "Alpha" ), static_cast< sal_Int32 >( UnoGraphicProperty::Alpha ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString( "Animated" ), static_cast< sal_Int32 >( UnoGraphicProperty::Animated ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString("Linked"), sal_Int32(UnoGraphicProperty::Linked), cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString("OriginURL"), sal_Int32(UnoGraphicProperty::OriginURL), cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+
+ return rtl::Reference<::comphelper::PropertySetInfo>( new ::comphelper::PropertySetInfo(aEntries) );
+}
+
+
+void GraphicDescriptor::_setPropertyValues( const comphelper::PropertyMapEntry** /*ppEntries*/, const uno::Any* /*pValues*/ )
+{
+ // we only have readonly attributes
+}
+
+
+void GraphicDescriptor::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, uno::Any* pValues )
+{
+ SolarMutexGuard aGuard;
+
+ while( *ppEntries )
+ {
+ UnoGraphicProperty theProperty = static_cast< UnoGraphicProperty >( (*ppEntries)->mnHandle );
+ switch( theProperty )
+ {
+ case UnoGraphicProperty::GraphicType:
+ {
+ const GraphicType eType( mpGraphic ? mpGraphic->GetType() : meType );
+
+ *pValues <<= ( eType == GraphicType::Bitmap ? graphic::GraphicType::PIXEL :
+ ( eType == GraphicType::GdiMetafile ? graphic::GraphicType::VECTOR :
+ graphic::GraphicType::EMPTY ) );
+ }
+ break;
+
+ case UnoGraphicProperty::MimeType:
+ {
+ OUString aMimeType;
+
+ if( mpGraphic )
+ {
+ if( mpGraphic->IsGfxLink() )
+ {
+ GfxLink aLink = mpGraphic->GetGfxLink();
+ switch (aLink.GetType())
+ {
+ case GfxLinkType::NativeGif: aMimeType = MIMETYPE_GIF; break;
+
+ // #i15508# added BMP type for better exports (checked, works)
+ case GfxLinkType::NativeBmp: aMimeType = MIMETYPE_BMP; break;
+
+ case GfxLinkType::NativeJpg: aMimeType = MIMETYPE_JPG; break;
+ case GfxLinkType::NativePng: aMimeType = MIMETYPE_PNG; break;
+ case GfxLinkType::NativeTif: aMimeType = MIMETYPE_TIF; break;
+ case GfxLinkType::NativeWmf: aMimeType = aLink.IsEMF() ? MIMETYPE_EMF : MIMETYPE_WMF; break;
+ case GfxLinkType::NativeMet: aMimeType = MIMETYPE_MET; break;
+ case GfxLinkType::NativePct: aMimeType = MIMETYPE_PCT; break;
+ case GfxLinkType::NativeWebp: aMimeType = MIMETYPE_WEBP; break;
+
+ // added Svg mimetype support
+ case GfxLinkType::NativeSvg: aMimeType = MIMETYPE_SVG; break;
+ case GfxLinkType::NativePdf: aMimeType = MIMETYPE_PDF; break;
+
+ default:
+ break;
+ }
+ }
+
+ if( aMimeType.isEmpty() && ( mpGraphic->GetType() != GraphicType::NONE ) )
+ aMimeType = MIMETYPE_VCLGRAPHIC;
+ }
+ else
+ aMimeType = maMimeType;
+
+ *pValues <<= aMimeType;
+ }
+ break;
+
+ case UnoGraphicProperty::SizePixel:
+ {
+ awt::Size aAWTSize( 0, 0 );
+
+ if( mpGraphic )
+ {
+ if( mpGraphic->GetType() == GraphicType::Bitmap )
+ {
+ const Size aSizePix( mpGraphic->GetSizePixel() );
+ aAWTSize = awt::Size( aSizePix.Width(), aSizePix.Height() );
+ }
+ }
+ else
+ aAWTSize = awt::Size( maSizePixel.Width(), maSizePixel.Height() );
+
+ *pValues <<= aAWTSize;
+ }
+ break;
+
+ case UnoGraphicProperty::Size100thMM:
+ {
+ awt::Size aAWTSize( 0, 0 );
+
+ if( mpGraphic )
+ {
+ if( mpGraphic->GetPrefMapMode().GetMapUnit() != MapUnit::MapPixel )
+ {
+ const Size aSizeLog( OutputDevice::LogicToLogic(
+ mpGraphic->GetPrefSize(),
+ mpGraphic->GetPrefMapMode(),
+ MapMode(MapUnit::Map100thMM)) );
+ aAWTSize = awt::Size( aSizeLog.Width(), aSizeLog.Height() );
+ }
+ }
+ else
+ aAWTSize = awt::Size( maSize100thMM.Width(), maSize100thMM.Height() );
+
+ *pValues <<= aAWTSize;
+ }
+ break;
+
+ case UnoGraphicProperty::BitsPerPixel:
+ {
+ sal_uInt16 nBitsPerPixel = 0;
+
+ if( mpGraphic )
+ {
+ if( mpGraphic->GetType() == GraphicType::Bitmap )
+ {
+ auto ePixelFormat = mpGraphic->GetBitmapEx().GetBitmap().getPixelFormat();
+ nBitsPerPixel = vcl::pixelFormatBitCount(ePixelFormat);
+ }
+ }
+ else
+ nBitsPerPixel = mnBitsPerPixel;
+
+ *pValues <<= sal::static_int_cast< sal_Int8 >(nBitsPerPixel);
+ }
+ break;
+
+ case UnoGraphicProperty::Transparent:
+ {
+ *pValues <<= mpGraphic ? mpGraphic->IsTransparent() : mbTransparent;
+ }
+ break;
+
+ case UnoGraphicProperty::Alpha:
+ {
+ *pValues <<= mpGraphic && mpGraphic->IsAlpha();
+ }
+ break;
+
+ case UnoGraphicProperty::Animated:
+ {
+ *pValues <<= mpGraphic && mpGraphic->IsAnimated();
+ }
+ break;
+
+ case UnoGraphicProperty::Linked:
+ {
+ *pValues <<= mpGraphic && !mpGraphic->getOriginURL().isEmpty();
+ }
+ break;
+
+ case UnoGraphicProperty::OriginURL:
+ {
+ OUString aOriginURL;
+ if (mpGraphic)
+ aOriginURL = mpGraphic->getOriginURL();
+
+ *pValues <<= aOriginURL;
+ }
+ break;
+ }
+
+ ++ppEntries;
+ ++pValues;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoGraphicMapper.cxx b/vcl/source/graphic/UnoGraphicMapper.cxx
new file mode 100644
index 0000000000..6bde780976
--- /dev/null
+++ b/vcl/source/graphic/UnoGraphicMapper.cxx
@@ -0,0 +1,87 @@
+/* -*- 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/.
+ */
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/graphic/XGraphicMapper.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/unique_disposing_ptr.hxx>
+
+#include <memory>
+#include <unordered_map>
+
+using namespace css;
+
+namespace
+{
+class GraphicMapper : public cppu::WeakImplHelper<graphic::XGraphicMapper, lang::XServiceInfo>
+{
+private:
+ std::unordered_map<OUString, uno::Reference<graphic::XGraphic>> maGraphicMap;
+
+public:
+ GraphicMapper() = default;
+
+protected:
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.graphic.GraphicMapper";
+ }
+ sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { "com.sun.star.graphic.GraphicMapper" };
+ }
+
+ // XTypeProvider
+ css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override
+ {
+ static const uno::Sequence<uno::Type> aTypes{
+ cppu::UnoType<lang::XServiceInfo>::get(), cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<graphic::XGraphicMapper>::get()
+ };
+ return aTypes;
+ }
+ css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+ // XGraphicMapper
+ css::uno::Reference<css::graphic::XGraphic> SAL_CALL findGraphic(const OUString& rId) override
+ {
+ auto aIterator = maGraphicMap.find(rId);
+
+ if (aIterator == maGraphicMap.end())
+ return css::uno::Reference<css::graphic::XGraphic>();
+
+ return aIterator->second;
+ }
+ void SAL_CALL putGraphic(const OUString& rId,
+ css::uno::Reference<css::graphic::XGraphic> const& rGraphic) override
+ {
+ maGraphicMap.emplace(rId, rGraphic);
+ }
+};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_graphic_GraphicMapper_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new GraphicMapper);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoGraphicObject.cxx b/vcl/source/graphic/UnoGraphicObject.cxx
new file mode 100644
index 0000000000..73bbcef3ec
--- /dev/null
+++ b/vcl/source/graphic/UnoGraphicObject.cxx
@@ -0,0 +1,101 @@
+/* -*- 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 <memory>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/graphic/XGraphicObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <vcl/GraphicObject.hxx>
+#include <mutex>
+
+using namespace css;
+
+namespace {
+
+typedef ::cppu::WeakImplHelper<graphic::XGraphicObject, css::lang::XServiceInfo> GraphicObject_BASE;
+
+ // Simple uno wrapper around the GraphicObject class to allow basic
+ // access. ( and solves a horrible cyclic link problem between
+ // goodies/toolkit/extensions )
+class GraphicObjectImpl : public GraphicObject_BASE
+{
+ std::mutex m_aMutex;
+ std::optional<GraphicObject> mpGraphicObject;
+
+public:
+ /// @throws uno::RuntimeException
+ explicit GraphicObjectImpl(uno::Sequence<uno::Any> const & rArgs);
+
+ // XGraphicObject
+ virtual uno::Reference<graphic::XGraphic> SAL_CALL getGraphic() override;
+ virtual void SAL_CALL setGraphic(uno::Reference<graphic::XGraphic> const & rxGraphic) override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.graphic.GraphicObject";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { "com.sun.star.graphic.GraphicObject" };
+ }
+};
+
+GraphicObjectImpl::GraphicObjectImpl(const uno::Sequence<uno::Any>& /*rArgs*/)
+{
+ mpGraphicObject.emplace();
+}
+
+uno::Reference<graphic::XGraphic> SAL_CALL GraphicObjectImpl::getGraphic()
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if (!mpGraphicObject)
+ throw uno::RuntimeException();
+ return mpGraphicObject->GetGraphic().GetXGraphic();
+}
+
+void SAL_CALL GraphicObjectImpl::setGraphic(uno::Reference<graphic::XGraphic> const & rxGraphic)
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if (!mpGraphicObject)
+ throw uno::RuntimeException();
+ Graphic aGraphic(rxGraphic);
+ mpGraphicObject->SetGraphic(aGraphic);
+}
+
+} // end anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT
+css::uno::XInterface* com_sun_star_graphic_GraphicObject_get_implementation(
+ SAL_UNUSED_PARAMETER uno::XComponentContext*,
+ uno::Sequence<uno::Any> const & rArguments)
+{
+ return cppu::acquire(new GraphicObjectImpl(rArguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/UnoGraphicProvider.cxx b/vcl/source/graphic/UnoGraphicProvider.cxx
new file mode 100644
index 0000000000..bceb9fbabe
--- /dev/null
+++ b/vcl/source/graphic/UnoGraphicProvider.cxx
@@ -0,0 +1,837 @@
+/* -*- 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 <o3tl/string_view.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/image.hxx>
+#include <vcl/metaact.hxx>
+#include <imagerepository.hxx>
+#include <tools/fract.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/wmfexternal.hxx>
+#include <vcl/virdev.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphicProvider2.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+
+#include <graphic/UnoGraphicDescriptor.hxx>
+#include <graphic/UnoGraphic.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/dibtools.hxx>
+#include <comphelper/sequence.hxx>
+#include <memory>
+#include <string_view>
+
+#include <vcl/TypeSerializer.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+class GraphicProvider : public ::cppu::WeakImplHelper< css::graphic::XGraphicProvider2,
+ css::lang::XServiceInfo >
+{
+public:
+
+ GraphicProvider();
+
+protected:
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XGraphicProvider
+ virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL queryGraphicDescriptor( const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override;
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL queryGraphic( const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override;
+ virtual void SAL_CALL storeGraphic( const css::uno::Reference< css::graphic::XGraphic >& Graphic, const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override;
+
+ // XGraphicProvider2
+ uno::Sequence< uno::Reference<graphic::XGraphic> > SAL_CALL queryGraphics(const uno::Sequence< uno::Sequence<beans::PropertyValue> >& MediaPropertiesSeq ) override;
+
+private:
+
+ static css::uno::Reference< css::graphic::XGraphic > implLoadMemory( std::u16string_view rResourceURL );
+ static css::uno::Reference< css::graphic::XGraphic > implLoadRepositoryImage( std::u16string_view rResourceURL );
+ static css::uno::Reference< css::graphic::XGraphic > implLoadBitmap( const css::uno::Reference< css::awt::XBitmap >& rBitmap );
+ static css::uno::Reference< css::graphic::XGraphic > implLoadStandardImage( std::u16string_view rResourceURL );
+};
+
+GraphicProvider::GraphicProvider()
+{
+}
+
+OUString SAL_CALL GraphicProvider::getImplementationName()
+{
+ return "com.sun.star.comp.graphic.GraphicProvider";
+}
+
+sal_Bool SAL_CALL GraphicProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL GraphicProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.graphic.GraphicProvider" };
+}
+
+uno::Sequence< uno::Type > SAL_CALL GraphicProvider::getTypes()
+{
+ static const uno::Sequence< uno::Type > aTypes {
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<graphic::XGraphicProvider>::get()
+ };
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GraphicProvider::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadMemory( std::u16string_view rResourceURL )
+{
+ uno::Reference< ::graphic::XGraphic > xRet;
+ sal_Int32 nIndex = 0;
+
+ if( o3tl::getToken(rResourceURL, 0, '/', nIndex ) == u"private:memorygraphic" )
+ {
+ sal_Int64 nGraphicAddress = o3tl::toInt64(o3tl::getToken(rResourceURL, 0, '/', nIndex ));
+
+ if( nGraphicAddress )
+ {
+ rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic;
+
+ pUnoGraphic->init( *reinterpret_cast< ::Graphic* >( nGraphicAddress ) );
+ xRet = pUnoGraphic;
+ }
+ }
+
+ return xRet;
+}
+
+uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadRepositoryImage( std::u16string_view rResourceURL )
+{
+ uno::Reference< ::graphic::XGraphic > xRet;
+
+ std::u16string_view sPathName;
+ if( o3tl::starts_with(rResourceURL, u"private:graphicrepository/", &sPathName) )
+ {
+ BitmapEx aBitmap;
+ if ( vcl::ImageRepository::loadImage( OUString(sPathName), aBitmap ) )
+ {
+ Graphic aGraphic(aBitmap);
+ aGraphic.setOriginURL(OUString(rResourceURL));
+ xRet = aGraphic.GetXGraphic();
+ }
+ }
+ return xRet;
+}
+
+uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadStandardImage( std::u16string_view rResourceURL )
+{
+ uno::Reference< ::graphic::XGraphic > xRet;
+
+ std::u16string_view sImageName;
+ if( o3tl::starts_with(rResourceURL, u"private:standardimage/", &sImageName) )
+ {
+ if ( sImageName == u"info" )
+ {
+ xRet = Graphic(GetStandardInfoBoxImage().GetBitmapEx()).GetXGraphic();
+ }
+ else if ( sImageName == u"warning" )
+ {
+ xRet = Graphic(GetStandardWarningBoxImage().GetBitmapEx()).GetXGraphic();
+ }
+ else if ( sImageName == u"error" )
+ {
+ xRet = Graphic(GetStandardErrorBoxImage().GetBitmapEx()).GetXGraphic();
+ }
+ else if ( sImageName == u"query" )
+ {
+ xRet = Graphic(GetStandardQueryBoxImage().GetBitmapEx()).GetXGraphic();
+ }
+ }
+ return xRet;
+}
+
+
+uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadBitmap( const uno::Reference< awt::XBitmap >& xBtm )
+{
+ uno::Reference< ::graphic::XGraphic > xRet;
+ uno::Sequence< sal_Int8 > aBmpSeq( xBtm->getDIB() );
+ uno::Sequence< sal_Int8 > aMaskSeq( xBtm->getMaskDIB() );
+ SvMemoryStream aBmpStream( aBmpSeq.getArray(), aBmpSeq.getLength(), StreamMode::READ );
+ Bitmap aBmp;
+ BitmapEx aBmpEx;
+
+ ReadDIB(aBmp, aBmpStream, true);
+
+ if( aMaskSeq.hasElements() )
+ {
+ SvMemoryStream aMaskStream( aMaskSeq.getArray(), aMaskSeq.getLength(), StreamMode::READ );
+ Bitmap aMask;
+
+ ReadDIB(aMask, aMaskStream, true);
+ aBmpEx = BitmapEx( aBmp, aMask );
+ }
+ else
+ aBmpEx = BitmapEx( aBmp );
+
+ if( !aBmpEx.IsEmpty() )
+ {
+ rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic;
+
+ pUnoGraphic->init( aBmpEx );
+ xRet = pUnoGraphic;
+ }
+ return xRet;
+}
+
+uno::Reference< beans::XPropertySet > SAL_CALL GraphicProvider::queryGraphicDescriptor( const uno::Sequence< beans::PropertyValue >& rMediaProperties )
+{
+ uno::Reference< beans::XPropertySet > xRet;
+
+ OUString aURL;
+ uno::Reference< io::XInputStream > xIStm;
+ uno::Reference< awt::XBitmap >xBtm;
+
+ for( const auto& rMediaProperty : rMediaProperties )
+ {
+ if (xRet.is())
+ break;
+
+ const OUString aName( rMediaProperty.Name );
+ const uno::Any aValue( rMediaProperty.Value );
+
+ if (aName == "URL")
+ {
+ aValue >>= aURL;
+ }
+ else if (aName == "InputStream")
+ {
+ aValue >>= xIStm;
+ }
+ else if (aName == "Bitmap")
+ {
+ aValue >>= xBtm;
+ }
+ }
+
+ SolarMutexGuard g;
+
+ if( xIStm.is() )
+ {
+ rtl::Reference<unographic::GraphicDescriptor> pDescriptor = new unographic::GraphicDescriptor;
+ pDescriptor->init( xIStm, aURL );
+ xRet = pDescriptor;
+ }
+ else if( !aURL.isEmpty() )
+ {
+ uno::Reference< ::graphic::XGraphic > xGraphic( implLoadMemory( aURL ) );
+
+ if ( !xGraphic.is() )
+ xGraphic = implLoadRepositoryImage( aURL );
+
+ if ( !xGraphic.is() )
+ xGraphic = implLoadStandardImage( aURL );
+
+ if( xGraphic.is() )
+ {
+ xRet.set( xGraphic, uno::UNO_QUERY );
+ }
+ else
+ {
+ rtl::Reference<unographic::GraphicDescriptor> pDescriptor = new unographic::GraphicDescriptor;
+ pDescriptor->init( aURL );
+ xRet = pDescriptor;
+ }
+ }
+ else if( xBtm.is() )
+ {
+ uno::Reference< ::graphic::XGraphic > xGraphic( implLoadBitmap( xBtm ) );
+ if( xGraphic.is() )
+ xRet.set( xGraphic, uno::UNO_QUERY );
+ }
+
+ return xRet;
+}
+
+
+uno::Reference< ::graphic::XGraphic > SAL_CALL GraphicProvider::queryGraphic( const uno::Sequence< ::beans::PropertyValue >& rMediaProperties )
+{
+ uno::Reference< ::graphic::XGraphic > xRet;
+ OUString aPath;
+
+ uno::Reference< io::XInputStream > xIStm;
+ uno::Reference< awt::XBitmap >xBtm;
+
+ uno::Sequence< ::beans::PropertyValue > aFilterData;
+
+ bool bLazyRead = false;
+ bool bLoadAsLink = false;
+
+ for (const auto& rMediaProperty : rMediaProperties)
+ {
+ if (xRet.is())
+ break;
+
+ const OUString aName( rMediaProperty.Name );
+ const uno::Any aValue( rMediaProperty.Value );
+
+ if (aName == "URL")
+ {
+ OUString aURL;
+ aValue >>= aURL;
+ aPath = aURL;
+ }
+ else if (aName == "InputStream")
+ {
+ aValue >>= xIStm;
+ }
+ else if (aName == "Bitmap")
+ {
+ aValue >>= xBtm;
+ }
+ else if (aName == "FilterData")
+ {
+ aValue >>= aFilterData;
+ }
+ else if (aName == "LazyRead")
+ {
+ aValue >>= bLazyRead;
+ }
+ else if (aName == "LoadAsLink")
+ {
+ aValue >>= bLoadAsLink;
+ }
+ }
+
+ // Check for the goal width and height if they are defined
+ sal_uInt16 nExtWidth = 0;
+ sal_uInt16 nExtHeight = 0;
+ sal_uInt16 nExtMapMode = 0;
+ for( const auto& rProp : std::as_const(aFilterData) )
+ {
+ const OUString aName( rProp.Name );
+ const uno::Any aValue( rProp.Value );
+
+ if (aName == "ExternalWidth")
+ {
+ aValue >>= nExtWidth;
+ }
+ else if (aName == "ExternalHeight")
+ {
+ aValue >>= nExtHeight;
+ }
+ else if (aName == "ExternalMapMode")
+ {
+ aValue >>= nExtMapMode;
+ }
+ }
+
+ SolarMutexGuard g;
+
+ std::unique_ptr<SvStream> pIStm;
+
+ if( xIStm.is() )
+ {
+ pIStm = ::utl::UcbStreamHelper::CreateStream( xIStm );
+ }
+ else if( !aPath.isEmpty() )
+ {
+ xRet = implLoadMemory( aPath );
+
+ if ( !xRet.is() )
+ xRet = implLoadRepositoryImage( aPath );
+
+ if ( !xRet.is() )
+ xRet = implLoadStandardImage( aPath );
+
+ if( !xRet.is() )
+ pIStm = ::utl::UcbStreamHelper::CreateStream( aPath, StreamMode::READ );
+ }
+ else if( xBtm.is() )
+ {
+ xRet = implLoadBitmap( xBtm );
+ }
+
+ if( pIStm )
+ {
+ ::GraphicFilter& rFilter = ::GraphicFilter::GetGraphicFilter();
+
+ {
+ Graphic aVCLGraphic;
+
+ // Define APM Header if goal height and width are defined
+ WmfExternal aExtHeader;
+ aExtHeader.xExt = nExtWidth;
+ aExtHeader.yExt = nExtHeight;
+ aExtHeader.mapMode = nExtMapMode;
+ if ( nExtMapMode > 0 )
+ {
+ bLazyRead = false;
+ }
+
+ ErrCode error = ERRCODE_NONE;
+ if (bLazyRead)
+ {
+ Graphic aGraphic = rFilter.ImportUnloadedGraphic(*pIStm);
+ if (!aGraphic.IsNone())
+ aVCLGraphic = aGraphic;
+ }
+ if (aVCLGraphic.IsNone())
+ error = rFilter.ImportGraphic(aVCLGraphic, aPath, *pIStm, GRFILTER_FORMAT_DONTKNOW, nullptr, GraphicFilterImportFlags::NONE);
+
+ if( (error == ERRCODE_NONE ) &&
+ ( aVCLGraphic.GetType() != GraphicType::NONE ) )
+ {
+ if (!aPath.isEmpty() && bLoadAsLink)
+ aVCLGraphic.setOriginURL(aPath);
+
+ rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic;
+
+ pUnoGraphic->init( aVCLGraphic );
+ xRet = pUnoGraphic;
+ }
+ else{
+ SAL_WARN("svtools", "Could not create graphic for:" << aPath << " error: " << error);
+ }
+ }
+ }
+
+ return xRet;
+}
+
+uno::Sequence< uno::Reference<graphic::XGraphic> > SAL_CALL GraphicProvider::queryGraphics(const uno::Sequence< uno::Sequence<beans::PropertyValue> >& rMediaPropertiesSeq)
+{
+ // Turn properties into streams.
+ std::vector< std::unique_ptr<SvStream> > aStreams;
+ for (const auto& rMediaProperties : rMediaPropertiesSeq)
+ {
+ std::unique_ptr<SvStream> pStream;
+ uno::Reference<io::XInputStream> xStream;
+
+ auto pProp = std::find_if(rMediaProperties.begin(), rMediaProperties.end(),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "InputStream"; });
+ if (pProp != rMediaProperties.end())
+ {
+ pProp->Value >>= xStream;
+ if (xStream.is())
+ pStream = utl::UcbStreamHelper::CreateStream(xStream);
+ }
+
+ aStreams.push_back(std::move(pStream));
+ }
+
+ // Import: streams to graphics.
+ std::vector< std::shared_ptr<Graphic> > aGraphics;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ rFilter.ImportGraphics(aGraphics, std::move(aStreams));
+
+ // Returning: graphics to UNO objects.
+ std::vector< uno::Reference<graphic::XGraphic> > aRet;
+ for (const auto& pGraphic : aGraphics)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ if (pGraphic)
+ {
+ rtl::Reference<unographic::Graphic> pUnoGraphic = new unographic::Graphic();
+ pUnoGraphic->init(*pGraphic);
+ xGraphic = pUnoGraphic;
+ }
+
+ aRet.push_back(xGraphic);
+ }
+
+ return comphelper::containerToSequence(aRet);
+}
+
+void ImplCalculateCropRect( ::Graphic const & rGraphic, const text::GraphicCrop& rGraphicCropLogic, tools::Rectangle& rGraphicCropPixel )
+{
+ if ( !(rGraphicCropLogic.Left || rGraphicCropLogic.Top || rGraphicCropLogic.Right || rGraphicCropLogic.Bottom) )
+ return;
+
+ Size aSourceSizePixel( rGraphic.GetSizePixel() );
+ if ( !(aSourceSizePixel.Width() && aSourceSizePixel.Height()) )
+ return;
+
+ if ( !(rGraphicCropLogic.Left || rGraphicCropLogic.Top || rGraphicCropLogic.Right || rGraphicCropLogic.Bottom) )
+ return;
+
+ Size aSize100thMM( 0, 0 );
+ if( rGraphic.GetPrefMapMode().GetMapUnit() != MapUnit::MapPixel )
+ {
+ aSize100thMM = OutputDevice::LogicToLogic(rGraphic.GetPrefSize(), rGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+ }
+ else
+ {
+ aSize100thMM = Application::GetDefaultDevice()->PixelToLogic(rGraphic.GetPrefSize(), MapMode(MapUnit::Map100thMM));
+ }
+ if ( aSize100thMM.Width() && aSize100thMM.Height() )
+ {
+ double fSourceSizePixelWidth = static_cast<double>(aSourceSizePixel.Width());
+ double fSourceSizePixelHeight= static_cast<double>(aSourceSizePixel.Height());
+ rGraphicCropPixel.SetLeft( static_cast< sal_Int32 >((fSourceSizePixelWidth * rGraphicCropLogic.Left ) / aSize100thMM.Width()) );
+ rGraphicCropPixel.SetTop( static_cast< sal_Int32 >((fSourceSizePixelHeight * rGraphicCropLogic.Top ) / aSize100thMM.Height()) );
+ rGraphicCropPixel.SetRight( static_cast< sal_Int32 >(( fSourceSizePixelWidth * ( aSize100thMM.Width() - rGraphicCropLogic.Right ) ) / aSize100thMM.Width() ) );
+ rGraphicCropPixel.SetBottom( static_cast< sal_Int32 >(( fSourceSizePixelHeight * ( aSize100thMM.Height() - rGraphicCropLogic.Bottom ) ) / aSize100thMM.Height() ) );
+ }
+}
+
+void ImplApplyBitmapScaling( ::Graphic& rGraphic, sal_Int32 nPixelWidth, sal_Int32 nPixelHeight )
+{
+ if ( nPixelWidth && nPixelHeight )
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+ MapMode aPrefMapMode( aBmpEx.GetPrefMapMode() );
+ Size aPrefSize( aBmpEx.GetPrefSize() );
+ aBmpEx.Scale( Size( nPixelWidth, nPixelHeight ) );
+ aBmpEx.SetPrefMapMode( aPrefMapMode );
+ aBmpEx.SetPrefSize( aPrefSize );
+ rGraphic = aBmpEx;
+ }
+}
+
+void ImplApplyBitmapResolution( ::Graphic& rGraphic, sal_Int32 nImageResolution, const Size& rVisiblePixelSize, const awt::Size& rLogicalSize )
+{
+ if ( !(nImageResolution && rLogicalSize.Width && rLogicalSize.Height) )
+ return;
+
+ const double fImageResolution = static_cast<double>( nImageResolution );
+ const double fSourceDPIX = ( static_cast<double>(rVisiblePixelSize.Width()) * 2540.0 ) / static_cast<double>(rLogicalSize.Width);
+ const double fSourceDPIY = ( static_cast<double>(rVisiblePixelSize.Height()) * 2540.0 ) / static_cast<double>(rLogicalSize.Height);
+ const sal_Int32 nSourcePixelWidth( rGraphic.GetSizePixel().Width() );
+ const sal_Int32 nSourcePixelHeight( rGraphic.GetSizePixel().Height() );
+ const double fSourcePixelWidth = static_cast<double>( nSourcePixelWidth );
+ const double fSourcePixelHeight= static_cast<double>( nSourcePixelHeight );
+
+ sal_Int32 nDestPixelWidth = nSourcePixelWidth;
+ sal_Int32 nDestPixelHeight = nSourcePixelHeight;
+
+ // check, if the bitmap DPI exceeds the maximum DPI
+ if( fSourceDPIX > fImageResolution )
+ {
+ nDestPixelWidth = static_cast<sal_Int32>(( fSourcePixelWidth * fImageResolution ) / fSourceDPIX);
+ if ( !nDestPixelWidth || ( nDestPixelWidth > nSourcePixelWidth ) )
+ nDestPixelWidth = nSourcePixelWidth;
+ }
+ if ( fSourceDPIY > fImageResolution )
+ {
+ nDestPixelHeight= static_cast<sal_Int32>(( fSourcePixelHeight* fImageResolution ) / fSourceDPIY);
+ if ( !nDestPixelHeight || ( nDestPixelHeight > nSourcePixelHeight ) )
+ nDestPixelHeight = nSourcePixelHeight;
+ }
+ if ( ( nDestPixelWidth != nSourcePixelWidth ) || ( nDestPixelHeight != nSourcePixelHeight ) )
+ ImplApplyBitmapScaling( rGraphic, nDestPixelWidth, nDestPixelHeight );
+}
+
+void ImplApplyFilterData( ::Graphic& rGraphic, const uno::Sequence< beans::PropertyValue >& rFilterData )
+{
+ /* this method applies following attributes to the graphic, in the first step the
+ cropping area (logical size in 100thmm) is applied, in the second step the resolution
+ is applied, in the third step the graphic is scaled to the corresponding pixelsize.
+ if a parameter value is zero or not available the corresponding step will be skipped */
+
+ sal_Int32 nPixelWidth = 0;
+ sal_Int32 nPixelHeight= 0;
+ sal_Int32 nImageResolution = 0;
+ awt::Size aLogicalSize( 0, 0 );
+ text::GraphicCrop aCropLogic( 0, 0, 0, 0 );
+ bool bRemoveCropArea = true;
+
+ for( const auto& rProp : rFilterData )
+ {
+ const OUString aName( rProp.Name );
+ const uno::Any aValue( rProp.Value );
+
+ if (aName == "PixelWidth")
+ aValue >>= nPixelWidth;
+ else if (aName == "PixelHeight")
+ aValue >>= nPixelHeight;
+ else if (aName == "LogicalSize")
+ aValue >>= aLogicalSize;
+ else if (aName == "GraphicCropLogic")
+ aValue >>= aCropLogic;
+ else if (aName == "RemoveCropArea")
+ aValue >>= bRemoveCropArea;
+ else if (aName == "ImageResolution")
+ aValue >>= nImageResolution;
+ }
+ if ( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ if(rGraphic.getVectorGraphicData())
+ {
+ // embedded Vector Graphic Data, no need to scale. Also no method to apply crop data currently
+ }
+ else
+ {
+ tools::Rectangle aCropPixel( Point( 0, 0 ), rGraphic.GetSizePixel() );
+ ImplCalculateCropRect( rGraphic, aCropLogic, aCropPixel );
+ if ( bRemoveCropArea )
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+ aBmpEx.Crop( aCropPixel );
+ rGraphic = aBmpEx;
+ }
+ Size aVisiblePixelSize( bRemoveCropArea ? rGraphic.GetSizePixel() : aCropPixel.GetSize() );
+ ImplApplyBitmapResolution( rGraphic, nImageResolution, aVisiblePixelSize, aLogicalSize );
+ ImplApplyBitmapScaling( rGraphic, nPixelWidth, nPixelHeight );
+ }
+ }
+ else if ( ( rGraphic.GetType() == GraphicType::GdiMetafile ) && nImageResolution )
+ {
+ ScopedVclPtrInstance< VirtualDevice > aDummyVDev;
+ GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
+ Size aMtfSize( OutputDevice::LogicToLogic(aMtf.GetPrefSize(), aMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) );
+ if ( aMtfSize.Width() && aMtfSize.Height() )
+ {
+ MapMode aNewMapMode( MapUnit::Map100thMM );
+ aNewMapMode.SetScaleX( Fraction( aLogicalSize.Width, aMtfSize.Width() ) );
+ aNewMapMode.SetScaleY( Fraction( aLogicalSize.Height, aMtfSize.Height() ) );
+ aDummyVDev->EnableOutput( false );
+ aDummyVDev->SetMapMode( aNewMapMode );
+
+ for( size_t i = 0, nObjCount = aMtf.GetActionSize(); i < nObjCount; i++ )
+ {
+ MetaAction* pAction = aMtf.GetAction( i );
+ switch( pAction->GetType() )
+ {
+ // only optimizing common bitmap actions:
+ case MetaActionType::MAPMODE:
+ {
+ pAction->Execute( aDummyVDev.get() );
+ break;
+ }
+ case MetaActionType::PUSH:
+ {
+ const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
+ aDummyVDev->Push( pA->GetFlags() );
+ break;
+ }
+ case MetaActionType::POP:
+ {
+ aDummyVDev->Pop();
+ break;
+ }
+ case MetaActionType::BMPSCALE:
+ case MetaActionType::BMPEXSCALE:
+ {
+ BitmapEx aBmpEx;
+ Point aPos;
+ Size aSize;
+ if ( pAction->GetType() == MetaActionType::BMPSCALE )
+ {
+ MetaBmpScaleAction* pScaleAction = dynamic_cast< MetaBmpScaleAction* >( pAction );
+ assert(pScaleAction);
+ aBmpEx = pScaleAction->GetBitmap();
+ aPos = pScaleAction->GetPoint();
+ aSize = pScaleAction->GetSize();
+ }
+ else
+ {
+ MetaBmpExScaleAction* pScaleAction = dynamic_cast< MetaBmpExScaleAction* >( pAction );
+ assert(pScaleAction);
+ aBmpEx = pScaleAction->GetBitmapEx();
+ aPos = pScaleAction->GetPoint();
+ aSize = pScaleAction->GetSize();
+ }
+ ::Graphic aGraphic( aBmpEx );
+ const Size aSize100thmm( aDummyVDev->LogicToPixel( aSize ) );
+ Size aSize100thmm2( aDummyVDev->PixelToLogic(aSize100thmm, MapMode(MapUnit::Map100thMM)) );
+
+ ImplApplyBitmapResolution( aGraphic, nImageResolution,
+ aGraphic.GetSizePixel(), awt::Size( aSize100thmm2.Width(), aSize100thmm2.Height() ) );
+
+ rtl::Reference<MetaAction> pNewAction = new MetaBmpExScaleAction( aPos, aSize, aGraphic.GetBitmapEx() );
+ aMtf.ReplaceAction( pNewAction, i );
+ break;
+ }
+ default:
+ case MetaActionType::BMP:
+ case MetaActionType::BMPSCALEPART:
+ case MetaActionType::BMPEX:
+ case MetaActionType::BMPEXSCALEPART:
+ case MetaActionType::MASK:
+ case MetaActionType::MASKSCALE:
+ break;
+ }
+ }
+ rGraphic = aMtf;
+ }
+ }
+}
+
+
+void SAL_CALL GraphicProvider::storeGraphic( const uno::Reference< ::graphic::XGraphic >& rxGraphic, const uno::Sequence< beans::PropertyValue >& rMediaProperties )
+{
+ std::unique_ptr<SvStream> pOStm;
+ OUString aPath;
+
+ for( const auto& rMediaProperty : rMediaProperties )
+ {
+ const OUString aName( rMediaProperty.Name );
+ const uno::Any aValue( rMediaProperty.Value );
+
+ if (aName == "URL")
+ {
+ OUString aURL;
+
+ aValue >>= aURL;
+ pOStm = ::utl::UcbStreamHelper::CreateStream( aURL, StreamMode::WRITE | StreamMode::TRUNC );
+ aPath = aURL;
+ }
+ else if (aName == "OutputStream")
+ {
+ uno::Reference< io::XStream > xOStm;
+
+ aValue >>= xOStm;
+
+ if( xOStm.is() )
+ pOStm = ::utl::UcbStreamHelper::CreateStream( xOStm );
+ }
+
+ if( pOStm )
+ break;
+ }
+
+ if( !pOStm )
+ return;
+
+ uno::Sequence< beans::PropertyValue > aFilterDataSeq;
+ OUString sFilterShortName;
+
+ for( const auto& rMediaProperty : rMediaProperties )
+ {
+ const OUString aName( rMediaProperty.Name );
+ const uno::Any aValue( rMediaProperty.Value );
+
+ if (aName == "FilterData")
+ {
+ aValue >>= aFilterDataSeq;
+ }
+ else if (aName == "MimeType")
+ {
+ OUString aMimeType;
+
+ aValue >>= aMimeType;
+
+ if (aMimeType == MIMETYPE_BMP)
+ sFilterShortName = "bmp";
+ else if (aMimeType == MIMETYPE_EPS)
+ sFilterShortName = "eps";
+ else if (aMimeType == MIMETYPE_GIF)
+ sFilterShortName = "gif";
+ else if (aMimeType == MIMETYPE_JPG)
+ sFilterShortName = "jpg";
+ else if (aMimeType == MIMETYPE_MET)
+ sFilterShortName = "met";
+ else if (aMimeType == MIMETYPE_PNG)
+ sFilterShortName = "png";
+ else if (aMimeType == MIMETYPE_PCT)
+ sFilterShortName = "pct";
+ else if (aMimeType == MIMETYPE_PBM)
+ sFilterShortName = "pbm";
+ else if (aMimeType == MIMETYPE_PGM)
+ sFilterShortName = "pgm";
+ else if (aMimeType == MIMETYPE_PPM)
+ sFilterShortName = "ppm";
+ else if (aMimeType == MIMETYPE_RAS)
+ sFilterShortName = "ras";
+ else if (aMimeType == MIMETYPE_SVM)
+ sFilterShortName = "svm";
+ else if (aMimeType == MIMETYPE_TIF)
+ sFilterShortName = "tif";
+ else if (aMimeType == MIMETYPE_EMF)
+ sFilterShortName = "emf";
+ else if (aMimeType == MIMETYPE_WMF)
+ sFilterShortName = "wmf";
+ else if (aMimeType == MIMETYPE_XPM)
+ sFilterShortName = "xpm";
+ else if (aMimeType == MIMETYPE_SVG)
+ sFilterShortName = "svg";
+ else if (aMimeType == MIMETYPE_VCLGRAPHIC)
+ sFilterShortName = MIMETYPE_VCLGRAPHIC;
+ }
+ }
+
+ if( sFilterShortName.isEmpty() )
+ return;
+
+ ::GraphicFilter& rFilter = ::GraphicFilter::GetGraphicFilter();
+
+ {
+ const uno::Reference< XInterface > xIFace( rxGraphic, uno::UNO_QUERY );
+ const ::unographic::Graphic* pUnoGraphic = dynamic_cast<::unographic::Graphic*>(xIFace.get());
+ const ::Graphic* pGraphic = pUnoGraphic ? &pUnoGraphic->GetGraphic() : nullptr;
+
+ if( pGraphic && ( pGraphic->GetType() != GraphicType::NONE ) )
+ {
+ ::Graphic aGraphic( *pGraphic );
+ ImplApplyFilterData( aGraphic, aFilterDataSeq );
+
+ /* sj: using a temporary memory stream, because some graphic filters are seeking behind
+ stream end (which leads to an invalid argument exception then). */
+ SvMemoryStream aMemStrm;
+ aMemStrm.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ if( sFilterShortName == MIMETYPE_VCLGRAPHIC )
+ {
+ TypeSerializer aSerializer(aMemStrm);
+ aSerializer.writeGraphic(aGraphic);
+ }
+ else
+ {
+ rFilter.ExportGraphic( aGraphic, aPath, aMemStrm,
+ rFilter.GetExportFormatNumberForShortName( sFilterShortName ),
+ ( aFilterDataSeq.hasElements() ? &aFilterDataSeq : nullptr ) );
+ }
+ pOStm->WriteBytes( aMemStrm.GetData(), aMemStrm.TellEnd() );
+ }
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_graphic_GraphicProvider_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new GraphicProvider);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/VectorGraphicLoader.cxx b/vcl/source/graphic/VectorGraphicLoader.cxx
new file mode 100644
index 0000000000..9976810923
--- /dev/null
+++ b/vcl/source/graphic/VectorGraphicLoader.cxx
@@ -0,0 +1,26 @@
+/* -*- 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/.
+ *
+ */
+
+#include <tools/stream.hxx>
+#include <graphic/VectorGraphicLoader.hxx>
+
+namespace vcl
+{
+std::shared_ptr<VectorGraphicData> loadVectorGraphic(BinaryDataContainer const& rDataContainer,
+ VectorGraphicDataType eType)
+{
+ if (rDataContainer.isEmpty())
+ return std::shared_ptr<VectorGraphicData>();
+
+ return std::make_shared<VectorGraphicData>(rDataContainer, eType);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx
new file mode 100644
index 0000000000..c2f6244175
--- /dev/null
+++ b/vcl/source/graphic/VectorGraphicSearch.cxx
@@ -0,0 +1,307 @@
+/* -*- 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/.
+ *
+ */
+
+#include <utility>
+#include <vcl/VectorGraphicSearch.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <sal/config.h>
+
+namespace
+{
+class SearchContext
+{
+private:
+ std::unique_ptr<vcl::pdf::PDFiumDocument>& mpPdfDocument;
+ std::unique_ptr<vcl::pdf::PDFiumPage> mpPage;
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> mpTextPage;
+ std::unique_ptr<vcl::pdf::PDFiumSearchHandle> mpSearchHandle;
+
+public:
+ sal_Int32 mnPageIndex;
+ int mnCurrentIndex;
+ OUString maSearchString;
+ VectorGraphicSearchOptions maOptions;
+
+ SearchContext(std::unique_ptr<vcl::pdf::PDFiumDocument>& pPdfDocument, sal_Int32 nPageIndex)
+ : mpPdfDocument(pPdfDocument)
+ , mnPageIndex(nPageIndex)
+ , mnCurrentIndex(-1)
+ {
+ }
+
+ ~SearchContext()
+ {
+ if (mpSearchHandle)
+ mpSearchHandle.reset();
+ if (mpTextPage)
+ mpTextPage.reset();
+ if (mpPage)
+ mpPage.reset();
+ }
+
+ basegfx::B2DSize getPageSize()
+ {
+ basegfx::B2DSize aSize;
+ if (!mpPdfDocument)
+ return aSize;
+
+ basegfx::B2DSize aPDFSize = mpPdfDocument->getPageSize(mnPageIndex);
+ aSize = basegfx::B2DSize(convertPointToMm100(aPDFSize.getWidth()),
+ convertPointToMm100(aPDFSize.getHeight()));
+ return aSize;
+ }
+
+ bool initialize(OUString const& rSearchString, VectorGraphicSearchOptions const& rOptions)
+ {
+ if (!mpPdfDocument)
+ return false;
+
+ if (rSearchString == maSearchString)
+ return true;
+
+ if (mpSearchHandle)
+ mpSearchHandle.reset();
+
+ if (mpTextPage)
+ mpTextPage.reset();
+
+ if (mpPage)
+ mpPage.reset();
+
+ maSearchString = rSearchString;
+ maOptions = rOptions;
+
+ mpPage = mpPdfDocument->openPage(mnPageIndex);
+ if (!mpPage)
+ return false;
+
+ mpTextPage = mpPage->getTextPage();
+ if (!mpTextPage)
+ return false;
+
+ // Index where to start to search. -1 => at the end
+ int nStartIndex = maOptions.meStartPosition == SearchStartPosition::End ? -1 : 0;
+
+ if (mnCurrentIndex >= 0)
+ nStartIndex = mnCurrentIndex;
+
+ // vcl::pdf::PDFFindFlags::MatchCase, vcl::pdf::PDFFindFlags::MatchWholeWord, vcl::pdf::PDFFindFlags::Consecutive
+ // vcl::pdf::PDFFindFlags::MatchCase - If not set, it will not match case by default.
+ // vcl::pdf::PDFFindFlags::MatchWholeWord - If not set, it will not match the whole word by default.
+ // vcl::pdf::PDFFindFlags::Consecutive - If not set, it will skip past the current match to look for the next match.
+ vcl::pdf::PDFFindFlags nSearchFlags{};
+ if (maOptions.mbMatchCase)
+ nSearchFlags |= vcl::pdf::PDFFindFlags::MatchCase;
+ if (maOptions.mbMatchWholeWord)
+ nSearchFlags |= vcl::pdf::PDFFindFlags::MatchWholeWord;
+
+ mpSearchHandle = mpTextPage->findStart(maSearchString, nSearchFlags, nStartIndex);
+
+ return mpSearchHandle != nullptr;
+ }
+
+ bool next()
+ {
+ if (mpSearchHandle && mpSearchHandle->findNext())
+ {
+ mnCurrentIndex = index();
+ return true;
+ }
+ return false;
+ }
+
+ bool previous()
+ {
+ if (mpSearchHandle && mpSearchHandle->findPrev())
+ {
+ mnCurrentIndex = index();
+ return true;
+ }
+ return false;
+ }
+
+ int index()
+ {
+ if (mpSearchHandle)
+ return mpSearchHandle->getSearchResultIndex();
+ return -1;
+ }
+
+ int size()
+ {
+ if (mpSearchHandle)
+ return mpSearchHandle->getSearchCount();
+ return -1;
+ }
+
+ std::vector<basegfx::B2DRectangle> getTextRectangles()
+ {
+ std::vector<basegfx::B2DRectangle> aRectangles;
+
+ if (!mpTextPage || !mpSearchHandle)
+ return aRectangles;
+
+ int nIndex = index();
+ if (nIndex < 0)
+ return aRectangles;
+
+ int nSize = size();
+ if (nSize <= 0)
+ return aRectangles;
+
+ double fPageHeight = getPageSize().getHeight();
+
+ for (int nCount = 0; nCount < nSize; nCount++)
+ {
+ basegfx::B2DRectangle aRectangle = mpTextPage->getCharBox(nIndex + nCount, fPageHeight);
+ if (!aRectangle.isEmpty())
+ {
+ aRectangles.push_back(aRectangle);
+ }
+ }
+
+ return aRectangles;
+ }
+};
+
+} // end anonymous namespace
+
+class VectorGraphicSearch::Implementation
+{
+public:
+ std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
+ std::unique_ptr<vcl::pdf::PDFiumDocument> mpPdfDocument;
+
+ std::unique_ptr<SearchContext> mpSearchContext;
+
+ Implementation()
+ : mpPDFium(vcl::pdf::PDFiumLibrary::get())
+ {
+ }
+
+ ~Implementation() { mpSearchContext.reset(); }
+};
+
+VectorGraphicSearch::VectorGraphicSearch(Graphic aGraphic)
+ : mpImplementation(std::make_unique<VectorGraphicSearch::Implementation>())
+ , maGraphic(std::move(aGraphic))
+{
+}
+
+VectorGraphicSearch::~VectorGraphicSearch() { mpImplementation.reset(); }
+
+bool VectorGraphicSearch::search(OUString const& rSearchString,
+ VectorGraphicSearchOptions const& rOptions)
+{
+ if (!mpImplementation->mpPDFium)
+ {
+ return false;
+ }
+
+ if (!mpImplementation->mpSearchContext)
+ {
+ auto pData = maGraphic.getVectorGraphicData();
+
+ if (pData && pData->getType() == VectorGraphicDataType::Pdf)
+ {
+ if (searchPDF(pData))
+ {
+ return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions);
+ }
+ }
+ return false;
+ }
+ return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions);
+}
+
+bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rData)
+{
+ if (!mpImplementation->mpPDFium)
+ {
+ return false;
+ }
+
+ mpImplementation->mpPdfDocument = mpImplementation->mpPDFium->openDocument(
+ rData->getBinaryDataContainer().getData(), rData->getBinaryDataContainer().getSize(),
+ OString());
+
+ if (!mpImplementation->mpPdfDocument)
+ {
+ //TODO: Handle failure to load.
+ switch (mpImplementation->mpPDFium->getLastErrorCode())
+ {
+ case vcl::pdf::PDFErrorType::Success:
+ break;
+ case vcl::pdf::PDFErrorType::Unknown:
+ break;
+ case vcl::pdf::PDFErrorType::File:
+ break;
+ case vcl::pdf::PDFErrorType::Format:
+ break;
+ case vcl::pdf::PDFErrorType::Password:
+ break;
+ case vcl::pdf::PDFErrorType::Security:
+ break;
+ case vcl::pdf::PDFErrorType::Page:
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ sal_Int32 nPageIndex = std::max(rData->getPageIndex(), sal_Int32(0));
+
+ mpImplementation->mpSearchContext.reset(
+ new SearchContext(mpImplementation->mpPdfDocument, nPageIndex));
+ return true;
+}
+
+basegfx::B2DSize VectorGraphicSearch::pageSize()
+{
+ basegfx::B2DSize aSize;
+ if (mpImplementation->mpSearchContext)
+ aSize = mpImplementation->mpSearchContext->getPageSize();
+ return aSize;
+}
+
+bool VectorGraphicSearch::next()
+{
+ if (mpImplementation->mpSearchContext)
+ return mpImplementation->mpSearchContext->next();
+ return false;
+}
+
+bool VectorGraphicSearch::previous()
+{
+ if (mpImplementation->mpSearchContext)
+ return mpImplementation->mpSearchContext->previous();
+ return false;
+}
+
+int VectorGraphicSearch::index()
+{
+ if (mpImplementation->mpSearchContext)
+ return mpImplementation->mpSearchContext->index();
+ return -1;
+}
+
+std::vector<basegfx::B2DRectangle> VectorGraphicSearch::getTextRectangles()
+{
+ if (mpImplementation->mpSearchContext)
+ return mpImplementation->mpSearchContext->getTextRectangles();
+
+ return std::vector<basegfx::B2DRectangle>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */