summaryrefslogtreecommitdiffstats
path: root/svx/qa/unit
diff options
context:
space:
mode:
Diffstat (limited to 'svx/qa/unit')
-rw-r--r--svx/qa/unit/XTableImportExportTest.cxx80
-rw-r--r--svx/qa/unit/classicshapes.cxx189
-rw-r--r--svx/qa/unit/customshapes.cxx819
-rw-r--r--svx/qa/unit/data/graphic.pdfbin0 -> 7243 bytes
-rw-r--r--svx/qa/unit/data/shadow-scale-origin.pptxbin0 -> 31984 bytes
-rw-r--r--svx/qa/unit/data/svx-dialogs-test.txt80
-rw-r--r--svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odpbin0 -> 12420 bytes
-rw-r--r--svx/qa/unit/data/tdf103474_commandT_CaseZeroHeight.odpbin0 -> 12833 bytes
-rw-r--r--svx/qa/unit/data/tdf115813_HandleMovementOOXMLPresetShapes.pptxbin0 -> 34858 bytes
-rw-r--r--svx/qa/unit/data/tdf121761_Accuracy_command_X.odpbin0 -> 11177 bytes
-rw-r--r--svx/qa/unit/data/tdf121845_HalfEllipseVML.docbin0 -> 27648 bytes
-rw-r--r--svx/qa/unit/data/tdf121845_Two_commands_U.odgbin0 -> 10751 bytes
-rw-r--r--svx/qa/unit/data/tdf121845_WidthOrientation_command_U.odgbin0 -> 9908 bytes
-rw-r--r--svx/qa/unit/data/tdf121845_start40_swing480.docbin0 -> 27648 bytes
-rw-r--r--svx/qa/unit/data/tdf121952_Toggle_direction_command_X.odpbin0 -> 12029 bytes
-rw-r--r--svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptxbin0 -> 15580 bytes
-rw-r--r--svx/qa/unit/data/tdf122964_MultipleMoveTo.odgbin0 -> 9666 bytes
-rw-r--r--svx/qa/unit/data/tdf124029_Arc_position.docbin0 -> 19456 bytes
-rw-r--r--svx/qa/unit/data/tdf124212_handle_position.odgbin0 -> 10108 bytes
-rw-r--r--svx/qa/unit/data/tdf124740_HandleInOOXMLUserShape.pptxbin0 -> 14929 bytes
-rw-r--r--svx/qa/unit/data/tdf125782_QuadraticCurveTo.odgbin0 -> 9910 bytes
-rw-r--r--svx/qa/unit/data/tdf126060_3D_Z_Rotation.pptxbin0 -> 32602 bytes
-rw-r--r--svx/qa/unit/data/tdf126512_OOXMLHandleMovementInODF.odpbin0 -> 62627 bytes
-rw-r--r--svx/qa/unit/data/tdf127785_Mirror.odpbin0 -> 13742 bytes
-rw-r--r--svx/qa/unit/data/tdf127785_TextRotateAngle.odpbin0 -> 12841 bytes
-rw-r--r--svx/qa/unit/data/tdf127785_asymmetricTextBoxFlipV.odgbin0 -> 12166 bytes
-rw-r--r--svx/qa/unit/data/tdf128413_tbrl_OnOff.odpbin0 -> 14170 bytes
-rw-r--r--svx/qa/unit/data/tdf129532_MatrixFlipV.odgbin0 -> 10036 bytes
-rw-r--r--svx/qa/unit/data/tdf136176_rot30_flip.odgbin0 -> 10344 bytes
-rw-r--r--svx/qa/unit/data/tdf93998.odpbin0 -> 11308 bytes
-rw-r--r--svx/qa/unit/data/tdf98583_ShearHorizontal.odpbin0 -> 14328 bytes
-rw-r--r--svx/qa/unit/data/tdf98584_ShearVertical.odgbin0 -> 9109 bytes
-rw-r--r--svx/qa/unit/data/unodraw-writer-image.odtbin0 -> 9564 bytes
-rw-r--r--svx/qa/unit/data/viewBox_positive_twolines_strict.odpbin0 -> 12107 bytes
-rw-r--r--svx/qa/unit/gallery/test_gallery.cxx306
-rw-r--r--svx/qa/unit/sdr.cxx94
-rw-r--r--svx/qa/unit/svdraw.cxx110
-rw-r--r--svx/qa/unit/svdraw/test_SdrTextObject.cxx46
-rw-r--r--svx/qa/unit/svx-dialogs-test.cxx63
-rw-r--r--svx/qa/unit/unodraw.cxx112
-rw-r--r--svx/qa/unit/xoutdev.cxx68
41 files changed, 1967 insertions, 0 deletions
diff --git a/svx/qa/unit/XTableImportExportTest.cxx b/svx/qa/unit/XTableImportExportTest.cxx
new file mode 100644
index 000000000..31962a52b
--- /dev/null
+++ b/svx/qa/unit/XTableImportExportTest.cxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <unotest/bootstrapfixturebase.hxx>
+
+#include <sal/types.h>
+#include <sfx2/app.hxx>
+#include <unotools/tempfile.hxx>
+#include <svx/xtable.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+using namespace css;
+
+class XTableImportExportTest : public CppUnit::TestFixture
+{
+public:
+ virtual void setUp() override
+ {
+ CppUnit::TestFixture::setUp();
+ SfxApplication::GetOrCreate();
+ }
+};
+
+CPPUNIT_TEST_FIXTURE(XTableImportExportTest, testImportExport)
+{
+ utl::TempFile aTempFile(nullptr, true);
+ aTempFile.EnableKillingFile();
+ OUString aTempURL = aTempFile.GetURL();
+ BitmapChecksum aChecksum(0);
+
+ {
+ XBitmapList xBitmapList(aTempURL, "REF");
+ uno::Reference<container::XNameContainer> xNameContainer(xBitmapList.createInstance());
+ CPPUNIT_ASSERT(xNameContainer.is());
+
+ Bitmap aBitmap(Size(5, 5), 24);
+ aBitmap.Erase(COL_RED);
+ BitmapEx aBitmapEx(aBitmap);
+ Graphic aGraphic(aBitmapEx);
+ uno::Reference<awt::XBitmap> xBitmap(aGraphic.GetXGraphic(), css::uno::UNO_QUERY);
+
+ xNameContainer->insertByName("SomeBitmap", uno::makeAny(xBitmap));
+ xBitmapList.Save();
+
+ aChecksum = aBitmap.GetChecksum();
+ }
+
+ {
+ XBitmapList xBitmapList(aTempURL, "REF");
+ bool bResult = xBitmapList.Load();
+ CPPUNIT_ASSERT(bResult);
+ uno::Reference<container::XNameContainer> xNameContainer(xBitmapList.createInstance());
+ CPPUNIT_ASSERT(xNameContainer.is());
+
+ uno::Any aAny = xNameContainer->getByName("SomeBitmap");
+ CPPUNIT_ASSERT(aAny.has<uno::Reference<awt::XBitmap>>());
+ auto xBitmap = aAny.get<uno::Reference<awt::XBitmap>>();
+ CPPUNIT_ASSERT(xBitmap.is());
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGraphic.is());
+ Graphic aGraphic(xGraphic);
+ CPPUNIT_ASSERT(!aGraphic.IsNone());
+ Bitmap aBitmap = aGraphic.GetBitmapEx().GetBitmap();
+ CPPUNIT_ASSERT_EQUAL(aChecksum, aBitmap.GetChecksum());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/classicshapes.cxx b/svx/qa/unit/classicshapes.cxx
new file mode 100644
index 000000000..b03210d12
--- /dev/null
+++ b/svx/qa/unit/classicshapes.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <rtl/ustring.hxx>
+#include <editeng/unoprnms.hxx>
+
+#include <cppunit/TestAssert.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+const OUString sDataDirectory("svx/qa/unit/data/");
+
+/// Tests not about special features of custom shapes, but about shapes in general.
+class ClassicshapesTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+protected:
+ uno::Reference<lang::XComponent> mxComponent;
+ uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex);
+
+public:
+ virtual void setUp() override
+ {
+ test::BootstrapFixture::setUp();
+ mxDesktop.set(frame::Desktop::create(m_xContext));
+ }
+
+ virtual void tearDown() override
+ {
+ if (mxComponent.is())
+ {
+ mxComponent->dispose();
+ }
+ test::BootstrapFixture::tearDown();
+ }
+};
+
+uno::Reference<drawing::XShape> ClassicshapesTest::getShape(sal_uInt8 nShapeIndex,
+ sal_uInt8 nPageIndex)
+{
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
+ uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
+ uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(nPageIndex),
+ uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is());
+ return xShape;
+}
+
+CPPUNIT_TEST_FIXTURE(ClassicshapesTest, testTdf98584ShearVertical)
+{
+ // The document contains draw:rect, draw:polygon and draw:path objects.
+ // They are vertical sheared by skewY(-0.927295218002) or by matrix(1 2 0 1 1cm 1cm).
+ // Notice, skewY and matrix are interpreted on file open, but not written on file save.
+ // They are converted to rotate * shear horizontal * scale.
+ // Besides using a wrong sign in shear angle, error was, that TRSetGeometry of SdrPathObj did
+ // not consider the additional scaling (tdf#98565).
+ const OUString sFileName("tdf98584_ShearVertical.odg");
+ const OUString sURL(m_directories.getURLFromSrc(sDataDirectory) + sFileName);
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+ // All tests have a small tolerance for to avoid failing because of rounding errors.
+
+ // Tests skewY
+ sal_Int32 nShearE = -5313; // expected angle for horizontal shear
+ sal_Int32 nRotE = 30687; // = -5313 expected angle for generated rotation
+ // expected width of shape, should not change on vertical shearing
+ sal_Int32 nWidthE = 5001;
+
+ for (sal_uInt8 nPageIndex = 0; nPageIndex < 3; ++nPageIndex)
+ {
+ awt::Rectangle aFrameRect;
+ uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
+ const sal_Int32 nWidthA(aFrameRect.Width);
+ if (abs(nWidthE - nWidthA) > 2)
+ sErrors += "skewY page " + OUString::number(nPageIndex) + " width expected "
+ + OUString::number(nWidthE) + ", actual " + OUString::number(nWidthA) + "\n";
+ sal_Int32 nShearA(0);
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_SHEARANGLE) >>= nShearA;
+ if (abs(nShearE - nShearA) > 2)
+ sErrors += "skewY page" + OUString::number(nPageIndex) + " shear angle expected "
+ + OUString::number(nShearE) + ", actual " + OUString::number(nShearA) + "\n";
+ sal_Int32 nRotA(0);
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nRotA;
+ if (abs(nRotE - nRotA) > 2)
+ sErrors += "skewY page" + OUString::number(nPageIndex) + " rotate angle expected "
+ + OUString::number(nRotE) + ", actual " + OUString::number(nRotA) + "\n";
+ }
+
+ // Tests matrix
+ nShearE = -6343;
+ nRotE = 29657;
+ nWidthE = 5001;
+
+ for (sal_uInt8 nPageIndex = 3; nPageIndex < 6; ++nPageIndex)
+ {
+ awt::Rectangle aFrameRect;
+ uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
+ const sal_Int32 nWidthA(aFrameRect.Width);
+ if (abs(nWidthE - nWidthA) > 2)
+ sErrors += "matrix page " + OUString::number(nPageIndex) + " width expected "
+ + OUString::number(nWidthE) + ", actual " + OUString::number(nWidthA) + "\n";
+ sal_Int32 nShearA(0);
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_SHEARANGLE) >>= nShearA;
+ if (abs(nShearE - nShearA) > 2)
+ sErrors += "matrix page" + OUString::number(nPageIndex) + " shear angle expected "
+ + OUString::number(nShearE) + ", actual " + OUString::number(nShearA) + "\n";
+ sal_Int32 nRotA(0);
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nRotA;
+ if (abs(nRotE - nRotA) > 2)
+ sErrors += "matrix page" + OUString::number(nPageIndex) + " rotate angle expected "
+ + OUString::number(nRotE) + ", actual " + OUString::number(nRotA) + "\n";
+ }
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(ClassicshapesTest, testTdf98583ShearHorizontal)
+{
+ // The document contains rectangles with LT 3000,5000 and RB 5000,9000.
+ // skewX (-0.78539816339744830961) = skewX(-45deg) is applied on the first page
+ // matrix(1 0 1 1 0cm 0cm) on the second page. Both should result in a parallelogram with
+ // LT 8000,5000 and RB 14000, 9000, which means width 6001, height 4001.
+ // Error was, that not the mathematical matrix was used, but the API matrix, which has
+ // wrong sign in shear angle.
+ const OUString sFileName("tdf98583_ShearHorizontal.odp");
+ const OUString sURL(m_directories.getURLFromSrc(sDataDirectory) + sFileName);
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+ // All tests have a small tolerance for to avoid failing because of rounding errors.
+ const sal_Int32 nLeftE(8000); // expected values
+ const sal_Int32 nTopE(5000);
+ const sal_Int32 nWidthE(6001);
+ const sal_Int32 nHeightE(4001);
+ for (sal_uInt8 nPageIndex = 0; nPageIndex < 2; ++nPageIndex)
+ {
+ awt::Rectangle aFrameRect;
+ uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
+ const sal_Int32 nLeftA(aFrameRect.X);
+ const sal_Int32 nTopA(aFrameRect.Y);
+ const sal_Int32 nWidthA(aFrameRect.Width);
+ const sal_Int32 nHeightA(aFrameRect.Height);
+ if (abs(nLeftE - nLeftA) > 2 || abs(nTopE - nTopA) > 2)
+ sErrors += "page " + OUString::number(nPageIndex) + " LT expected "
+ + OUString::number(nLeftE) + " | " + OUString::number(nTopE) + ", actual "
+ + OUString::number(nLeftA) + " | " + OUString::number(nTopA) + "\n";
+ if (abs(nWidthE - nWidthA) > 2 || abs(nHeightE - nHeightA) > 2)
+ sErrors += "page " + OUString::number(nPageIndex) + " width x height expected "
+ + OUString::number(nWidthE) + " x " + OUString::number(nHeightE)
+ + ", actual " + OUString::number(nWidthA) + " x "
+ + OUString::number(nHeightA) + "\n";
+ }
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx
new file mode 100644
index 000000000..0c362cac1
--- /dev/null
+++ b/svx/qa/unit/customshapes.cxx
@@ -0,0 +1,819 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <rtl/ustring.hxx>
+#include <editeng/unoprnms.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/unoapi.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <cppunit/TestAssert.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+const OUString sDataDirectory("svx/qa/unit/data/");
+
+/// Tests for svx/source/customshapes/ code.
+class CustomshapesTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+protected:
+ uno::Reference<lang::XComponent> mxComponent;
+ // get shape nShapeIndex from page 0
+ uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex);
+ sal_uInt8 countShapes();
+
+public:
+ virtual void setUp() override
+ {
+ test::BootstrapFixture::setUp();
+ mxDesktop.set(frame::Desktop::create(m_xContext));
+ }
+
+ virtual void tearDown() override
+ {
+ if (mxComponent.is())
+ {
+ mxComponent->dispose();
+ }
+ test::BootstrapFixture::tearDown();
+ }
+};
+
+uno::Reference<drawing::XShape> CustomshapesTest::getShape(sal_uInt8 nShapeIndex)
+{
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
+ uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
+ uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is());
+ return xShape;
+}
+
+sal_uInt8 CustomshapesTest::countShapes()
+{
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
+ uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
+ uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
+ return xDrawPage->getCount();
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testViewBoxLeftTop)
+{
+ // tdf#121890 formula values "left" and "top" are wrongly calculated
+ // Load a document with two custom shapes of type "non-primitive"
+ OUString aURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "viewBox_positive_twolines_strict.odp";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Get the shape "leftright". Error was, that the identifier "left" was always set to zero, thus
+ // the path was outside the frame rectangle for a viewBox having a positive "left" value.
+ uno::Reference<drawing::XShape> xShapeLR(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeLRProps(xShapeLR, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'leftright' properties", xShapeLRProps.is());
+ awt::Rectangle aFrameRectLR;
+ xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectLR;
+ awt::Rectangle aBoundRectLR;
+ xShapeLRProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectLR;
+ // difference should be zero, but allow some rounding errors
+ CPPUNIT_ASSERT_LESS(static_cast<long>(3), labs(aFrameRectLR.X - aBoundRectLR.X));
+
+ // Get the shape "topbottom". Error was, that the identifier "top" was always set to zero, thus
+ // the path was outside the frame rectangle for a viewBox having a positive "top" value.
+ uno::Reference<drawing::XShape> xShapeTB(getShape(1));
+ uno::Reference<beans::XPropertySet> xShapeTBProps(xShapeTB, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape 'topbottom' properties", xShapeTBProps.is());
+ awt::Rectangle aFrameRectTB;
+ xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRectTB;
+ awt::Rectangle aBoundRectTB;
+ xShapeTBProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectTB;
+ // difference should be zero, but allow some rounding errors
+ CPPUNIT_ASSERT_LESS(static_cast<long>(3), labs(aFrameRectTB.Y - aBoundRectTB.Y));
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testAccuracyCommandX)
+{
+ // 121761 Increase accuracy of quarter circles drawn by command X or Y
+ // The loaded document has a quarter circle with radius 10000 (unit 1/100 mm)
+ // which is rotated by 45deg. The test considers the segment.
+ OUString aURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf121761_Accuracy_command_X.odp";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Get the shape "arc_45deg_rotated". Error was, that a Bezier curve with bad parameters
+ // was used, thus the segment height was obviously smaller than for a true circle.
+ // Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ double fHeight = static_cast<double>(aBoundRect.Height);
+ // The tolerance is a guess, might be smaller.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 2942.0, fHeight, 8.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testToggleCommandXY)
+{
+ // 121952 Toggle x- and y-direction if command X has several parameters
+ // The loaded document has a shape with command X and two parameter placed on a diagonal.
+ // The radius of the quarter circles are both 10000 (unit 1/100 mm).
+ // The shape is rotated by 45deg, so you get two segments, one up and one down.
+ OUString aURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf121952_Toggle_direction_command_X.odp";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Error was, that the second segment was drawn with same direction as first one. If drawn
+ // correctly, the bounding box height of the segments together is about twice the single
+ // segment height. Math: segment height approx 10000 * ( 1 - sqrt(0.5)) + line width
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ double fHeight = static_cast<double>(aBoundRect.Height);
+ // The tolerance is a guess, might be smaller.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("segment height out of tolerance", 5871.0, fHeight, 16.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testMultipleMoveTo)
+{
+ // tdf122964 Multiple moveTo has to be treated as lineTo in draw:enhanced-path
+ // Load a document with path "M 0 0 5 10 10 0 N"
+ OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf122964_MultipleMoveTo.odg";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Error was, that the second and further parameter pairs were treated as moveTo,
+ // and so the generated path was empty, resulting in zero width and height of the
+ // bounding box. It has to be treated same as "M 0 0 L 5 10 10 0 N".
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ bool bIsZero(aBoundRect.Height == 0 && aBoundRect.Width == 0);
+ CPPUNIT_ASSERT_MESSAGE("Path is empty", !bIsZero);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testWidthOrientationCommandU)
+{
+ // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
+ // Load a document with path "M 750 0 L 750 500 250 500 250 0 U 500 0 500 500 0 180 N"
+ // in viewBox="0 0 1000 500" and width="10cm", height="5cm".
+ const OUString sFileName("tdf121845_WidthOrientation_command_U.odg");
+ const OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Error was, that the width and height of the ellipse was halved and that the ellipse
+ // was not drawn clockwise but counter clockwise.
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const double fWidth = static_cast<double>(aBoundRect.Width);
+ // Need some tolerance for line width
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong width", 10000.0, fWidth, 40.0);
+ const double fHeight = static_cast<double>(aBoundRect.Height);
+ // Wrong orientation draws segment above the top of the viewBox and so increases 'Height'.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("wrong orientation", 5000.0, fHeight, 40.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testHalfEllipseVML)
+{
+ // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
+ // Load a document which was converted from VML to doc by Word. It had a VML
+ // path="m750,al500,,500,500,,-11796480e" resulting in a lower half circle.
+ const OUString sFileName("tdf121845_HalfEllipseVML.doc");
+ const OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Error was, that a full circle instead of the half circle was draw.
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const double fDiff2HmW = static_cast<double>(2 * aBoundRect.Height - aBoundRect.Width);
+ // Need some tolerance for line width
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("not a half circle", 0.0, fDiff2HmW, 40.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testLargeSwingAngleVML)
+{
+ // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
+ // Load a document which was converted from VML to doc by Word. It had a VML
+ // path="al50,50,45,45,2621440,31457280e" resulting in a full circle plus 120 deg segment.
+ const OUString sFileName("tdf121845_start40_swing480.doc");
+ const OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ // Error was, that only the 120 deg segment was drawn.
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const double fDiffWmH = static_cast<double>(aBoundRect.Width - aBoundRect.Height);
+ // Need some tolerance for line width
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Full circle plus segment expected", 0.0, fDiffWmH, 10.0);
+}
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf121845_two_commands_U)
+{
+ // tdf121845 custom shape with command U (angleellipse) is wrongly drawn
+ // Load a document with path "U 950 250 200 200 90 180 250 250 200 200 180 270 N"
+ // Error was, that the second ellipse segment was interpreted as command T and
+ // thus a line from first to second segment was drawn.
+ const OUString sFileName("tdf121845_Two_commands_U.odg");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // In case no line is drawn, two polygons are generated; with line only one polygon
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+ CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+ const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(2),
+ aPolyPolygon.count());
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124212_handle_position)
+{
+ // tdf124212 Adjustment handle reacts wrongly, if custom shape has a non
+ // default viewBox. Load a document with svg:viewBox="10800 0 10800 21600"
+ // Error was, that moving the controller results in a handle position that
+ // does not reflect the movement.
+ const OUString sFileName("tdf124212_handle_position.odg");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // The shape has one, horizontal adjust handle.
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ Point aInitialPosition;
+ aCustomShape2d.GetHandlePosition(0, aInitialPosition);
+ css::awt::Point aDesiredPosition(aInitialPosition.X() + 1000, aInitialPosition.Y());
+ aCustomShape2d.SetHandleControllerPosition(0, aDesiredPosition);
+ Point aObservedPosition;
+ aCustomShape2d.GetHandlePosition(0, aObservedPosition);
+ sal_Int32 nDesiredX(aDesiredPosition.X); // awt::Point
+ sal_Int32 nObservedX(aObservedPosition.X()); // tools::Point
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("handle X coordinate", nDesiredX, nObservedX);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124029_arc_position)
+{
+ // tdf121029 MS binary custom shape mso_sptArc has wrong position
+ // MS uses the sector for position reference. Error was, that
+ // LibreOffice has used the underlying ellipse.
+ const OUString sFileName("tdf124029_Arc_position.doc");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.text.TextDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // The visual wrong position is due to a wrong shape width.
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aFrameRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("shape width", static_cast<sal_uInt32>(1610),
+ static_cast<sal_uInt32>(aFrameRect.Width));
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf124740_handle_path_coordsystem)
+{
+ // tdf124740 OOXML shape with handle and w and h attribute on path has wrong
+ // handle position
+ // The handle position was scaled erroneously twice.
+ const OUString sFileName("tdf124740_HandleInOOXMLUserShape.pptx");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // The shape has one, horizontal adjust handle. It is about 1/5 of 10cm from left
+ // shape edge, shape is 6cm from left . That results in a position
+ // of 8cm from left page edge, which is 8000 in 1/100 mm unit.
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ Point aPosition;
+ aCustomShape2d.GetHandlePosition(0, aPosition);
+ double fX(aPosition.X());
+ // tolerance for rounding to integer
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("handle X coordinate", 8000.0, fX, 2.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf115813_OOXML_XY_handle)
+{
+ // The test covers all preset shapes with handles. Only these ones are
+ // excluded: arc, blockArc, chord, circularArrow, gear6, gear9, mathNotEqual, pie,
+ // leftCircularArrow, leftRightCircularArrow, swooshArrow.
+ // Connectors are included as ordinary shapes to prevent converting.
+ // Error was, that the handle movement and the changes to the shape did not follow
+ // the mouse movement.
+ const OUString sFileName("tdf115813_HandleMovementOOXMLPresetShapes.pptx");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+
+ OUString sErrors;
+ // values in vector InteractionsHandles are in 1/100 mm and refer to page
+ for (sal_uInt8 i = 0; i < countShapes(); i++)
+ {
+ uno::Reference<drawing::XShape> xShape(getShape(i));
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ OUString sShapeType("non-primitive"); // default for ODF
+ const SdrCustomShapeGeometryItem& rGeometryItem(
+ rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+ const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
+ if (pAny)
+ *pAny >>= sShapeType;
+
+ sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
+ for (sal_uInt8 j = 0; j < nHandlesCount; j++)
+ {
+ css::awt::Point aInitialPosition(
+ rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
+ // The handles are initialized in the test document, so that if the handle is moveable in
+ // that direction at all, then it can move at least with an amount of 100.
+ Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
+ rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
+ css::awt::Point aObservedPosition(
+ rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
+ sal_Int32 nDesiredX(aDesiredPosition.X()); // tools::Point
+ sal_Int32 nDesiredY(aDesiredPosition.Y());
+ sal_Int32 nObservedX(aObservedPosition.X); // css::awt::Point
+ sal_Int32 nObservedY(aObservedPosition.Y);
+ // If a handle only moves in one direction, the difference is 100 for the other direction.
+ // There exists some rounding differences, therefore '<= 1' instead of '== 0'.
+ // The condition has the form '!(good cases)'.
+ if (!((abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) == 100)
+ || (abs(nDesiredX - nObservedX) == 100 && abs(nDesiredY - nObservedY) <= 1)
+ || (abs(nDesiredX - nObservedX) <= 1 && abs(nDesiredY - nObservedY) <= 1)))
+ {
+ sErrors += "\n" +
+ //sErrors += OUString(sal_Unicode(10));
+ OUString::number(i) + " " + sShapeType + ": " + OUString::number(j)
+ + " X " + OUString::number(nDesiredX) + "|"
+ + OUString::number(nObservedX) + " Y " + OUString::number(nDesiredY)
+ + "|" + OUString::number(nObservedY);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testQuadraticCurveTo)
+{
+ // tdf125782 command Q (quadraticcurveto) uses wrong 'current point'.
+ // When converting to cubic Bezier curve, this had resulted in a wrong first control point.
+ // The quadraticcurveto segment starts in shape center in the test file. The first control
+ // point should produce a horizontal tangent in the start point.
+ const OUString sFileName("tdf125782_QuadraticCurveTo.odg");
+ const OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const double fHeight = static_cast<double>(aBoundRect.Height);
+ //Add some tolerance
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("bad height of quadraticcurveto", 3004, fHeight, 10.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126512_OOXML_handle_in_ODP)
+{
+ // The test covers all preset shapes with handles. Connectors are included as ordinary
+ // shapes to prevent converting. The file was created in PowerPoint 365 and then
+ // opened and exported to ODF format by LibreOffice.
+ // Error was, that for shapes, which were originally imported from OOXML, the handles
+ // could not be moved at all.
+ const OUString sFileName("tdf126512_OOXMLHandleMovementInODF.odp");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+
+ OUString sErrors; // sErrors collects shape type and handle index for failing cases
+ for (sal_uInt8 i = 0; i < countShapes(); i++)
+ {
+ uno::Reference<drawing::XShape> xShape(getShape(i));
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ OUString sShapeType("non-primitive"); // only to initialize, value not used here
+ const SdrCustomShapeGeometryItem& rGeometryItem(
+ rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+ const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
+ if (pAny)
+ *pAny >>= sShapeType;
+
+ sal_uInt8 nHandlesCount = rSdrObjCustomShape.GetInteractionHandles().size();
+ for (sal_uInt8 j = 0; j < nHandlesCount; j++)
+ {
+ css::awt::Point aInitialPosition(
+ rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
+ // The handles are initialized in the test document, so that if the handle is moveable
+ // in that direction at all, then it can move at least with an amount of 100.
+ Point aDesiredPosition(aInitialPosition.X + 100, aInitialPosition.Y + 100);
+ rSdrObjCustomShape.DragMoveCustomShapeHdl(aDesiredPosition, j, false);
+ css::awt::Point aObservedPosition(
+ rSdrObjCustomShape.GetInteractionHandles()[j].aPosition);
+ if (aInitialPosition.X == aObservedPosition.X
+ && aInitialPosition.Y == aObservedPosition.Y)
+ {
+ sErrors
+ += "\n" + OUString::number(i) + " " + sShapeType + " " + OUString::number(j);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Mirror)
+{
+ // The document contains two shapes, one with horizontal flip, the other with vertical
+ // flip. They are diamonds, so their text frame is symmetric to the center of the shape.
+ // The shapes have not stroke and no fill, so that the bounding box surrounds the text
+ // and therefore equals approximately the text frame.
+ // Error was, that because of wrong calculation, the flipped shapes do not use the
+ // text frame but the frame rectangle for their text.
+ const OUString sFileName("tdf127785_Mirror.odp");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+
+ uno::Reference<drawing::XShape> xShapeV(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeVProps(xShapeV, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeVProps.is());
+ awt::Rectangle aBoundRectV;
+ xShapeVProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectV;
+ const sal_Int32 nHeightV = aBoundRectV.Height;
+ const sal_Int32 nWidthV = aBoundRectV.Width;
+ const sal_Int32 nLeftV = aBoundRectV.X;
+ const sal_Int32 nTopV = aBoundRectV.Y;
+ if (abs(nHeightV - 4149) > 5 || abs(nWidthV - 3819) > 5)
+ sErrors += "Flip vertical wrong size.";
+ if (abs(nLeftV - 3155) > 5 || abs(nTopV - 3736) > 5)
+ sErrors += " Flip vertical wrong position.";
+
+ uno::Reference<drawing::XShape> xShapeH(getShape(1));
+ uno::Reference<beans::XPropertySet> xShapeHProps(xShapeH, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeHProps.is());
+ awt::Rectangle aBoundRectH;
+ xShapeHProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRectH;
+ const sal_Int32 nHeightH = aBoundRectH.Height;
+ const sal_Int32 nWidthH = aBoundRectH.Width;
+ const sal_Int32 nLeftH = aBoundRectH.X;
+ const sal_Int32 nTopH = aBoundRectH.Y;
+ if (abs(nHeightH - 4149) > 5 || abs(nWidthH - 3819) > 5)
+ sErrors += " Flip horizontal wrong size.";
+ if (abs(nLeftH - 15026) > 5 || abs(nTopH - 4115) > 5)
+ sErrors += " Flip horizontal wrong position.";
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf126060_3D_Z_Rotation)
+{
+ // The document contains one textbox with inside overflowed text
+ // and the text has 3D z rotation. When we open the document we
+ // should see the text vertically and rotated from text bound center not text box.
+
+ const OUString sFileName("tdf126060_3D_Z_Rotation.pptx");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+
+ if (rSdrObjCustomShape.GetCameraZRotation() != 90)
+ sErrors += "Wrong text camera Z rotation";
+
+ basegfx::B2DHomMatrix aObjectTransform;
+ basegfx::B2DPolyPolygon aObjectPolyPolygon;
+ rSdrObjCustomShape.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon);
+
+ if (aObjectTransform.get(0, 0) != 1492 || aObjectTransform.get(0, 1) != 0
+ || aObjectTransform.get(0, 2) != 1129 || aObjectTransform.get(1, 0) != 0
+ || aObjectTransform.get(1, 1) != 2500 || aObjectTransform.get(1, 2) != 5846)
+ sErrors += " Wrong transformation matrix";
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_Asymmetric)
+{
+ // The document contains a shapes with vertical flip and text frame asymmetrical
+ // to shape. The shape has not stroke and no fill, so that the bounding box surrounds
+ // the text and therefore equals approximately the text frame.
+ // Error was, that the 180deg text rotation was not compensated for the position of
+ // the flipped text box.
+ const OUString sFileName("tdf127785_asymmetricTextBoxFlipV.odg");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const sal_Int32 nLeft = aBoundRect.X;
+ const sal_Int32 nTop = aBoundRect.Y;
+ const sal_Int32 nRight = aBoundRect.X + aBoundRect.Width - 1;
+ const sal_Int32 nBottom = aBoundRect.Y + aBoundRect.Height - 1;
+ if (abs(nLeft - 10034) > 5)
+ sErrors += "wrong left";
+ if (abs(nRight - 12973) > 5)
+ sErrors += " wrong right";
+ if (abs(nTop - 7892) > 5)
+ sErrors += " wrong top";
+ if (abs(nBottom - 14884) > 5)
+ sErrors += " wrong bottom";
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf127785_TextRotateAngle)
+{
+ // The document contains a shapes with vertical flip and a text frame with own
+ // rotate angle. The shape has not stroke and no fill, so that the bounding box
+ // surrounds the text and therefore equals approximately the text frame.
+ // Error was, that the compensation for the 180° rotation added for vertical
+ // flip were not made to the text box position but to the text matrix.
+ const OUString sFileName("tdf127785_TextRotateAngle.odp");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
+ const sal_Int32 nLeft = aBoundRect.X;
+ const sal_Int32 nTop = aBoundRect.Y;
+ const sal_Int32 nRight = aBoundRect.X + aBoundRect.Width - 1;
+ const sal_Int32 nBottom = aBoundRect.Y + aBoundRect.Height - 1;
+ if (abs(nLeft - 5054) > 5)
+ sErrors += "wrong left";
+ if (abs(nRight - 6374) > 5)
+ sErrors += " wrong right";
+ if (abs(nTop - 4516) > 5)
+ sErrors += " wrong top";
+ if (abs(nBottom - 8930) > 5)
+ sErrors += " wrong bottom";
+
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf128413_tbrlOnOff)
+{
+ // The document contains a rotated shape with text. The error was, that switching
+ // tb-rl writing-mode on, changed the shape size and position.
+
+ const OUString sFileName("tdf128413_tbrl_OnOff.odp");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is());
+ awt::Rectangle aOrigRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aOrigRect;
+
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ rSdrObjCustomShape.SetVerticalWriting(true);
+
+ awt::Rectangle aObservedRect;
+ xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aObservedRect;
+ OUString sError;
+ if (aOrigRect.Width != aObservedRect.Width || aOrigRect.Height != aObservedRect.Height
+ || aOrigRect.X != aObservedRect.X || aOrigRect.Y != aObservedRect.Y)
+ {
+ sError = "Shape has wrong size or wrong position.";
+ }
+ CPPUNIT_ASSERT_EQUAL(OUString(), sError);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf129532_MatrixFlipV)
+{
+ // The document contains two rotated shapes with the same geometry. For one of them
+ // "matrix(1 0 0 -1 0cm 0cm)" was manually added to the value of the draw:transform
+ // attribute. That should result in mirroring on the x-axis. Error was, that the lines
+ // which are drawn on the shape rectangle were mirrored, but not the rectangle itself.
+ // The rectangle was only shifted.
+ const OUString sFileName("tdf129532_MatrixFlipV.odg");
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName;
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ OUString sErrors; // sErrors collects the errors and should be empty in case all is OK.
+
+ uno::Reference<drawing::XShape> xShape0(getShape(0));
+ uno::Reference<beans::XPropertySet> xShape0Props(xShape0, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape0Props.is());
+ awt::Rectangle aBoundRect0;
+ xShape0Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect0;
+
+ uno::Reference<drawing::XShape> xShape1(getShape(1));
+ uno::Reference<beans::XPropertySet> xShape1Props(xShape1, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape1Props.is());
+ awt::Rectangle aBoundRect1;
+ xShape1Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect1;
+
+ // The size of the two BoundRect rectangles are the same in case of correct
+ // vertical mirroring.
+ if (aBoundRect0.Width != aBoundRect1.Width)
+ {
+ sErrors += "\n Width expected: " + OUString::number(aBoundRect1.Width)
+ + " actual: " + OUString::number(aBoundRect0.Width);
+ }
+ if (aBoundRect0.Height != aBoundRect1.Height)
+ {
+ sErrors += "\n Height expected: " + OUString::number(aBoundRect1.Height)
+ + " actual: " + OUString::number(aBoundRect0.Height);
+ }
+ CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandT_CaseZeroHeight)
+{
+ // tdf103474 custom shape with command T to create quater ellipses in a bracket,
+ // corner case where the ellipse has zero height.
+ // Error was, that the calculation of the circle angle from the ellipse
+ // angle results in a wrong angle for the case 180° and height zero.
+ OUString sURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandT_CaseZeroHeight.odp";
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // The end points of the straight line segment should have the same x-coordinate of left
+ // of shape, and different y-coordinates, one top and the other bottom of the shape.
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+ CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+ const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
+ aPolyPolygon.count());
+ const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+ // Get the middle points of the polygon. They are the endpoints of the
+ // straight line segment regardless of the quarter ellipse parts, because
+ // the shape is symmetric.
+ const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
+ const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 13999.0, aStart.getX(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 13999.0, aEnd.getX(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandG_CaseZeroHeight)
+{
+ // Some as above, but with shape with command G.
+ OUString sURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandG_CaseZeroHeight.odp";
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ // The end points of the straight line segment should have the same x-coordinate of left
+ // of shape, and different y-coordinates, one top and the other bottom of the shape.
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+ CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+ const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
+ aPolyPolygon.count());
+ const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+ // Get the middle points of the polygon. They are the endpoints of the
+ // straight line segment regardless of the quarter ellipse parts, because
+ // the shape is symmetric.
+ const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
+ const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 1999.0, aStart.getX(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 1999.0, aEnd.getX(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf122323_largeSwingAngle)
+{
+ // SwingAngles are clamped to [-360;360] in MS Office. Error was, that LO calculated
+ // the end angle and used it modulo 360, no full ellipse was drawn.
+ OUString sURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf122323_swingAngle_larger360deg.pptx";
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+ CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+ const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+ const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+ const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(0));
+ // last point comes from line to center, therefore -2 instead of -1
+ const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() - 2));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Start <> End", aStart, aEnd);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf136176)
+{
+ // Error was, that fObjectRotation was not correctly updated after shearing.
+ // The problem becomes visible after save and reload.
+ OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf136176_rot30_flip.odg";
+ mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument");
+ CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+
+ for (sal_uInt16 i = 0; i < 3; i++)
+ {
+ // get shape
+ uno::Reference<drawing::XShape> xShape(getShape(i));
+ SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ // apply shearing 20deg
+ const Point aCenter = rSdrObjCustomShape.GetSnapRect().Center();
+ rSdrObjCustomShape.Shear(aCenter, 2000, tan(basegfx::deg2rad(20.0)), false);
+ }
+
+ // Save and reload
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::TempFile aTempFile;
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw8");
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL());
+
+ // Expected values of point 4 of the shape polygon
+ const OString sTestCase[] = { "FlipH", "FlipV", "FlipHV" };
+ const double fX[] = { 14981.0, 3849.0, 15214.0 };
+ const double fY[] = { 9366.0, 16464.0, 23463.0 };
+
+ // Verify correct positions
+ for (sal_uInt16 i = 0; i < 3; i++)
+ {
+ // Get shape
+ const uno::Reference<drawing::XShape> xShape(getShape(i));
+ const SdrObjCustomShape& rSdrObjCustomShape(
+ static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+ // Create polygon from shape and examine point 4 of the polygon
+ const basegfx::B2DPolyPolygon aLineGeometry = rSdrObjCustomShape.GetLineGeometry(false);
+ const basegfx::B2DPoint aPoint(aLineGeometry.getB2DPolygon(0).getB2DPoint(4));
+ // Allow some tolerance for rounding errors
+ if (fabs(aPoint.getX() - fX[i]) > 2.0 || fabs(aPoint.getY() - fY[i]) > 2.0)
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sTestCase[i].getStr(), aPoint,
+ basegfx::B2DPoint(fX[i], fY[i]));
+ }
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/data/graphic.pdf b/svx/qa/unit/data/graphic.pdf
new file mode 100644
index 000000000..4b53d2056
--- /dev/null
+++ b/svx/qa/unit/data/graphic.pdf
Binary files differ
diff --git a/svx/qa/unit/data/shadow-scale-origin.pptx b/svx/qa/unit/data/shadow-scale-origin.pptx
new file mode 100644
index 000000000..a0a164a3c
--- /dev/null
+++ b/svx/qa/unit/data/shadow-scale-origin.pptx
Binary files differ
diff --git a/svx/qa/unit/data/svx-dialogs-test.txt b/svx/qa/unit/data/svx-dialogs-test.txt
new file mode 100644
index 000000000..bd7ff04ed
--- /dev/null
+++ b/svx/qa/unit/data/svx-dialogs-test.txt
@@ -0,0 +1,80 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# This file contains all dialogs that the unit tests in the module
+# will work on if it is in script mode. It will read one-by-one,
+# try to open it and create a screenshot that will be saved in
+# workdir/screenshots using the pattern of the ui-file name.
+#
+# Syntax:
+# - empty lines are allowed
+# - lines starting with '#' are treated as comment
+# - all other lines should contain a *.ui filename in the same
+# notation as in the dialog constructors (see code)
+
+#
+# The 'known' dialogs which have a hard-coded representation
+# in registerKnownDialogsByID/createDialogByID
+#
+
+# No known dialogs in svx for now
+
+#
+# Dialogs without a hard-coded representation. These will
+# be visualized using a fallback based on VclBuilder
+#
+
+# currently deactivated, leads to problems and the test to not work
+# This is typically a hint that these should be hard-coded in the
+# test case since they need some document and model data to work
+#
+# svx/ui/asianphoneticguidedialog.ui <- problems under linux
+
+svx/ui/textcontrolchardialog.ui
+svx/ui/textcontrolparadialog.ui
+svx/ui/datanavigator.ui
+svx/ui/redlineviewpage.ui
+svx/ui/redlinefilterpage.ui
+svx/ui/headfootformatpage.ui
+svx/ui/optgridpage.ui
+svx/ui/xformspage.ui
+svx/ui/compressgraphicdialog.ui
+svx/ui/compressgraphicdialog.ui
+svx/ui/docrecoveryprogressdialog.ui
+svx/ui/docrecoverybrokendialog.ui
+svx/ui/passwd.ui
+svx/ui/adddataitemdialog.ui
+svx/ui/addconditiondialog.ui
+svx/ui/namespacedialog.ui
+svx/ui/addnamespacedialog.ui
+svx/ui/addsubmissiondialog.ui
+svx/ui/addmodeldialog.ui
+svx/ui/addinstancedialog.ui
+svx/ui/extrustiondepthdialog.ui
+svx/ui/fontworkgallerydialog.ui
+svx/ui/fontworkspacingdialog.ui
+svx/ui/chinesedictionary.ui
+svx/ui/chineseconversiondialog.ui
+svx/ui/imapdialog.ui
+svx/ui/findreplacedialog.ui
+svx/ui/crashreportdlg.ui
+svx/ui/docrecoverysavedialog.ui
+svx/ui/docrecoveryrecoverdialog.ui
+svx/ui/querysavecontchangesdialog.ui
+svx/ui/querydeletecontourdialog.ui
+svx/ui/queryunlinkgraphicsdialog.ui
+svx/ui/querynewcontourdialog.ui
+svx/ui/querymodifyimagemapchangesdialog.ui
+svx/ui/querysaveimagemapchangesdialog.ui
+svx/ui/querysaveimagemapchangesdialog.ui
+svx/ui/linkwarndialog.ui
+svx/ui/formlinkwarndialog.ui
+svx/ui/savemodifieddialog.ui
+svx/ui/querydeletethemedialog.ui
+svx/ui/querydeleteobjectdialog.ui
diff --git a/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp
new file mode 100644
index 000000000..9b36d45ee
--- /dev/null
+++ b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf103474_commandT_CaseZeroHeight.odp b/svx/qa/unit/data/tdf103474_commandT_CaseZeroHeight.odp
new file mode 100644
index 000000000..54a4377ca
--- /dev/null
+++ b/svx/qa/unit/data/tdf103474_commandT_CaseZeroHeight.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf115813_HandleMovementOOXMLPresetShapes.pptx b/svx/qa/unit/data/tdf115813_HandleMovementOOXMLPresetShapes.pptx
new file mode 100644
index 000000000..3435fef61
--- /dev/null
+++ b/svx/qa/unit/data/tdf115813_HandleMovementOOXMLPresetShapes.pptx
Binary files differ
diff --git a/svx/qa/unit/data/tdf121761_Accuracy_command_X.odp b/svx/qa/unit/data/tdf121761_Accuracy_command_X.odp
new file mode 100644
index 000000000..1de391758
--- /dev/null
+++ b/svx/qa/unit/data/tdf121761_Accuracy_command_X.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf121845_HalfEllipseVML.doc b/svx/qa/unit/data/tdf121845_HalfEllipseVML.doc
new file mode 100644
index 000000000..043e4e15f
--- /dev/null
+++ b/svx/qa/unit/data/tdf121845_HalfEllipseVML.doc
Binary files differ
diff --git a/svx/qa/unit/data/tdf121845_Two_commands_U.odg b/svx/qa/unit/data/tdf121845_Two_commands_U.odg
new file mode 100644
index 000000000..c0f7ff34f
--- /dev/null
+++ b/svx/qa/unit/data/tdf121845_Two_commands_U.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf121845_WidthOrientation_command_U.odg b/svx/qa/unit/data/tdf121845_WidthOrientation_command_U.odg
new file mode 100644
index 000000000..349c2eb81
--- /dev/null
+++ b/svx/qa/unit/data/tdf121845_WidthOrientation_command_U.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf121845_start40_swing480.doc b/svx/qa/unit/data/tdf121845_start40_swing480.doc
new file mode 100644
index 000000000..ff37aab3f
--- /dev/null
+++ b/svx/qa/unit/data/tdf121845_start40_swing480.doc
Binary files differ
diff --git a/svx/qa/unit/data/tdf121952_Toggle_direction_command_X.odp b/svx/qa/unit/data/tdf121952_Toggle_direction_command_X.odp
new file mode 100644
index 000000000..fbe1b7d40
--- /dev/null
+++ b/svx/qa/unit/data/tdf121952_Toggle_direction_command_X.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx
new file mode 100644
index 000000000..919675ef9
--- /dev/null
+++ b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx
Binary files differ
diff --git a/svx/qa/unit/data/tdf122964_MultipleMoveTo.odg b/svx/qa/unit/data/tdf122964_MultipleMoveTo.odg
new file mode 100644
index 000000000..63d80fd06
--- /dev/null
+++ b/svx/qa/unit/data/tdf122964_MultipleMoveTo.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf124029_Arc_position.doc b/svx/qa/unit/data/tdf124029_Arc_position.doc
new file mode 100644
index 000000000..d5396c375
--- /dev/null
+++ b/svx/qa/unit/data/tdf124029_Arc_position.doc
Binary files differ
diff --git a/svx/qa/unit/data/tdf124212_handle_position.odg b/svx/qa/unit/data/tdf124212_handle_position.odg
new file mode 100644
index 000000000..7a4eb0517
--- /dev/null
+++ b/svx/qa/unit/data/tdf124212_handle_position.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf124740_HandleInOOXMLUserShape.pptx b/svx/qa/unit/data/tdf124740_HandleInOOXMLUserShape.pptx
new file mode 100644
index 000000000..dd6df7b03
--- /dev/null
+++ b/svx/qa/unit/data/tdf124740_HandleInOOXMLUserShape.pptx
Binary files differ
diff --git a/svx/qa/unit/data/tdf125782_QuadraticCurveTo.odg b/svx/qa/unit/data/tdf125782_QuadraticCurveTo.odg
new file mode 100644
index 000000000..ba7b49536
--- /dev/null
+++ b/svx/qa/unit/data/tdf125782_QuadraticCurveTo.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf126060_3D_Z_Rotation.pptx b/svx/qa/unit/data/tdf126060_3D_Z_Rotation.pptx
new file mode 100644
index 000000000..8c8798f21
--- /dev/null
+++ b/svx/qa/unit/data/tdf126060_3D_Z_Rotation.pptx
Binary files differ
diff --git a/svx/qa/unit/data/tdf126512_OOXMLHandleMovementInODF.odp b/svx/qa/unit/data/tdf126512_OOXMLHandleMovementInODF.odp
new file mode 100644
index 000000000..7dd283f88
--- /dev/null
+++ b/svx/qa/unit/data/tdf126512_OOXMLHandleMovementInODF.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf127785_Mirror.odp b/svx/qa/unit/data/tdf127785_Mirror.odp
new file mode 100644
index 000000000..ff867839f
--- /dev/null
+++ b/svx/qa/unit/data/tdf127785_Mirror.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf127785_TextRotateAngle.odp b/svx/qa/unit/data/tdf127785_TextRotateAngle.odp
new file mode 100644
index 000000000..742f12d4d
--- /dev/null
+++ b/svx/qa/unit/data/tdf127785_TextRotateAngle.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf127785_asymmetricTextBoxFlipV.odg b/svx/qa/unit/data/tdf127785_asymmetricTextBoxFlipV.odg
new file mode 100644
index 000000000..ea700eaf7
--- /dev/null
+++ b/svx/qa/unit/data/tdf127785_asymmetricTextBoxFlipV.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf128413_tbrl_OnOff.odp b/svx/qa/unit/data/tdf128413_tbrl_OnOff.odp
new file mode 100644
index 000000000..f10f98bbe
--- /dev/null
+++ b/svx/qa/unit/data/tdf128413_tbrl_OnOff.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf129532_MatrixFlipV.odg b/svx/qa/unit/data/tdf129532_MatrixFlipV.odg
new file mode 100644
index 000000000..eb0c10b3d
--- /dev/null
+++ b/svx/qa/unit/data/tdf129532_MatrixFlipV.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf136176_rot30_flip.odg b/svx/qa/unit/data/tdf136176_rot30_flip.odg
new file mode 100644
index 000000000..75707a0f5
--- /dev/null
+++ b/svx/qa/unit/data/tdf136176_rot30_flip.odg
Binary files differ
diff --git a/svx/qa/unit/data/tdf93998.odp b/svx/qa/unit/data/tdf93998.odp
new file mode 100644
index 000000000..889aeeb02
--- /dev/null
+++ b/svx/qa/unit/data/tdf93998.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf98583_ShearHorizontal.odp b/svx/qa/unit/data/tdf98583_ShearHorizontal.odp
new file mode 100644
index 000000000..37719e825
--- /dev/null
+++ b/svx/qa/unit/data/tdf98583_ShearHorizontal.odp
Binary files differ
diff --git a/svx/qa/unit/data/tdf98584_ShearVertical.odg b/svx/qa/unit/data/tdf98584_ShearVertical.odg
new file mode 100644
index 000000000..457521d50
--- /dev/null
+++ b/svx/qa/unit/data/tdf98584_ShearVertical.odg
Binary files differ
diff --git a/svx/qa/unit/data/unodraw-writer-image.odt b/svx/qa/unit/data/unodraw-writer-image.odt
new file mode 100644
index 000000000..5264118a3
--- /dev/null
+++ b/svx/qa/unit/data/unodraw-writer-image.odt
Binary files differ
diff --git a/svx/qa/unit/data/viewBox_positive_twolines_strict.odp b/svx/qa/unit/data/viewBox_positive_twolines_strict.odp
new file mode 100644
index 000000000..3425582b8
--- /dev/null
+++ b/svx/qa/unit/data/viewBox_positive_twolines_strict.odp
Binary files differ
diff --git a/svx/qa/unit/gallery/test_gallery.cxx b/svx/qa/unit/gallery/test_gallery.cxx
new file mode 100644
index 000000000..4c95e461a
--- /dev/null
+++ b/svx/qa/unit/gallery/test_gallery.cxx
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* This file is part of the LibreOffice project.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include <tools/urlobj.hxx>
+#include <sfx2/app.hxx>
+#include <unotools/tempfile.hxx>
+#include <comphelper/DirectoryHelper.hxx>
+
+#include <svx/gallery1.hxx>
+#include <svx/galtheme.hxx>
+#include <galobj.hxx>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+class GalleryObjTest : public CppUnit::TestFixture
+{
+public:
+ void TestCreateTheme();
+ void TestDeleteTheme();
+ void TestSetThemeName();
+ void TestThemeURLCase();
+ void TestThemeCount();
+ void TestGalleryThemeEntry();
+ void TestInsertGalleryObject();
+
+ CPPUNIT_TEST_SUITE(GalleryObjTest);
+
+ CPPUNIT_TEST(TestCreateTheme);
+ CPPUNIT_TEST(TestDeleteTheme);
+ CPPUNIT_TEST(TestSetThemeName);
+ CPPUNIT_TEST(TestThemeURLCase);
+ CPPUNIT_TEST(TestThemeCount);
+ CPPUNIT_TEST(TestGalleryThemeEntry);
+ CPPUNIT_TEST(TestInsertGalleryObject);
+
+ CPPUNIT_TEST_SUITE_END();
+};
+
+// Create and Dereference a theme, check that file exists
+void GalleryObjTest::TestCreateTheme()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ // Check if directory exists
+ CPPUNIT_ASSERT_MESSAGE("Could not create temporary directory",
+ comphelper::DirectoryHelper::dirExists(aGalleryURL));
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+ const OUString myThemeName = "addytesttheme";
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+ // check if files exist
+ CPPUNIT_ASSERT_MESSAGE(
+ "Could not find .thm file inside it",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".thm"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "Could not find .sdv file inside it",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".sdv"));
+}
+
+// Create and Delete Theme, check the file doesn't exist
+void GalleryObjTest::TestDeleteTheme()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+ const OUString myThemeName = "addytesttheme";
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+ // Delete Theme
+ CPPUNIT_ASSERT_MESSAGE("Could not remove theme", pGallery->RemoveTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not remove theme, theme found even after trying to remove",
+ !pGallery->HasTheme(myThemeName));
+
+ // Check that files do not exist
+ CPPUNIT_ASSERT_MESSAGE(
+ "Found .thm file inside it after deletion",
+ !comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".thm"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "Found .sdv file inside it after deletion",
+ !comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".sdv"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "Found .sdg file inside it after deletion",
+ !comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".sdg"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "Found .str file inside it after deletion",
+ !comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".str"));
+}
+
+// Create theme, set name, assert the name is expected
+void GalleryObjTest::TestSetThemeName()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+ const OUString myThemeName = "addytesttheme";
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+ // Rename theme
+ const OUString myNewThemeName = "addytestthemenew";
+ pGallery->RenameTheme(myThemeName, myNewThemeName);
+ CPPUNIT_ASSERT_MESSAGE("Could not rename theme because old theme name still exists",
+ !pGallery->HasTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find new renamed theme", pGallery->HasTheme(myNewThemeName));
+
+ // Check that files are not renamed
+ CPPUNIT_ASSERT_MESSAGE(
+ "Could not find .thm file inside it",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".thm"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "Could not find .sdv file inside it",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".sdv"));
+}
+
+void GalleryObjTest::TestThemeURLCase()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ // Check if directory exists
+ CPPUNIT_ASSERT_MESSAGE("Could not create temporary directory",
+ comphelper::DirectoryHelper::dirExists(aGalleryURL));
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+
+ // Mixed Case Theme Name
+ const OUString myThemeName = "AddyTestTheme";
+
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+#if defined(LINUX)
+ CPPUNIT_ASSERT_MESSAGE("[LINUX] Could not find .thm in lowercase",
+ comphelper::DirectoryHelper::fileExists(
+ aGalleryURL + "/" + myThemeName.toAsciiLowerCase() + ".thm"));
+ CPPUNIT_ASSERT_MESSAGE("[LINUX] Could not find .sdv in lowercase",
+ comphelper::DirectoryHelper::fileExists(
+ aGalleryURL + "/" + myThemeName.toAsciiLowerCase() + ".sdv"));
+#else
+ CPPUNIT_ASSERT_MESSAGE(
+ "[WINDOWS] Could not find .thm in mixed case",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".thm"));
+ CPPUNIT_ASSERT_MESSAGE(
+ "[WINDOWS] Could not find .sdv in mixed case",
+ comphelper::DirectoryHelper::fileExists(aGalleryURL + "/" + myThemeName + ".sdv"));
+#endif
+}
+
+void GalleryObjTest::TestThemeCount()
+{
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ // Check if directory exists
+ CPPUNIT_ASSERT_MESSAGE("Could not create temporary directory",
+ comphelper::DirectoryHelper::dirExists(aGalleryURL));
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+
+ // Loop through and test theme count in each pass.
+ sal_uInt32 nLimit = 10;
+ for (sal_uInt32 i = 1; i <= nLimit; i++)
+ {
+ OUString myThemeName = "addytesttheme" + OUString::number(i);
+ // Create theme
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Inconsistent theme count",
+ static_cast<sal_uInt32>(pGallery->GetThemeCount()), i);
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Inconsistent theme count",
+ static_cast<sal_uInt32>(pGallery->GetThemeCount()), nLimit);
+ for (sal_uInt32 i = nLimit; i > 0; i--)
+ {
+ OUString myThemeName = "addytesttheme" + OUString::number(i);
+ // Delete Theme
+ CPPUNIT_ASSERT_MESSAGE("Could not remove theme", pGallery->RemoveTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not remove theme, theme found even after trying to remove",
+ !pGallery->HasTheme(myThemeName));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Inconsistent theme count",
+ static_cast<sal_uInt32>(pGallery->GetThemeCount()), i - 1);
+ }
+}
+
+void GalleryObjTest::TestGalleryThemeEntry()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ // Check if directory exists
+ CPPUNIT_ASSERT_MESSAGE("Could not create temporary directory",
+ comphelper::DirectoryHelper::dirExists(aGalleryURL));
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+ const OUString myThemeName = "addytesttheme";
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+ // Get Theme Entry Object
+ const GalleryThemeEntry* mpThemeEntry = pGallery->GetThemeInfo(myThemeName);
+
+ // Check Theme Name
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Theme name doesn't match", mpThemeEntry->GetThemeName(),
+ myThemeName);
+
+ // Check URLs
+ INetURLObject aURL(aGalleryURL);
+ aURL.Append(myThemeName);
+ INetURLObject aThemeURL(aURL), aSdvURL(aURL), aSdgURL(aURL), aStrURL(aURL);
+ aThemeURL.setExtension("thm");
+ aSdvURL.setExtension("sdv");
+ aSdgURL.setExtension("sdg");
+ aStrURL.setExtension("str");
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "Theme URL doesn't match",
+ mpThemeEntry->GetThmURL().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous),
+ aThemeURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "Sdv URL doesn't match",
+ mpThemeEntry->GetSdvURL().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous),
+ aSdvURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "Sdg URL doesn't match",
+ mpThemeEntry->GetSdgURL().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous),
+ aSdgURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "Str URL doesn't match",
+ mpThemeEntry->GetStrURL().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous),
+ aStrURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
+}
+
+void GalleryObjTest::TestInsertGalleryObject()
+{
+ // Create theme
+ std::unique_ptr<utl::TempFile> pTempDir;
+ pTempDir.reset(new utl::TempFile(nullptr, true));
+ CPPUNIT_ASSERT_MESSAGE("Could not create valid temporary directory", pTempDir->IsValid());
+ pTempDir->EnableKillingFile();
+ const OUString aGalleryURL = pTempDir->GetURL();
+
+ // Check if directory exists
+ CPPUNIT_ASSERT_MESSAGE("Could not create temporary directory",
+ comphelper::DirectoryHelper::dirExists(aGalleryURL));
+
+ std::unique_ptr<Gallery> pGallery(new Gallery(aGalleryURL));
+ CPPUNIT_ASSERT_MESSAGE("Could not create gallery instance", (pGallery != nullptr));
+ const OUString myThemeName = "addytesttheme";
+ CPPUNIT_ASSERT_MESSAGE("Could not create theme", pGallery->CreateTheme(myThemeName));
+ CPPUNIT_ASSERT_MESSAGE("Could not find theme", pGallery->HasTheme(myThemeName));
+
+ // Insert Objects Into Theme
+ SfxListener aListener;
+ SfxApplication::GetOrCreate();
+ GalleryTheme* pGalleryTheme = pGallery->AcquireTheme(myThemeName, aListener);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(0), pGalleryTheme->GetObjectCount());
+
+ pGallery->ReleaseTheme(pGalleryTheme, aListener);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GalleryObjTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/svx/qa/unit/sdr.cxx b/svx/qa/unit/sdr.cxx
new file mode 100644
index 000000000..18cfd8400
--- /dev/null
+++ b/svx/qa/unit/sdr.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <drawinglayer/tools/primitive2dxmldump.hxx>
+#include <rtl/ustring.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/virdev.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Tests for svx/source/sdr/ code.
+class SdrTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
+{
+protected:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ virtual void setUp() override
+ {
+ test::BootstrapFixture::setUp();
+ mxDesktop.set(frame::Desktop::create(m_xContext));
+ }
+
+ virtual void tearDown() override
+ {
+ if (mxComponent.is())
+ {
+ mxComponent->dispose();
+ }
+ test::BootstrapFixture::tearDown();
+ }
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+CPPUNIT_TEST_FIXTURE(SdrTest, testShadowScaleOrigin)
+{
+ // Load a document containing a custom shape.
+ test::Directories aDirectories;
+ OUString aURL = aDirectories.getURLFromSrc("svx/qa/unit/data/shadow-scale-origin.pptx");
+ getComponent() = loadFromDesktop(aURL);
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+
+ // Render it.
+ auto pDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
+ CPPUNIT_ASSERT(pDrawPage);
+ SdrPage* pSdrPage = pDrawPage->GetSdrPage();
+ ScopedVclPtrInstance<VirtualDevice> aVirtualDevice;
+ sdr::contact::ObjectContactOfObjListPainter aObjectContact(*aVirtualDevice,
+ { pSdrPage->GetObj(0) }, nullptr);
+ const sdr::contact::ViewObjectContact& rDrawPageVOContact
+ = pSdrPage->GetViewContact().GetViewObjectContact(aObjectContact);
+ sdr::contact::DisplayInfo aDisplayInfo;
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence
+ = rDrawPageVOContact.getPrimitive2DSequenceHierarchy(aDisplayInfo);
+
+ // Examine the created primitives.
+ drawinglayer::tools::Primitive2dXmlDump aDumper;
+ xmlDocUniquePtr pDocument = aDumper.dumpAndParse(xPrimitiveSequence);
+ double fShadowX = getXPath(pDocument, "//shadow/transform", "xy13").toDouble();
+ double fShadowY = getXPath(pDocument, "//shadow/transform", "xy23").toDouble();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: -705
+ // - Actual : -158
+ // i.e. the shadow origin was not the top right corner for scaling (larger x position, so it was
+ // visible on the right of the shape as well).
+ CPPUNIT_ASSERT_EQUAL(-705., std::round(fShadowX));
+ CPPUNIT_ASSERT_EQUAL(-685., std::round(fShadowY));
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx
new file mode 100644
index 000000000..5a0f46bba
--- /dev/null
+++ b/svx/qa/unit/svdraw.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+#include <drawinglayer/tools/primitive2dxmldump.hxx>
+#include <rtl/ustring.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/virdev.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Tests for svx/source/svdraw/ code.
+class SvdrawTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
+{
+protected:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ virtual void setUp() override
+ {
+ test::BootstrapFixture::setUp();
+ mxDesktop.set(frame::Desktop::create(m_xContext));
+ }
+
+ virtual void tearDown() override
+ {
+ if (mxComponent.is())
+ {
+ mxComponent->dispose();
+ }
+ test::BootstrapFixture::tearDown();
+ }
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+CPPUNIT_TEST_FIXTURE(SvdrawTest, testSemiTransparentText)
+{
+ // Create a new Draw document with a rectangle.
+ getComponent() = loadFromDesktop("private:factory/sdraw");
+ uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(10000, 10000));
+ xShape->setPosition(awt::Point(1000, 1000));
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ xDrawPage->add(xShape);
+
+ // Add semi-transparent text on the rectangle.
+ uno::Reference<text::XTextRange> xShapeText(xShape, uno::UNO_QUERY);
+ xShapeText->getText()->setString("hello");
+
+ uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
+ xShapeProperties->setPropertyValue("CharColor", uno::makeAny(COL_RED));
+ sal_Int16 nTransparence = 75;
+ xShapeProperties->setPropertyValue("CharTransparence", uno::makeAny(nTransparence));
+
+ // Generates drawinglayer primitives for the page.
+ auto pDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
+ CPPUNIT_ASSERT(pDrawPage);
+ SdrPage* pSdrPage = pDrawPage->GetSdrPage();
+ ScopedVclPtrInstance<VirtualDevice> aVirtualDevice;
+ sdr::contact::ObjectContactOfObjListPainter aObjectContact(*aVirtualDevice,
+ { pSdrPage->GetObj(0) }, nullptr);
+ const sdr::contact::ViewObjectContact& rDrawPageVOContact
+ = pSdrPage->GetViewContact().GetViewObjectContact(aObjectContact);
+ sdr::contact::DisplayInfo aDisplayInfo;
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence
+ = rDrawPageVOContact.getPrimitive2DSequenceHierarchy(aDisplayInfo);
+
+ // Make sure the text is semi-transparent.
+ drawinglayer::tools::Primitive2dXmlDump aDumper;
+ xmlDocUniquePtr pDocument = aDumper.dumpAndParse(xPrimitiveSequence);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // - XPath '//unifiedtransparence' number of nodes is incorrect
+ // i.e. the text was just plain red, not semi-transparent.
+ double fTransparence = getXPath(pDocument, "//unifiedtransparence", "transparence").toDouble();
+ CPPUNIT_ASSERT_EQUAL(nTransparence,
+ static_cast<sal_Int16>(basegfx::fround(fTransparence * 100)));
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/svdraw/test_SdrTextObject.cxx b/svx/qa/unit/svdraw/test_SdrTextObject.cxx
new file mode 100644
index 000000000..7af646b0b
--- /dev/null
+++ b/svx/qa/unit/svdraw/test_SdrTextObject.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <svx/svdotext.hxx>
+#include <rtl/ustring.hxx>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+class SdrTextObjTest : public CppUnit::TestFixture {
+public:
+ void AllFamiliesCanBeRestoredFromSavedString();
+
+ CPPUNIT_TEST_SUITE(SdrTextObjTest);
+ CPPUNIT_TEST(AllFamiliesCanBeRestoredFromSavedString);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void SdrTextObjTest::AllFamiliesCanBeRestoredFromSavedString() {
+ std::vector<SfxStyleFamily> allFamilies;
+ allFamilies.push_back(SfxStyleFamily::Char);
+ allFamilies.push_back(SfxStyleFamily::Para);
+ allFamilies.push_back(SfxStyleFamily::Page);
+ allFamilies.push_back(SfxStyleFamily::Pseudo);
+
+ for (SfxStyleFamily family : allFamilies) {
+ OUString styleName = "styleName";
+ SdrTextObj::AppendFamilyToStyleName(styleName, family);
+ SfxStyleFamily readFamily = SdrTextObj::ReadFamilyFromStyleName(styleName);
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(family), static_cast<int>(readFamily));
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SdrTextObjTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/svx-dialogs-test.cxx b/svx/qa/unit/svx-dialogs-test.cxx
new file mode 100644
index 000000000..eaa829569
--- /dev/null
+++ b/svx/qa/unit/svx-dialogs-test.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+#include <test/screenshot_test.hxx>
+#include <vcl/abstdlg.hxx>
+
+using namespace ::com::sun::star;
+
+/// Test opening a dialog in svx
+class SvxDialogsTest : public ScreenshotTest
+{
+private:
+ /// helper method to populate KnownDialogs, called in setUp(). Needs to be
+ /// written and has to add entries to KnownDialogs
+ virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override;
+
+ /// dialog creation for known dialogs by ID. Has to be implemented for
+ /// each registered known dialog
+ virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override;
+
+public:
+ SvxDialogsTest();
+
+ // try to open a dialog
+ void openAnyDialog();
+
+ CPPUNIT_TEST_SUITE(SvxDialogsTest);
+ CPPUNIT_TEST(openAnyDialog);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+SvxDialogsTest::SvxDialogsTest()
+{
+}
+
+void SvxDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/)
+{
+ // fill map of known dialogs
+}
+
+VclPtr<VclAbstractDialog> SvxDialogsTest::createDialogByID(sal_uInt32 /*nID*/)
+{
+ return nullptr;
+}
+
+void SvxDialogsTest::openAnyDialog()
+{
+ /// process input file containing the UXMLDescriptions of the dialogs to dump
+ processDialogBatchFile("svx/qa/unit/data/svx-dialogs-test.txt");
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SvxDialogsTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/unodraw.cxx b/svx/qa/unit/unodraw.cxx
new file mode 100644
index 000000000..a835dee95
--- /dev/null
+++ b/svx/qa/unit/unodraw.cxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppunit/TestAssert.h>
+
+#include <com/sun/star/drawing/GraphicExportFilter.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <unotools/tempfile.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+char const DATA_DIRECTORY[] = "/svx/qa/unit/data/";
+
+/// Tests for svx/source/unodraw/ code.
+class UnodrawTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+protected:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+};
+
+void UnodrawTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void UnodrawTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+CPPUNIT_TEST_FIXTURE(UnodrawTest, testWriterGraphicExport)
+{
+ // Load a document with a Writer picture in it.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "unodraw-writer-image.odt";
+ mxComponent = loadFromDesktop(aURL);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ uno::Reference<lang::XComponent> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+
+ // Export it as JPEG.
+ uno::Reference<drawing::XGraphicExportFilter> xExportFilter
+ = drawing::GraphicExportFilter::create(mxComponentContext);
+ // This resulted in a css::lang::IllegalArgumentException for a Writer
+ // picture.
+ xExportFilter->setSourceDocument(xShape);
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ uno::Sequence<beans::PropertyValue> aProperties(
+ comphelper::InitPropertySequence({ { "URL", uno::Any(aTempFile.GetURL()) },
+ { "MediaType", uno::Any(OUString("image/jpeg")) } }));
+ CPPUNIT_ASSERT(xExportFilter->filter(aProperties));
+}
+
+CPPUNIT_TEST_FIXTURE(UnodrawTest, testTdf93998)
+{
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf93998.odp");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xDrawPagesSupplier.is());
+
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xDrawPage.is());
+
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xShape.is());
+
+ uno::Reference<lang::XMultiServiceFactory> xFactory = comphelper::getProcessServiceFactory();
+ uno::Reference<awt::XControlModel> xModel(
+ xFactory->createInstance("com.sun.star.awt.UnoControlDialogModel"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xModel.is());
+
+ uno::Reference<beans::XPropertySet> xModelProps(xModel, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xModelProps.is());
+
+ // This resulted in a uno::RuntimeException, assigning a shape to a dialog model's image was
+ // broken.
+ xModelProps->setPropertyValue("ImageURL", xShape->getPropertyValue("GraphicURL"));
+ uno::Reference<graphic::XGraphic> xGraphic;
+ xModelProps->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT(xGraphic.is());
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/xoutdev.cxx b/svx/qa/unit/xoutdev.cxx
new file mode 100644
index 000000000..0b5148b39
--- /dev/null
+++ b/svx/qa/unit/xoutdev.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config_features.h>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <unotest/bootstrapfixturebase.hxx>
+
+#include <sal/types.h>
+#include <sfx2/app.hxx>
+#include <tools/stream.hxx>
+#include <unotest/directories.hxx>
+#include <unotools/tempfile.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svx/xoutbmp.hxx>
+
+class XOutdevTest : public CppUnit::TestFixture
+{
+public:
+ virtual void setUp() override
+ {
+ CppUnit::TestFixture::setUp();
+ SfxApplication::GetOrCreate();
+ }
+};
+
+CPPUNIT_TEST_FIXTURE(XOutdevTest, testPdfGraphicExport)
+{
+#if HAVE_FEATURE_PDFIUM
+ // Import the graphic.
+ Graphic aGraphic;
+ test::Directories aDirectories;
+ OUString aURL = aDirectories.getURLFromSrc("svx/qa/unit/data/graphic.pdf");
+ SvFileStream aStream(aURL, StreamMode::READ);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE,
+ GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, aURL, aStream));
+
+ // Export it.
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ XOutFlags const eFlags = XOutFlags::DontExpandFilename | XOutFlags::DontAddExtension
+ | XOutFlags::UseNativeIfPossible;
+ OUString aTempURL = aTempFile.GetURL();
+ XOutBitmap::WriteGraphic(aGraphic, aTempURL, "pdf", eFlags);
+
+ // Assert that the output looks like a PDF.
+ SvStream* pStream = aTempFile.GetStream(StreamMode::READ);
+ CPPUNIT_ASSERT(pStream->TellEnd() > 5);
+ sal_uInt8 sFirstBytes[5];
+ pStream->ReadBytes(sFirstBytes, 5);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>('%'), sFirstBytes[0]);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>('P'), sFirstBytes[1]);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>('D'), sFirstBytes[2]);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>('F'), sFirstBytes[3]);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>('-'), sFirstBytes[4]);
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */