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 
10 #include <sal/config.h>
11 
12 #include <cstdlib>
13 
14 #include <test/bootstrapfixture.hxx>
15 #include <unotest/macros_test.hxx>
16 #include <rtl/ustring.hxx>
17 #include <editeng/unoprnms.hxx>
18 #include <basegfx/polygon/b2dpolypolygon.hxx>
19 #include <basegfx/polygon/b2dpolygon.hxx>
20 #include <basegfx/point/b2dpoint.hxx>
21 #include <sfx2/request.hxx>
22 #include <sfx2/viewfrm.hxx>
23 #include <sfx2/viewsh.hxx>
24 #include <svl/intitem.hxx>
25 #include <svx/EnhancedCustomShape2d.hxx>
26 #include <svx/extrusionbar.hxx>
27 #include <svx/svdoashp.hxx>
28 #include <svx/svdopath.hxx>
29 #include <svx/svdview.hxx>
30 #include <svx/svxids.hrc>
31 #include <svx/unoapi.hxx>
32 #include <unotools/mediadescriptor.hxx>
33 #include <unotools/tempfile.hxx>
34 
35 #include <cppunit/TestAssert.h>
36 
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
39 #include <com/sun/star/drawing/XDrawPage.hpp>
40 #include <com/sun/star/awt/Rectangle.hpp>
41 #include <com/sun/star/frame/Desktop.hpp>
42 #include <com/sun/star/frame/XStorable.hpp>
43 
44 using namespace ::com::sun::star;
45 
46 namespace
47 {
48 constexpr OUStringLiteral sDataDirectory(u"svx/qa/unit/data/");
49 
50 /// Tests for svx/source/customshapes/ code.
51 class CustomshapesTest : public test::BootstrapFixture, public unotest::MacrosTest
52 {
53 protected:
54     uno::Reference<lang::XComponent> mxComponent;
55     // get shape nShapeIndex from page 0
56     uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex);
57     sal_uInt8 countShapes();
58 
59 public:
setUp()60     virtual void setUp() override
61     {
62         test::BootstrapFixture::setUp();
63         mxDesktop.set(frame::Desktop::create(m_xContext));
64     }
65 
tearDown()66     virtual void tearDown() override
67     {
68         if (mxComponent.is())
69         {
70             mxComponent->dispose();
71         }
72         test::BootstrapFixture::tearDown();
73     }
74 };
75 
getShape(sal_uInt8 nShapeIndex)76 uno::Reference<drawing::XShape> CustomshapesTest::getShape(sal_uInt8 nShapeIndex)
77 {
78     uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
79                                                                    uno::UNO_QUERY_THROW);
80     CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
81     uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
82     uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
83     CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
84     uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY);
85     CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is());
86     return xShape;
87 }
88 
countShapes()89 sal_uInt8 CustomshapesTest::countShapes()
90 {
91     uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
92                                                                    uno::UNO_QUERY_THROW);
93     CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
94     uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
95     uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
96     CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
97     return xDrawPage->getCount();
98 }
99 
lcl_AssertRectEqualWithTolerance(std::string_view sInfo,const tools::Rectangle & rExpected,const tools::Rectangle & rActual,const sal_Int32 nTolerance)100 void lcl_AssertRectEqualWithTolerance(std::string_view sInfo, const tools::Rectangle& rExpected,
101                                       const tools::Rectangle& rActual, const sal_Int32 nTolerance)
102 {
103     // Left
104     OString sMsg = OString::Concat(sInfo) + " Left expected " + OString::number(rExpected.Left())
105                    + " actual " + OString::number(rActual.Left()) + " Tolerance "
106                    + OString::number(nTolerance);
107     CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
108                            std::abs(rExpected.Left() - rActual.Left()) <= nTolerance);
109 
110     // Top
111     sMsg = OString::Concat(sInfo) + " Top expected " + OString::number(rExpected.Top()) + " actual "
112            + OString::number(rActual.Top()) + " Tolerance " + OString::number(nTolerance);
113     CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(), std::abs(rExpected.Top() - rActual.Top()) <= nTolerance);
114 
115     // Width
116     sMsg = OString::Concat(sInfo) + " Width expected " + OString::number(rExpected.GetWidth())
117            + " actual " + OString::number(rActual.GetWidth()) + " Tolerance "
118            + OString::number(nTolerance);
119     CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
120                            std::abs(rExpected.GetWidth() - rActual.GetWidth()) <= nTolerance);
121 
122     // Height
123     sMsg = OString::Concat(sInfo) + " Height expected " + OString::number(rExpected.GetHeight())
124            + " actual " + OString::number(rActual.GetHeight()) + " Tolerance "
125            + OString::number(nTolerance);
126     CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
127                            std::abs(rExpected.GetHeight() - rActual.GetHeight()) <= nTolerance);
128 }
129 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf145245_ExtrusionPosition)130 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145245_ExtrusionPosition)
131 {
132     // The second parameter of the extrusion-depth property specifies how much of the extrusion
133     // lies before the shape. The file contains three shapes which have the values 0, 0.5 and 1.
134     // They are rotated around the x-axis so that the extrusion becomes visible. The extrusion
135     // depth itself is 5cm. Y-coordinate of shape is 6cm.
136 
137     // Load document
138     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf145245_ExtrusionPosition.odp";
139     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
140 
141     // The tolerance 40 is estimated and can be adjusted if required for HiDPI.
142     {
143         // First shape has extrusion behind the shape.
144         uno::Reference<drawing::XShape> xShape0(getShape(0));
145         SdrObjCustomShape& rSdrCustomShape(
146             static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape0)));
147         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
148         tools::Rectangle aExpected(Point(1000, 1000), Size(6002, 5001));
149         lcl_AssertRectEqualWithTolerance("Pos 0.0 extrusion", aExpected, aBoundRect, 40);
150     }
151     {
152         // Second shape has half of extrusion behind the shape.
153         uno::Reference<drawing::XShape> xShape(getShape(1));
154         SdrObjCustomShape& rSdrCustomShape(
155             static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
156         // Without the fix the height was 1 instead of 5001.
157         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
158         tools::Rectangle aExpected(Point(9000, 3500), Size(6002, 5001));
159         lcl_AssertRectEqualWithTolerance("Pos 0.5 extrusion", aExpected, aBoundRect, 40);
160     }
161     {
162         // Third shape has extrusion before the shape.
163         uno::Reference<drawing::XShape> xShape(getShape(2));
164         SdrObjCustomShape& rSdrCustomShape(
165             static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
166         // Without the fix the y-coordinate was 1000 instead of 6000.
167         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
168         tools::Rectangle aExpected(Point(18000, 6000), Size(6002, 5001));
169         lcl_AssertRectEqualWithTolerance("Pos 1.0 extrusion", aExpected, aBoundRect, 40);
170     }
171 }
172 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf145111_Fontwork_rendering_font_size)173 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_Fontwork_rendering_font_size)
174 {
175     // The tested position and height depend on dpi.
176     if (!IsDefaultDPI())
177         return;
178 
179     // tdf#144988 In case ScaleX is true in property TextPath, the rendering font size should be
180     // reduced in case any of the paragraphs would be longer as its sub-path. That was wrong, if
181     // the first paragraph was too long and the second would fit. It resulted in wrong position
182     // and height and overlapping characters.
183 
184     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf144988_Fontwork_FontSize.odp";
185     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
186     uno::Reference<drawing::XShape> xShape(getShape(0));
187     SdrObjCustomShape& rSdrCustomShape(
188         static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
189 
190     // Without the fix in place left|top, width x height was 1279|1279, 2815 x 2448.
191     // The expected values 1501|1777, 3941 x 1446 are only valid for 96dpi.
192     tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
193     tools::Rectangle aExpected(Point(1501, 1777), Size(3941, 1446));
194     lcl_AssertRectEqualWithTolerance("Wrong text rendering", aExpected, aBoundRect, 5);
195 }
196 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf145111_anchor_in_Fontwork)197 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_anchor_in_Fontwork)
198 {
199     // The tested positions depend on dpi.
200     if (!IsDefaultDPI())
201         return;
202 
203     // tdf#145004 In case ScaleX is true in property TextPath, SDRTEXTVERTADJUST is
204     // evaluated and should shift the Fontwork text. That did not work for
205     // 'Top-Left' and 'Bottom-Left'.
206 
207     // Load document
208     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf145111_TL_BL_Fontwork.odp";
209     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
210 
211     {
212         // First shape has anchor set to Top-Left, which shifts Fontwork text down.
213         uno::Reference<drawing::XShape> xShape(getShape(0));
214         SdrObjCustomShape& rSdrCustomShape(
215             static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
216 
217         // Without the fix in place top was 2295, but should be 2916 for 96dpi.
218         // Was 2184, should be 2886 for 120dpi.
219         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
220         CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(2916), aBoundRect.Top(), 5);
221     }
222     {
223         // Second shape has anchor set to Bottom-Left, which shifts Fontwork text up.
224         uno::Reference<drawing::XShape> xShape(getShape(1));
225         SdrObjCustomShape& rSdrCustomShape(
226             static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
227 
228         // Without the fix in place top was 10294, but should be 9519 for 96dpi.
229         // Was 10184, should be 9481 for 120dpi.
230         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
231         CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(9519), aBoundRect.Top(), 5);
232     }
233 }
234 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf145004_gap_by_ScaleX)235 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145004_gap_by_ScaleX)
236 {
237     if (!IsDefaultDPI())
238         return;
239     // tdf#145004 In case property ScaleX=true was set in property 'TextPath' an additional
240     // padding was added to the scaling factor. That results in a gap at start or/and end of
241     // the text. Such gap should not be there.
242 
243     // Load document and get shape. It is a custom shape from pptx import of a WordArt of
244     // kind 'Follow Path'.
245     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf145004_gap_by_ScaleX.pptx";
246     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
247     uno::Reference<drawing::XShape> xShape(getShape(0));
248     SdrObjCustomShape& rSdrCustomShape(
249         static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
250 
251     // Verify width. Without the fix in place the width was 8231, but should be 8496 for 96dpi.
252     // Was 8328, should be 8527 for 120dpi.
253     tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
254     CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(8496), aBoundRect.GetWidth(), 5);
255 }
256 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf141021ExtrusionNorth)257 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf141021ExtrusionNorth)
258 {
259     // tdf#141021 Setting extrusion direction in projection method 'perspective' to
260     // 'Extrusion North' had used a wrong origin for the ViewPoint and thus the
261     // side faces were wrong calculated.
262 
263     // Load document and get shape. It is a custom shape in 3D mode.
264     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf141021_ExtrusionNorth.odp";
265     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
266     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
267     uno::Reference<drawing::XShape> xShape(getShape(0));
268     SdrObjCustomShape& rSdrCustomShape(
269         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
270 
271     // Mark Object
272     SfxViewShell* pViewShell = SfxViewShell::Current();
273     SdrView* pSdrView = pViewShell->GetDrawView();
274     pSdrView->MarkObj(&rSdrCustomShape, pSdrView->GetSdrPageView());
275 
276     // Set direction
277     SfxRequest aReq(pViewShell->GetViewFrame(), SID_EXTRUSION_DIRECTION);
278     SfxInt32Item aItem(SID_EXTRUSION_DIRECTION, 90);
279     aReq.AppendItem(aItem);
280     svx::ExtrusionBar::execute(pSdrView, aReq, SfxViewFrame::Current()->GetBindings());
281 
282     // Verify height. Without the fix in place the height would 4001.
283     tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
284     CPPUNIT_ASSERT_EQUAL(tools::Long(5895), aBoundRect.GetHeight());
285 }
286 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testResizeRotatedShape)287 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testResizeRotatedShape)
288 {
289     // tdf#138945 Setting width or height for a rotated or sheared shape in the Position&Size dialog
290     // had resulted in a mismatch of handle position and shape outline. That becomes visible in object
291     // properties as mismatch of frame rectangle and bound rectangle.
292     // Problem was, that fObjectRotation was not updated.
293 
294     // Load document and get shape. It is a rectangle custom shape with 45° shear and 330° rotation.
295     OUString aURL
296         = m_directories.getURLFromSrc(sDataDirectory) + "tdf138945_resizeRotatedShape.odg";
297     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
298     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
299     uno::Reference<drawing::XShape> xShape(getShape(0));
300 
301     // Change height and mirror vertical
302     {
303         SdrObjCustomShape& rSdrShape(
304             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
305         rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(1.0), Fraction(-0.5));
306         tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
307         tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
308         lcl_AssertRectEqualWithTolerance("height changed, mirror vert", aSnapRect, aBoundRect, 3);
309     }
310 
311     // Change height
312     {
313         SdrObjCustomShape& rSdrShape(
314             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
315         rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(1.0), Fraction(2.0));
316         tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
317         tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
318         lcl_AssertRectEqualWithTolerance("height changed", aSnapRect, aBoundRect, 3);
319     }
320 
321     // Change width
322     {
323         SdrObjCustomShape& rSdrShape(
324             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
325         rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(2.0), Fraction(1.0));
326         tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
327         tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
328         lcl_AssertRectEqualWithTolerance("width changed", aSnapRect, aBoundRect, 3);
329     }
330 
331     // Change width and mirror horizontal
332     {
333         SdrObjCustomShape& rSdrShape(
334             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
335         rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(-0.5), Fraction(1.0));
336         tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
337         tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
338         lcl_AssertRectEqualWithTolerance("width changed, mirror hori", aSnapRect, aBoundRect, 3);
339     }
340 }
341 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testViewBoxLeftTop)342 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testViewBoxLeftTop)
343 {
344     // tdf#121890 formula values "left" and "top" are wrongly calculated
345     // Load a document with two custom shapes of type "non-primitive"
346     OUString aURL
347         = m_directories.getURLFromSrc(sDataDirectory) + "viewBox_positive_twolines_strict.odp";
348     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
349     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
350     // Get the shape "leftright". Error was, that the identifier "left" was always set to zero, thus
351     // the path was outside the frame rectangle for a viewBox having a positive "left" value.
352     uno::Reference<drawing::XShape> xShapeLR(getShape(0));
353     uno::Reference<beans::XPropertySet> xShapeLRProps(xShapeLR, uno::UNO_QUERY);
354     CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'leftright' properties", xShapeLRProps.is());
355     awt::Rectangle aFrameRectLR;
356     xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectLR;
357     awt::Rectangle aBoundRectLR;
358     xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectLR;
359     // difference should be zero, but allow some rounding errors
360     CPPUNIT_ASSERT_LESS(sal_Int32(3), std::abs(aFrameRectLR.X - aBoundRectLR.X));
361 
362     // Get the shape "topbottom". Error was, that the identifier "top" was always set to zero, thus
363     // the path was outside the frame rectangle for a viewBox having a positive "top" value.
364     uno::Reference<drawing::XShape> xShapeTB(getShape(1));
365     uno::Reference<beans::XPropertySet> xShapeTBProps(xShapeTB, uno::UNO_QUERY);
366     CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'topbottom' properties", xShapeTBProps.is());
367     awt::Rectangle aFrameRectTB;
368     xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectTB;
369     awt::Rectangle aBoundRectTB;
370     xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectTB;
371     // difference should be zero, but allow some rounding errors
372     CPPUNIT_ASSERT_LESS(sal_Int32(3), std::abs(aFrameRectTB.Y - aBoundRectTB.Y));
373 }
374 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testAccuracyCommandX)375 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testAccuracyCommandX)
376 {
377     // 121761 Increase accuracy of quarter circles drawn by command X or Y
378     // The loaded document has a quarter circle with radius 10000 (unit 1/100 mm)
379     // which is rotated by 45deg. The test considers the segment.
380     OUString aURL
381         = m_directories.getURLFromSrc(sDataDirectory) + "tdf121761_Accuracy_command_X.odp";
382     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
383     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
384     // Get the shape "arc_45deg_rotated". Error was, that a Bezier curve with bad parameters
385     // was used, thus the segment height was obviously smaller than for a true circle.
386     // Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
387     uno::Reference<drawing::XShape> xShape(getShape(0));
388     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
389     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
390     awt::Rectangle aBoundRect;
391     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
392     double fHeight = static_cast<double>(aBoundRect.Height);
393     // The tolerance is a guess, might be smaller.
394     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 2942.0, fHeight, 8.0);
395 }
396 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testToggleCommandXY)397 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testToggleCommandXY)
398 {
399     // 121952 Toggle x- and y-direction if command X has several parameters
400     // The loaded document has a shape with command X and two parameter placed on a diagonal.
401     // The radius of the quarter circles are both 10000 (unit 1/100 mm).
402     // The shape is rotated by 45deg, so you get two segments, one up and one down.
403     OUString aURL
404         = m_directories.getURLFromSrc(sDataDirectory) + "tdf121952_Toggle_direction_command_X.odp";
405     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
406     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
407     // Error was, that the second segment was drawn with same direction as first one. If drawn
408     // correctly, the bounding box height of the segments together is about twice the single
409     // segment height. Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
410     uno::Reference<drawing::XShape> xShape(getShape(0));
411     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
412     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
413     awt::Rectangle aBoundRect;
414     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
415     double fHeight = static_cast<double>(aBoundRect.Height);
416     // The tolerance is a guess, might be smaller.
417     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 5871.0, fHeight, 16.0);
418 }
419 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testMultipleMoveTo)420 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testMultipleMoveTo)
421 {
422     // tdf122964 Multiple moveTo has to be treated as lineTo in draw:enhanced-path
423     // Load a document with path "M 0 0 5 10 10 0 N"
424     OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf122964_MultipleMoveTo.odg";
425     mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.drawing.DrawingDocument");
426     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
427     // Error was, that the second and further parameter pairs were treated as moveTo,
428     // and so the generated path was empty, resulting in zero width and height of the
429     // bounding box. It has to be treated same as "M 0 0 L 5 10 10 0 N".
430     uno::Reference<drawing::XShape> xShape(getShape(0));
431     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
432     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
433     awt::Rectangle aBoundRect;
434     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
435     bool bIsZero(aBoundRect.Height == 0 && aBoundRect.Width == 0);
436     CPPUNIT_ASSERT_MESSAGE("Path is empty", !bIsZero);
437 }
438 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testWidthOrientationCommandU)439 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testWidthOrientationCommandU)
440 {
441     // tdf121845 custom shape with command U (angleellipse) is  wrongly drawn
442     // Load a document with path "M 750 0 L 750 500 250 500 250 0 U 500 0 500 500 0 180 N"
443     // in viewBox="0 0 1000 500" and width="10cm", height="5cm".
444     const OUString sURL
445         = m_directories.getURLFromSrc(sDataDirectory) + "tdf121845_WidthOrientation_command_U.odg";
446     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
447     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
448     // Error was, that the width and height of the ellipse was halved and that the ellipse
449     // was not drawn clockwise but counter clockwise.
450     uno::Reference<drawing::XShape> xShape(getShape(0));
451     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
452     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
453     awt::Rectangle aBoundRect;
454     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
455     const double fWidth = static_cast<double>(aBoundRect.Width);
456     // Need some tolerance for line width
457     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong width", 10000.0, fWidth, 40.0);
458     const double fHeight = static_cast<double>(aBoundRect.Height);
459     // Wrong orientation draws segment above the top of the viewBox and so increases 'Height'.
460     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong orientation", 5000.0, fHeight, 40.0);
461 }
462 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testHalfEllipseVML)463 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testHalfEllipseVML)
464 {
465     // tdf121845 custom shape with command U (angleellipse) is  wrongly drawn
466     // Load a document which was converted from VML to doc by Word. It had a VML
467     // path="m750,al500,,500,500,,-11796480e" resulting in a lower half circle.
468     const OUString sURL
469         = m_directories.getURLFromSrc(sDataDirectory) + "tdf121845_HalfEllipseVML.doc";
470     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
471     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
472     // Error was, that a full circle instead of the half circle was draw.
473     uno::Reference<drawing::XShape> xShape(getShape(0));
474     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
475     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
476     awt::Rectangle aBoundRect;
477     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
478     const double fDiff2HmW = static_cast<double>(2 * aBoundRect.Height - aBoundRect.Width);
479     // Need some tolerance for line width
480     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("not a half circle", 0.0, fDiff2HmW, 40.0);
481 }
482 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testLargeSwingAngleVML)483 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testLargeSwingAngleVML)
484 {
485     // tdf121845 custom shape with command U (angleellipse) is  wrongly drawn
486     // Load a document which was converted from VML to doc by Word. It had a VML
487     // path="al50,50,45,45,2621440,31457280e" resulting in a full circle plus 120 deg segment.
488     const OUString sURL
489         = m_directories.getURLFromSrc(sDataDirectory) + "tdf121845_start40_swing480.doc";
490     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
491     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
492     // Error was, that only the 120 deg segment was drawn.
493     uno::Reference<drawing::XShape> xShape(getShape(0));
494     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
495     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
496     awt::Rectangle aBoundRect;
497     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
498     const double fDiffWmH = static_cast<double>(aBoundRect.Width - aBoundRect.Height);
499     // Need some tolerance for line width
500     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Full circle plus segment expected", 0.0, fDiffWmH, 10.0);
501 }
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf121845_two_commands_U)502 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf121845_two_commands_U)
503 {
504     // tdf121845 custom shape with command U (angleellipse) is  wrongly drawn
505     // Load a document with path "U 950 250 200 200 90 180 250 250 200 200 180 270 N"
506     // Error was, that the second ellipse segment was interpreted as command T and
507     // thus a line from first to second segment was drawn.
508     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf121845_Two_commands_U.odg";
509     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
510     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
511     uno::Reference<drawing::XShape> xShape(getShape(0));
512     // In case no line is drawn, two polygons are generated; with line only one polygon
513     SdrObjCustomShape& rSdrObjCustomShape(
514         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
515     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
516     SdrPathObjUniquePtr pPathObj(
517         static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().release()));
518     CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
519     const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
520     CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(2),
521                                  aPolyPolygon.count());
522 }
523 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf124212_handle_position)524 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124212_handle_position)
525 {
526     // tdf124212 Adjustment handle reacts wrongly, if custom shape has a non
527     // default viewBox. Load a document with svg:viewBox="10800 0 10800 21600"
528     // Error was, that moving the controller results in a handle position that
529     // does not reflect the movement.
530     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf124212_handle_position.odg";
531     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
532     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
533     uno::Reference<drawing::XShape> xShape(getShape(0));
534     // The shape has one, horizontal adjust handle.
535     SdrObjCustomShape& rSdrObjCustomShape(
536         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
537     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
538     Point aInitialPosition;
539     aCustomShape2d.GetHandlePosition(0, aInitialPosition);
540     css::awt::Point aDesiredPosition(aInitialPosition.X() + 1000, aInitialPosition.Y());
541     aCustomShape2d.SetHandleControllerPosition(0, aDesiredPosition);
542     Point aObservedPosition;
543     aCustomShape2d.GetHandlePosition(0, aObservedPosition);
544     sal_Int32 nDesiredX(aDesiredPosition.X); // awt::Point
545     sal_Int32 nObservedX(aObservedPosition.X()); // tools::Point
546     CPPUNIT_ASSERT_EQUAL_MESSAGE("handle X coordinate", nDesiredX, nObservedX);
547 }
548 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf124029_arc_position)549 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124029_arc_position)
550 {
551     // tdf121029 MS binary custom shape mso_sptArc has wrong position
552     // MS uses the sector for position reference. Error was, that
553     // LibreOffice has used the underlying ellipse.
554     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf124029_Arc_position.doc";
555     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
556     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
557     uno::Reference<drawing::XShape> xShape(getShape(0));
558     // The visual wrong position is due to a wrong shape width.
559     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
560     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
561     awt::Rectangle aFrameRect;
562     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
563     CPPUNIT_ASSERT_EQUAL_MESSAGE("shape width", static_cast<sal_uInt32>(1610),
564                                  static_cast<sal_uInt32>(aFrameRect.Width));
565 }
566 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf124740_handle_path_coordsystem)567 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124740_handle_path_coordsystem)
568 {
569     // tdf124740 OOXML shape with handle and w and h attribute on path has wrong
570     // handle position
571     // The handle position was scaled erroneously twice.
572     OUString sURL
573         = m_directories.getURLFromSrc(sDataDirectory) + "tdf124740_HandleInOOXMLUserShape.pptx";
574     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
575     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
576     uno::Reference<drawing::XShape> xShape(getShape(0));
577     // The shape has one, horizontal adjust handle. It is about 1/5 of 10cm from left
578     // shape edge, shape is 6cm from left . That results in a position
579     // of 8cm from left page edge, which is 8000 in 1/100 mm unit.
580     SdrObjCustomShape& rSdrObjCustomShape(
581         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
582     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
583     Point aPosition;
584     aCustomShape2d.GetHandlePosition(0, aPosition);
585     double fX(aPosition.X());
586     // tolerance for rounding to integer
587     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("handle X coordinate", 8000.0, fX, 2.0);
588 }
589 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf115813_OOXML_XY_handle)590 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf115813_OOXML_XY_handle)
591 {
592     // The test covers all preset shapes with handles. Only these ones are
593     // excluded: arc, blockArc, chord, circularArrow, gear6, gear9, mathNotEqual, pie,
594     // leftCircularArrow, leftRightCircularArrow, swooshArrow.
595     // Connectors are included as ordinary shapes to prevent converting.
596     // Error was, that the handle movement and the changes to the shape did not follow
597     // the mouse movement.
598     OUString sURL = m_directories.getURLFromSrc(sDataDirectory)
599                     + "tdf115813_HandleMovementOOXMLPresetShapes.pptx";
600     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
601     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
602 
603     OUString sErrors;
604     // values in vector InteractionsHandles are in 1/100 mm and refer to page
605     for (sal_uInt8 i = 0; i < countShapes(); i++)
606     {
607         uno::Reference<drawing::XShape> xShape(getShape(i));
608         SdrObjCustomShape& rSdrObjCustomShape(
609             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
610         OUString sShapeType("non-primitive"); // default for ODF
611         const SdrCustomShapeGeometryItem& rGeometryItem(
612             rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
613         const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
614         if (pAny)
615             *pAny >>= sShapeType;
616 
617         sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
618         for (sal_uInt8 j = 0; j < nHandlesCount; j++)
619         {
620             css::awt::Point aInitialPosition(
621                 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
622             // The handles are initialized in the test document, so that if the handle is moveable in
623             // that direction at all, then it can move at least with an amount of 100.
624             Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
625             rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
626             css::awt::Point aObservedPosition(
627                 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
628             sal_Int32 nDesiredX(aDesiredPosition.X()); // tools::Point
629             sal_Int32 nDesiredY(aDesiredPosition.Y());
630             sal_Int32 nObservedX(aObservedPosition.X); // css::awt::Point
631             sal_Int32 nObservedY(aObservedPosition.Y);
632             // If a handle only moves in one direction, the difference is 100 for the other direction.
633             // There exists some rounding differences, therefore '<= 1' instead of '== 0'.
634             // The condition has the form '!(good cases)'.
635             if (!((abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) == 100)
636                   || (abs(nDesiredX - nObservedX) == 100 && abs(nDesiredY - nObservedY) <= 1)
637                   || (abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) <= 1)))
638             {
639                 sErrors += "\n" +
640                            //sErrors += OUString(sal_Unicode(10));
641                            OUString::number(i) + " " + sShapeType + ": " + OUString::number(j)
642                            + " X " + OUString::number(nDesiredX) + "|"
643                            + OUString::number(nObservedX) + " Y " + OUString::number(nDesiredY)
644                            + "|" + OUString::number(nObservedY);
645             }
646         }
647     }
648     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
649 }
650 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testQuadraticCurveTo)651 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testQuadraticCurveTo)
652 {
653     // tdf125782 command Q (quadraticcurveto) uses wrong 'current point'.
654     // When converting to cubic Bezier curve, this had resulted in a wrong first control point.
655     // The quadraticcurveto segment starts in shape center in the test file. The first control
656     // point should produce a horizontal tangent in the start point.
657     const OUString sURL
658         = m_directories.getURLFromSrc(sDataDirectory) + "tdf125782_QuadraticCurveTo.odg";
659     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
660     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
661     uno::Reference<drawing::XShape> xShape(getShape(0));
662     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
663     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
664     awt::Rectangle aBoundRect;
665     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
666     const double fHeight = static_cast<double>(aBoundRect.Height);
667     //Add some tolerance
668     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("bad height of quadraticcurveto", 3004, fHeight, 10.0);
669 }
670 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf126512_OOXML_handle_in_ODP)671 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126512_OOXML_handle_in_ODP)
672 {
673     // The test covers all preset shapes with handles. Connectors are included as ordinary
674     // shapes to prevent converting. The file was created in PowerPoint 365 and then
675     // opened and exported to ODF format by LibreOffice.
676     // Error was, that for shapes, which were originally imported from OOXML, the handles
677     // could not be moved at all.
678     OUString sURL
679         = m_directories.getURLFromSrc(sDataDirectory) + "tdf126512_OOXMLHandleMovementInODF.odp";
680     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
681     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
682 
683     OUString sErrors; // sErrors collects shape type and handle index for failing cases
684     for (sal_uInt8 i = 0; i < countShapes(); i++)
685     {
686         uno::Reference<drawing::XShape> xShape(getShape(i));
687         SdrObjCustomShape& rSdrObjCustomShape(
688             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
689         OUString sShapeType("non-primitive"); // only to initialize, value not used here
690         const SdrCustomShapeGeometryItem& rGeometryItem(
691             rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
692         const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
693         if (pAny)
694             *pAny >>= sShapeType;
695 
696         sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
697         for (sal_uInt8 j = 0; j < nHandlesCount; j++)
698         {
699             css::awt::Point aInitialPosition(
700                 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
701             // The handles are initialized in the test document, so that if the handle is moveable
702             // in that direction at all, then it can move at least with an amount of 100.
703             Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
704             rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
705             css::awt::Point aObservedPosition(
706                 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
707             if (aInitialPosition.X == aObservedPosition.X
708                 && aInitialPosition.Y == aObservedPosition.Y)
709             {
710                 sErrors
711                     += "\n" + OUString::number(i) + " " + sShapeType + "  " + OUString::number(j);
712             }
713         }
714     }
715     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
716 }
717 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf127785_Mirror)718 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Mirror)
719 {
720     // The document contains two shapes, one with horizontal flip, the other with vertical
721     // flip. They are diamonds, so their text frame is symmetric to the center of the shape.
722     // The shapes have not stroke and no fill, so that the bounding box surrounds the text
723     // and therefore equals approximately the text frame.
724     // Error was, that because of wrong calculation, the flipped shapes do not use the
725     // text frame but the frame rectangle for their text.
726     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf127785_Mirror.odp";
727     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
728     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
729     OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
730 
731     uno::Reference<drawing::XShape> xShapeV(getShape(0));
732     uno::Reference<beans::XPropertySet> xShapeVProps(xShapeV, uno::UNO_QUERY);
733     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeVProps.is());
734     awt::Rectangle aBoundRectV;
735     xShapeVProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectV;
736     const sal_Int32 nHeightV = aBoundRectV.Height;
737     const sal_Int32 nWidthV = aBoundRectV.Width;
738     const sal_Int32 nLeftV = aBoundRectV.X;
739     const sal_Int32 nTopV = aBoundRectV.Y;
740     if (abs(nHeightV - 8000) > 10 || abs(nWidthV - 8000) > 10)
741         sErrors += "Flip vertical wrong size.";
742     if (abs(nLeftV - 1000) > 10 || abs(nTopV - 2000) > 10)
743         sErrors += " Flip vertical wrong position.";
744 
745     uno::Reference<drawing::XShape> xShapeH(getShape(1));
746     uno::Reference<beans::XPropertySet> xShapeHProps(xShapeH, uno::UNO_QUERY);
747     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeHProps.is());
748     awt::Rectangle aBoundRectH;
749     xShapeHProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectH;
750     const sal_Int32 nHeightH = aBoundRectH.Height;
751     const sal_Int32 nWidthH = aBoundRectH.Width;
752     const sal_Int32 nLeftH = aBoundRectH.X;
753     const sal_Int32 nTopH = aBoundRectH.Y;
754     if (abs(nHeightH - 8000) > 10 || abs(nWidthH - 8000) > 10)
755         sErrors += " Flip horizontal wrong size.";
756     if (abs(nLeftH - 13000) > 10 || abs(nTopH - 2000) > 10)
757         sErrors += " Flip horizontal wrong position.";
758 
759     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
760 }
761 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf126060_3D_Z_Rotation)762 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126060_3D_Z_Rotation)
763 {
764     // The document contains one textbox with inside overflowed text
765     // and the text has 3D z rotation. When we open the document we
766     // should see the text vertically and rotated from text bound center not text box.
767 
768     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf126060_3D_Z_Rotation.pptx";
769     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
770     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
771 
772     uno::Reference<drawing::XShape> xShape(getShape(0));
773     SdrObjCustomShape& rSdrObjCustomShape(
774         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
775 
776     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong text camera Z rotation", 90.0,
777                                  rSdrObjCustomShape.GetCameraZRotation());
778 
779     basegfx::B2DHomMatrix aObjectTransform;
780     basegfx::B2DPolyPolygon aObjectPolyPolygon;
781     rSdrObjCustomShape.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon);
782 
783     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,0 position", 1492.0,
784                                  aObjectTransform.get(0, 0));
785     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,1 position", 0.0,
786                                  aObjectTransform.get(0, 1));
787     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,2 position", 1129.0,
788                                  aObjectTransform.get(0, 2));
789     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,0 position", 0.0,
790                                  aObjectTransform.get(1, 0));
791     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,1 position", 2500.0,
792                                  aObjectTransform.get(1, 1));
793     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,2 position", 5846.0,
794                                  aObjectTransform.get(1, 2));
795 }
796 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf127785_Asymmetric)797 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Asymmetric)
798 {
799     // The document contains a shapes with vertical flip and text frame asymmetrical
800     // to shape. The shape has not stroke and no fill, so that the bounding box surrounds
801     // the text and therefore equals approximately the text frame.
802     // Error was, that the 180deg text rotation was not compensated for the position of
803     // the flipped text box.
804     OUString sURL
805         = m_directories.getURLFromSrc(sDataDirectory) + "tdf127785_asymmetricTextBoxFlipV.odg";
806     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
807     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
808     OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
809 
810     uno::Reference<drawing::XShape> xShape(getShape(0));
811     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
812     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
813     awt::Rectangle aBoundRect;
814     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
815     const sal_Int32 nLeft = aBoundRect.X;
816     const sal_Int32 nTop = aBoundRect.Y;
817     const sal_Int32 nRight = aBoundRect.X + aBoundRect.Width - 1;
818     const sal_Int32 nBottom = aBoundRect.Y + aBoundRect.Height - 1;
819     if (abs(nLeft - 9000) > 10)
820         sErrors += "wrong left";
821     if (abs(nRight - 19000) > 10)
822         sErrors += " wrong right";
823     if (abs(nTop - 3000) > 10)
824         sErrors += " wrong top";
825     if (abs(nBottom - 18000) > 10)
826         sErrors += " wrong bottom";
827 
828     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
829 }
830 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf127785_TextRotateAngle)831 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_TextRotateAngle)
832 {
833     // The document contains a shapes with vertical flip and a text frame with own
834     // rotate angle. The shape has not stroke and no fill, so that the bounding box
835     // surrounds the text and therefore equals approximately the text frame.
836     // Error was, that the compensation for the 180° rotation added for vertical
837     // flip were not made to the text box position but to the text matrix.
838     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf127785_TextRotateAngle.odp";
839     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
840     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
841     OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
842 
843     uno::Reference<drawing::XShape> xShape(getShape(0));
844     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
845     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
846     awt::Rectangle aBoundRect;
847     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
848     const sal_Int32 nLeft = aBoundRect.X;
849     const sal_Int32 nTop = aBoundRect.Y;
850     const sal_Int32 nRight = aBoundRect.X + aBoundRect.Width - 1;
851     const sal_Int32 nBottom = aBoundRect.Y + aBoundRect.Height - 1;
852     if (abs(nLeft - 2000) > 10)
853         sErrors += "wrong left";
854     if (abs(nRight - 14000) > 10)
855         sErrors += " wrong right";
856     if (abs(nTop - 3000) > 10)
857         sErrors += " wrong top";
858     if (abs(nBottom - 9000) > 10)
859         sErrors += " wrong bottom";
860 
861     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
862 }
863 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf128413_tbrlOnOff)864 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf128413_tbrlOnOff)
865 {
866     // The document contains a rotated shape with text. The error was, that switching
867     // tb-rl writing-mode on, changed the shape size and position.
868 
869     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf128413_tbrl_OnOff.odp";
870     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
871     uno::Reference<drawing::XShape> xShape(getShape(0));
872     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
873     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
874     awt::Rectangle aOrigRect;
875     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aOrigRect;
876 
877     SdrObjCustomShape& rSdrObjCustomShape(
878         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
879     rSdrObjCustomShape.SetVerticalWriting(true);
880 
881     awt::Rectangle aObservedRect;
882     xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aObservedRect;
883 
884     CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong width", aOrigRect.Width, aObservedRect.Width);
885     CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong height", aOrigRect.Height, aObservedRect.Height);
886     CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong X position", aOrigRect.X, aObservedRect.X);
887     CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong Y position", aOrigRect.Y, aObservedRect.Y);
888 }
889 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf129532_MatrixFlipV)890 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf129532_MatrixFlipV)
891 {
892     // The document contains two rotated shapes with the same geometry. For one of them
893     // "matrix(1 0 0 -1 0cm 0cm)" was manually added to the value of the draw:transform
894     // attribute. That should result in mirroring on the x-axis. Error was, that the lines
895     // which are drawn on the shape rectangle were mirrored, but not the rectangle itself.
896     // The rectangle was only shifted.
897     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf129532_MatrixFlipV.odg";
898     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
899     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
900     OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
901 
902     uno::Reference<drawing::XShape> xShape0(getShape(0));
903     uno::Reference<beans::XPropertySet> xShape0Props(xShape0, uno::UNO_QUERY);
904     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape0Props.is());
905     awt::Rectangle aBoundRect0;
906     xShape0Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect0;
907 
908     uno::Reference<drawing::XShape> xShape1(getShape(1));
909     uno::Reference<beans::XPropertySet> xShape1Props(xShape1, uno::UNO_QUERY);
910     CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape1Props.is());
911     awt::Rectangle aBoundRect1;
912     xShape1Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect1;
913 
914     // The size of the two BoundRect rectangles are the same in case of correct
915     // vertical mirroring.
916     if (aBoundRect0.Width != aBoundRect1.Width)
917     {
918         sErrors += "\n Width expected: " + OUString::number(aBoundRect1.Width)
919                    + " actual: " + OUString::number(aBoundRect0.Width);
920     }
921     if (aBoundRect0.Height != aBoundRect1.Height)
922     {
923         sErrors += "\n Height expected: " + OUString::number(aBoundRect1.Height)
924                    + " actual: " + OUString::number(aBoundRect0.Height);
925     }
926     CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
927 }
928 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf103474_commandT_CaseZeroHeight)929 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandT_CaseZeroHeight)
930 {
931     // tdf103474 custom shape with command T to create quarter ellipses in a bracket,
932     // corner case where the ellipse has zero height.
933     // Error was, that the calculation of the circle angle from the ellipse
934     // angle results in a wrong angle for the case 180° and height zero.
935     OUString sURL
936         = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandT_CaseZeroHeight.odp";
937     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
938     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
939     uno::Reference<drawing::XShape> xShape(getShape(0));
940     // The end points of the straight line segment should have the same x-coordinate of left
941     // of shape, and different y-coordinates, one top and the other bottom of the shape.
942     SdrObjCustomShape& rSdrObjCustomShape(
943         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
944     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
945     SdrPathObjUniquePtr pPathObj(
946         static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().release()));
947     CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
948     const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
949     CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
950                                  aPolyPolygon.count());
951     const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
952     // Get the middle points of the polygon. They are the endpoints of the
953     // straight line segment regardless of the quarter ellipse parts, because
954     // the shape is symmetric.
955     const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
956     const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
957     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 13999.0, aStart.getX(), 1.0);
958     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 13999.0, aEnd.getX(), 1.0);
959     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
960     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
961 }
962 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf103474_commandG_CaseZeroHeight)963 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandG_CaseZeroHeight)
964 {
965     // Some as above, but with shape with command G.
966     OUString sURL
967         = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandG_CaseZeroHeight.odp";
968     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
969     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
970     uno::Reference<drawing::XShape> xShape(getShape(0));
971     // The end points of the straight line segment should have the same x-coordinate of left
972     // of shape, and different y-coordinates, one top and the other bottom of the shape.
973     SdrObjCustomShape& rSdrObjCustomShape(
974         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
975     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
976     SdrPathObjUniquePtr pPathObj(
977         static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().release()));
978     CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
979     const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
980     CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
981                                  aPolyPolygon.count());
982     const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
983     // Get the middle points of the polygon. They are the endpoints of the
984     // straight line segment regardless of the quarter ellipse parts, because
985     // the shape is symmetric.
986     const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
987     const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
988     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 1999.0, aStart.getX(), 1.0);
989     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 1999.0, aEnd.getX(), 1.0);
990     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
991     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
992 }
993 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf122323_largeSwingAngle)994 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf122323_largeSwingAngle)
995 {
996     // SwingAngles are clamped to [-360;360] in MS Office. Error was, that LO calculated
997     // the end angle and used it modulo 360, no full ellipse was drawn.
998     OUString sURL
999         = m_directories.getURLFromSrc(sDataDirectory) + "tdf122323_swingAngle_larger360deg.pptx";
1000     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
1001     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
1002     uno::Reference<drawing::XShape> xShape(getShape(0));
1003     uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
1004     SdrObjCustomShape& rSdrObjCustomShape(
1005         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
1006     EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
1007     SdrPathObjUniquePtr pPathObj(
1008         static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().release()));
1009     CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
1010     const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
1011     const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1012     const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(0));
1013     // last point comes from line to center, therefore -2 instead of -1
1014     const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() - 2));
1015     CPPUNIT_ASSERT_EQUAL_MESSAGE("Start <> End", aStart, aEnd);
1016 }
1017 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf141268)1018 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf141268)
1019 {
1020     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf141268.odp";
1021     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
1022     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
1023     uno::Reference<drawing::XShape> xShape(getShape(0));
1024     SdrObjCustomShape& rSdrCustomShape(
1025         static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
1026 
1027     // Check left/bottom of bound rect. Without fix it would be left=6722, bottom=9483.
1028     tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
1029     CPPUNIT_ASSERT_EQUAL(tools::Long(7620), aBoundRect.Left());
1030     CPPUNIT_ASSERT_EQUAL(tools::Long(8585), aBoundRect.Bottom());
1031 }
1032 
CPPUNIT_TEST_FIXTURE(CustomshapesTest,testTdf136176)1033 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf136176)
1034 {
1035     // Error was, that fObjectRotation was not correctly updated after shearing.
1036     // The problem becomes visible after save and reload.
1037     OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf136176_rot30_flip.odg";
1038     mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
1039     CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
1040 
1041     for (sal_uInt16 i = 0; i < 3; i++)
1042     {
1043         // get shape
1044         uno::Reference<drawing::XShape> xShape(getShape(i));
1045         SdrObjCustomShape& rSdrObjCustomShape(
1046             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
1047         // apply shearing 20deg
1048         const Point aCenter = rSdrObjCustomShape.GetSnapRect().Center();
1049         rSdrObjCustomShape.Shear(aCenter, 2000_deg100, tan(basegfx::deg2rad(20.0)), false);
1050     }
1051 
1052     // Save and reload
1053     uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
1054     utl::TempFile aTempFile;
1055     aTempFile.EnableKillingFile();
1056     utl::MediaDescriptor aMediaDescriptor;
1057     aMediaDescriptor["FilterName"] <<= OUString("draw8");
1058     xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
1059     mxComponent->dispose();
1060     mxComponent = loadFromDesktop(aTempFile.GetURL());
1061 
1062     // Expected values of point 4 of the shape polygon
1063     const OString sTestCase[] = { "FlipH", "FlipV", "FlipHV" };
1064     const double fX[] = { 14981.0, 3849.0, 15214.0 };
1065     const double fY[] = { 9366.0, 16464.0, 23463.0 };
1066 
1067     // Verify correct positions
1068     for (sal_uInt16 i = 0; i < 3; i++)
1069     {
1070         // Get shape
1071         const uno::Reference<drawing::XShape> xShape(getShape(i));
1072         const SdrObjCustomShape& rSdrObjCustomShape(
1073             static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
1074         // Create polygon from shape and examine point 4 of the polygon
1075         const basegfx::B2DPolyPolygon aLineGeometry = rSdrObjCustomShape.GetLineGeometry(false);
1076         const basegfx::B2DPoint aPoint(aLineGeometry.getB2DPolygon(0).getB2DPoint(4));
1077         // Allow some tolerance for rounding errors
1078         if (fabs(aPoint.getX() - fX[i]) > 2.0 || fabs(aPoint.getY() - fY[i]) > 2.0)
1079         {
1080             CPPUNIT_ASSERT_EQUAL_MESSAGE(sTestCase[i].getStr(), aPoint,
1081                                          basegfx::B2DPoint(fX[i], fY[i]));
1082         }
1083     }
1084 }
1085 }
1086 
1087 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1088