LibreOffice Module vcl (master)  1
vectorgraphicdata.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <tools/diagnose_ex.h>
21 #include <tools/stream.hxx>
22 #include <sal/log.hxx>
25 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
26 #include <com/sun/star/graphic/PdfTools.hpp>
27 #include <com/sun/star/graphic/SvgTools.hpp>
28 #include <com/sun/star/graphic/EmfTools.hpp>
29 #include <com/sun/star/graphic/Primitive2DTools.hpp>
30 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
31 #include <com/sun/star/util/XAccounting.hpp>
32 #include <com/sun/star/util/XBinaryDataContainer.hpp>
34 #include <vcl/canvastools.hxx>
35 #include <comphelper/seqstream.hxx>
36 #include <comphelper/sequence.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/outdev.hxx>
41 #include <vcl/wmfexternal.hxx>
42 #include <vcl/pdfread.hxx>
43 #include <unotools/streamwrap.hxx>
45 
46 using namespace ::com::sun::star;
47 
49  const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
50  const basegfx::B2DRange& rTargetRange,
51  const sal_uInt32 nMaximumQuadraticPixels,
52  const o3tl::Length eTargetUnit,
53  const std::optional<Size>& rTargetDPI)
54 {
55  BitmapEx aRetval;
56 
57  if(!rSequence.empty())
58  {
59  // create replacement graphic from maSequence
60  // create XPrimitive2DRenderer
61  try
62  {
63  uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
64  const uno::Reference< graphic::XPrimitive2DRenderer > xPrimitive2DRenderer = graphic::Primitive2DTools::create(xContext);
65 
66  uno::Sequence< beans::PropertyValue > aViewParameters = {
67  comphelper::makePropertyValue("RangeUnit", static_cast<sal_Int32>(eTargetUnit)),
68  };
69  geometry::RealRectangle2D aRealRect;
70 
71  aRealRect.X1 = rTargetRange.getMinX();
72  aRealRect.Y1 = rTargetRange.getMinY();
73  aRealRect.X2 = rTargetRange.getMaxX();
74  aRealRect.Y2 = rTargetRange.getMaxY();
75 
76  // get system DPI
77  Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
78  if (rTargetDPI.has_value())
79  {
80  aDPI = *rTargetDPI;
81  }
82 
83  const uno::Reference< rendering::XBitmap > xBitmap(
84  xPrimitive2DRenderer->rasterize(
86  aViewParameters,
87  aDPI.getWidth(),
88  aDPI.getHeight(),
89  aRealRect,
90  nMaximumQuadraticPixels));
91 
92  if(xBitmap.is())
93  {
94  const uno::Reference< rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW);
95  aRetval = vcl::unotools::bitmapExFromXBitmap(xIntBmp);
96  }
97  }
98  catch (const uno::Exception&)
99  {
100  TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
101  }
102  catch (const std::exception& e)
103  {
104  SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
105  }
106  }
107 
108  return aRetval;
109 }
110 
111 static size_t estimateSize(
112  std::deque<uno::Reference<graphic::XPrimitive2D>> const& rSequence)
113 {
114  size_t nRet(0);
115  for (auto& it : rSequence)
116  {
117  uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
118  assert(xAcc.is()); // we expect only BasePrimitive2D from SVG parser
119  nRet += xAcc->estimateUsage();
120  }
121  return nRet;
122 }
123 
125 {
126  if (getType() == rCandidate.getType())
127  {
128  if (maDataContainer.getSize() == rCandidate.maDataContainer.getSize())
129  {
130  if (0 == memcmp(
132  rCandidate.maDataContainer.getData(),
134  {
135  return true;
136  }
137  }
138  }
139 
140  return false;
141 }
142 
144 {
145  if (!mpExternalHeader)
146  {
147  mpExternalHeader.reset( new WmfExternal );
148  }
149 
150  *mpExternalHeader = aExtHeader;
151 }
152 
154 {
155  assert(getType() == VectorGraphicDataType::Pdf);
156 
157  if (!maReplacement.IsEmpty())
158  return; // nothing to do
159 
160  // use PDFium directly
161  std::vector<BitmapEx> aBitmaps;
162  sal_Int32 nUsePageIndex = 0;
163  if (mnPageIndex >= 0)
164  nUsePageIndex = mnPageIndex;
166  maDataContainer.getSize(), aBitmaps, nUsePageIndex, 1,
167  &maSizeHint);
168  if (!aBitmaps.empty())
169  maReplacement = aBitmaps[0];
170 }
171 
173 {
174  if (!maReplacement.IsEmpty())
175  return; // nothing to do
176 
177  // shortcut for PDF - PDFium can generate the replacement bitmap for us
178  // directly
180  {
182  return;
183  }
184 
186 
187  if (!maSequence.empty())
188  {
190  }
191 }
192 
194 {
196  return;
197 
198  // import SVG to maSequence, also set maRange
199  maRange.reset();
200 
201  // create Vector Graphic Data interpreter
202  uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
203 
204  switch (getType())
205  {
207  {
208  css::uno::Sequence<sal_Int8> aDataSequence(maDataContainer.getSize());
209  std::copy(maDataContainer.cbegin(), maDataContainer.cend(), aDataSequence.begin());
210  const uno::Reference<io::XInputStream> xInputStream(new comphelper::SequenceInputStream(aDataSequence));
211 
212 
213  const uno::Reference< graphic::XSvgParser > xSvgParser = graphic::SvgTools::create(xContext);
214 
215  if (xInputStream.is())
216  maSequence = comphelper::sequenceToContainer<std::deque<css::uno::Reference< css::graphic::XPrimitive2D >>>(xSvgParser->getDecomposition(xInputStream, OUString()));
217 
218  break;
219  }
222  {
223  const uno::Reference< graphic::XEmfParser > xEmfParser = graphic::EmfTools::create(xContext);
224 
225  css::uno::Sequence<sal_Int8> aDataSequence(maDataContainer.getSize());
226  std::copy(maDataContainer.cbegin(), maDataContainer.cend(), aDataSequence.begin());
227  const uno::Reference<io::XInputStream> xInputStream(new comphelper::SequenceInputStream(aDataSequence));
228 
229  uno::Sequence< ::beans::PropertyValue > aPropertySequence;
230 
231  if (mpExternalHeader)
232  {
233  aPropertySequence = mpExternalHeader->getSequence();
234  }
235 
236  if (xInputStream.is())
237  {
238  // Pass the size hint of the graphic to the EMF parser.
239  geometry::RealPoint2D aSizeHint;
240  aSizeHint.X = maSizeHint.getX();
241  aSizeHint.Y = maSizeHint.getY();
242  xEmfParser->setSizeHint(aSizeHint);
243 
244  if (!mbEnableEMFPlus)
245  {
246  auto aVector = comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aPropertySequence);
247  aVector.push_back(comphelper::makePropertyValue("EMFPlusEnable", uno::makeAny(false)));
248  aPropertySequence = comphelper::containerToSequence(aVector);
249  }
250 
251  maSequence = comphelper::sequenceToContainer<std::deque<css::uno::Reference< css::graphic::XPrimitive2D >>>(xEmfParser->getDecomposition(xInputStream, OUString(), aPropertySequence));
252  }
253 
254  break;
255  }
257  {
258  const uno::Reference<graphic::XPdfDecomposer> xPdfDecomposer = graphic::PdfTools::create(xContext);
259  uno::Sequence<beans::PropertyValue> aDecompositionParameters = comphelper::InitPropertySequence({
260  {"PageIndex", uno::makeAny<sal_Int32>(mnPageIndex)},
261  });
262 
264 
265  auto xPrimitive2D = xPdfDecomposer->getDecomposition(xDataContainer, aDecompositionParameters);
266  maSequence = comphelper::sequenceToContainer<std::deque<uno::Reference<graphic::XPrimitive2D>>>(xPrimitive2D);
267 
268  break;
269  }
270  }
271 
272  if(!maSequence.empty())
273  {
274  const sal_Int32 nCount(maSequence.size());
275  geometry::RealRectangle2D aRealRect;
276  uno::Sequence< beans::PropertyValue > aViewParameters;
277 
278  for(sal_Int32 a(0); a < nCount; a++)
279  {
280  // get reference
281  const css::uno::Reference< css::graphic::XPrimitive2D > xReference(maSequence[a]);
282 
283  if(xReference.is())
284  {
285  aRealRect = xReference->getRange(aViewParameters);
286 
287  maRange.expand(
289  aRealRect.X1,
290  aRealRect.Y1,
291  aRealRect.X2,
292  aRealRect.Y2));
293  }
294  }
295  }
297  mbSequenceCreated = true;
298 }
299 
300 std::pair<VectorGraphicData::State, size_t> VectorGraphicData::getSizeBytes() const
301 {
302  if (!maSequence.empty() && !maDataContainer.isEmpty())
303  {
304  return std::make_pair(State::PARSED, maDataContainer.getSize() + mNestedBitmapSize);
305  }
306  else
307  {
308  return std::make_pair(State::UNPARSED, maDataContainer.getSize());
309  }
310 }
311 
313  const BinaryDataContainer& rDataContainer,
314  VectorGraphicDataType eVectorDataType,
315  sal_Int32 nPageIndex)
316 : maDataContainer(rDataContainer),
317  mbSequenceCreated(false),
319  meType(eVectorDataType),
320  mnPageIndex(nPageIndex)
321 {
322 }
323 
325  const OUString& rPath,
326  VectorGraphicDataType eVectorDataType)
327 : mbSequenceCreated(false),
328  mNestedBitmapSize(0),
329  meType(eVectorDataType),
330  mnPageIndex(-1)
331 {
332  SvFileStream rIStm(rPath, StreamMode::STD_READ);
333  if(rIStm.GetError())
334  return;
335  const sal_uInt32 nStmLen(rIStm.remainingSize());
336  if (nStmLen)
337  {
338  VectorGraphicDataArray aVectorGraphicDataArray(nStmLen);
339  rIStm.ReadBytes(aVectorGraphicDataArray.begin(), nStmLen);
340 
341  if (!rIStm.GetError())
342  {
343  maDataContainer = BinaryDataContainer(reinterpret_cast<const sal_uInt8*>(aVectorGraphicDataArray.begin()), aVectorGraphicDataArray.getLength());
344  }
345  }
346 }
347 
349 {
350 }
351 
353 {
354  const_cast< VectorGraphicData* >(this)->ensureSequenceAndRange();
355 
356  return maRange;
357 }
358 
359 const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& VectorGraphicData::getPrimitive2DSequence() const
360 {
361  const_cast< VectorGraphicData* >(this)->ensureSequenceAndRange();
362 
363  return maSequence;
364 }
365 
367 {
368  const_cast< VectorGraphicData* >(this)->ensureReplacement();
369 
370  return maReplacement;
371 }
372 
374 {
376 }
377 
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const BitmapEx & getReplacement() const
VectorGraphicDataType
double getY() const
sal_uInt64 BitmapChecksum
Definition: checksum.hxx:30
void expand(const B2DTuple &rTuple)
sal_Int32 mnPageIndex
If the vector format has more pages this denotes which page to render.
size_t RenderPDFBitmaps(const void *pBuffer, int nSize, std::vector< BitmapEx > &rBitmaps, const size_t nFirstPage, int nPages, const basegfx::B2DTuple *pSizeHint)
Fills the rBitmaps vector with rendered pages.
Definition: pdfread.cxx:120
const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > & getPrimitive2DSequence() const
basegfx::B2DTuple maSizeHint
Useful for PDF, which is vector-based, but still rendered to a bitmap.
BitmapChecksum vcl_get_checksum(BitmapChecksum Checksum, const void *Data, sal_uInt32 DatLen)
Definition: checksum.hxx:72
const basegfx::B2DRange & getRange() const
data read and evtl. on demand creation
void setWmfExternalHeader(const WmfExternal &aExtHeader)
special: needed for emf/wmf, maybe replaced by scaling the result later (?)
const BinaryDataContainer & getBinaryDataContainer() const
data read
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XIntegerReadOnlyBitmap > &xInputBitmap)
double getMaxX() const
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1069
DstType sequenceToContainer(const css::uno::Sequence< SrcType > &i_Sequence)
ErrCode GetError() const
Container for the binary data, whose responsibility is to manage the make it as simple as possible to...
int nCount
BitmapChecksum GetChecksum() const
constexpr tools::Long getHeight() const
sal_uInt64 remainingSize()
double getMaxY() const
css::uno::Sequence< css::beans::PropertyValue > InitPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
basegfx::B2DRange maRange
std::pair< State, size_t > getSizeBytes() const
bool IsEmpty() const
Definition: BitmapEx.cxx:177
#define TOOLS_WARN_EXCEPTION(area, stream)
uno_Any a
bool operator==(const VectorGraphicData &rCandidate) const
compare op
const sal_uInt8 * getData() const
std::size_t ReadBytes(void *pData, std::size_t nSize)
css::uno::Sequence< sal_Int8 > VectorGraphicDataArray
std::unique_ptr< WmfExternal > mpExternalHeader
double getMinY() const
static size_t estimateSize(std::deque< uno::Reference< graphic::XPrimitive2D >> const &rSequence)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
VectorGraphicDataType meType
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
VectorGraphicData(const VectorGraphicData &)=delete
Reference< XComponentContext > getProcessComponentContext()
BitmapEx convertPrimitive2DSequenceToBitmapEx(const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > &rSequence, const basegfx::B2DRange &rTargetRange, const sal_uInt32 nMaximumQuadraticPixels, const o3tl::Length eTargetUnit, const std::optional< Size > &rTargetDPI)
#define SAL_WARN(area, stream)
double getX() const
const VectorGraphicDataType & getType() const
double getMinX() const
constexpr tools::Long getWidth() const
std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > maSequence
RedlineType meType
BinaryDataContainer maDataContainer