1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/diagnose_ex.hxx>
21 #include <tools/stream.hxx>
22 #include <sal/log.hxx>
24 #include <vcl/vectorgraphicdata.hxx>
25 #include <comphelper/processfactory.hxx>
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/graphic/PdfTools.hpp>
28 #include <com/sun/star/graphic/SvgTools.hpp>
29 #include <com/sun/star/graphic/EmfTools.hpp>
30 #include <com/sun/star/graphic/Primitive2DTools.hpp>
31 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
32 #include <com/sun/star/util/XAccounting.hpp>
33 #include <com/sun/star/util/XBinaryDataContainer.hpp>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <vcl/canvastools.hxx>
36 #include <comphelper/seqstream.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <comphelper/propertysequence.hxx>
39 #include <comphelper/propertyvalue.hxx>
40 #include <pdf/PdfConfig.hxx>
42 #include <vcl/svapp.hxx>
43 #include <vcl/outdev.hxx>
44 #include <vcl/wmfexternal.hxx>
45 #include <vcl/pdfread.hxx>
46 #include <unotools/streamwrap.hxx>
47 #include <graphic/UnoBinaryDataContainer.hxx>
49 using namespace ::com::sun::star
;
51 BitmapEx
convertPrimitive2DSequenceToBitmapEx(
52 const std::deque
< css::uno::Reference
< css::graphic::XPrimitive2D
> >& rSequence
,
53 const basegfx::B2DRange
& rTargetRange
,
54 const sal_uInt32 nMaximumQuadraticPixels
,
55 const o3tl::Length eTargetUnit
,
56 const std::optional
<Size
>& rTargetDPI
)
60 if(!rSequence
.empty())
62 // create replacement graphic from maSequence
63 // create XPrimitive2DRenderer
66 const uno::Reference
< uno::XComponentContext
>& xContext(::comphelper::getProcessComponentContext());
67 const uno::Reference
< graphic::XPrimitive2DRenderer
> xPrimitive2DRenderer
= graphic::Primitive2DTools::create(xContext
);
69 uno::Sequence
< beans::PropertyValue
> aViewParameters
= {
70 comphelper::makePropertyValue(u
"RangeUnit"_ustr
, static_cast<sal_Int32
>(eTargetUnit
)),
72 geometry::RealRectangle2D aRealRect
;
74 aRealRect
.X1
= rTargetRange
.getMinX();
75 aRealRect
.Y1
= rTargetRange
.getMinY();
76 aRealRect
.X2
= rTargetRange
.getMaxX();
77 aRealRect
.Y2
= rTargetRange
.getMaxY();
80 Size
aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch
)));
81 if (rTargetDPI
.has_value())
86 const uno::Reference
< rendering::XBitmap
> xBitmap(
87 xPrimitive2DRenderer
->rasterize(
88 comphelper::containerToSequence(rSequence
),
93 nMaximumQuadraticPixels
));
97 const uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntBmp(xBitmap
, uno::UNO_QUERY_THROW
);
98 aRetval
= vcl::unotools::bitmapExFromXBitmap(xIntBmp
);
101 catch (const uno::Exception
&)
103 TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
105 catch (const std::exception
& e
)
107 SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e
.what());
114 static size_t estimateSize(
115 std::deque
<uno::Reference
<graphic::XPrimitive2D
>> const& rSequence
)
118 for (auto& it
: rSequence
)
120 uno::Reference
<util::XAccounting
> const xAcc(it
, uno::UNO_QUERY
);
121 assert(xAcc
.is()); // we expect only BasePrimitive2D from SVG parser
122 nRet
+= xAcc
->estimateUsage();
127 bool VectorGraphicData::operator==(const VectorGraphicData
& rCandidate
) const
129 if (getType() == rCandidate
.getType())
131 if (maDataContainer
.getSize() == rCandidate
.maDataContainer
.getSize())
134 maDataContainer
.getData(),
135 rCandidate
.maDataContainer
.getData(),
136 maDataContainer
.getSize()))
146 void VectorGraphicData::ensurePdfReplacement()
148 assert(getType() == VectorGraphicDataType::Pdf
);
150 if (!maReplacement
.IsEmpty())
151 return; // nothing to do
153 // use PDFium directly
154 std::vector
<BitmapEx
> aBitmaps
;
155 sal_Int32 nUsePageIndex
= 0;
156 if (mnPageIndex
>= 0)
157 nUsePageIndex
= mnPageIndex
;
158 vcl::RenderPDFBitmaps(maDataContainer
.getData(),
159 maDataContainer
.getSize(), aBitmaps
, nUsePageIndex
, 1,
161 if (!aBitmaps
.empty())
162 maReplacement
= aBitmaps
[0];
165 void VectorGraphicData::ensureReplacement()
167 if (!maReplacement
.IsEmpty())
168 return; // nothing to do
170 // shortcut for PDF - PDFium can generate the replacement bitmap for us
172 if (getType() == VectorGraphicDataType::Pdf
)
174 ensurePdfReplacement();
178 ensureSequenceAndRange();
180 if (!maSequence
.empty())
182 maReplacement
= convertPrimitive2DSequenceToBitmapEx(maSequence
, getRange());
186 BitmapEx
VectorGraphicData::getBitmap(const Size
& pixelSize
) const
188 if (!maReplacement
.IsEmpty() && maReplacement
.GetSizePixel() == pixelSize
)
189 return maReplacement
;
191 if (getType() == VectorGraphicDataType::Pdf
)
193 // use PDFium directly
194 const sal_Int32 nUsePageIndex
= mnPageIndex
> 0 ? mnPageIndex
: 0;
195 const double dpi
= vcl::pdf::getDefaultPdfResolutionDpi();
196 basegfx::B2DTuple
sizeMM100(
197 o3tl::convert(pixelSize
.Width() / dpi
/ vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
, o3tl::Length::in
, o3tl::Length::mm100
),
198 o3tl::convert(pixelSize
.Height() / dpi
/ vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
, o3tl::Length::in
, o3tl::Length::mm100
));
199 std::vector
<BitmapEx
> aBitmaps
;
200 vcl::RenderPDFBitmaps(maDataContainer
.getData(), maDataContainer
.getSize(), aBitmaps
,
201 nUsePageIndex
, 1, &sizeMM100
);
202 if (!aBitmaps
.empty())
206 if (getPrimitive2DSequence().empty())
210 std::round(pixelSize
.Width() / o3tl::convert(maRange
.getWidth(), o3tl::Length::mm100
, o3tl::Length::in
)),
211 std::round(pixelSize
.Height() / o3tl::convert(maRange
.getHeight(), o3tl::Length::mm100
, o3tl::Length::in
)));
212 return convertPrimitive2DSequenceToBitmapEx(maSequence
, maRange
, 4096 * 4096, o3tl::Length::mm100
, dpi
);
215 void VectorGraphicData::ensureSequenceAndRange()
217 if (mbSequenceCreated
|| maDataContainer
.isEmpty())
220 // import SVG to maSequence, also set maRange
223 // create Vector Graphic Data interpreter
224 const uno::Reference
<uno::XComponentContext
>& xContext(::comphelper::getProcessComponentContext());
228 case VectorGraphicDataType::Svg
:
230 const uno::Reference
<io::XInputStream
> xInputStream
= maDataContainer
.getAsXInputStream();
232 const uno::Reference
< graphic::XSvgParser
> xSvgParser
= graphic::SvgTools::create(xContext
);
234 if (xInputStream
.is())
235 maSequence
= comphelper::sequenceToContainer
<std::deque
<css::uno::Reference
< css::graphic::XPrimitive2D
>>>(xSvgParser
->getDecomposition(xInputStream
, OUString()));
239 case VectorGraphicDataType::Emf
:
240 case VectorGraphicDataType::Wmf
:
242 const uno::Reference
< graphic::XEmfParser
> xEmfParser
= graphic::EmfTools::create(xContext
);
244 const uno::Reference
<io::XInputStream
> xInputStream
= maDataContainer
.getAsXInputStream();
246 if (xInputStream
.is())
248 uno::Sequence
< ::beans::PropertyValue
> aPropertySequence
;
250 // Pass the size hint of the graphic to the EMF parser.
251 geometry::RealPoint2D aSizeHint
;
252 aSizeHint
.X
= maSizeHint
.getX();
253 aSizeHint
.Y
= maSizeHint
.getY();
254 xEmfParser
->setSizeHint(aSizeHint
);
256 if (!mbEnableEMFPlus
)
258 aPropertySequence
= { comphelper::makePropertyValue(u
"EMFPlusEnable"_ustr
, uno::Any(false)) };
261 maSequence
= comphelper::sequenceToContainer
<std::deque
<css::uno::Reference
< css::graphic::XPrimitive2D
>>>(xEmfParser
->getDecomposition(xInputStream
, OUString(), aPropertySequence
));
266 case VectorGraphicDataType::Pdf
:
268 const uno::Reference
<graphic::XPdfDecomposer
> xPdfDecomposer
= graphic::PdfTools::create(xContext
);
269 uno::Sequence
<beans::PropertyValue
> aDecompositionParameters
= comphelper::InitPropertySequence({
270 {"PageIndex", uno::Any(sal_Int32(mnPageIndex
))},
273 rtl::Reference
<UnoBinaryDataContainer
> xDataContainer
= new UnoBinaryDataContainer(getBinaryDataContainer());
275 auto xPrimitive2D
= xPdfDecomposer
->getDecomposition(xDataContainer
, aDecompositionParameters
);
276 maSequence
= comphelper::sequenceToContainer
<std::deque
<uno::Reference
<graphic::XPrimitive2D
>>>(xPrimitive2D
);
282 if(!maSequence
.empty())
284 const sal_Int32
nCount(maSequence
.size());
285 geometry::RealRectangle2D aRealRect
;
286 uno::Sequence
< beans::PropertyValue
> aViewParameters
;
288 for(sal_Int32
a(0); a
< nCount
; a
++)
291 const css::uno::Reference
< css::graphic::XPrimitive2D
> xReference(maSequence
[a
]);
295 aRealRect
= xReference
->getRange(aViewParameters
);
306 mNestedBitmapSize
= estimateSize(maSequence
);
307 mbSequenceCreated
= true;
310 std::pair
<VectorGraphicData::State
, size_t> VectorGraphicData::getSizeBytes() const
312 if (!maSequence
.empty() && !maDataContainer
.isEmpty())
314 return std::make_pair(State::PARSED
, maDataContainer
.getSize() + mNestedBitmapSize
);
318 return std::make_pair(State::UNPARSED
, maDataContainer
.getSize());
322 VectorGraphicData::VectorGraphicData(
323 BinaryDataContainer aDataContainer
,
324 VectorGraphicDataType eVectorDataType
,
325 sal_Int32 nPageIndex
)
326 : maDataContainer(std::move(aDataContainer
)),
327 mbSequenceCreated(false),
328 mNestedBitmapSize(0),
329 meType(eVectorDataType
),
330 mnPageIndex(nPageIndex
)
334 VectorGraphicData::VectorGraphicData(
335 const OUString
& rPath
,
336 VectorGraphicDataType eVectorDataType
)
337 : mbSequenceCreated(false),
338 mNestedBitmapSize(0),
339 meType(eVectorDataType
),
342 SvFileStream
rIStm(rPath
, StreamMode::STD_READ
);
345 const sal_uInt32
nStmLen(rIStm
.remainingSize());
348 BinaryDataContainer
aData(rIStm
, nStmLen
);
350 if (!rIStm
.GetError())
352 maDataContainer
= std::move(aData
);
357 VectorGraphicData::~VectorGraphicData()
361 const basegfx::B2DRange
& VectorGraphicData::getRange() const
363 const_cast< VectorGraphicData
* >(this)->ensureSequenceAndRange();
368 const std::deque
< css::uno::Reference
< css::graphic::XPrimitive2D
> >& VectorGraphicData::getPrimitive2DSequence() const
370 const_cast< VectorGraphicData
* >(this)->ensureSequenceAndRange();
375 const BitmapEx
& VectorGraphicData::getReplacement() const
377 const_cast< VectorGraphicData
* >(this)->ensureReplacement();
379 return maReplacement
;
382 BitmapChecksum
VectorGraphicData::GetChecksum() const
384 return rtl_crc32(0, maDataContainer
.getData(), maDataContainer
.getSize());
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */