calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / svx / qa / unit / customshapes.cxx
blobefe322d54e765a0fdc5a100355b74cad84d9850b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <sal/config.h>
12 #include <cstdlib>
14 #include <test/unoapi_test.hxx>
15 #include <rtl/ustring.hxx>
16 #include <basegfx/polygon/b2dpolypolygon.hxx>
17 #include <basegfx/polygon/b2dpolygon.hxx>
18 #include <basegfx/point/b2dpoint.hxx>
19 #include <comphelper/propertyvalue.hxx>
20 #include <editeng/unoprnms.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/graphichelper.hxx>
28 #include <svx/svdoashp.hxx>
29 #include <svx/svdopath.hxx>
30 #include <svx/svdview.hxx>
31 #include <svx/svxids.hrc>
32 #include <unotools/mediadescriptor.hxx>
33 #include <unotools/tempfile.hxx>
34 #include <vcl/filter/PngImageReader.hxx>
35 #include <vcl/BitmapReadAccess.hxx>
37 #include <cppunit/TestAssert.h>
39 #include <com/sun/star/awt/Rectangle.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
42 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
43 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
44 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
45 #include <com/sun/star/drawing/XDrawPage.hpp>
46 #include <com/sun/star/frame/Desktop.hpp>
47 #include <com/sun/star/frame/XStorable.hpp>
49 using namespace ::com::sun::star;
51 namespace
53 /// Tests for svx/source/customshapes/ code.
54 class CustomshapesTest : public UnoApiTest
56 public:
57 CustomshapesTest()
58 : UnoApiTest("svx/qa/unit/data/")
62 protected:
63 // get shape nShapeIndex from page 0
64 uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex);
65 sal_uInt8 countShapes();
68 uno::Reference<drawing::XShape> CustomshapesTest::getShape(sal_uInt8 nShapeIndex)
70 uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
71 uno::UNO_QUERY_THROW);
72 CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
73 uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
74 uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
75 CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
76 uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY);
77 CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is());
78 return xShape;
81 sal_uInt8 CustomshapesTest::countShapes()
83 uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
84 uno::UNO_QUERY_THROW);
85 CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
86 uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
87 uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
88 CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
89 return xDrawPage->getCount();
92 void lcl_AssertRectEqualWithTolerance(std::string_view sInfo, const tools::Rectangle& rExpected,
93 const tools::Rectangle& rActual, const sal_Int32 nTolerance)
95 // Left
96 OString sMsg = OString::Concat(sInfo) + " Left expected " + OString::number(rExpected.Left())
97 + " actual " + OString::number(rActual.Left()) + " Tolerance "
98 + OString::number(nTolerance);
99 CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
100 std::abs(rExpected.Left() - rActual.Left()) <= nTolerance);
102 // Top
103 sMsg = OString::Concat(sInfo) + " Top expected " + OString::number(rExpected.Top()) + " actual "
104 + OString::number(rActual.Top()) + " Tolerance " + OString::number(nTolerance);
105 CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(), std::abs(rExpected.Top() - rActual.Top()) <= nTolerance);
107 // Width
108 sMsg = OString::Concat(sInfo) + " Width expected " + OString::number(rExpected.GetWidth())
109 + " actual " + OString::number(rActual.GetWidth()) + " Tolerance "
110 + OString::number(nTolerance);
111 CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
112 std::abs(rExpected.GetWidth() - rActual.GetWidth()) <= nTolerance);
114 // Height
115 sMsg = OString::Concat(sInfo) + " Height expected " + OString::number(rExpected.GetHeight())
116 + " actual " + OString::number(rActual.GetHeight()) + " Tolerance "
117 + OString::number(nTolerance);
118 CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(),
119 std::abs(rExpected.GetHeight() - rActual.GetHeight()) <= nTolerance);
122 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf147409_GeomItemHash)
124 loadFromURL(u"tdf147409_GeomItemHash.odg");
125 uno::Reference<drawing::XShape> xShape(getShape(0));
126 SdrObjCustomShape* pSdrCustomShape(
127 static_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)));
129 // Mark Object
130 SfxViewShell* pViewShell = SfxViewShell::Current();
131 SdrView* pSdrView = pViewShell->GetDrawView();
132 pSdrView->MarkObj(pSdrCustomShape, pSdrView->GetSdrPageView());
134 // Apply FontworkSameLetterHeights toggle
135 // Without patch a debug build fails assert in SfxItemPool::PutImpl and so crashes.
136 dispatchCommand(mxComponent, ".uno:FontworkSameLetterHeights", {});
139 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf146866_GeomItemHash)
141 loadFromURL(u"tdf147409_GeomItemHash.odg");
142 uno::Reference<drawing::XShape> xShape(getShape(0));
143 SdrObjCustomShape* pSdrCustomShape(
144 static_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)));
146 // Mark Object
147 SfxViewShell* pViewShell = SfxViewShell::Current();
148 SdrView* pSdrView = pViewShell->GetDrawView();
149 pSdrView->MarkObj(pSdrCustomShape, pSdrView->GetSdrPageView());
151 // Apply extrusion toggle
152 // Without patch a debug build fails assert in SfxItemPool::PutImpl and so crashes.
153 dispatchCommand(mxComponent, ".uno:ExtrusionToggle", {});
156 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_NonUI)
158 // The document contains first light soft, no ambient color, no second light and shininess 6.
159 // Such settings are not available in the UI. It tests the actual color, not the geometry.
160 // Load document
161 loadFromURL(u"tdf145700_3D_NonUI.doc");
163 // Generate bitmap from shape
164 uno::Reference<drawing::XShape> xShape = getShape(0);
165 GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", maTempFile.GetURL());
167 // Read bitmap and test color
168 // The expected values are taken from an image generated by Word
169 // Without the changed methods the colors were in range RGB(17,11,17) to RGB(87,55,89).
170 SvFileStream aFileStream(maTempFile.GetURL(), StreamMode::READ);
171 vcl::PngImageReader aPNGReader(aFileStream);
172 BitmapEx aBMPEx = aPNGReader.read();
173 Bitmap aBMP = aBMPEx.GetBitmap();
174 Bitmap::ScopedReadAccess pRead(aBMP);
175 Size aSize = aBMP.GetSizePixel();
176 // GetColor(Y,X)
177 Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.125);
178 Color aExpectedColor(107, 67, 109);
179 sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
180 CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
181 // The current solution for soft light still can be improved. nColorDistance is high.
182 aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.45);
183 aExpectedColor = Color(179, 113, 183);
184 nColorDistance = aExpectedColor.GetColorError(aActualColor);
185 CPPUNIT_ASSERT_LESS(sal_uInt16(54), nColorDistance);
186 // This point tests whether shininess is read and used. With default shininess it would be white.
187 aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.72);
188 aExpectedColor = Color(255, 231, 255);
189 nColorDistance = aExpectedColor.GetColorError(aActualColor);
190 CPPUNIT_ASSERT_LESS(sal_uInt16(14), nColorDistance);
193 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_FrontLightDim)
195 // This tests the actual color, not the geometry.
196 // Load document
197 loadFromURL(u"tdf145700_3D_FrontLightDim.doc");
199 // Generate bitmap from shape
200 uno::Reference<drawing::XShape> xShape = getShape(0);
201 GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", maTempFile.GetURL());
203 // Read bitmap and test color
204 // The expected values are taken from an image generated by Word
205 // Without the changed methods the nColorDistance was 476 and 173 respectively.
206 SvFileStream aFileStream(maTempFile.GetURL(), StreamMode::READ);
207 vcl::PngImageReader aPNGReader(aFileStream);
208 BitmapEx aBMPEx = aPNGReader.read();
209 Bitmap aBMP = aBMPEx.GetBitmap();
210 Bitmap::ScopedReadAccess pRead(aBMP);
211 Size aSize = aBMP.GetSizePixel();
212 // GetColor(Y,X)
213 Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.4);
214 Color aExpectedColor(240, 224, 229);
215 sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
216 CPPUNIT_ASSERT_LESS(sal_uInt16(9), nColorDistance);
217 aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.9);
218 aExpectedColor = Color(96, 90, 92);
219 nColorDistance = aExpectedColor.GetColorError(aActualColor);
220 CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
223 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_FirstLightHarsh)
225 // Load document
226 loadFromURL(u"tdf145700_3D_FirstLightHarsh.doc");
228 // Generate bitmap from shape
229 uno::Reference<drawing::XShape> xShape = getShape(0);
230 GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", maTempFile.GetURL());
232 // Read bitmap and test color in center
233 SvFileStream aFileStream(maTempFile.GetURL(), StreamMode::READ);
234 vcl::PngImageReader aPNGReader(aFileStream);
235 BitmapEx aBMPEx = aPNGReader.read();
236 Bitmap aBMP = aBMPEx.GetBitmap();
237 Bitmap::ScopedReadAccess pRead(aBMP);
238 Size aSize = aBMP.GetSizePixel();
239 // GetColor(Y,X)
240 const Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() / 2);
241 const Color aExpectedColor(211, 247, 255); // from image generated by Word
242 sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
243 CPPUNIT_ASSERT_LESS(sal_uInt16(3), nColorDistance);
246 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145956_Origin_Relative_BoundRect)
248 // The ViewPoint is relative to point Origin. The coordinates of point Origin are fractions of
249 // the actual (2D) bounding rectangle of the shape, including rotation around z-axis and flip.
250 // Error (among others) was, that the unrotated snap rectangle was used.
252 // Load document
253 loadFromURL(u"tdf145956_Origin.odp");
255 // The shape is extruded with 10cm. viewpoint="(0cm 0cm 25cm)", origin="0 0".
256 uno::Reference<drawing::XShape> xShape(getShape(0));
257 uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
258 CPPUNIT_ASSERT_MESSAGE("Could not get the properties", xPropSet.is());
259 awt::Rectangle aBoundRect;
260 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
261 sal_Int32 nActualTop = aBoundRect.Y;
263 // Without the fix it would have failed with top = 9462.
264 // The tolerance 10 is estimated and can be adjusted if required for HiDPI.
265 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("top", 10448, nActualTop, 10);
268 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145904_Extrusion_CenterZ_odt)
270 // The Z-component of the extrusion rotation center specifies the position in Hmm.
271 // Error (among others) was, that the value was interpreted as Twips.
273 // Load document
274 loadFromURL(u"tdf145904_center_Zminus2000.odt");
276 // The shape is extruded and tilt left 60deg. The rotation center is at -2000Hmm on the z-axis.
277 // That is a position behind the back face of the extruded shape.
278 uno::Reference<drawing::XShape> xShape(getShape(0));
279 uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
280 CPPUNIT_ASSERT_MESSAGE("Could not get the properties", xPropSet.is());
281 awt::Rectangle aBoundRect;
282 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
283 awt::Point aAnchorPosition;
284 xPropSet->getPropertyValue("AnchorPosition") >>= aAnchorPosition;
285 sal_Int32 nActualLeft = aBoundRect.X - aAnchorPosition.X;
287 // Without the fix it would have failed with left = 7731.
288 // The tolerance 10 is estimated and can be adjusted if required for HiDPI.
289 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("left", 3501, nActualLeft, 10);
292 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145904_Extrusion_CenterY_odt)
294 // The X- and Y-component of the extrusion rotation center specify the position as fraction of
295 // shape size. Error was, that the relative fraction was handled as absolute value in Hmm.
297 // Load document
298 loadFromURL(u"tdf145904_center_Y0dot25.odt");
300 // The shape is extruded and tilt down 90deg. The rotation center is in the middle between shape
301 // center and bottom shape edge. The bottom edge of the projected solid has roughly the
302 // y-coordinate of the rotation center.
303 uno::Reference<drawing::XShape> xShape(getShape(0));
304 uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
305 CPPUNIT_ASSERT_MESSAGE("Could not get the properties", xPropSet.is());
306 awt::Rectangle aBoundRect;
307 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
308 awt::Point aAnchorPosition;
309 xPropSet->getPropertyValue("AnchorPosition") >>= aAnchorPosition;
310 sal_Int32 nActualTop = aBoundRect.Y - aAnchorPosition.Y;
312 // Without the fix it would have failed with top = 2252.
313 // The tolerance 10 is estimated and can be adjusted if required for HiDPI.
314 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("top", 3208, nActualTop, 10);
317 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145904_Extrusion_CenterY_doc)
319 // The X- and Y-component of the extrusion rotation center specify the position as fraction of
320 // shape size. Error was, that the relative fraction was handled as absolute value in EMU.
322 // Load document
323 loadFromURL(u"tdf145904_center_Y0dot25.doc");
325 // The shape is extruded and tilt down 90deg. The rotation center is in the middle between shape
326 // center and bottom shape edge. The bottom edge of the projected solid has roughly the
327 // y-coordinate of the center.
328 uno::Reference<drawing::XShape> xShape(getShape(0));
329 uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
330 CPPUNIT_ASSERT_MESSAGE("Could not get the properties", xPropSet.is());
331 awt::Rectangle aBoundRect;
332 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
333 awt::Point aAnchorPosition;
334 xPropSet->getPropertyValue("AnchorPosition") >>= aAnchorPosition;
335 sal_Int32 nActualTop = aBoundRect.Y - aAnchorPosition.Y;
337 // Without the fix it would have failed with top = 2330
338 // The tolerance 10 is estimated and can be adjusted if required for HiDPI.
339 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("top", 3208, nActualTop, 10);
342 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145245_ExtrusionPosition)
344 // The second parameter of the extrusion-depth property specifies how much of the extrusion
345 // lies before the shape. The file contains three shapes which have the values 0, 0.5 and 1.
346 // They are rotated around the x-axis so that the extrusion becomes visible. The extrusion
347 // depth itself is 5cm. Y-coordinate of shape is 6cm.
349 // Load document
350 loadFromURL(u"tdf145245_ExtrusionPosition.odp");
352 // The tolerance 40 is estimated and can be adjusted if required for HiDPI.
354 // First shape has extrusion behind the shape.
355 uno::Reference<drawing::XShape> xShape0(getShape(0));
356 SdrObjCustomShape& rSdrCustomShape(
357 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape0)));
358 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
359 tools::Rectangle aExpected(Point(1000, 1000), Size(6002, 5001));
360 lcl_AssertRectEqualWithTolerance("Pos 0.0 extrusion", aExpected, aBoundRect, 40);
363 // Second shape has half of extrusion behind the shape.
364 uno::Reference<drawing::XShape> xShape(getShape(1));
365 SdrObjCustomShape& rSdrCustomShape(
366 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
367 // Without the fix the height was 1 instead of 5001.
368 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
369 tools::Rectangle aExpected(Point(9000, 3500), Size(6002, 5001));
370 lcl_AssertRectEqualWithTolerance("Pos 0.5 extrusion", aExpected, aBoundRect, 40);
373 // Third shape has extrusion before the shape.
374 uno::Reference<drawing::XShape> xShape(getShape(2));
375 SdrObjCustomShape& rSdrCustomShape(
376 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
377 // Without the fix the y-coordinate was 1000 instead of 6000.
378 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
379 tools::Rectangle aExpected(Point(18000, 6000), Size(6002, 5001));
380 lcl_AssertRectEqualWithTolerance("Pos 1.0 extrusion", aExpected, aBoundRect, 40);
384 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_Fontwork_rendering_font_size)
386 // The tested position and height depend on dpi.
387 if (!IsDefaultDPI())
388 return;
390 // tdf#144988 In case ScaleX is true in property TextPath, the rendering font size should be
391 // reduced in case any of the paragraphs would be longer as its sub-path. That was wrong, if
392 // the first paragraph was too long and the second would fit. It resulted in wrong position
393 // and height and overlapping characters.
395 loadFromURL(u"tdf144988_Fontwork_FontSize.odp");
396 uno::Reference<drawing::XShape> xShape(getShape(0));
397 SdrObjCustomShape& rSdrCustomShape(
398 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
400 // Without the fix in place left|top, width x height was 1279|1279, 2815 x 2448.
401 // The expected values 1501|1777, 3941 x 1446 are only valid for 96dpi.
402 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
403 tools::Rectangle aExpected(Point(1501, 1777), Size(3941, 1446));
404 lcl_AssertRectEqualWithTolerance("Wrong text rendering", aExpected, aBoundRect, 5);
407 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_anchor_in_Fontwork)
409 // The tested positions depend on dpi.
410 if (!IsDefaultDPI())
411 return;
413 // tdf#145004 In case ScaleX is true in property TextPath, SDRTEXTVERTADJUST is
414 // evaluated and should shift the Fontwork text. That did not work for
415 // 'Top-Left' and 'Bottom-Left'.
417 // Load document
418 loadFromURL(u"tdf145111_TL_BL_Fontwork.odp");
421 // First shape has anchor set to Top-Left, which shifts Fontwork text down.
422 uno::Reference<drawing::XShape> xShape(getShape(0));
423 SdrObjCustomShape& rSdrCustomShape(
424 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
426 // Without the fix in place top was 2295, but should be 2916 for 96dpi.
427 // Was 2184, should be 2886 for 120dpi.
428 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
429 CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(2916), aBoundRect.Top(), 5);
432 // Second shape has anchor set to Bottom-Left, which shifts Fontwork text up.
433 uno::Reference<drawing::XShape> xShape(getShape(1));
434 SdrObjCustomShape& rSdrCustomShape(
435 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
437 // Without the fix in place top was 10294, but should be 9519 for 96dpi.
438 // Was 10184, should be 9481 for 120dpi.
439 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
440 CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(9519), aBoundRect.Top(), 5);
444 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145004_gap_by_ScaleX)
446 if (!IsDefaultDPI())
447 return;
448 // tdf#145004 In case property ScaleX=true was set in property 'TextPath' an additional
449 // padding was added to the scaling factor. That results in a gap at start or/and end of
450 // the text. Such gap should not be there.
452 // Load document and get shape. It is a custom shape from pptx import of a WordArt of
453 // kind 'Follow Path'.
454 loadFromURL(u"tdf145004_gap_by_ScaleX.pptx");
455 uno::Reference<drawing::XShape> xShape(getShape(0));
456 SdrObjCustomShape& rSdrCustomShape(
457 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
459 // Verify width. Without the fix in place the width was 8231, but should be 8496 for 96dpi.
460 // Was 8328, should be 8527 for 120dpi.
461 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
462 CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(8496), aBoundRect.GetWidth(), 5);
465 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf141021ExtrusionNorth)
467 // tdf#141021 Setting extrusion direction in projection method 'perspective' to
468 // 'Extrusion North' had used a wrong origin for the ViewPoint and thus the
469 // side faces were wrong calculated.
471 // Load document and get shape. It is a custom shape in 3D mode.
472 loadFromURL(u"tdf141021_ExtrusionNorth.odp");
473 uno::Reference<drawing::XShape> xShape(getShape(0));
474 SdrObjCustomShape& rSdrCustomShape(
475 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
477 // Mark Object
478 SfxViewShell* pViewShell = SfxViewShell::Current();
479 SdrView* pSdrView = pViewShell->GetDrawView();
480 pSdrView->MarkObj(&rSdrCustomShape, pSdrView->GetSdrPageView());
482 // Set direction
483 SfxRequest aReq(pViewShell->GetViewFrame(), SID_EXTRUSION_DIRECTION);
484 SfxInt32Item aItem(SID_EXTRUSION_DIRECTION, 90);
485 aReq.AppendItem(aItem);
486 svx::ExtrusionBar::execute(pSdrView, aReq, SfxViewFrame::Current()->GetBindings());
488 // Verify height. Without the fix in place the height would 4001.
489 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
490 CPPUNIT_ASSERT_EQUAL(tools::Long(5895), aBoundRect.GetHeight());
493 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testResizeRotatedShape)
495 // tdf#138945 Setting width or height for a rotated or sheared shape in the Position&Size dialog
496 // had resulted in a mismatch of handle position and shape outline. That becomes visible in object
497 // properties as mismatch of frame rectangle and bound rectangle.
498 // Problem was, that fObjectRotation was not updated.
500 // Load document and get shape. It is a rectangle custom shape with 45° shear and 330° rotation.
501 loadFromURL(u"tdf138945_resizeRotatedShape.odg");
502 uno::Reference<drawing::XShape> xShape(getShape(0));
504 // Change height and mirror vertical
506 SdrObjCustomShape& rSdrShape(
507 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
508 rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(1.0), Fraction(-0.5));
509 tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
510 tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
511 lcl_AssertRectEqualWithTolerance("height changed, mirror vert", aSnapRect, aBoundRect, 3);
514 // Change height
516 SdrObjCustomShape& rSdrShape(
517 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
518 rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(1.0), Fraction(2.0));
519 tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
520 tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
521 lcl_AssertRectEqualWithTolerance("height changed", aSnapRect, aBoundRect, 3);
524 // Change width
526 SdrObjCustomShape& rSdrShape(
527 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
528 rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(2.0), Fraction(1.0));
529 tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
530 tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
531 lcl_AssertRectEqualWithTolerance("width changed", aSnapRect, aBoundRect, 3);
534 // Change width and mirror horizontal
536 SdrObjCustomShape& rSdrShape(
537 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
538 rSdrShape.NbcResize(rSdrShape.GetRelativePos(), Fraction(-0.5), Fraction(1.0));
539 tools::Rectangle aSnapRect(rSdrShape.GetSnapRect());
540 tools::Rectangle aBoundRect(rSdrShape.GetCurrentBoundRect());
541 lcl_AssertRectEqualWithTolerance("width changed, mirror hori", aSnapRect, aBoundRect, 3);
545 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testViewBoxLeftTop)
547 // tdf#121890 formula values "left" and "top" are wrongly calculated
548 // Load a document with two custom shapes of type "non-primitive"
549 loadFromURL(u"viewBox_positive_twolines_strict.odp");
550 // Get the shape "leftright". Error was, that the identifier "left" was always set to zero, thus
551 // the path was outside the frame rectangle for a viewBox having a positive "left" value.
552 uno::Reference<drawing::XShape> xShapeLR(getShape(0));
553 uno::Reference<beans::XPropertySet> xShapeLRProps(xShapeLR, uno::UNO_QUERY);
554 CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'leftright' properties", xShapeLRProps.is());
555 awt::Rectangle aFrameRectLR;
556 xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectLR;
557 awt::Rectangle aBoundRectLR;
558 xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectLR;
559 // difference should be zero, but allow some rounding errors
560 CPPUNIT_ASSERT_LESS(sal_Int32(3), std::abs(aFrameRectLR.X - aBoundRectLR.X));
562 // Get the shape "topbottom". Error was, that the identifier "top" was always set to zero, thus
563 // the path was outside the frame rectangle for a viewBox having a positive "top" value.
564 uno::Reference<drawing::XShape> xShapeTB(getShape(1));
565 uno::Reference<beans::XPropertySet> xShapeTBProps(xShapeTB, uno::UNO_QUERY);
566 CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'topbottom' properties", xShapeTBProps.is());
567 awt::Rectangle aFrameRectTB;
568 xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectTB;
569 awt::Rectangle aBoundRectTB;
570 xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectTB;
571 // difference should be zero, but allow some rounding errors
572 CPPUNIT_ASSERT_LESS(sal_Int32(3), std::abs(aFrameRectTB.Y - aBoundRectTB.Y));
575 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testAccuracyCommandX)
577 // 121761 Increase accuracy of quarter circles drawn by command X or Y
578 // The loaded document has a quarter circle with radius 10000 (unit 1/100 mm)
579 // which is rotated by 45deg. The test considers the segment.
580 loadFromURL(u"tdf121761_Accuracy_command_X.odp");
581 // Get the shape "arc_45deg_rotated". Error was, that a Bezier curve with bad parameters
582 // was used, thus the segment height was obviously smaller than for a true circle.
583 // Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
584 uno::Reference<drawing::XShape> xShape(getShape(0));
585 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
586 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
587 awt::Rectangle aBoundRect;
588 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
589 double fHeight = static_cast<double>(aBoundRect.Height);
590 // The tolerance is a guess, might be smaller.
591 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 2942.0, fHeight, 8.0);
594 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testToggleCommandXY)
596 // 121952 Toggle x- and y-direction if command X has several parameters
597 // The loaded document has a shape with command X and two parameter placed on a diagonal.
598 // The radius of the quarter circles are both 10000 (unit 1/100 mm).
599 // The shape is rotated by 45deg, so you get two segments, one up and one down.
600 loadFromURL(u"tdf121952_Toggle_direction_command_X.odp");
601 // Error was, that the second segment was drawn with same direction as first one. If drawn
602 // correctly, the bounding box height of the segments together is about twice the single
603 // segment height. Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
604 uno::Reference<drawing::XShape> xShape(getShape(0));
605 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
606 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
607 awt::Rectangle aBoundRect;
608 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
609 double fHeight = static_cast<double>(aBoundRect.Height);
610 // The tolerance is a guess, might be smaller.
611 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 5871.0, fHeight, 16.0);
614 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testMultipleMoveTo)
616 // tdf122964 Multiple moveTo has to be treated as lineTo in draw:enhanced-path
617 // Load a document with path "M 0 0 5 10 10 0 N"
618 loadFromURL(u"tdf122964_MultipleMoveTo.odg");
619 // Error was, that the second and further parameter pairs were treated as moveTo,
620 // and so the generated path was empty, resulting in zero width and height of the
621 // bounding box. It has to be treated same as "M 0 0 L 5 10 10 0 N".
622 uno::Reference<drawing::XShape> xShape(getShape(0));
623 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
624 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
625 awt::Rectangle aBoundRect;
626 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
627 bool bIsZero(aBoundRect.Height == 0 && aBoundRect.Width == 0);
628 CPPUNIT_ASSERT_MESSAGE("Path is empty", !bIsZero);
631 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testWidthOrientationCommandU)
633 // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
634 // Load a document with path "M 750 0 L 750 500 250 500 250 0 U 500 0 500 500 0 180 N"
635 // in viewBox="0 0 1000 500" and width="10cm", height="5cm".
636 loadFromURL(u"tdf121845_WidthOrientation_command_U.odg");
637 // Error was, that the width and height of the ellipse was halved and that the ellipse
638 // was not drawn clockwise but counter clockwise.
639 uno::Reference<drawing::XShape> xShape(getShape(0));
640 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
641 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
642 awt::Rectangle aBoundRect;
643 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
644 const double fWidth = static_cast<double>(aBoundRect.Width);
645 // Need some tolerance for line width
646 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong width", 10000.0, fWidth, 40.0);
647 const double fHeight = static_cast<double>(aBoundRect.Height);
648 // Wrong orientation draws segment above the top of the viewBox and so increases 'Height'.
649 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong orientation", 5000.0, fHeight, 40.0);
652 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testHalfEllipseVML)
654 // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
655 // Load a document which was converted from VML to doc by Word. It had a VML
656 // path="m750,al500,,500,500,,-11796480e" resulting in a lower half circle.
657 loadFromURL(u"tdf121845_HalfEllipseVML.doc");
658 // Error was, that a full circle instead of the half circle was draw.
659 uno::Reference<drawing::XShape> xShape(getShape(0));
660 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
661 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
662 awt::Rectangle aBoundRect;
663 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
664 const double fDiff2HmW = static_cast<double>(2 * aBoundRect.Height - aBoundRect.Width);
665 // Need some tolerance for line width
666 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("not a half circle", 0.0, fDiff2HmW, 40.0);
669 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testLargeSwingAngleVML)
671 // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
672 // Load a document which was converted from VML to doc by Word. It had a VML
673 // path="al50,50,45,45,2621440,31457280e" resulting in a full circle plus 120 deg segment.
674 loadFromURL(u"tdf121845_start40_swing480.doc");
675 // Error was, that only the 120 deg segment was drawn.
676 uno::Reference<drawing::XShape> xShape(getShape(0));
677 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
678 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
679 awt::Rectangle aBoundRect;
680 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
681 const double fDiffWmH = static_cast<double>(aBoundRect.Width - aBoundRect.Height);
682 // Need some tolerance for line width
683 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Full circle plus segment expected", 0.0, fDiffWmH, 10.0);
685 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf121845_two_commands_U)
687 // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
688 // Load a document with path "U 950 250 200 200 90 180 250 250 200 200 180 270 N"
689 // Error was, that the second ellipse segment was interpreted as command T and
690 // thus a line from first to second segment was drawn.
691 loadFromURL(u"tdf121845_Two_commands_U.odg");
692 uno::Reference<drawing::XShape> xShape(getShape(0));
693 // In case no line is drawn, two polygons are generated; with line only one polygon
694 SdrObjCustomShape& rSdrObjCustomShape(
695 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
696 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
697 rtl::Reference<SdrPathObj> pPathObj(
698 static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().get()));
699 CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
700 const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
701 CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(2),
702 aPolyPolygon.count());
705 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124212_handle_position)
707 // tdf124212 Adjustment handle reacts wrongly, if custom shape has a non
708 // default viewBox. Load a document with svg:viewBox="10800 0 10800 21600"
709 // Error was, that moving the controller results in a handle position that
710 // does not reflect the movement.
711 loadFromURL(u"tdf124212_handle_position.odg");
712 uno::Reference<drawing::XShape> xShape(getShape(0));
713 // The shape has one, horizontal adjust handle.
714 SdrObjCustomShape& rSdrObjCustomShape(
715 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
716 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
717 Point aInitialPosition;
718 aCustomShape2d.GetHandlePosition(0, aInitialPosition);
719 css::awt::Point aDesiredPosition(aInitialPosition.X() + 1000, aInitialPosition.Y());
720 aCustomShape2d.SetHandleControllerPosition(0, aDesiredPosition);
721 Point aObservedPosition;
722 aCustomShape2d.GetHandlePosition(0, aObservedPosition);
723 sal_Int32 nDesiredX(aDesiredPosition.X); // awt::Point
724 sal_Int32 nObservedX(aObservedPosition.X()); // tools::Point
725 CPPUNIT_ASSERT_EQUAL_MESSAGE("handle X coordinate", nDesiredX, nObservedX);
728 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124029_arc_position)
730 // tdf121029 MS binary custom shape mso_sptArc has wrong position
731 // MS uses the sector for position reference. Error was, that
732 // LibreOffice has used the underlying ellipse.
733 loadFromURL(u"tdf124029_Arc_position.doc");
734 uno::Reference<drawing::XShape> xShape(getShape(0));
735 // The visual wrong position is due to a wrong shape width.
736 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
737 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
738 awt::Rectangle aFrameRect;
739 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
740 CPPUNIT_ASSERT_EQUAL_MESSAGE("shape width", static_cast<sal_uInt32>(1610),
741 static_cast<sal_uInt32>(aFrameRect.Width));
744 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124740_handle_path_coordsystem)
746 // tdf124740 OOXML shape with handle and w and h attribute on path has wrong
747 // handle position
748 // The handle position was scaled erroneously twice.
749 loadFromURL(u"tdf124740_HandleInOOXMLUserShape.pptx");
750 uno::Reference<drawing::XShape> xShape(getShape(0));
751 // The shape has one, horizontal adjust handle. It is about 1/5 of 10cm from left
752 // shape edge, shape is 6cm from left . That results in a position
753 // of 8cm from left page edge, which is 8000 in 1/100 mm unit.
754 SdrObjCustomShape& rSdrObjCustomShape(
755 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
756 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
757 Point aPosition;
758 aCustomShape2d.GetHandlePosition(0, aPosition);
759 double fX(aPosition.X());
760 // tolerance for rounding to integer
761 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("handle X coordinate", 8000.0, fX, 2.0);
764 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf115813_OOXML_XY_handle)
766 // The test covers all preset shapes with handles. Only these ones are
767 // excluded: arc, blockArc, chord, circularArrow, gear6, gear9, mathNotEqual, pie,
768 // leftCircularArrow, leftRightCircularArrow, swooshArrow.
769 // Connectors are included as ordinary shapes to prevent converting.
770 // Error was, that the handle movement and the changes to the shape did not follow
771 // the mouse movement.
772 loadFromURL(u"tdf115813_HandleMovementOOXMLPresetShapes.pptx");
774 // values in vector InteractionsHandles are in 1/100 mm and refer to page
775 for (sal_uInt8 i = 0; i < countShapes(); i++)
777 uno::Reference<drawing::XShape> xShape(getShape(i));
778 SdrObjCustomShape& rSdrObjCustomShape(
779 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
780 OUString sShapeType("non-primitive"); // default for ODF
781 const SdrCustomShapeGeometryItem& rGeometryItem(
782 rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
783 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
784 if (pAny)
785 *pAny >>= sShapeType;
787 sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
788 for (sal_uInt8 j = 0; j < nHandlesCount; j++)
790 css::awt::Point aInitialPosition(
791 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
792 // The handles are initialized in the test document, so that if the handle is moveable in
793 // that direction at all, then it can move at least with an amount of 100.
794 Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
795 rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
796 css::awt::Point aObservedPosition(
797 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
798 sal_Int32 nDesiredX(aDesiredPosition.X()); // tools::Point
799 sal_Int32 nDesiredY(aDesiredPosition.Y());
800 sal_Int32 nObservedX(aObservedPosition.X); // css::awt::Point
801 sal_Int32 nObservedY(aObservedPosition.Y);
802 // If a handle only moves in one direction, the difference is 100 for the other direction.
803 // There exists some rounding differences, therefore '<= 1' instead of '== 0'.
804 // The condition has the form '!(good cases)'.
805 if (!((abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) == 100)
806 || (abs(nDesiredX - nObservedX) == 100 && abs(nDesiredY - nObservedY) <= 1)
807 || (abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) <= 1)))
809 OUString sError
810 = OUString::number(i) + " " + sShapeType + ": " + OUString::number(j) + " X "
811 + OUString::number(nDesiredX) + "|" + OUString::number(nObservedX) + " Y "
812 + OUString::number(nDesiredY) + "|" + OUString::number(nObservedY);
813 CPPUNIT_FAIL(sError.toUtf8().getStr());
819 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testQuadraticCurveTo)
821 // tdf125782 command Q (quadraticcurveto) uses wrong 'current point'.
822 // When converting to cubic Bezier curve, this had resulted in a wrong first control point.
823 // The quadraticcurveto segment starts in shape center in the test file. The first control
824 // point should produce a horizontal tangent in the start point.
825 loadFromURL(u"tdf125782_QuadraticCurveTo.odg");
826 uno::Reference<drawing::XShape> xShape(getShape(0));
827 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
828 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
829 awt::Rectangle aBoundRect;
830 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
831 const double fHeight = static_cast<double>(aBoundRect.Height);
832 //Add some tolerance
833 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("bad height of quadraticcurveto", 3004, fHeight, 10.0);
836 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126512_OOXML_handle_in_ODP)
838 // The test covers all preset shapes with handles. Connectors are included as ordinary
839 // shapes to prevent converting. The file was created in PowerPoint 365 and then
840 // opened and exported to ODF format by LibreOffice.
841 // Error was, that for shapes, which were originally imported from OOXML, the handles
842 // could not be moved at all.
843 loadFromURL(u"tdf126512_OOXMLHandleMovementInODF.odp");
845 for (sal_uInt8 i = 0; i < countShapes(); i++)
847 uno::Reference<drawing::XShape> xShape(getShape(i));
848 SdrObjCustomShape& rSdrObjCustomShape(
849 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
850 OUString sShapeType("non-primitive"); // only to initialize, value not used here
851 const SdrCustomShapeGeometryItem& rGeometryItem(
852 rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
853 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
854 if (pAny)
855 *pAny >>= sShapeType;
857 sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
858 for (sal_uInt8 j = 0; j < nHandlesCount; j++)
860 css::awt::Point aInitialPosition(
861 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
862 // The handles are initialized in the test document, so that if the handle is moveable
863 // in that direction at all, then it can move at least with an amount of 100.
864 Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
865 rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
866 css::awt::Point aObservedPosition(
867 rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
868 if (aInitialPosition.X == aObservedPosition.X
869 && aInitialPosition.Y == aObservedPosition.Y)
871 OUString sError
872 = OUString::number(i) + " " + sShapeType + " " + OUString::number(j);
873 CPPUNIT_FAIL(sError.toUtf8().getStr());
879 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Mirror)
881 // The document contains two shapes, one with horizontal flip, the other with vertical
882 // flip. They are diamonds, so their text frame is symmetric to the center of the shape.
883 // The shapes have not stroke and no fill, so that the bounding box surrounds the text
884 // and therefore equals approximately the text frame.
885 // Error was, that because of wrong calculation, the flipped shapes do not use the
886 // text frame but the frame rectangle for their text.
887 loadFromURL(u"tdf127785_Mirror.odp");
889 uno::Reference<drawing::XShape> xShapeV(getShape(0));
890 uno::Reference<beans::XPropertySet> xShapeVProps(xShapeV, uno::UNO_QUERY);
891 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeVProps.is());
892 awt::Rectangle aBoundRectV;
893 xShapeVProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectV;
894 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip vertical wrong size.", 8000.0, aBoundRectV.Height,
895 10.0);
896 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip vertical wrong size.", 8000.0, aBoundRectV.Width,
897 10.0);
898 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip vertical wrong position.", 1000.0, aBoundRectV.X,
899 10.0);
900 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip vertical wrong position.", 2000.0, aBoundRectV.Y,
901 10.0);
903 uno::Reference<drawing::XShape> xShapeH(getShape(1));
904 uno::Reference<beans::XPropertySet> xShapeHProps(xShapeH, uno::UNO_QUERY);
905 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeHProps.is());
906 awt::Rectangle aBoundRectH;
907 xShapeHProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectH;
908 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip horizontal wrong size.", 8000.0, aBoundRectH.Height,
909 10.0);
910 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip horizontal wrong size.", 8000.0, aBoundRectH.Width,
911 10.0);
912 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip horizontal wrong position.", 13000.0, aBoundRectH.X,
913 10.0);
914 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Flip horizontal wrong position.", 2000.0, aBoundRectH.Y,
915 10.0);
918 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126060_3D_Z_Rotation)
920 // The document contains one textbox with inside overflowed text
921 // and the text has 3D z rotation. When we open the document we
922 // should see the text vertically and rotated from text bound center not text box.
924 loadFromURL(u"tdf126060_3D_Z_Rotation.pptx");
926 uno::Reference<drawing::XShape> xShape(getShape(0));
927 SdrObjCustomShape& rSdrObjCustomShape(
928 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
930 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong text camera Z rotation", 90.0,
931 rSdrObjCustomShape.GetCameraZRotation());
933 basegfx::B2DHomMatrix aObjectTransform;
934 basegfx::B2DPolyPolygon aObjectPolyPolygon;
935 rSdrObjCustomShape.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon);
937 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,0 position", 1492.0,
938 aObjectTransform.get(0, 0));
939 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,1 position", 0.0,
940 aObjectTransform.get(0, 1));
941 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 0,2 position", 1129.0,
942 aObjectTransform.get(0, 2));
943 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,0 position", 0.0,
944 aObjectTransform.get(1, 0));
945 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,1 position", 2500.0,
946 aObjectTransform.get(1, 1));
947 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong transformation at 1,2 position", 5846.0,
948 aObjectTransform.get(1, 2));
951 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Asymmetric)
953 // The document contains a shapes with vertical flip and text frame asymmetrical
954 // to shape. The shape has not stroke and no fill, so that the bounding box surrounds
955 // the text and therefore equals approximately the text frame.
956 // Error was, that the 180deg text rotation was not compensated for the position of
957 // the flipped text box.
958 loadFromURL(u"tdf127785_asymmetricTextBoxFlipV.odg");
960 uno::Reference<drawing::XShape> xShape(getShape(0));
961 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
962 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
963 awt::Rectangle aBoundRect;
964 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
966 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong left", 9000.0, aBoundRect.X, 10.0);
967 const double nRight = aBoundRect.X + aBoundRect.Width - 1;
968 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong right", 19000.0, nRight, 10.0);
969 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong top", 3000.0, aBoundRect.Y, 10.0);
970 const double nBottom = aBoundRect.Y + aBoundRect.Height - 1;
971 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong bottom", 18000.0, nBottom, 10.0);
974 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_TextRotateAngle)
976 // The document contains a shapes with vertical flip and a text frame with own
977 // rotate angle. The shape has not stroke and no fill, so that the bounding box
978 // surrounds the text and therefore equals approximately the text frame.
979 // Error was, that the compensation for the 180° rotation added for vertical
980 // flip were not made to the text box position but to the text matrix.
981 loadFromURL(u"tdf127785_TextRotateAngle.odp");
983 uno::Reference<drawing::XShape> xShape(getShape(0));
984 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
985 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
986 awt::Rectangle aBoundRect;
987 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
989 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong left", 2000.0, aBoundRect.X, 10.0);
990 const double nRight = aBoundRect.X + aBoundRect.Width - 1;
991 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong right", 14000.0, nRight, 10.0);
992 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong top", 3000.0, aBoundRect.Y, 10.0);
993 const double nBottom = aBoundRect.Y + aBoundRect.Height - 1;
994 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong bottom", 9000.0, nBottom, 10.0);
997 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf128413_tbrlOnOff)
999 // The document contains a rotated shape with text. The error was, that switching
1000 // tb-rl writing-mode on, changed the shape size and position.
1002 loadFromURL(u"tdf128413_tbrl_OnOff.odp");
1003 uno::Reference<drawing::XShape> xShape(getShape(0));
1004 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
1005 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
1006 awt::Rectangle aOrigRect;
1007 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aOrigRect;
1009 SdrObjCustomShape& rSdrObjCustomShape(
1010 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1011 rSdrObjCustomShape.SetVerticalWriting(true);
1013 awt::Rectangle aObservedRect;
1014 xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aObservedRect;
1016 CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong width", aOrigRect.Width, aObservedRect.Width);
1017 CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong height", aOrigRect.Height, aObservedRect.Height);
1018 CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong X position", aOrigRect.X, aObservedRect.X);
1019 CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape has wrong Y position", aOrigRect.Y, aObservedRect.Y);
1022 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf129532_MatrixFlipV)
1024 // The document contains two rotated shapes with the same geometry. For one of them
1025 // "matrix(1 0 0 -1 0cm 0cm)" was manually added to the value of the draw:transform
1026 // attribute. That should result in mirroring on the x-axis. Error was, that the lines
1027 // which are drawn on the shape rectangle were mirrored, but not the rectangle itself.
1028 // The rectangle was only shifted.
1029 loadFromURL(u"tdf129532_MatrixFlipV.odg");
1031 uno::Reference<drawing::XShape> xShape0(getShape(0));
1032 uno::Reference<beans::XPropertySet> xShape0Props(xShape0, uno::UNO_QUERY);
1033 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape0Props.is());
1034 awt::Rectangle aBoundRect0;
1035 xShape0Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect0;
1037 uno::Reference<drawing::XShape> xShape1(getShape(1));
1038 uno::Reference<beans::XPropertySet> xShape1Props(xShape1, uno::UNO_QUERY);
1039 CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape1Props.is());
1040 awt::Rectangle aBoundRect1;
1041 xShape1Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect1;
1043 // The size of the two BoundRect rectangles are the same in case of correct
1044 // vertical mirroring.
1045 CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect width", aBoundRect0.Width, aBoundRect1.Width);
1046 CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect height", aBoundRect0.Height, aBoundRect1.Height);
1049 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandT_CaseZeroHeight)
1051 // tdf103474 custom shape with command T to create quarter ellipses in a bracket,
1052 // corner case where the ellipse has zero height.
1053 // Error was, that the calculation of the circle angle from the ellipse
1054 // angle results in a wrong angle for the case 180° and height zero.
1055 loadFromURL(u"tdf103474_commandT_CaseZeroHeight.odp");
1056 uno::Reference<drawing::XShape> xShape(getShape(0));
1057 // The end points of the straight line segment should have the same x-coordinate of left
1058 // of shape, and different y-coordinates, one top and the other bottom of the shape.
1059 SdrObjCustomShape& rSdrObjCustomShape(
1060 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1061 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
1062 rtl::Reference<SdrPathObj> pPathObj(
1063 static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().get()));
1064 CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
1065 const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
1066 CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
1067 aPolyPolygon.count());
1068 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1069 // Get the middle points of the polygon. They are the endpoints of the
1070 // straight line segment regardless of the quarter ellipse parts, because
1071 // the shape is symmetric.
1072 const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
1073 const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
1074 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 13999.0, aStart.getX(), 1.0);
1075 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 13999.0, aEnd.getX(), 1.0);
1076 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
1077 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
1080 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandG_CaseZeroHeight)
1082 // Some as above, but with shape with command G.
1083 loadFromURL(u"tdf103474_commandG_CaseZeroHeight.odp");
1084 uno::Reference<drawing::XShape> xShape(getShape(0));
1085 // The end points of the straight line segment should have the same x-coordinate of left
1086 // of shape, and different y-coordinates, one top and the other bottom of the shape.
1087 SdrObjCustomShape& rSdrObjCustomShape(
1088 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1089 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
1090 rtl::Reference<SdrPathObj> pPathObj(
1091 static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().get()));
1092 CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
1093 const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
1094 CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
1095 aPolyPolygon.count());
1096 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1097 // Get the middle points of the polygon. They are the endpoints of the
1098 // straight line segment regardless of the quarter ellipse parts, because
1099 // the shape is symmetric.
1100 const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
1101 const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
1102 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 1999.0, aStart.getX(), 1.0);
1103 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 1999.0, aEnd.getX(), 1.0);
1104 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
1105 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
1108 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf122323_largeSwingAngle)
1110 // SwingAngles are clamped to [-360;360] in MS Office. Error was, that LO calculated
1111 // the end angle and used it modulo 360, no full ellipse was drawn.
1112 loadFromURL(u"tdf122323_swingAngle_larger360deg.pptx");
1113 uno::Reference<drawing::XShape> xShape(getShape(0));
1114 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
1115 SdrObjCustomShape& rSdrObjCustomShape(
1116 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1117 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
1118 rtl::Reference<SdrPathObj> pPathObj(
1119 static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().get()));
1120 CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
1121 const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
1122 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1123 const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(0));
1124 // last point comes from line to center, therefore -2 instead of -1
1125 const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() - 2));
1126 CPPUNIT_ASSERT_EQUAL_MESSAGE("Start <> End", aStart, aEnd);
1129 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf141268)
1131 loadFromURL(u"tdf141268.odp");
1132 uno::Reference<drawing::XShape> xShape(getShape(0));
1133 SdrObjCustomShape& rSdrCustomShape(
1134 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1136 // Check left/bottom of bound rect. Without fix it would be left=6722, bottom=9483.
1137 tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
1138 CPPUNIT_ASSERT_EQUAL(tools::Long(7620), aBoundRect.Left());
1139 CPPUNIT_ASSERT_EQUAL(tools::Long(8584), aBoundRect.Bottom());
1142 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf136176)
1144 // Error was, that fObjectRotation was not correctly updated after shearing.
1145 // The problem becomes visible after save and reload.
1146 loadFromURL(u"tdf136176_rot30_flip.odg");
1148 for (sal_uInt16 i = 0; i < 3; i++)
1150 // get shape
1151 uno::Reference<drawing::XShape> xShape(getShape(i));
1152 SdrObjCustomShape& rSdrObjCustomShape(
1153 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1154 // apply shearing 20deg
1155 const Point aCenter = rSdrObjCustomShape.GetSnapRect().Center();
1156 rSdrObjCustomShape.Shear(aCenter, 2000_deg100, tan(basegfx::deg2rad(20.0)), false);
1159 // Save and reload
1160 saveAndReload("draw8");
1162 // Expected values of point 4 of the shape polygon
1163 const OString sTestCase[] = { "FlipH", "FlipV", "FlipHV" };
1164 const double fX[] = { 14981.0, 3849.0, 15214.0 };
1165 const double fY[] = { 9366.0, 16464.0, 23463.0 };
1167 // Verify correct positions
1168 for (sal_uInt16 i = 0; i < 3; i++)
1170 // Get shape
1171 const uno::Reference<drawing::XShape> xShape(getShape(i));
1172 const SdrObjCustomShape& rSdrObjCustomShape(
1173 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1174 // Create polygon from shape and examine point 4 of the polygon
1175 const basegfx::B2DPolyPolygon aLineGeometry = rSdrObjCustomShape.GetLineGeometry(false);
1176 const basegfx::B2DPoint aPoint(aLineGeometry.getB2DPolygon(0).getB2DPoint(4));
1177 // Allow some tolerance for rounding errors
1178 if (fabs(aPoint.getX() - fX[i]) > 2.0 || fabs(aPoint.getY() - fY[i]) > 2.0)
1180 CPPUNIT_ASSERT_EQUAL_MESSAGE(sTestCase[i].getStr(), aPoint,
1181 basegfx::B2DPoint(fX[i], fY[i]));
1186 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf148501_OctagonBevel)
1188 // The document contains a shape "Octagon Bevel". It should use shadings 40%, 20%, -20%, -40%
1189 // from left-top to bottom-right. The test examines actual color, not the geometry.
1190 // Load document
1191 loadFromURL(u"tdf148501_OctagonBevel.odp");
1193 // Generate bitmap from shape
1194 uno::Reference<drawing::XShape> xShape = getShape(0);
1195 GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", maTempFile.GetURL());
1197 // Read bitmap and test color
1198 // expected in order top-left, top, top-right, right, bottom-right:
1199 // RGB(165|195|266), RGB(139|176|217), RGB(91|127|166), RGB(68|95|124), RGB(68|95|124)
1200 // Without applied patch the colors were:
1201 // RGB(193|214,236), RGB(193|214,236), RGB(80|111|145), RGB(23|32|41), RGB(193|214|236)
1202 // So we test segments top, right and bottom-right.
1203 SvFileStream aFileStream(maTempFile.GetURL(), StreamMode::READ);
1204 vcl::PngImageReader aPNGReader(aFileStream);
1205 BitmapEx aBMPEx = aPNGReader.read();
1206 Bitmap aBMP = aBMPEx.GetBitmap();
1207 Bitmap::ScopedReadAccess pRead(aBMP);
1208 Size aSize = aBMP.GetSizePixel();
1210 // GetColor(Y,X). The chosen threshold for the ColorDistance can be adapted if necessary.
1211 Color aActualColor = pRead->GetColor(aSize.Height() * 0.17, aSize.Width() * 0.5); // top
1212 Color aExpectedColor(139, 176, 217);
1213 sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
1214 CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
1215 aActualColor = pRead->GetColor(aSize.Height() * 0.5, aSize.Width() * 0.83); // right
1216 aExpectedColor = Color(68, 95, 124); // same for right and bottom-right
1217 nColorDistance = aExpectedColor.GetColorError(aActualColor);
1218 CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
1219 aActualColor = pRead->GetColor(aSize.Height() * 0.75, aSize.Width() * 0.75); // bottom-right
1220 nColorDistance = aExpectedColor.GetColorError(aActualColor);
1221 CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
1224 bool lcl_getShapeSegments(uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments,
1225 const uno::Reference<drawing::XShape>& xShape)
1227 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY_THROW);
1228 uno::Any anotherAny = xShapeProps->getPropertyValue("CustomShapeGeometry");
1229 uno::Sequence<beans::PropertyValue> aCustomShapeGeometry;
1230 if (!(anotherAny >>= aCustomShapeGeometry))
1231 return false;
1232 uno::Sequence<beans::PropertyValue> aPathProps;
1233 for (beans::PropertyValue const& rProp : std::as_const(aCustomShapeGeometry))
1235 if (rProp.Name == "Path")
1237 rProp.Value >>= aPathProps;
1238 break;
1242 for (beans::PropertyValue const& rProp : std::as_const(aPathProps))
1244 if (rProp.Name == "Segments")
1246 rProp.Value >>= rSegments;
1247 break;
1250 if (rSegments.getLength() > 2)
1251 return true;
1252 else
1253 return false;
1256 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf148714_CurvedArrows)
1258 // Error was, that the line between 1. and 2. arc was missing.
1259 loadFromURL(u"tdf148714_CurvedArrows.ppt");
1261 for (sal_Int32 nShapeIndex = 0; nShapeIndex < 4; nShapeIndex++)
1263 uno::Reference<drawing::XShape> xShape(getShape(nShapeIndex));
1264 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
1265 CPPUNIT_ASSERT(lcl_getShapeSegments(aSegments, xShape));
1267 if (nShapeIndex == 0 || nShapeIndex == 3)
1269 // curvedDownArrow or curvedLeftArrow. Segments should start with VW. Without fix it was
1270 // V with count 2, which means VV.
1271 CPPUNIT_ASSERT_EQUAL(
1272 sal_Int16(drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC),
1273 aSegments[0].Command);
1274 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), aSegments[0].Count);
1275 CPPUNIT_ASSERT_EQUAL(
1276 sal_Int16(drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO),
1277 aSegments[1].Command);
1278 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), aSegments[1].Count);
1280 else
1282 // curvedUpArrow or curvedRightArrow. Segments should start with BA. Without fix is was
1283 // B with count 2, which means BB.
1284 CPPUNIT_ASSERT_EQUAL(sal_Int16(drawing::EnhancedCustomShapeSegmentCommand::ARC),
1285 aSegments[0].Command);
1286 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), aSegments[0].Count);
1287 CPPUNIT_ASSERT_EQUAL(sal_Int16(drawing::EnhancedCustomShapeSegmentCommand::ARCTO),
1288 aSegments[1].Command);
1289 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), aSegments[1].Count);
1294 CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf148707_two_commands_B_V)
1296 // tdf148707 custom shape with multiple command B or multiple command V were drawn with a line
1297 // between the arcs as if the second command was a A or W respectively.
1298 // The test document has a shape with path "V 0 0 50 100 0 50 25 0 50 0 100 100 75 0 100 50 N"
1299 // and a shape with path "B 0 0 50 100 0 50 25 100 50 0 100 100 75 100 100 50 N".
1300 loadFromURL(u"tdf148707_two_commands_B_V.odp");
1301 for (sal_uInt8 i = 0; i < 2; i++)
1303 uno::Reference<drawing::XShape> xShape(getShape(i));
1304 // In case no line is drawn, two polygons are generated; with line only one polygon
1305 SdrObjCustomShape& rSdrObjCustomShape(
1306 static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
1307 EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
1308 rtl::Reference<SdrPathObj> pPathObj(
1309 static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry().get()));
1310 CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
1311 const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
1312 CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", sal_uInt32(2), aPolyPolygon.count());
1317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */