Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/vectorgraphicdata.cxx
Line
Count
Source
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 <comphelper/diagnose_ex.hxx>
21
#include <tools/mapunit.hxx>
22
#include <tools/stream.hxx>
23
#include <sal/log.hxx>
24
#include <utility>
25
#include <vcl/vectorgraphicdata.hxx>
26
#include <comphelper/processfactory.hxx>
27
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
28
#include <com/sun/star/graphic/PdfTools.hpp>
29
#include <com/sun/star/graphic/SvgTools.hpp>
30
#include <com/sun/star/graphic/EmfTools.hpp>
31
#include <com/sun/star/graphic/Primitive2DTools.hpp>
32
#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
33
#include <com/sun/star/util/XAccounting.hpp>
34
#include <com/sun/star/util/XBinaryDataContainer.hpp>
35
#include <basegfx/matrix/b2dhommatrixtools.hxx>
36
#include <vcl/canvastools.hxx>
37
#include <comphelper/seqstream.hxx>
38
#include <comphelper/sequence.hxx>
39
#include <comphelper/propertysequence.hxx>
40
#include <comphelper/propertyvalue.hxx>
41
#include <pdf/PdfConfig.hxx>
42
#include <rtl/crc.h>
43
#include <vcl/svapp.hxx>
44
#include <vcl/outdev.hxx>
45
#include <vcl/wmfexternal.hxx>
46
#include <vcl/pdfread.hxx>
47
#include <unotools/streamwrap.hxx>
48
#include <graphic/UnoBinaryDataContainer.hxx>
49
50
using namespace ::com::sun::star;
51
52
Bitmap convertPrimitive2DSequenceToBitmap(
53
    const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
54
    const basegfx::B2DRange& rTargetRange,
55
    const sal_uInt32 nMaximumQuadraticPixels,
56
    const o3tl::Length eTargetUnit,
57
    const std::optional<Size>& rTargetDPI)
58
1.38k
{
59
1.38k
    Bitmap aRetval;
60
61
1.38k
    if(!rSequence.empty())
62
1.38k
    {
63
        // create replacement graphic from maSequence
64
        // create XPrimitive2DRenderer
65
1.38k
        try
66
1.38k
        {
67
1.38k
            const uno::Reference< uno::XComponentContext >& xContext(::comphelper::getProcessComponentContext());
68
1.38k
            const uno::Reference< graphic::XPrimitive2DRenderer > xPrimitive2DRenderer = graphic::Primitive2DTools::create(xContext);
69
70
1.38k
            uno::Sequence< beans::PropertyValue > aViewParameters = {
71
1.38k
                comphelper::makePropertyValue(u"RangeUnit"_ustr, static_cast<sal_Int32>(eTargetUnit)),
72
1.38k
            };
73
1.38k
            geometry::RealRectangle2D aRealRect;
74
75
1.38k
            aRealRect.X1 = rTargetRange.getMinX();
76
1.38k
            aRealRect.Y1 = rTargetRange.getMinY();
77
1.38k
            aRealRect.X2 = rTargetRange.getMaxX();
78
1.38k
            aRealRect.Y2 = rTargetRange.getMaxY();
79
80
            // get system DPI
81
1.38k
            Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
82
1.38k
            if (rTargetDPI.has_value())
83
0
            {
84
0
                aDPI = *rTargetDPI;
85
0
            }
86
87
1.38k
            const uno::Reference< rendering::XBitmap > xBitmap(
88
1.38k
                xPrimitive2DRenderer->rasterize(
89
1.38k
                    comphelper::containerToSequence(rSequence),
90
1.38k
                    aViewParameters,
91
1.38k
                    aDPI.getWidth(),
92
1.38k
                    aDPI.getHeight(),
93
1.38k
                    aRealRect,
94
1.38k
                    nMaximumQuadraticPixels));
95
96
1.38k
            if(xBitmap.is())
97
4
            {
98
4
                const uno::Reference< rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW);
99
4
                aRetval = vcl::unotools::bitmapFromXBitmap(xIntBmp);
100
4
            }
101
1.38k
        }
102
1.38k
        catch (const uno::Exception&)
103
1.38k
        {
104
1.38k
            TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
105
1.38k
        }
106
1.38k
        catch (const std::exception& e)
107
1.38k
        {
108
0
            SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
109
0
        }
110
1.38k
    }
111
112
1.38k
    return aRetval;
113
1.38k
}
114
115
static size_t estimateSize(
116
    std::deque<uno::Reference<graphic::XPrimitive2D>> const& rSequence)
117
31.4k
{
118
31.4k
    size_t nRet(0);
119
31.4k
    for (auto& it : rSequence)
120
6.25k
    {
121
6.25k
        uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
122
6.25k
        assert(xAcc.is()); // we expect only BasePrimitive2D from SVG parser
123
6.25k
        nRet += xAcc->estimateUsage();
124
6.25k
    }
125
31.4k
    return nRet;
126
31.4k
}
127
128
bool VectorGraphicData::operator==(const VectorGraphicData& rCandidate) const
129
0
{
130
0
    if (getType() == rCandidate.getType())
131
0
    {
132
0
        if (maDataContainer.getSize() == rCandidate.maDataContainer.getSize())
133
0
        {
134
0
            if (0 == memcmp(
135
0
                maDataContainer.getData(),
136
0
                rCandidate.maDataContainer.getData(),
137
0
                maDataContainer.getSize()))
138
0
            {
139
0
                return true;
140
0
            }
141
0
        }
142
0
    }
143
144
0
    return false;
145
0
}
146
147
void VectorGraphicData::ensurePdfReplacement()
148
3.85k
{
149
3.85k
    assert(getType() == VectorGraphicDataType::Pdf);
150
151
3.85k
    if (!maReplacement.IsEmpty())
152
0
        return; // nothing to do
153
154
    // use PDFium directly
155
3.85k
    std::vector<Bitmap> aBitmaps;
156
3.85k
    sal_Int32 nUsePageIndex = 0;
157
3.85k
    if (mnPageIndex >= 0)
158
0
        nUsePageIndex = mnPageIndex;
159
3.85k
    vcl::RenderPDFBitmaps(maDataContainer.getData(),
160
3.85k
                          maDataContainer.getSize(), aBitmaps, nUsePageIndex, 1,
161
3.85k
                          &maSizeHint);
162
3.85k
    if (!aBitmaps.empty())
163
3.12k
        maReplacement = aBitmaps[0];
164
3.85k
}
165
166
void VectorGraphicData::ensureReplacement()
167
20.3k
{
168
20.3k
    if (!maReplacement.IsEmpty())
169
0
        return; // nothing to do
170
171
    // shortcut for PDF - PDFium can generate the replacement bitmap for us
172
    // directly
173
20.3k
    if (getType() == VectorGraphicDataType::Pdf)
174
3.85k
    {
175
3.85k
        ensurePdfReplacement();
176
3.85k
        return;
177
3.85k
    }
178
179
16.4k
    ensureSequenceAndRange();
180
181
16.4k
    if (!maSequence.empty())
182
1.38k
    {
183
1.38k
        maReplacement = convertPrimitive2DSequenceToBitmap(maSequence, getRange());
184
1.38k
    }
185
16.4k
}
186
187
Bitmap VectorGraphicData::getBitmap(const Size& pixelSize) const
188
0
{
189
0
    if (!maReplacement.IsEmpty() && maReplacement.GetSizePixel() == pixelSize)
190
0
        return maReplacement;
191
192
0
    if (getType() == VectorGraphicDataType::Pdf)
193
0
    {
194
        // use PDFium directly
195
0
        const sal_Int32 nUsePageIndex = mnPageIndex > 0 ? mnPageIndex : 0;
196
0
        const double dpi = vcl::pdf::getDefaultPdfResolutionDpi();
197
0
        basegfx::B2DTuple sizeMM100(
198
0
            o3tl::convert(pixelSize.Width() / dpi / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR, o3tl::Length::in, o3tl::Length::mm100),
199
0
            o3tl::convert(pixelSize.Height() / dpi / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR, o3tl::Length::in, o3tl::Length::mm100));
200
0
        std::vector<Bitmap> aBitmaps;
201
0
        vcl::RenderPDFBitmaps(maDataContainer.getData(), maDataContainer.getSize(), aBitmaps,
202
0
                              nUsePageIndex, 1, &sizeMM100);
203
0
        if (!aBitmaps.empty())
204
0
            return aBitmaps[0];
205
0
    }
206
207
0
    if (getPrimitive2DSequence().empty())
208
0
        return {};
209
210
0
    Size dpi(
211
0
        std::round(pixelSize.Width() / o3tl::convert(maRange.getWidth(), o3tl::Length::mm100, o3tl::Length::in)),
212
0
        std::round(pixelSize.Height() / o3tl::convert(maRange.getHeight(), o3tl::Length::mm100, o3tl::Length::in)));
213
0
    return convertPrimitive2DSequenceToBitmap(maSequence, maRange, 4096 * 4096, o3tl::Length::mm100, dpi);
214
0
}
215
216
void VectorGraphicData::ensureSequenceAndRange()
217
60.5k
{
218
60.5k
    if (mbSequenceCreated || maDataContainer.isEmpty())
219
29.0k
        return;
220
221
    // import SVG to maSequence, also set maRange
222
31.4k
    maRange.reset();
223
224
    // create Vector Graphic Data interpreter
225
31.4k
    const uno::Reference<uno::XComponentContext>& xContext(::comphelper::getProcessComponentContext());
226
227
31.4k
    switch (getType())
228
31.4k
    {
229
11.3k
        case VectorGraphicDataType::Svg:
230
11.3k
        {
231
11.3k
            const uno::Reference<io::XInputStream> xInputStream = maDataContainer.getAsXInputStream();
232
233
11.3k
            const uno::Reference< graphic::XSvgParser > xSvgParser = graphic::SvgTools::create(xContext);
234
235
11.3k
            if (xInputStream.is())
236
11.3k
                maSequence = comphelper::sequenceToContainer<std::deque<css::uno::Reference< css::graphic::XPrimitive2D >>>(xSvgParser->getDecomposition(xInputStream, OUString()));
237
238
11.3k
            break;
239
0
        }
240
18.8k
        case VectorGraphicDataType::Emf:
241
20.0k
        case VectorGraphicDataType::Wmf:
242
20.0k
        {
243
20.0k
            const uno::Reference< graphic::XEmfParser > xEmfParser = graphic::EmfTools::create(xContext);
244
245
20.0k
            const uno::Reference<io::XInputStream> xInputStream = maDataContainer.getAsXInputStream();
246
247
20.0k
            if (xInputStream.is())
248
20.0k
            {
249
20.0k
                uno::Sequence< ::beans::PropertyValue > aPropertySequence;
250
251
                // Pass the size hint of the graphic to the EMF parser.
252
20.0k
                geometry::RealPoint2D aSizeHint;
253
20.0k
                aSizeHint.X = maSizeHint.getX();
254
20.0k
                aSizeHint.Y = maSizeHint.getY();
255
20.0k
                xEmfParser->setSizeHint(aSizeHint);
256
257
20.0k
                if (!mbEnableEMFPlus)
258
0
                {
259
0
                    aPropertySequence = { comphelper::makePropertyValue(u"EMFPlusEnable"_ustr, uno::Any(false)) };
260
0
                }
261
262
20.0k
                maSequence = comphelper::sequenceToContainer<std::deque<css::uno::Reference< css::graphic::XPrimitive2D >>>(xEmfParser->getDecomposition(xInputStream, OUString(), aPropertySequence));
263
20.0k
            }
264
265
20.0k
            break;
266
18.8k
        }
267
0
        case VectorGraphicDataType::Pdf:
268
0
        {
269
0
            const uno::Reference<graphic::XPdfDecomposer> xPdfDecomposer = graphic::PdfTools::create(xContext);
270
0
            uno::Sequence<beans::PropertyValue> aDecompositionParameters = comphelper::InitPropertySequence({
271
0
                {"PageIndex", uno::Any(sal_Int32(mnPageIndex))},
272
0
            });
273
274
0
            rtl::Reference<UnoBinaryDataContainer> xDataContainer = new UnoBinaryDataContainer(getBinaryDataContainer());
275
276
0
            auto xPrimitive2D = xPdfDecomposer->getDecomposition(xDataContainer, aDecompositionParameters);
277
0
            maSequence = comphelper::sequenceToContainer<std::deque<uno::Reference<graphic::XPrimitive2D>>>(xPrimitive2D);
278
279
0
            break;
280
18.8k
        }
281
31.4k
    }
282
283
31.4k
    if(!maSequence.empty())
284
6.25k
    {
285
6.25k
        const sal_Int32 nCount(maSequence.size());
286
6.25k
        geometry::RealRectangle2D aRealRect;
287
6.25k
        uno::Sequence< beans::PropertyValue > aViewParameters;
288
289
12.5k
        for(sal_Int32 a(0); a < nCount; a++)
290
6.25k
        {
291
            // get reference
292
6.25k
            const css::uno::Reference< css::graphic::XPrimitive2D > xReference(maSequence[a]);
293
294
6.25k
            if(xReference.is())
295
6.25k
            {
296
6.25k
                aRealRect = xReference->getRange(aViewParameters);
297
298
6.25k
                maRange.expand(
299
6.25k
                    basegfx::B2DRange(
300
6.25k
                        aRealRect.X1,
301
6.25k
                        aRealRect.Y1,
302
6.25k
                        aRealRect.X2,
303
6.25k
                        aRealRect.Y2));
304
6.25k
            }
305
6.25k
        }
306
6.25k
    }
307
31.4k
    mNestedBitmapSize = estimateSize(maSequence);
308
31.4k
    mbSequenceCreated = true;
309
31.4k
}
310
311
std::pair<VectorGraphicData::State, size_t> VectorGraphicData::getSizeBytes() const
312
48.3k
{
313
48.3k
    if (!maSequence.empty() && !maDataContainer.isEmpty())
314
0
    {
315
0
        return std::make_pair(State::PARSED, maDataContainer.getSize() + mNestedBitmapSize);
316
0
    }
317
48.3k
    else
318
48.3k
    {
319
48.3k
        return std::make_pair(State::UNPARSED, maDataContainer.getSize());
320
48.3k
    }
321
48.3k
}
322
323
VectorGraphicData::VectorGraphicData(BinaryDataContainer aDataContainer, VectorGraphicDataType eVectorDataType, sal_Int32 nPageIndex)
324
35.7k
:   maDataContainer(std::move(aDataContainer)),
325
35.7k
    mbSequenceCreated(false),
326
35.7k
    mNestedBitmapSize(0),
327
35.7k
    meType(eVectorDataType),
328
35.7k
    mnPageIndex(nPageIndex)
329
35.7k
{
330
35.7k
}
331
332
VectorGraphicData::~VectorGraphicData()
333
35.7k
{
334
35.7k
}
335
336
const basegfx::B2DRange& VectorGraphicData::getRange() const
337
23.6k
{
338
23.6k
    const_cast< VectorGraphicData* >(this)->ensureSequenceAndRange();
339
340
23.6k
    return maRange;
341
23.6k
}
342
343
const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& VectorGraphicData::getPrimitive2DSequence() const
344
20.4k
{
345
20.4k
    const_cast< VectorGraphicData* >(this)->ensureSequenceAndRange();
346
347
20.4k
    return maSequence;
348
20.4k
}
349
350
const Bitmap& VectorGraphicData::getReplacement() const
351
20.3k
{
352
20.3k
    const_cast< VectorGraphicData* >(this)->ensureReplacement();
353
354
20.3k
    return maReplacement;
355
20.3k
}
356
357
BitmapChecksum VectorGraphicData::GetChecksum() const
358
0
{
359
0
    return rtl_crc32(0, maDataContainer.getData(), maDataContainer.getSize());
360
0
}
361
362
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */