summaryrefslogtreecommitdiffstats
path: root/oox/qa/unit/wpc_drawing_canvas.cxx
blob: eadd1a26446b4b5fce5fe568fb07a5fbdf824185 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/* -*- 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 <test/unoapixml_test.hxx>

#include <docmodel/color/ComplexColor.hxx>
#include <docmodel/uno/UnoComplexColor.hxx>
#include <editeng/unoprnms.hxx>

#include <com/sun/star/awt/Gradient2.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/drawing/ConnectorType.hpp>
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/util/XComplexColor.hpp>
using namespace ::com::sun::star;

namespace
{
/// The test suite covers tests for import of Word drawing canvas (wpc), available since LO 24.2.
/// Before its implementation the VML fallback was used. That lost properties because VML is not able
/// to describe them or the VML import of LO has deficits.
class TestWPC : public UnoApiXmlTest
{
public:
    TestWPC()
        : UnoApiXmlTest("/oox/qa/unit/data/")
    {
    }
};

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_Table_inside_Textbox)
{
    // The document has a table inside a text box on a drawing canvas.
    loadFromFile(u"WPC_tdf48610_Textbox_with_table_inside.docx");

    // Make sure the table exists. Without import of drawing canvas, the table was lost.
    uno::Reference<text::XTextTablesSupplier> xTextDocument(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTextDocument->getTextTables(), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_Text_in_ellipse)
{
    // The document has text in an ellipse on a drawing canvas.
    loadFromFile(u"WPC_Textwrap_in_ellipse.docx");

    // The VML import creates for an ellipse not a custom shape but a legacy ellipse and that has no
    // word wrap. Thus the text was in one line and overflows the shape. This overflow becomes visible
    // in the bounding box. Without fix the rectangle width was 9398 Hmm.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    // getByIndex(0) gives the background shape, the ellipse is at index 1
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
    awt::Rectangle aBoundRect;
    xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
    // The tolerance 10 is estimated and can be adjusted if required for HiDPI.
    CPPUNIT_ASSERT_DOUBLES_EQUAL(4740, aBoundRect.Width, 10);
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_MulticolorGradient)
{
    // The document has a shape with multi color gradient fill on a drawing canvas.
    loadFromFile(u"WPC_MulticolorGradient.docx");

    // The VML import was not able to import multicolor gradients. Thus only start and end color
    // were imported, ColorStops had only two elements.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
    awt::Gradient2 aGradient;
    xShapeProps->getPropertyValue(UNO_NAME_FILLGRADIENT) >>= aGradient;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aGradient.ColorStops.getLength());
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_CanvasBackground)
{
    // The document has a drawing canvas with color fill.
    loadFromFile(u"WPC_CanvasBackground.docx");

    // The VML import displayed the background as if it was transparent. Thus the BoundRect
    // of the shape which represents the background was zero.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(0), uno::UNO_QUERY);
    awt::Rectangle aBoundRect;
    xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
    CPPUNIT_ASSERT(aBoundRect.Width > 0);
    CPPUNIT_ASSERT(aBoundRect.Height > 0);
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_Glow)
{
    // The document has a shape with glow effect.
    loadFromFile(u"WPC_Glow.docx");

    // VML does not know any glow effect. Thus it was lost on import.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);

    // Check glow properties
    sal_Int32 nGlowEffectRad = 0;
    xShapeProps->getPropertyValue(u"GlowEffectRadius"_ustr) >>= nGlowEffectRad;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(564), nGlowEffectRad); // 16 pt = 564.444... mm/100
    Color nGlowEffectColor;
    xShapeProps->getPropertyValue(u"GlowEffectColor"_ustr) >>= nGlowEffectColor;
    CPPUNIT_ASSERT_EQUAL(Color(0xFFFF00), nGlowEffectColor); // "Yellow"
    sal_Int16 nGlowEffectTransparency = 0;
    xShapeProps->getPropertyValue(u"GlowEffectTransparency"_ustr) >>= nGlowEffectTransparency;
    CPPUNIT_ASSERT_EQUAL(sal_Int16(10), nGlowEffectTransparency); // 10%
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_BentConnector)
{
    // The document has two shapes connected with a bentConnector on a drawing canvas.
    loadFromFile(u"WPC_BentConnector.docx");

    // VML has no information about the target shapes of the connector. The connector was imported as
    // custom shape, not as connector shape
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<lang::XServiceInfo> xInfo(xGroup->getByIndex(2), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInfo->supportsService("com.sun.star.drawing.ConnectorShape"));

    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(2), uno::UNO_QUERY);
    com::sun::star::drawing::ConnectorType eEdgeKind;
    xShapeProps->getPropertyValue(UNO_NAME_EDGEKIND) >>= eEdgeKind;
    CPPUNIT_ASSERT_EQUAL(drawing::ConnectorType::ConnectorType_STANDARD, eEdgeKind);

    sal_Int32 nEdgeLineDelta;
    xShapeProps->getPropertyValue(UNO_NAME_EDGELINE1DELTA) >>= nEdgeLineDelta;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(-635), nEdgeLineDelta);
    xShapeProps->getPropertyValue(UNO_NAME_EDGELINE2DELTA) >>= nEdgeLineDelta;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1949), nEdgeLineDelta);
    xShapeProps->getPropertyValue(UNO_NAME_EDGELINE3DELTA) >>= nEdgeLineDelta;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(887), nEdgeLineDelta);
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_ThemeColor)
{
    // The document has a shape with color fill used as pseudo background and a 'heart' shape with
    // color fill and colored line. All colors are theme colors.
    loadFromFile(u"WPC_ThemeColor.docx");

    // VML has no information about theme colors. Thus ThemeColorType was always 'Unknown'.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);

    // Check color of shape used for pseudo background
    {
        uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(0), uno::UNO_QUERY);
        uno::Reference<util::XComplexColor> xComplexColor;
        CPPUNIT_ASSERT(xShapeProps->getPropertyValue(UNO_NAME_FILL_COMPLEX_COLOR)
                       >>= xComplexColor);
        CPPUNIT_ASSERT(xComplexColor.is());
        auto aComplexColor = model::color::getFromXComplexColor(xComplexColor);
        CPPUNIT_ASSERT_EQUAL(model::ThemeColorType::Dark2, aComplexColor.getThemeColorType());
        {
            auto const& rTrans = aComplexColor.getTransformations();
            CPPUNIT_ASSERT_EQUAL(size_t(2), rTrans.size());
            CPPUNIT_ASSERT_EQUAL(model::TransformationType::LumMod, rTrans[0].meType);
            CPPUNIT_ASSERT_EQUAL(sal_Int16(7500), rTrans[1].mnValue);
            CPPUNIT_ASSERT_EQUAL(model::TransformationType::LumOff, rTrans[1].meType);
            CPPUNIT_ASSERT_EQUAL(sal_Int16(2500), rTrans[0].mnValue);
        }
    }
    // Check colors of 'heart' shape
    {
        uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
        uno::Reference<util::XComplexColor> xComplexColor;
        CPPUNIT_ASSERT(xShapeProps->getPropertyValue(UNO_NAME_FILL_COMPLEX_COLOR)
                       >>= xComplexColor);
        CPPUNIT_ASSERT(xComplexColor.is());
        auto aComplexColor = model::color::getFromXComplexColor(xComplexColor);
        CPPUNIT_ASSERT_EQUAL(model::ThemeColorType::Accent5, aComplexColor.getThemeColorType());
    }
    {
        uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
        uno::Reference<util::XComplexColor> xComplexColor;
        CPPUNIT_ASSERT(xShapeProps->getPropertyValue(UNO_NAME_LINE_COMPLEX_COLOR)
                       >>= xComplexColor);
        CPPUNIT_ASSERT(xComplexColor.is());
        auto aComplexColor = model::color::getFromXComplexColor(xComplexColor);
        CPPUNIT_ASSERT_EQUAL(model::ThemeColorType::Accent4, aComplexColor.getThemeColorType());
    }
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_tdf104671_Cloud)
{
    // The document has 'cloud' shape on a drawing canvas.
    loadFromFile(u"WPC_tdf104671_Cloud.docx");

    // MS Office writes the 'cloud' shape without type to the VML fallback. Thus the VLM import uses
    // ClosedBezierShape with several closed polygons. That produces holes because of the even-odd
    // rule, and inner lines. The fix uses the mc:Choice alternative which provides the type for a
    // custom shape.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<lang::XServiceInfo> xInfo(xGroup->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInfo->supportsService("com.sun.star.drawing.CustomShape"));
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_Shadow)
{
    // The document has a shape with blur shadow on a drawing canvas.
    loadFromFile(u"WPC_Shadow.docx");

    // The VML fallback contains a block shadow. Blur is not available in VML. The VML import does not
    // import shadow at all.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
    bool bHasShadow = false;
    xShapeProps->getPropertyValue(UNO_NAME_SHADOW) >>= bHasShadow;
    CPPUNIT_ASSERT(bHasShadow);
    sal_Int32 nValue;
    xShapeProps->getPropertyValue(UNO_NAME_SHADOWBLUR) >>= nValue;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(282), nValue);
    xShapeProps->getPropertyValue(UNO_NAME_SHADOWXDIST) >>= nValue;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(224), nValue);
    xShapeProps->getPropertyValue(UNO_NAME_SHADOWYDIST) >>= nValue;
    CPPUNIT_ASSERT_EQUAL(sal_Int32(224), nValue);
    Color nColor;
    xShapeProps->getPropertyValue(UNO_NAME_SHADOWCOLOR) >>= nColor;
    CPPUNIT_ASSERT_EQUAL(Color(0x808080), nColor);
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_tdf158339_shape_text_in_group)
{
    // The document has a group of two shapes with text. This group is child of a drawing canvas.
    // Without fix the text of the shapes were imported as separate text boxes.
    loadFromFile(u"WPC_tdf158339_shape_text_in_group.docx");

    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    // Make sure there is only one object on that page. Without fix there were three objects.
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xDrawPage->getCount());
    // Get the group which represents the drawing canvas and the group object inside.
    uno::Reference<drawing::XShapes> xCanvas(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xCanvas->getByIndex(1), uno::UNO_QUERY);
    // Get the properties of the second shape inside the group
    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(1), uno::UNO_QUERY);
    // and make sure the shape has text.
    uno::Reference<css::text::XTextFrame> xTextFrame;
    xShapeProps->getPropertyValue(u"TextBoxContent"_ustr) >>= xTextFrame;
    CPPUNIT_ASSERT(xTextFrame.is());
    CPPUNIT_ASSERT_EQUAL(OUString("Group"), xTextFrame->getText()->getString());
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_tdf158348_shape_text_in_table_cell)
{
    // The document has a shape with text on a drawing canvas in a table cell.
    // Without fix the text of the shape becomes part of the paragraph of the table cell.
    loadFromFile(u"WPC_tdf158348_shape_text_in_table_cell.docx");

    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);

    // Get the shape and make sure it has text.
    uno::Reference<drawing::XShapes> xCanvas(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShapeProps(xCanvas->getByIndex(1), uno::UNO_QUERY);
    uno::Reference<css::text::XTextFrame> xTextFrame;
    xShapeProps->getPropertyValue(u"TextBoxContent"_ustr) >>= xTextFrame;
    CPPUNIT_ASSERT(xTextFrame.is());
    // The string was empty without fix.
    CPPUNIT_ASSERT_EQUAL(u"Inside shape"_ustr, xTextFrame->getText()->getString());

    // Get the table and make sure the cell has only its own text.
    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(
        xTablesSupplier->getTextTables()->getByName(u"Table1"_ustr), uno::UNO_QUERY);
    uno::Reference<text::XTextRange> xCellA1(xTextTable->getCellByName("A1"), uno::UNO_QUERY);
    // The string had started with "Inside shape" without fix.
    CPPUNIT_ASSERT(xCellA1->getString().startsWith("Inside table"));
}

CPPUNIT_TEST_FIXTURE(TestWPC, WPC_CurvedConnector2)
{
    // The document has two shapes connected with a curvedConnector2 on a drawing canvas.
    // This connector is a single Bezier segment without handles.
    loadFromFile(u"WPC_CurvedConnector2.docx");

    // LO and OOXML differ in the position of the control points. LibreOffice uses 2/3 but OOXML
    // uses 1/2 of width or height. The path by LO looks more round.
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
                                                 uno::UNO_QUERY);
    uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<lang::XServiceInfo> xInfo(xGroup->getByIndex(3), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInfo->supportsService("com.sun.star.drawing.ConnectorShape"));

    uno::Reference<beans::XPropertySet> xShapeProps(xGroup->getByIndex(3), uno::UNO_QUERY);
    com::sun::star::drawing::ConnectorType eEdgeKind;
    xShapeProps->getPropertyValue(UNO_NAME_EDGEKIND) >>= eEdgeKind;
    CPPUNIT_ASSERT_EQUAL(drawing::ConnectorType::ConnectorType_CURVE, eEdgeKind);

    // Make sure the path is OOXML compatible
    drawing::PolyPolygonBezierCoords aPolyPolygonBezierCoords;
    xShapeProps->getPropertyValue("PolyPolygonBezier") >>= aPolyPolygonBezierCoords;
    drawing::PointSequence aPolygon = aPolyPolygonBezierCoords.Coordinates[0];
    // First control point. LO routing would generate point (4372|5584).
    CPPUNIT_ASSERT_EQUAL(sal_Int32(5149), aPolygon[1].Y);
    // Second control point. LO routing would generate point (5887|6458).
    CPPUNIT_ASSERT_EQUAL(sal_Int32(6645), aPolygon[2].X);
}
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */