Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/xmloff/source/draw/XMLImageMapExport.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 <XMLImageMapExport.hxx>
21
#include <o3tl/any.hxx>
22
#include <rtl/ustring.hxx>
23
#include <rtl/ustrbuf.hxx>
24
#include <tools/debug.hxx>
25
#include <com/sun/star/uno/Reference.h>
26
#include <com/sun/star/uno/Sequence.h>
27
#include <com/sun/star/beans/XPropertySet.hpp>
28
#include <com/sun/star/lang/XServiceInfo.hpp>
29
#include <com/sun/star/container/XIndexContainer.hpp>
30
#include <com/sun/star/document/XEventsSupplier.hpp>
31
#include <com/sun/star/awt/Rectangle.hpp>
32
#include <com/sun/star/awt/Point.hpp>
33
#include <com/sun/star/drawing/PointSequence.hpp>
34
#include <xmloff/xmlexp.hxx>
35
#include <xmloff/xmlnamespace.hxx>
36
#include <xmloff/xmltoken.hxx>
37
#include <xmloff/XMLEventExport.hxx>
38
#include <xmloff/xmluconv.hxx>
39
#include <xexptran.hxx>
40
#include <basegfx/polygon/b2dpolygon.hxx>
41
#include <basegfx/polygon/b2dpolygontools.hxx>
42
43
using namespace ::com::sun::star;
44
using namespace ::xmloff::token;
45
46
using ::com::sun::star::uno::Any;
47
using ::com::sun::star::uno::UNO_QUERY;
48
using ::com::sun::star::uno::Sequence;
49
using ::com::sun::star::uno::Reference;
50
using ::com::sun::star::beans::XPropertySet;
51
using ::com::sun::star::container::XIndexContainer;
52
using ::com::sun::star::document::XEventsSupplier;
53
using ::com::sun::star::lang::XServiceInfo;
54
using ::com::sun::star::drawing::PointSequence;
55
56
constexpr OUStringLiteral gsBoundary(u"Boundary");
57
constexpr OUStringLiteral gsCenter(u"Center");
58
constexpr OUStringLiteral gsDescription(u"Description");
59
constexpr OUString gsImageMap(u"ImageMap"_ustr);
60
constexpr OUStringLiteral gsIsActive(u"IsActive");
61
constexpr OUStringLiteral gsName(u"Name");
62
constexpr OUStringLiteral gsPolygon(u"Polygon");
63
constexpr OUStringLiteral gsRadius(u"Radius");
64
constexpr OUStringLiteral gsTarget(u"Target");
65
constexpr OUStringLiteral gsURL(u"URL");
66
constexpr OUStringLiteral gsTitle(u"Title");
67
68
XMLImageMapExport::XMLImageMapExport(SvXMLExport& rExp) :
69
6
    mrExport(rExp)
70
6
{
71
6
}
72
73
void XMLImageMapExport::Export(
74
    const Reference<XPropertySet> & rPropertySet)
75
199
{
76
199
    if (rPropertySet->getPropertySetInfo()->hasPropertyByName(gsImageMap))
77
199
    {
78
199
        Any aAny = rPropertySet->getPropertyValue(gsImageMap);
79
199
        Reference<XIndexContainer> aContainer;
80
199
        aAny >>= aContainer;
81
82
199
        Export(aContainer);
83
199
    }
84
    // else: no ImageMap property -> nothing to do
85
199
}
86
87
void XMLImageMapExport::Export(
88
    const Reference<XIndexContainer> & rContainer)
89
199
{
90
199
    if (!rContainer.is())
91
0
        return;
92
93
199
    if (!rContainer->hasElements())
94
199
        return;
95
96
    // image map container element
97
0
    SvXMLElementExport aImageMapElement(
98
0
        mrExport, XML_NAMESPACE_DRAW, XML_IMAGE_MAP,
99
0
        true/*bWhiteSpace*/, true/*bWhiteSpace*/);
100
101
    // iterate over image map elements and call ExportMapEntry(...)
102
    // for each
103
0
    sal_Int32 nLength = rContainer->getCount();
104
0
    for(sal_Int32 i = 0; i < nLength; i++)
105
0
    {
106
0
        Any aAny = rContainer->getByIndex(i);
107
0
        Reference<XPropertySet> rElement;
108
0
        aAny >>= rElement;
109
110
0
        DBG_ASSERT(rElement.is(), "Image map element is empty!");
111
0
        if (rElement.is())
112
0
        {
113
0
            ExportMapEntry(rElement);
114
0
        }
115
0
    }
116
    // else: container is empty -> nothing to do
117
    // else: no container -> nothing to do
118
0
}
119
120
121
void XMLImageMapExport::ExportMapEntry(
122
    const Reference<XPropertySet> & rPropertySet)
123
0
{
124
0
    Reference<XServiceInfo> xServiceInfo(rPropertySet, UNO_QUERY);
125
0
    if (!xServiceInfo.is())
126
0
        return;
127
128
0
    enum XMLTokenEnum eType = XML_TOKEN_INVALID;
129
130
    // distinguish map entries by their service name
131
0
    const Sequence<OUString> sServiceNames =
132
0
        xServiceInfo->getSupportedServiceNames();
133
0
    for( const OUString& rName : sServiceNames )
134
0
    {
135
0
        if ( rName == "com.sun.star.image.ImageMapRectangleObject" )
136
0
        {
137
0
            eType = XML_AREA_RECTANGLE;
138
0
            break;
139
0
        }
140
0
        else if ( rName == "com.sun.star.image.ImageMapCircleObject" )
141
0
        {
142
0
            eType = XML_AREA_CIRCLE;
143
0
            break;
144
0
        }
145
0
        else if ( rName == "com.sun.star.image.ImageMapPolygonObject" )
146
0
        {
147
0
            eType = XML_AREA_POLYGON;
148
0
            break;
149
0
        }
150
0
    }
151
152
    // return from method if no proper service is found!
153
0
    DBG_ASSERT(XML_TOKEN_INVALID != eType,
154
0
               "Image map element doesn't support appropriate service!");
155
0
    if (XML_TOKEN_INVALID == eType)
156
0
        return;
157
158
    // now: handle ImageMapObject properties (those for all types)
159
160
    // XLINK (URL property)
161
0
    Any aAny = rPropertySet->getPropertyValue(gsURL);
162
0
    OUString sHref;
163
0
    aAny >>= sHref;
164
0
    if (!sHref.isEmpty())
165
0
    {
166
0
        mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, mrExport.GetRelativeReference(sHref));
167
0
    }
168
0
    mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
169
170
    // Target property (and xlink:show)
171
0
    aAny = rPropertySet->getPropertyValue(gsTarget);
172
0
    OUString sTargt;
173
0
    aAny >>= sTargt;
174
0
    if (!sTargt.isEmpty())
175
0
    {
176
0
        mrExport.AddAttribute(
177
0
            XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, sTargt);
178
179
0
        mrExport.AddAttribute(
180
0
            XML_NAMESPACE_XLINK, XML_SHOW,
181
0
            sTargt == "_blank" ? XML_NEW : XML_REPLACE );
182
0
    }
183
184
    // name
185
0
    aAny = rPropertySet->getPropertyValue(gsName);
186
0
    OUString sItemName;
187
0
    aAny >>= sItemName;
188
0
    if (!sItemName.isEmpty())
189
0
    {
190
0
        mrExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_NAME, sItemName);
191
0
    }
192
193
    // is-active
194
0
    aAny = rPropertySet->getPropertyValue(gsIsActive);
195
0
    if (! *o3tl::doAccess<bool>(aAny))
196
0
    {
197
0
        mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_NOHREF, XML_NOHREF);
198
0
    }
199
200
    // call specific rectangle/circle/... method
201
    // also prepare element name
202
0
    switch (eType)
203
0
    {
204
0
        case XML_AREA_RECTANGLE:
205
0
            ExportRectangle(rPropertySet);
206
0
            break;
207
0
        case XML_AREA_CIRCLE:
208
0
            ExportCircle(rPropertySet);
209
0
            break;
210
0
        case XML_AREA_POLYGON:
211
0
            ExportPolygon(rPropertySet);
212
0
            break;
213
0
        default:
214
0
            break;
215
0
    }
216
217
    // write element
218
0
    DBG_ASSERT(XML_TOKEN_INVALID != eType,
219
0
               "No name?! How did this happen?");
220
0
    SvXMLElementExport aAreaElement(mrExport, XML_NAMESPACE_DRAW, eType,
221
0
                                    true/*bWhiteSpace*/, true/*bWhiteSpace*/);
222
223
    // title property (as <svg:title> element)
224
0
    OUString sTitle;
225
0
    rPropertySet->getPropertyValue(gsTitle) >>= sTitle;
226
0
    if(!sTitle.isEmpty())
227
0
    {
228
0
        SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SVG, XML_TITLE, true/*bWhiteSpace*/, false);
229
0
        mrExport.Characters(sTitle);
230
0
    }
231
232
    // description property (as <svg:desc> element)
233
0
    OUString sDescription;
234
0
    rPropertySet->getPropertyValue(gsDescription) >>= sDescription;
235
0
    if (!sDescription.isEmpty())
236
0
    {
237
0
        SvXMLElementExport aDesc(mrExport, XML_NAMESPACE_SVG, XML_DESC, true/*bWhiteSpace*/, false);
238
0
        mrExport.Characters(sDescription);
239
0
    }
240
241
    // export events attached to this
242
0
    Reference<XEventsSupplier> xSupplier(rPropertySet, UNO_QUERY);
243
0
    mrExport.GetEventExport().Export(xSupplier);
244
245
    // else: no service info -> can't determine type -> ignore entry
246
0
}
247
248
void XMLImageMapExport::ExportRectangle(
249
    const Reference<XPropertySet> & rPropertySet)
250
0
{
251
    // get boundary rectangle
252
0
    Any aAny = rPropertySet->getPropertyValue(gsBoundary);
253
0
    awt::Rectangle aRectangle;
254
0
    aAny >>= aRectangle;
255
256
    // parameters svg:x, svg:y, svg:width, svg:height
257
0
    OUStringBuffer aBuffer;
258
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, aRectangle.X);
259
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X,
260
0
                          aBuffer.makeStringAndClear() );
261
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, aRectangle.Y);
262
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y,
263
0
                          aBuffer.makeStringAndClear() );
264
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer,
265
0
            aRectangle.Width);
266
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,
267
0
                          aBuffer.makeStringAndClear() );
268
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer,
269
0
            aRectangle.Height);
270
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT,
271
0
                          aBuffer.makeStringAndClear() );
272
0
}
273
274
void XMLImageMapExport::ExportCircle(
275
    const Reference<XPropertySet> & rPropertySet)
276
0
{
277
    // get boundary rectangle
278
0
    Any aAny = rPropertySet->getPropertyValue(gsCenter);
279
0
    awt::Point aCenter;
280
0
    aAny >>= aCenter;
281
282
    // parameters svg:cx, svg:cy
283
0
    OUStringBuffer aBuffer;
284
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, aCenter.X);
285
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_CX,
286
0
                          aBuffer.makeStringAndClear() );
287
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, aCenter.Y);
288
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_CY,
289
0
                          aBuffer.makeStringAndClear() );
290
291
    // radius
292
0
    aAny = rPropertySet->getPropertyValue(gsRadius);
293
0
    sal_Int32 nRadius = 0;
294
0
    aAny >>= nRadius;
295
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, nRadius);
296
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_R,
297
0
                          aBuffer.makeStringAndClear() );
298
0
}
299
300
void XMLImageMapExport::ExportPolygon(const Reference<XPropertySet> & rPropertySet)
301
0
{
302
    // polygons get exported as bounding box, viewbox, and coordinate
303
    // pair sequence. The bounding box is always the entire image.
304
305
    // get polygon point sequence
306
0
    Any aAny = rPropertySet->getPropertyValue(gsPolygon);
307
0
    PointSequence aPoly;
308
0
    aAny >>= aPoly;
309
310
0
    const basegfx::B2DPolygon aPolygon(
311
0
        basegfx::utils::UnoPointSequenceToB2DPolygon(
312
0
            aPoly));
313
0
    const basegfx::B2DRange aPolygonRange(aPolygon.getB2DRange());
314
315
    // parameters svg:x, svg:y, svg:width, svg:height
316
0
    OUStringBuffer aBuffer;
317
318
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, 0);
319
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, aBuffer.makeStringAndClear() );
320
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, 0);
321
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, aBuffer.makeStringAndClear() );
322
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, basegfx::fround(aPolygonRange.getWidth()));
323
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH, aBuffer.makeStringAndClear() );
324
0
    mrExport.GetMM100UnitConverter().convertMeasureToXML(aBuffer, basegfx::fround(aPolygonRange.getHeight()));
325
0
    mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT, aBuffer.makeStringAndClear() );
326
327
    // svg:viewbox
328
0
    SdXMLImExViewBox aViewBox(0.0, 0.0, aPolygonRange.getWidth(), aPolygonRange.getHeight());
329
0
    mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString());
330
331
    // export point sequence
332
0
    const OUString aPointString(
333
0
        basegfx::utils::exportToSvgPoints(
334
0
            aPolygon));
335
336
0
    mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_POINTS, aPointString);
337
0
}
338
339
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */