summaryrefslogtreecommitdiffstats
path: root/oox/qa/unit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /oox/qa/unit
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--oox/qa/unit/CryptoTest.cxx439
-rw-r--r--oox/qa/unit/data/FaultyPathStart.odpbin0 -> 14582 bytes
-rw-r--r--oox/qa/unit/data/camera-rotation-revolution-nonwps.pptxbin0 -> 31425 bytes
-rw-r--r--oox/qa/unit/data/camera-rotation-revolution.docxbin0 -> 23748 bytes
-rw-r--r--oox/qa/unit/data/chart-data-label-char-color.docxbin0 -> 26014 bytes
-rw-r--r--oox/qa/unit/data/chart-theme-override.pptxbin0 -> 37016 bytes
-rw-r--r--oox/qa/unit/data/customshape-position.docxbin0 -> 42054 bytes
-rw-r--r--oox/qa/unit/data/dml-groupshape-polygon.docxbin0 -> 24492 bytes
-rw-r--r--oox/qa/unit/data/endParaRPr-newline-textsize.pptxbin0 -> 13349 bytes
-rw-r--r--oox/qa/unit/data/gradient-multistep-transparency.pptxbin0 -> 34042 bytes
-rw-r--r--oox/qa/unit/data/graphic-stroke.pptxbin0 -> 29396 bytes
-rw-r--r--oox/qa/unit/data/group-spt202.docxbin0 -> 28238 bytes
-rw-r--r--oox/qa/unit/data/import-characters.pptxbin0 -> 33620 bytes
-rw-r--r--oox/qa/unit/data/import-mce.pptxbin0 -> 33763 bytes
-rw-r--r--oox/qa/unit/data/multiple-group-shapes.docxbin0 -> 29653 bytes
-rw-r--r--oox/qa/unit/data/preset-adjust-value.pptxbin0 -> 33233 bytes
-rw-r--r--oox/qa/unit/data/refer-to-theme-shape-fill.odpbin0 -> 14638 bytes
-rw-r--r--oox/qa/unit/data/refer-to-theme.pptxbin0 -> 33853 bytes
-rw-r--r--oox/qa/unit/data/shape-non-autosize-with-text.docxbin0 -> 35448 bytes
-rw-r--r--oox/qa/unit/data/shape-text-adjust-left.pptxbin0 -> 59199 bytes
-rw-r--r--oox/qa/unit/data/shape-text-alignment.pptxbin0 -> 30844 bytes
-rw-r--r--oox/qa/unit/data/smartart-groupshape.pptxbin0 -> 40995 bytes
-rw-r--r--oox/qa/unit/data/table-shadow.pptxbin0 -> 16837 bytes
-rw-r--r--oox/qa/unit/data/tdf100391_TextAreaRect.odpbin0 -> 13624 bytes
-rw-r--r--oox/qa/unit/data/tdf109169_OctagonBevel.odtbin0 -> 11122 bytes
-rw-r--r--oox/qa/unit/data/tdf112450_vml_polyline.docxbin0 -> 18114 bytes
-rw-r--r--oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptxbin0 -> 14733 bytes
-rw-r--r--oox/qa/unit/data/tdf125582_TextOnCircle.pptxbin0 -> 15956 bytes
-rw-r--r--oox/qa/unit/data/tdf131082.pptxbin0 -> 25881 bytes
-rw-r--r--oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docxbin0 -> 18070 bytes
-rw-r--r--oox/qa/unit/data/tdf141463_GroupTransform.pptxbin0 -> 16921 bytes
-rw-r--r--oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odtbin0 -> 10127 bytes
-rw-r--r--oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odtbin0 -> 15507 bytes
-rw-r--r--oox/qa/unit/data/tdf142602_CustomShapeArrows.odtbin0 -> 11637 bytes
-rw-r--r--oox/qa/unit/data/tdf142605_CurveSize.odpbin0 -> 11220 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_endsubpath.odpbin0 -> 14505 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_enhancedPath_commandA.odpbin0 -> 12755 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odpbin0 -> 14922 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_enhancedPath_commandT.odpbin0 -> 12869 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odpbin0 -> 13166 bytes
-rw-r--r--oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptxbin0 -> 15040 bytes
-rw-r--r--oox/qa/unit/data/tdf148784_StretchCommandQ.odpbin0 -> 15311 bytes
-rw-r--r--oox/qa/unit/data/tdf148784_StretchCommandVW.odpbin0 -> 15218 bytes
-rw-r--r--oox/qa/unit/data/tdf148784_StretchXY.odpbin0 -> 11512 bytes
-rw-r--r--oox/qa/unit/data/testTdf132557_footerCustomShapes.pptxbin0 -> 20137 bytes
-rw-r--r--oox/qa/unit/data/theme-tint.pptxbin0 -> 16742 bytes
-rw-r--r--oox/qa/unit/data/theme.pptxbin0 -> 33063 bytes
-rw-r--r--oox/qa/unit/data/transparent-text.pptxbin0 -> 32797 bytes
-rw-r--r--oox/qa/unit/data/vba/complex1.binbin0 -> 1086 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/complex1.binbin0 -> 579 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/simple1.binbin0 -> 18 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/simple2.binbin0 -> 13 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/simple3.bin1
-rw-r--r--oox/qa/unit/data/vba/reference/spec321.binbin0 -> 29 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/spec322.binbin0 -> 51 bytes
-rw-r--r--oox/qa/unit/data/vba/reference/spec323.binbin0 -> 7 bytes
-rw-r--r--oox/qa/unit/data/vba/simple1.binbin0 -> 13 bytes
-rw-r--r--oox/qa/unit/data/vba/simple2.binbin0 -> 11 bytes
-rw-r--r--oox/qa/unit/data/vba/simple3.bin1
-rw-r--r--oox/qa/unit/data/vba/spec321.bin1
-rw-r--r--oox/qa/unit/data/vba/spec322.bin1
-rw-r--r--oox/qa/unit/data/vba/spec323.bin1
-rw-r--r--oox/qa/unit/data/watermark.docxbin0 -> 21621 bytes
-rw-r--r--oox/qa/unit/drawingml.cxx566
-rw-r--r--oox/qa/unit/export.cxx777
-rw-r--r--oox/qa/unit/mathml.cxx71
-rw-r--r--oox/qa/unit/shape.cxx180
-rw-r--r--oox/qa/unit/vba_compression.cxx255
-rw-r--r--oox/qa/unit/vba_encryption.cxx82
-rw-r--r--oox/qa/unit/vml.cxx236
70 files changed, 2611 insertions, 0 deletions
diff --git a/oox/qa/unit/CryptoTest.cxx b/oox/qa/unit/CryptoTest.cxx
new file mode 100644
index 000000000..cbdf99b65
--- /dev/null
+++ b/oox/qa/unit/CryptoTest.cxx
@@ -0,0 +1,439 @@
+/* -*- 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_oox.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestFixture.h>
+
+#include <algorithm>
+#include <tools/stream.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <oox/crypto/Standard2007Engine.hxx>
+#include <oox/crypto/AgileEngine.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/binaryoutputstream.hxx>
+
+#if USE_TLS_NSS
+#include <nss.h>
+#endif
+
+using namespace css;
+
+class CryptoTest : public CppUnit::TestFixture
+{
+public:
+ virtual ~CryptoTest() override;
+ void testCryptoHash();
+ void testRoundUp();
+ void testStandard2007();
+ void testAgileEncryptionVerifier();
+ void testAgileEncryptionInfoWritingAndParsing();
+ void testAgileDataIntegrityHmacKey();
+ void testAgileEncryptingAndDecrypting();
+
+ CPPUNIT_TEST_SUITE(CryptoTest);
+ CPPUNIT_TEST(testCryptoHash);
+ CPPUNIT_TEST(testRoundUp);
+ CPPUNIT_TEST(testStandard2007);
+ CPPUNIT_TEST(testAgileEncryptionVerifier);
+ CPPUNIT_TEST(testAgileEncryptionInfoWritingAndParsing);
+ CPPUNIT_TEST(testAgileDataIntegrityHmacKey);
+ CPPUNIT_TEST(testAgileEncryptingAndDecrypting);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+namespace
+{
+std::string toString(std::vector<sal_uInt8> const& aInput)
+{
+ std::stringstream aStream;
+ for (auto const& aValue : aInput)
+ {
+ aStream << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(aValue);
+ }
+
+ return aStream.str();
+}
+}
+
+CryptoTest::~CryptoTest()
+{
+#if USE_TLS_NSS
+ NSS_Shutdown();
+#endif
+}
+
+void CryptoTest::testCryptoHash()
+{
+ // Check examples from Wikipedia (https://en.wikipedia.org/wiki/HMAC)
+ OString aContentString("The quick brown fox jumps over the lazy dog");
+ std::vector<sal_uInt8> aContent(aContentString.getStr(),
+ aContentString.getStr() + aContentString.getLength());
+ std::vector<sal_uInt8> aKey = { 'k', 'e', 'y' };
+ {
+ oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA1);
+ aCryptoHash.update(aContent);
+ std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(std::string("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
+ toString(aHash));
+ }
+
+ {
+ oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA256);
+ aCryptoHash.update(aContent);
+ std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(
+ std::string("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"),
+ toString(aHash));
+ }
+
+ {
+ oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA512);
+ aCryptoHash.update(aContent);
+ std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(
+ std::string("b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549"
+ "f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"),
+ toString(aHash));
+ }
+}
+
+void CryptoTest::testRoundUp()
+{
+ CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(16, 16));
+ CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(32, 16));
+ CPPUNIT_ASSERT_EQUAL(64, oox::crypto::roundUp(64, 16));
+
+ CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(01, 16));
+ CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(17, 16));
+ CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(31, 16));
+}
+
+void CryptoTest::testStandard2007()
+{
+ oox::crypto::Standard2007Engine aEngine;
+ {
+ aEngine.setupEncryption("Password");
+
+ SvMemoryStream aEncryptionInfo;
+ oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), false);
+
+ aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
+ aBinaryEncryptionInfoOutputStream.close();
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo.GetSize());
+ }
+
+ SvMemoryStream aUnencryptedInput;
+ SvMemoryStream aEncryptedStream;
+
+ OString aTestString = "1234567890ABCDEFG";
+
+ aUnencryptedInput.WriteBytes(aTestString.getStr(), aTestString.getLength() + 1);
+ aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN);
+
+ {
+ uno::Reference<io::XInputStream> xInputStream(
+ new utl::OSeekableInputStreamWrapper(aUnencryptedInput));
+ uno::Reference<io::XOutputStream> xOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptedStream));
+
+ aEngine.encrypt(xInputStream, xOutputStream, aUnencryptedInput.GetSize());
+
+ xOutputStream->flush();
+
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>(aEncryptedStream.GetData());
+ sal_uInt64 nSize = aEncryptedStream.GetSize();
+
+ std::vector<sal_uInt8> aData(nSize);
+ std::copy(pData, pData + nSize, aData.data());
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize);
+ }
+
+ aEncryptedStream.Seek(STREAM_SEEK_TO_BEGIN);
+ SvMemoryStream aUnencryptedOutput;
+
+ {
+ oox::BinaryXInputStream aBinaryInputStream(
+ new utl::OSeekableInputStreamWrapper(aEncryptedStream), true);
+ oox::BinaryXOutputStream aBinaryOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput), true);
+
+ aEngine.decrypt(aBinaryInputStream, aBinaryOutputStream);
+ aBinaryOutputStream.close();
+ aBinaryInputStream.close();
+
+ const char* pData = static_cast<const char*>(aUnencryptedOutput.GetData());
+ sal_uInt64 nSize = aUnencryptedOutput.GetSize();
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(18), nSize);
+
+ OString aString(pData);
+
+ CPPUNIT_ASSERT_EQUAL(aTestString, aString);
+ }
+}
+
+void CryptoTest::testAgileEncryptionVerifier()
+{
+ oox::crypto::AgileEngine aEngine;
+
+ OUString aPassword("Password");
+
+ aEngine.setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"),
+ OUString("ChainingModeCBC"), OUString("SHA1") });
+
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.generateAndEncryptVerifierHash(aPassword));
+ CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong"));
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword));
+
+ aEngine.setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"),
+ OUString("ChainingModeCBC"), OUString("SHA512") });
+
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.generateAndEncryptVerifierHash(aPassword));
+ CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong"));
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword));
+}
+
+void CryptoTest::testAgileEncryptionInfoWritingAndParsing()
+{
+ OUString aPassword("Password");
+ std::vector<sal_uInt8> aKeyDataSalt;
+
+ { // Preset AES128 - SHA1
+ SvMemoryStream aEncryptionInfo;
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ aEngine.setPreset(oox::crypto::AgileEncryptionPreset::AES_128_SHA1);
+ aEngine.setupEncryption(aPassword);
+ aKeyDataSalt = aEngine.getInfo().keyDataSalt;
+
+ oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true);
+
+ aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
+ aBinaryEncryptionInfoOutputStream.close();
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(996), aEncryptionInfo.GetSize());
+ }
+
+ aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);
+
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ uno::Reference<io::XInputStream> xInputStream(
+ new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
+
+ xInputStream->skipBytes(4); // Encryption type -> Agile
+
+ CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream));
+
+ oox::crypto::AgileEncryptionInfo& rInfo = aEngine.getInfo();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo.spinCount);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.saltSize);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo.keyBits);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(20), rInfo.hashSize);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.blockSize);
+ CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo.cipherAlgorithm);
+ CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo.cipherChaining);
+ CPPUNIT_ASSERT_EQUAL(OUString("SHA1"), rInfo.hashAlgorithm);
+ CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(rInfo.keyDataSalt));
+
+ CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong"));
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword));
+ }
+ }
+
+ { // Preset AES256 - SHA512
+ SvMemoryStream aEncryptionInfo;
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ aEngine.setPreset(oox::crypto::AgileEncryptionPreset::AES_256_SHA512);
+ aEngine.setupEncryption(aPassword);
+ aKeyDataSalt = aEngine.getInfo().keyDataSalt;
+
+ oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true);
+
+ aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
+ aBinaryEncryptionInfoOutputStream.close();
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(1112), aEncryptionInfo.GetSize());
+ }
+
+ aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);
+
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ uno::Reference<io::XInputStream> xInputStream(
+ new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
+
+ xInputStream->skipBytes(4); // Encryption type -> Agile
+
+ CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream));
+
+ oox::crypto::AgileEncryptionInfo& rInfo = aEngine.getInfo();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo.spinCount);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.saltSize);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(256), rInfo.keyBits);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(64), rInfo.hashSize);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.blockSize);
+ CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo.cipherAlgorithm);
+ CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo.cipherChaining);
+ CPPUNIT_ASSERT_EQUAL(OUString("SHA512"), rInfo.hashAlgorithm);
+ CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(rInfo.keyDataSalt));
+
+ CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong"));
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword));
+ }
+ }
+}
+
+void CryptoTest::testAgileDataIntegrityHmacKey()
+{
+ OUString aPassword("Password");
+
+ std::vector<sal_uInt8> aKeyDataSalt;
+
+ std::vector<sal_uInt8> aHmacKey;
+ std::vector<sal_uInt8> aHmacEncryptedKey;
+
+ SvMemoryStream aEncryptionInfo;
+ {
+ oox::crypto::AgileEngine aEngine;
+ aEngine.setupEncryption(aPassword);
+ oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true);
+ aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
+ aBinaryEncryptionInfoOutputStream.close();
+
+ aHmacKey = aEngine.getInfo().hmacKey;
+ aKeyDataSalt = aEngine.getInfo().keyDataSalt;
+ aHmacEncryptedKey = aEngine.getInfo().hmacEncryptedKey;
+ }
+
+ aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);
+
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ uno::Reference<io::XInputStream> xInputStream(
+ new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
+
+ xInputStream->skipBytes(4); // Encryption type -> Agile
+
+ CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream));
+ CPPUNIT_ASSERT(aEngine.generateEncryptionKey(aPassword));
+
+ CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(aEngine.getInfo().keyDataSalt));
+
+ CPPUNIT_ASSERT_EQUAL(toString(aHmacEncryptedKey),
+ toString(aEngine.getInfo().hmacEncryptedKey));
+
+ CPPUNIT_ASSERT_EQUAL(size_t(64), aHmacKey.size());
+ CPPUNIT_ASSERT_EQUAL(toString(aHmacKey), toString(aEngine.getInfo().hmacKey));
+ }
+}
+
+void CryptoTest::testAgileEncryptingAndDecrypting()
+{
+ OUString aPassword("Password");
+
+ SvMemoryStream aEncryptionInfo;
+ SvMemoryStream aEncryptedStream;
+
+ OString aTestString = "1234567890ABCDEFGH";
+
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ // Setup input
+ SvMemoryStream aUnencryptedInput;
+ uno::Reference<io::XInputStream> xInputStream(
+ new utl::OSeekableInputStreamWrapper(aUnencryptedInput));
+
+ aUnencryptedInput.WriteBytes(aTestString.getStr(), aTestString.getLength() + 1);
+ aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Setup output
+ uno::Reference<io::XOutputStream> xOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptedStream));
+
+ // Write content
+ aEngine.setupEncryption(aPassword);
+ aEngine.encrypt(xInputStream, xOutputStream, aUnencryptedInput.GetSize());
+ xOutputStream->flush();
+
+ // Check content
+ sal_uInt64 nSize = aEncryptedStream.GetSize();
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize);
+
+ // Setup and write encryption info
+ oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true);
+ aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
+ aBinaryEncryptionInfoOutputStream.close();
+ }
+
+ aEncryptedStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);
+
+ {
+ oox::crypto::AgileEngine aEngine;
+
+ // Read encryption info
+ uno::Reference<io::XInputStream> xEncryptionInfo(
+ new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
+
+ xEncryptionInfo->skipBytes(4); // Encryption type -> Agile
+
+ CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xEncryptionInfo));
+
+ // Setup password
+ CPPUNIT_ASSERT(aEngine.generateEncryptionKey(aPassword));
+
+ // Setup encrypted input stream
+ oox::BinaryXInputStream aBinaryInputStream(
+ new utl::OSeekableInputStreamWrapper(aEncryptedStream), true);
+
+ // Setup output stream
+ SvMemoryStream aUnencryptedOutput;
+ oox::BinaryXOutputStream aBinaryOutputStream(
+ new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput), true);
+
+ // Decrypt
+ aEngine.decrypt(aBinaryInputStream, aBinaryOutputStream);
+ aBinaryOutputStream.close();
+ aBinaryInputStream.close();
+
+ // Check decrypted output
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(19), aUnencryptedOutput.GetSize());
+
+ OString aString(static_cast<const char*>(aUnencryptedOutput.GetData()));
+ CPPUNIT_ASSERT_EQUAL(aTestString, aString);
+
+ // Check data integrity
+ CPPUNIT_ASSERT_EQUAL(true, aEngine.checkDataIntegrity());
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/data/FaultyPathStart.odp b/oox/qa/unit/data/FaultyPathStart.odp
new file mode 100644
index 000000000..219795ffa
--- /dev/null
+++ b/oox/qa/unit/data/FaultyPathStart.odp
Binary files differ
diff --git a/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx b/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx
new file mode 100644
index 000000000..db26dc032
--- /dev/null
+++ b/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx
Binary files differ
diff --git a/oox/qa/unit/data/camera-rotation-revolution.docx b/oox/qa/unit/data/camera-rotation-revolution.docx
new file mode 100644
index 000000000..74054aa82
--- /dev/null
+++ b/oox/qa/unit/data/camera-rotation-revolution.docx
Binary files differ
diff --git a/oox/qa/unit/data/chart-data-label-char-color.docx b/oox/qa/unit/data/chart-data-label-char-color.docx
new file mode 100644
index 000000000..0e389f923
--- /dev/null
+++ b/oox/qa/unit/data/chart-data-label-char-color.docx
Binary files differ
diff --git a/oox/qa/unit/data/chart-theme-override.pptx b/oox/qa/unit/data/chart-theme-override.pptx
new file mode 100644
index 000000000..85243b678
--- /dev/null
+++ b/oox/qa/unit/data/chart-theme-override.pptx
Binary files differ
diff --git a/oox/qa/unit/data/customshape-position.docx b/oox/qa/unit/data/customshape-position.docx
new file mode 100644
index 000000000..227928f20
--- /dev/null
+++ b/oox/qa/unit/data/customshape-position.docx
Binary files differ
diff --git a/oox/qa/unit/data/dml-groupshape-polygon.docx b/oox/qa/unit/data/dml-groupshape-polygon.docx
new file mode 100644
index 000000000..6d20b0342
--- /dev/null
+++ b/oox/qa/unit/data/dml-groupshape-polygon.docx
Binary files differ
diff --git a/oox/qa/unit/data/endParaRPr-newline-textsize.pptx b/oox/qa/unit/data/endParaRPr-newline-textsize.pptx
new file mode 100644
index 000000000..109f818ec
--- /dev/null
+++ b/oox/qa/unit/data/endParaRPr-newline-textsize.pptx
Binary files differ
diff --git a/oox/qa/unit/data/gradient-multistep-transparency.pptx b/oox/qa/unit/data/gradient-multistep-transparency.pptx
new file mode 100644
index 000000000..83946c71b
--- /dev/null
+++ b/oox/qa/unit/data/gradient-multistep-transparency.pptx
Binary files differ
diff --git a/oox/qa/unit/data/graphic-stroke.pptx b/oox/qa/unit/data/graphic-stroke.pptx
new file mode 100644
index 000000000..f4465476f
--- /dev/null
+++ b/oox/qa/unit/data/graphic-stroke.pptx
Binary files differ
diff --git a/oox/qa/unit/data/group-spt202.docx b/oox/qa/unit/data/group-spt202.docx
new file mode 100644
index 000000000..14bf00b50
--- /dev/null
+++ b/oox/qa/unit/data/group-spt202.docx
Binary files differ
diff --git a/oox/qa/unit/data/import-characters.pptx b/oox/qa/unit/data/import-characters.pptx
new file mode 100644
index 000000000..293380236
--- /dev/null
+++ b/oox/qa/unit/data/import-characters.pptx
Binary files differ
diff --git a/oox/qa/unit/data/import-mce.pptx b/oox/qa/unit/data/import-mce.pptx
new file mode 100644
index 000000000..65cf1cbab
--- /dev/null
+++ b/oox/qa/unit/data/import-mce.pptx
Binary files differ
diff --git a/oox/qa/unit/data/multiple-group-shapes.docx b/oox/qa/unit/data/multiple-group-shapes.docx
new file mode 100644
index 000000000..c751bcdf6
--- /dev/null
+++ b/oox/qa/unit/data/multiple-group-shapes.docx
Binary files differ
diff --git a/oox/qa/unit/data/preset-adjust-value.pptx b/oox/qa/unit/data/preset-adjust-value.pptx
new file mode 100644
index 000000000..d1d570a19
--- /dev/null
+++ b/oox/qa/unit/data/preset-adjust-value.pptx
Binary files differ
diff --git a/oox/qa/unit/data/refer-to-theme-shape-fill.odp b/oox/qa/unit/data/refer-to-theme-shape-fill.odp
new file mode 100644
index 000000000..3a32aa716
--- /dev/null
+++ b/oox/qa/unit/data/refer-to-theme-shape-fill.odp
Binary files differ
diff --git a/oox/qa/unit/data/refer-to-theme.pptx b/oox/qa/unit/data/refer-to-theme.pptx
new file mode 100644
index 000000000..9f05bf7b0
--- /dev/null
+++ b/oox/qa/unit/data/refer-to-theme.pptx
Binary files differ
diff --git a/oox/qa/unit/data/shape-non-autosize-with-text.docx b/oox/qa/unit/data/shape-non-autosize-with-text.docx
new file mode 100644
index 000000000..b9ae501f4
--- /dev/null
+++ b/oox/qa/unit/data/shape-non-autosize-with-text.docx
Binary files differ
diff --git a/oox/qa/unit/data/shape-text-adjust-left.pptx b/oox/qa/unit/data/shape-text-adjust-left.pptx
new file mode 100644
index 000000000..d197425b9
--- /dev/null
+++ b/oox/qa/unit/data/shape-text-adjust-left.pptx
Binary files differ
diff --git a/oox/qa/unit/data/shape-text-alignment.pptx b/oox/qa/unit/data/shape-text-alignment.pptx
new file mode 100644
index 000000000..ff4ff06f2
--- /dev/null
+++ b/oox/qa/unit/data/shape-text-alignment.pptx
Binary files differ
diff --git a/oox/qa/unit/data/smartart-groupshape.pptx b/oox/qa/unit/data/smartart-groupshape.pptx
new file mode 100644
index 000000000..81dcee1e5
--- /dev/null
+++ b/oox/qa/unit/data/smartart-groupshape.pptx
Binary files differ
diff --git a/oox/qa/unit/data/table-shadow.pptx b/oox/qa/unit/data/table-shadow.pptx
new file mode 100644
index 000000000..3247404cb
--- /dev/null
+++ b/oox/qa/unit/data/table-shadow.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf100391_TextAreaRect.odp b/oox/qa/unit/data/tdf100391_TextAreaRect.odp
new file mode 100644
index 000000000..b9b9e5b39
--- /dev/null
+++ b/oox/qa/unit/data/tdf100391_TextAreaRect.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf109169_OctagonBevel.odt b/oox/qa/unit/data/tdf109169_OctagonBevel.odt
new file mode 100644
index 000000000..2ba39b102
--- /dev/null
+++ b/oox/qa/unit/data/tdf109169_OctagonBevel.odt
Binary files differ
diff --git a/oox/qa/unit/data/tdf112450_vml_polyline.docx b/oox/qa/unit/data/tdf112450_vml_polyline.docx
new file mode 100644
index 000000000..a31124ee7
--- /dev/null
+++ b/oox/qa/unit/data/tdf112450_vml_polyline.docx
Binary files differ
diff --git a/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx b/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx
new file mode 100644
index 000000000..1862c0257
--- /dev/null
+++ b/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf125582_TextOnCircle.pptx b/oox/qa/unit/data/tdf125582_TextOnCircle.pptx
new file mode 100644
index 000000000..b2c09e515
--- /dev/null
+++ b/oox/qa/unit/data/tdf125582_TextOnCircle.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf131082.pptx b/oox/qa/unit/data/tdf131082.pptx
new file mode 100644
index 000000000..dbe88126f
--- /dev/null
+++ b/oox/qa/unit/data/tdf131082.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx b/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx
new file mode 100644
index 000000000..17c8cabc0
--- /dev/null
+++ b/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx
Binary files differ
diff --git a/oox/qa/unit/data/tdf141463_GroupTransform.pptx b/oox/qa/unit/data/tdf141463_GroupTransform.pptx
new file mode 100644
index 000000000..36c506262
--- /dev/null
+++ b/oox/qa/unit/data/tdf141463_GroupTransform.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt
new file mode 100644
index 000000000..d40dccac8
--- /dev/null
+++ b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt
Binary files differ
diff --git a/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt
new file mode 100644
index 000000000..e932cf47b
--- /dev/null
+++ b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt
Binary files differ
diff --git a/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt
new file mode 100644
index 000000000..bc0357a1a
--- /dev/null
+++ b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt
Binary files differ
diff --git a/oox/qa/unit/data/tdf142605_CurveSize.odp b/oox/qa/unit/data/tdf142605_CurveSize.odp
new file mode 100644
index 000000000..c797d378f
--- /dev/null
+++ b/oox/qa/unit/data/tdf142605_CurveSize.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_endsubpath.odp b/oox/qa/unit/data/tdf147978_endsubpath.odp
new file mode 100644
index 000000000..2dfd55de1
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_endsubpath.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp
new file mode 100644
index 000000000..99ddda7c1
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp
new file mode 100644
index 000000000..49e01bc09
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp
new file mode 100644
index 000000000..3dcd0d567
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp
new file mode 100644
index 000000000..611225178
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx
new file mode 100644
index 000000000..bbedc7ab9
--- /dev/null
+++ b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx
Binary files differ
diff --git a/oox/qa/unit/data/tdf148784_StretchCommandQ.odp b/oox/qa/unit/data/tdf148784_StretchCommandQ.odp
new file mode 100644
index 000000000..3da092b2c
--- /dev/null
+++ b/oox/qa/unit/data/tdf148784_StretchCommandQ.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf148784_StretchCommandVW.odp b/oox/qa/unit/data/tdf148784_StretchCommandVW.odp
new file mode 100644
index 000000000..bf1054a20
--- /dev/null
+++ b/oox/qa/unit/data/tdf148784_StretchCommandVW.odp
Binary files differ
diff --git a/oox/qa/unit/data/tdf148784_StretchXY.odp b/oox/qa/unit/data/tdf148784_StretchXY.odp
new file mode 100644
index 000000000..f9df40570
--- /dev/null
+++ b/oox/qa/unit/data/tdf148784_StretchXY.odp
Binary files differ
diff --git a/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx b/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx
new file mode 100644
index 000000000..4dbf3717d
--- /dev/null
+++ b/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx
Binary files differ
diff --git a/oox/qa/unit/data/theme-tint.pptx b/oox/qa/unit/data/theme-tint.pptx
new file mode 100644
index 000000000..23ab7589d
--- /dev/null
+++ b/oox/qa/unit/data/theme-tint.pptx
Binary files differ
diff --git a/oox/qa/unit/data/theme.pptx b/oox/qa/unit/data/theme.pptx
new file mode 100644
index 000000000..5268e4041
--- /dev/null
+++ b/oox/qa/unit/data/theme.pptx
Binary files differ
diff --git a/oox/qa/unit/data/transparent-text.pptx b/oox/qa/unit/data/transparent-text.pptx
new file mode 100644
index 000000000..b7b3ede4d
--- /dev/null
+++ b/oox/qa/unit/data/transparent-text.pptx
Binary files differ
diff --git a/oox/qa/unit/data/vba/complex1.bin b/oox/qa/unit/data/vba/complex1.bin
new file mode 100644
index 000000000..390208290
--- /dev/null
+++ b/oox/qa/unit/data/vba/complex1.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/complex1.bin b/oox/qa/unit/data/vba/reference/complex1.bin
new file mode 100644
index 000000000..335b8e808
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/complex1.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/simple1.bin b/oox/qa/unit/data/vba/reference/simple1.bin
new file mode 100644
index 000000000..a4644fbb7
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/simple1.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/simple2.bin b/oox/qa/unit/data/vba/reference/simple2.bin
new file mode 100644
index 000000000..5de3f5ae0
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/simple2.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/simple3.bin b/oox/qa/unit/data/vba/reference/simple3.bin
new file mode 100644
index 000000000..a38e8a868
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/simple3.bin
@@ -0,0 +1 @@
+°
diff --git a/oox/qa/unit/data/vba/reference/spec321.bin b/oox/qa/unit/data/vba/reference/spec321.bin
new file mode 100644
index 000000000..3120c7f50
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/spec321.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/spec322.bin b/oox/qa/unit/data/vba/reference/spec322.bin
new file mode 100644
index 000000000..7724b62a4
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/spec322.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/reference/spec323.bin b/oox/qa/unit/data/vba/reference/spec323.bin
new file mode 100644
index 000000000..51104ee0b
--- /dev/null
+++ b/oox/qa/unit/data/vba/reference/spec323.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/simple1.bin b/oox/qa/unit/data/vba/simple1.bin
new file mode 100644
index 000000000..d59c1d5ef
--- /dev/null
+++ b/oox/qa/unit/data/vba/simple1.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/simple2.bin b/oox/qa/unit/data/vba/simple2.bin
new file mode 100644
index 000000000..1b9b88b16
--- /dev/null
+++ b/oox/qa/unit/data/vba/simple2.bin
Binary files differ
diff --git a/oox/qa/unit/data/vba/simple3.bin b/oox/qa/unit/data/vba/simple3.bin
new file mode 100644
index 000000000..72060e8a2
--- /dev/null
+++ b/oox/qa/unit/data/vba/simple3.bin
@@ -0,0 +1 @@
+
diff --git a/oox/qa/unit/data/vba/spec321.bin b/oox/qa/unit/data/vba/spec321.bin
new file mode 100644
index 000000000..c5d48c9fc
--- /dev/null
+++ b/oox/qa/unit/data/vba/spec321.bin
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuv. \ No newline at end of file
diff --git a/oox/qa/unit/data/vba/spec322.bin b/oox/qa/unit/data/vba/spec322.bin
new file mode 100644
index 000000000..781c426c0
--- /dev/null
+++ b/oox/qa/unit/data/vba/spec322.bin
@@ -0,0 +1 @@
+#aaabcdefaaaaghijaaaaaklaaamnopqaaaaaaaaaaaarstuvwxyzaaa \ No newline at end of file
diff --git a/oox/qa/unit/data/vba/spec323.bin b/oox/qa/unit/data/vba/spec323.bin
new file mode 100644
index 000000000..9ca58b98a
--- /dev/null
+++ b/oox/qa/unit/data/vba/spec323.bin
@@ -0,0 +1 @@
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file
diff --git a/oox/qa/unit/data/watermark.docx b/oox/qa/unit/data/watermark.docx
new file mode 100644
index 000000000..c9eacff9a
--- /dev/null
+++ b/oox/qa/unit/data/watermark.docx
Binary files differ
diff --git a/oox/qa/unit/drawingml.cxx b/oox/qa/unit/drawingml.cxx
new file mode 100644
index 000000000..dedea55b9
--- /dev/null
+++ b/oox/qa/unit/drawingml.cxx
@@ -0,0 +1,566 @@
+/* -*- 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 <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/XMasterPageTarget.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Gets one child of xShape, which one is specified by nIndex.
+uno::Reference<drawing::XShape> getChildShape(const uno::Reference<drawing::XShape>& xShape,
+ sal_Int32 nIndex)
+{
+ uno::Reference<container::XIndexAccess> xGroup(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGroup.is());
+
+ CPPUNIT_ASSERT(xGroup->getCount() > nIndex);
+
+ uno::Reference<drawing::XShape> xRet(xGroup->getByIndex(nIndex), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xRet.is());
+
+ return xRet;
+}
+}
+
+/// oox drawingml tests.
+class OoxDrawingmlTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+ void load(const OUString& rURL);
+ void loadAndReload(const OUString& rURL, const OUString& rFilterName);
+};
+
+void OoxDrawingmlTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void OoxDrawingmlTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void OoxDrawingmlTest::load(const OUString& rURL) { mxComponent = loadFromDesktop(rURL); }
+
+void OoxDrawingmlTest::loadAndReload(const OUString& rURL, const OUString& rFilterName)
+{
+ load(rURL);
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ mxComponent->dispose();
+ validate(aTempFile.GetFileName(), test::OOXML);
+ mxComponent = loadFromDesktop(aTempFile.GetURL());
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTransparentText)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "transparent-text.pptx";
+ loadAndReload(aURL, "Impress Office Open XML");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParagraph(
+ xShape->createEnumeration()->nextElement(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPortion(xParagraph->createEnumeration()->nextElement(),
+ uno::UNO_QUERY);
+
+ sal_Int16 nTransparency = 0;
+ xPortion->getPropertyValue("CharTransparence") >>= nTransparency;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 75
+ // - Actual : 0
+ // i.e. the transparency of the character color was lost on import/export.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(75), nTransparency);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTdf131082)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf131082.pptx";
+ loadAndReload(aURL, "Impress Office Open XML");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> XPropSet(getChildShape(getChildShape(xShape, 0), 0),
+ uno::UNO_QUERY);
+
+ drawing::FillStyle eFillStyle = drawing::FillStyle_NONE;
+ XPropSet->getPropertyValue("FillStyle") >>= eFillStyle;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // with drawing::FillStyle_NONE - 0
+ CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_SOLID, eFillStyle);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testPresetAdjustValue)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "preset-adjust-value.pptx";
+
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aGeoPropSeq;
+ xShapeProps->getPropertyValue("CustomShapeGeometry") >>= aGeoPropSeq;
+ comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
+ uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq;
+ aGeoPropMap.getValue("AdjustmentValues") >>= aAdjustmentSeq;
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aAdjustmentSeq.getLength());
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 11587
+ // - Actual : 10813
+ // i.e. the adjust value was set from the placeholder, not from the shape.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(11587), aAdjustmentSeq[0].Value.get<sal_Int32>());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testChartDataLabelCharColor)
+{
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "chart-data-label-char-color.docx";
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<chart2::XChartDocument> xModel(xShape->getPropertyValue("Model"),
+ uno::UNO_QUERY);
+ uno::Reference<chart2::XCoordinateSystemContainer> xDiagram(xModel->getFirstDiagram(),
+ uno::UNO_QUERY);
+
+ uno::Reference<chart2::XChartTypeContainer> xCoordinateSystem(
+ xDiagram->getCoordinateSystems()[0], uno::UNO_QUERY);
+
+ uno::Reference<chart2::XDataSeriesContainer> xChartType(xCoordinateSystem->getChartTypes()[0],
+ uno::UNO_QUERY);
+
+ uno::Reference<chart2::XDataSeries> xDataSeries = xChartType->getDataSeries()[0];
+
+ uno::Reference<beans::XPropertySet> xDataPoint = xDataSeries->getDataPointByIndex(0);
+
+ uno::Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aLabels;
+ xDataPoint->getPropertyValue("CustomLabelFields") >>= aLabels;
+ uno::Reference<beans::XPropertySet> xLabel = aLabels[0];
+
+ sal_Int32 nCharColor = 0;
+ xLabel->getPropertyValue("CharColor") >>= nCharColor;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 16777215
+ // - Actual : -1
+ // i.e. the data label had no explicit (white) color.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0xffffff), nCharColor);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testGradientMultiStepTransparency)
+{
+ // Load a document with a multi-step gradient.
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "gradient-multistep-transparency.pptx";
+ load(aURL);
+
+ // Check the end transparency of the gradient.
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xShape(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("Rectangle 4"), xShape->getName());
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ awt::Gradient aTransparence;
+ xShapeProps->getPropertyValue("FillTransparenceGradient") >>= aTransparence;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 16777215 (0xffffff)
+ // - Actual : 3487029 (0x353535)
+ // i.e. the end transparency was not 100%, but was 21%, leading to an unexpected visible line on
+ // the right of this shape.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0xffffff), aTransparence.EndColor);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testShapeTextAlignment)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "shape-text-alignment.pptx";
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ sal_Int16 nParaAdjust = -1;
+ CPPUNIT_ASSERT(xShape->getPropertyValue("ParaAdjust") >>= nParaAdjust);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0
+ // - Actual : 3
+ // i.e. text which is meant to be left-aligned was centered at a paragraph level.
+ CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_LEFT,
+ static_cast<style::ParagraphAdjust>(nParaAdjust));
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testShapeTextAdjustLeft)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "shape-text-adjust-left.pptx";
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ drawing::TextHorizontalAdjust eAdjust;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 3 (center)
+ // - Actual : 1 (block)
+ // i.e. text was center-adjusted, not default-adjusted (~left).
+ CPPUNIT_ASSERT(xShape->getPropertyValue("TextHorizontalAdjust") >>= eAdjust);
+ CPPUNIT_ASSERT_EQUAL(drawing::TextHorizontalAdjust_BLOCK, eAdjust);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testCameraRotationRevolution)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "camera-rotation-revolution.docx";
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xShape0(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xShape1(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps0(xShape0, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps1(xShape1, uno::UNO_QUERY);
+ sal_Int32 nRotateAngle0;
+ sal_Int32 nRotateAngle1;
+ xShapeProps0->getPropertyValue("RotateAngle") >>= nRotateAngle0;
+ xShapeProps1->getPropertyValue("RotateAngle") >>= nRotateAngle1;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 8000
+ // - Actual : 0
+ // so the camera rotation would not have been factored into how the shape is displayed
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(8000), nRotateAngle0);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(27000), nRotateAngle1);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTdf146534_CameraRotationRevolutionNonWpsShapes)
+{
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "camera-rotation-revolution-nonwps.pptx";
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xShape0(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xShape1(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps0(xShape0, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps1(xShape1, uno::UNO_QUERY);
+ sal_Int32 nRotateAngle0;
+ sal_Int32 nRotateAngle1;
+ xShapeProps0->getPropertyValue("RotateAngle") >>= nRotateAngle0;
+ xShapeProps1->getPropertyValue("RotateAngle") >>= nRotateAngle1;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 9000
+ // - Actual : 0
+ // so the camera rotation would not have been factored into how the shape is displayed
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(9000), nRotateAngle0);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(30500), nRotateAngle1);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTableShadow)
+{
+ auto verify = [](const uno::Reference<lang::XComponent>& xComponent) {
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(
+ xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ bool bShadow = false;
+ CPPUNIT_ASSERT(xShape->getPropertyValue("Shadow") >>= bShadow);
+
+ CPPUNIT_ASSERT(bShadow);
+ sal_Int32 nColor = 0;
+ CPPUNIT_ASSERT(xShape->getPropertyValue("ShadowColor") >>= nColor);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0xff0000), nColor);
+ };
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "table-shadow.pptx";
+ load(aURL);
+ // Without the accompanying fix in place, this test would have failed, because shadow on a table
+ // was lost on import.
+ verify(getComponent());
+
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("Impress Office Open XML");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ getComponent()->dispose();
+ validate(aTempFile.GetFileName(), test::OOXML);
+ getComponent() = loadFromDesktop(aTempFile.GetURL());
+ // Without the accompanying fix in place, this test would have failed, because shadow on a table
+ // was lost on export.
+ verify(getComponent());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testGroupShapeSmartArt)
+{
+ // Given a file with a smartart inside a group shape:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "smartart-groupshape.pptx";
+
+ // When loading that file:
+ load(aURL);
+
+ // Then make sure that the smartart is not just an empty group shape:
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XShapes> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<drawing::XShapes> xSmartArt(xGroup->getByIndex(0), uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed, because we lost all
+ // children of the group shape representing the smartart.
+ CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), xSmartArt->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTdf142605_CurveSize)
+{
+ // The document contains a Bezier curve, where the control points are outside the bounding
+ // rectangle of the shape. Error was, that the export uses a path size which included the
+ // control points.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf142605_CurveSize.odp";
+ loadAndReload(aURL, "Impress Office Open XML");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ css::awt::Rectangle aBoundRect;
+ xShapeProps->getPropertyValue("BoundRect") >>= aBoundRect;
+ // Without fix, size was 6262 x 3509, and position was 10037|6790.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(8601), aBoundRect.Width, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(4601), aBoundRect.Height, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(7699), aBoundRect.X, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(5699), aBoundRect.Y, 1);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testChartThemeOverride)
+{
+ // Given a document with 2 slides, slide1 has a chart with a theme override and slide2 has a
+ // shape:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "chart-theme-override.pptx";
+
+ // When loading that document:
+ load(aURL);
+
+ // Then make sure that the slide 2 shape's text color is blue, not red:
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(1),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xPara(xText->createEnumeration()->nextElement(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPortion(xPara->createEnumeration()->nextElement(),
+ uno::UNO_QUERY);
+ sal_Int32 nActual{ 0 };
+ xPortion->getPropertyValue("CharColor") >>= nActual;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 4485828 (0x4472c4)
+ // - Actual : 16711680 (0xff0000)
+ // i.e. the text color was red, not blue.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0x4472C4), nActual);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testPptxTheme)
+{
+ // Given a PPTX file with a slide -> master slide -> theme:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "theme.pptx";
+
+ // When importing the document:
+ load(aURL);
+
+ // Then make sure the theme + referring to that theme is imported:
+ // Check the imported theme of the master page:
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XMasterPageTarget> xDrawPage(
+ xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xMasterpage(xDrawPage->getMasterPage(), uno::UNO_QUERY);
+ comphelper::SequenceAsHashMap aMap(xMasterpage->getPropertyValue("Theme"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Office Theme"), aMap["Name"].get<OUString>());
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Cannot extract an Any(void) to string!
+ // i.e. the name of the color scheme was lost on import.
+ CPPUNIT_ASSERT_EQUAL(OUString("Office"), aMap["ColorSchemeName"].get<OUString>());
+
+ // Check the last color in the color set, value is from ppt/theme/theme1.xml.
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Cannot extract an Any(void) to []long!
+ auto aColorScheme = aMap["ColorScheme"].get<uno::Sequence<util::Color>>();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12), aColorScheme.getLength());
+ CPPUNIT_ASSERT_EQUAL(static_cast<util::Color>(0x954F72), aColorScheme[11]);
+
+ // Check the reference to that theme:
+ uno::Reference<drawing::XShapes> xDrawPageShapes(xDrawPage, uno::UNO_QUERY);
+ uno::Reference<text::XTextRange> xShape(xDrawPageShapes->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xPara(xText->createEnumeration()->nextElement(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPortion(xPara->createEnumeration()->nextElement(),
+ uno::UNO_QUERY);
+ // 4 is accent1, see oox::drawingml::Color::getSchemeColorIndex().
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4),
+ xPortion->getPropertyValue("CharColorTheme").get<sal_Int32>());
+ // 60000 in the file, just 100th vs 1000th percents.
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 6000
+ // - Actual : 10000
+ // i.e. we had the default 100% value, not the value from the file.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6000),
+ xPortion->getPropertyValue("CharColorLumMod").get<sal_Int32>());
+
+ // 40000 in the file, just 100th vs 1000th percents.
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 4000
+ // - Actual : 0
+ // i.e. we had the default 0% value, not the value from the file.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4000),
+ xPortion->getPropertyValue("CharColorLumOff").get<sal_Int32>());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTdf132557_footerCustomShapes)
+{
+ // slide with date, footer, slide number with custom shapes
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "testTdf132557_footerCustomShapes.pptx";
+ // When importing the document:
+ load(aURL);
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+
+ // Test if we were able to import the footer shapes with CustomShape service.
+ uno::Reference<drawing::XShape> xShapeDateTime(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.drawing.CustomShape"),
+ xShapeDateTime->getShapeType());
+ // Without the accompanying fix in place, this test would have failed with:
+ // An uncaught exception of type com.sun.star.lang.IndexOutOfBoundsException
+ // i.e. the shape wasn't on the slide there since it was imported as a property, not a shape.
+
+ uno::Reference<drawing::XShape> xShapeFooter(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.drawing.CustomShape"),
+ xShapeFooter->getShapeType());
+
+ uno::Reference<drawing::XShape> xShapeSlideNum(xDrawPage->getByIndex(2), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.drawing.CustomShape"),
+ xShapeSlideNum->getShapeType());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testThemeTint)
+{
+ // Given a document with a table style, using theme color with tinting in the A2 cell:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "theme-tint.pptx";
+
+ // When loading that document:
+ load(aURL);
+
+ // Then make sure that we only import theming info to the doc model if the effects are limited
+ // to lum mod / off that we can handle (i.e. no tint/shade):
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<table::XCellRange> xTable;
+ CPPUNIT_ASSERT(xShape->getPropertyValue("Model") >>= xTable);
+ uno::Reference<beans::XPropertySet> xA1(xTable->getCellByPosition(0, 0), uno::UNO_QUERY);
+ sal_Int16 nFillColorTheme{};
+ CPPUNIT_ASSERT(xA1->getPropertyValue("FillColorTheme") >>= nFillColorTheme);
+ // This is OK, no problematic effects:
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(4), nFillColorTheme);
+ uno::Reference<beans::XPropertySet> xA2(xTable->getCellByPosition(0, 1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xA2->getPropertyValue("FillColorTheme") >>= nFillColorTheme);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: -1
+ // - Actual : 4
+ // i.e. we remembered the theme index, without being able to remember the tint effect, leading
+ // to a bad background color.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(-1), nFillColorTheme);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testTdf113187ConstantArcTo)
+{
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf113187_arcTo_withoutReferences.pptx";
+ load(aURL);
+ // Get ViewBox of shape
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aGeoPropSeq;
+ xShapeProps->getPropertyValue("CustomShapeGeometry") >>= aGeoPropSeq;
+ comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
+ // Without the fix width and height of the ViewBox were 0 and thus the shape was not shown.
+ auto aViewBox = aGeoPropMap["ViewBox"].get<css::awt::Rectangle>();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3600000), aViewBox.Width);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3600000), aViewBox.Height);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
new file mode 100644
index 000000000..90089a994
--- /dev/null
+++ b/oox/qa/unit/export.cxx
@@ -0,0 +1,777 @@
+/* -*- 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/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Covers ooox/source/export/ fixes.
+class Test : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+ utl::TempFile maTempFile;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
+ utl::TempFile& getTempFile() { return maTempFile; }
+ void loadAndSave(const OUString& rURL, const OUString& rFilterName);
+};
+
+void Test::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void Test::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void Test::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
+{
+ XmlTestTools::registerOOXMLNamespaces(pXmlXpathCtx);
+}
+
+void Test::loadAndSave(const OUString& rURL, const OUString& rFilterName)
+{
+ mxComponent = loadFromDesktop(rURL);
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ maTempFile.EnableKillingFile();
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ mxComponent->dispose();
+ mxComponent.clear();
+ // too many DOCX validation errors right now
+ if (rFilterName != "Office Open XML Text")
+ {
+ validate(maTempFile.GetFileName(), test::OOXML);
+ }
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+
+CPPUNIT_TEST_FIXTURE(Test, testPolylineConnectorPosition)
+{
+ // Given a document with a group shape and therein a polyline and a connector.
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141786_PolylineConnectorInGroup.odt";
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then make sure polyline and connector have the correct position.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // For child elements of groups in Writer the position has to be adapted to be relative
+ // to group instead of being relative to anchor. That was missing for polyline and
+ // connector.
+ // Polyline: Without fix it would have failed with expected: 0, actual: 1800360
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "x", "0");
+ // ... failed with expected: 509400, actual: 1229400
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "y", "509400");
+
+ // Connector: Without fix it would have failed with expected: 763200, actual: 2563560
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[3]/wps:spPr/a:xfrm/a:off", "x", "763200");
+ // ... failed with expected: 0, actual: 720000
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[3]/wps:spPr/a:xfrm/a:off", "y", "0");
+ // Polyline and connector were shifted 1800360EMU right, 720000EMU down.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testRotatedShapePosition)
+{
+ // Given a document with a group shape and therein a rotated custom shape.
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141786_RotatedShapeInGroup.odt";
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then make sure the rotated child shape has the correct position.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // For a group itself and for shapes outside of groups, the position calculation is done in
+ // DocxSdrExport. For child elements of groups it has to be done in
+ // DrawingML::WriteShapeTransformation(), but was missing.
+ // Without fix it would have failed with expected: 469440, actual: 92160
+ // The shape was about 1cm shifted up and partly outside its group.
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "y", "469440");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testDmlGroupshapePolygon)
+{
+ // Given a document with a group shape, containing a single polygon child shape:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "dml-groupshape-polygon.docx";
+
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then make sure that the group shape, the group shape's child size and the child shape's size
+ // match:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//wpg:grpSpPr/a:xfrm/a:ext", "cx", "5328360");
+ // Without the accompanying fix in place, this test would have failed, the <a:chExt> element was
+ // not written.
+ assertXPath(pXmlDoc, "//wpg:grpSpPr/a:xfrm/a:chExt", "cx", "5328360");
+ assertXPath(pXmlDoc, "//wps:spPr/a:xfrm/a:ext", "cx", "5328360");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testCustomShapeArrowExport)
+{
+ // Given a document with a few different kinds of arrow shapes in it:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf142602_CustomShapeArrows.odt";
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then the shapes should retain their correct control values.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // Without the fix the output OOXML would have no <a:prstGeom> tags in it.
+
+ // Right arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "rightArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 46321");
+
+ // Left arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "leftArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 52939");
+
+ // Down arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "downArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 59399");
+
+ // Up arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "upArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 63885");
+
+ // Left-right arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "leftRightArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 53522");
+
+ // Up-down arrow
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "upDownArrow");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 50000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 62743");
+
+ // Right arrow callout
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "rightArrowCallout");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 25002");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 25000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+ "fmla", "val 25052");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+ "fmla", "val 66667");
+
+ // Left arrow callout
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "leftArrowCallout");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 25002");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 25000");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+ "fmla", "val 25057");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+ "fmla", "val 66673");
+
+ // Down arrow callout
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "downArrowCallout");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 29415");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 29413");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+ "fmla", "val 16667");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+ "fmla", "val 66667");
+
+ // Up arrow callout
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+ "prst", "upArrowCallout");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+ "fmla", "val 31033");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+ "fmla", "val 31030");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+ "fmla", "val 16667");
+ assertXPath(pXmlDoc,
+ "//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+ "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+ "fmla", "val 66660");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testCameraRevolutionGrabBag)
+{
+ // Given a PPTX file that contains camera revolution (rotation around z axis) applied shapes
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "camera-rotation-revolution-nonwps.pptx";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Then make sure the revolution is exported without a problem:
+ // First shape textbox:
+ assertXPath(pXmlDoc, "//p:sp[1]/p:spPr/a:scene3d/a:camera/a:rot", "rev", "5400000");
+
+ // Second shape rectangle:
+ assertXPath(pXmlDoc, "//p:sp[2]/p:spPr/a:scene3d/a:camera/a:rot", "rev", "18300000");
+
+ // Make sure Shape3DProperties don't leak under txBody
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0
+ // - Actual : 1
+ // - In <>, XPath '//p:sp[1]/p:txBody/a:bodyPr/a:scene3d/a:camera/a:rot' number of nodes is incorrect
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:bodyPr/a:scene3d/a:camera/a:rot", 0);
+ assertXPath(pXmlDoc, "//p:sp[2]/p:txBody/a:bodyPr/a:scene3d/a:camera/a:rot", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testReferToTheme)
+{
+ // Given a PPTX file that contains references to a theme:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "refer-to-theme.pptx";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Then make sure the shape text color is a scheme color:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // - XPath '//p:sp/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr' number of nodes is incorrect
+ // i.e. the <a:schemeClr> element was not written.
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr", "val",
+ "accent1");
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumMod", 0);
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumOff", 0);
+
+ // Second shape: lighter color:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // - XPath '//p:sp[2]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr' number of nodes is incorrect
+ // i.e. the effects case did not write scheme colors.
+ assertXPath(pXmlDoc, "//p:sp[2]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr", "val",
+ "accent1");
+ assertXPath(pXmlDoc, "//p:sp[2]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumMod", "val",
+ "40000");
+ assertXPath(pXmlDoc, "//p:sp[2]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumOff", "val",
+ "60000");
+
+ // Third shape, darker color:
+ assertXPath(pXmlDoc, "//p:sp[3]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr", "val",
+ "accent1");
+ assertXPath(pXmlDoc, "//p:sp[3]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumMod", "val",
+ "75000");
+ assertXPath(pXmlDoc, "//p:sp[3]/p:txBody/a:p/a:r/a:rPr/a:solidFill/a:schemeClr/a:lumOff", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testReferToThemeShapeFill)
+{
+ // Given an ODP file that contains references to a theme for shape fill:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "refer-to-theme-shape-fill.odp";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Then make sure the shape fill color is a scheme color:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the <a:schemeClr> element was not written. Note that this was already working from PPTX
+ // files via grab-bags, so this test intentionally uses an ODP file as input.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//p:sp[1]/p:spPr/a:solidFill/a:schemeClr", "val", "accent1");
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//p:sp[1]/p:spPr/a:solidFill/a:schemeClr/a:lumMod' number of nodes is incorrect
+ // i.e. the effects of the themed color were lost.
+ assertXPath(pXmlDoc, "//p:sp[1]/p:spPr/a:solidFill/a:schemeClr/a:lumMod", "val", "40000");
+ assertXPath(pXmlDoc, "//p:sp[1]/p:spPr/a:solidFill/a:schemeClr/a:lumOff", "val", "60000");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf146690_endParagraphRunPropertiesNewLinesTextSize)
+{
+ // Given a PPTX file that contains references to a theme:
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "endParaRPr-newline-textsize.pptx";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Make sure the text size is exported correctly:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 500
+ // - Actual : 1800
+ // i.e. the endParaRPr 'size' wasn't exported correctly
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[1]/a:endParaRPr", "sz", "500");
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[2]/a:endParaRPr", "sz", "500");
+ assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[3]/a:endParaRPr", "sz", "500");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_endsubpath)
+{
+ // Given an odp file that contains a non-primitive custom shape with command N
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_endsubpath.odp";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Then make sure the pathLst has two child elements,
+ // Without the accompanying fix in place, only one element a:path was exported.
+ assertXPathChildren(pXmlDoc, "//a:pathLst", 2);
+ // and make sure first path with no stroke, second with no fill
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "stroke", "0");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "none");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandA)
+{
+ // Given an odp file that contains a non-primitive custom shape with command N
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandA.odp";
+
+ // When saving that document:
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Then make sure the path has a child element arcTo. Prior to the fix that part of the curve was
+ // not exported at all. In odp it is a command A. Such does not exist in OOXML and is therefore
+ // exported as a:lnTo followed by a:arcTo
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 2);
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1);
+ // And assert its attribute values
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "7200");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "5400");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "7719588");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "-5799266");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandT)
+{
+ // The odp file contains a non-primitive custom shape with commands MTZ
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandT.odp";
+
+ // Export to pptx had only exported the command M and has used a wrong path size
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // File has draw:viewBox="0 0 216 216"
+ assertXPath(pXmlDoc, "//a:pathLst/a:path", "w", "216");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path", "h", "216");
+ // Command T is exported as lnTo followed by arcTo.
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo", 1);
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 1);
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1);
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:close", 1);
+ // And assert its values
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "x", "108");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "y", "162");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "x", "138");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "y", "110");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "108");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "54");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "18000000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "18000000");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandXY)
+{
+ // The odp file contains a non-primitive custom shapes with commands XY
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandXY.odp";
+
+ // Export to pptx had dropped commands X and Y.
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // File has draw:viewBox="0 0 10 10"
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10");
+ // Shape has M 0 5 Y 5 0 10 5 5 10 F Y 0 5 N M 10 10 X 0 0
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "x", "0");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "y", "5");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "wR", "5");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "hR", "5");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "stAng", "10800000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "swAng", "5400000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "stAng", "16200000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "swAng", "5400000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "stAng", "0");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "swAng", "5400000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "stAng", "0");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "swAng", "-5400000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "x", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "y", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "wR", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "hR", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "stAng", "5400000");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "swAng", "5400000");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandHIJK)
+{
+ // The odp file contains a non-primitive custom shapes with commands H,I,J,K
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandHIJK.odp";
+
+ // Export to pptx had dropped commands X and Y.
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // File has draw:viewBox="0 0 80 80"
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "80");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "80");
+ // File uses from back to front J (lighten), I (lightenLess), normal fill, K (darkenLess),
+ // H (darken). New feature, old versions did not export these at all.
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "fill", "lighten");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "lightenLess");
+ assertXPathNoAttribute(pXmlDoc, "//a:pathLst/a:path[3]", "fill");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "fill", "darkenLess");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "darken");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath)
+{
+ // The odp file contains a non-primitive custom shapes with commands H,I,J,K
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_subpath.pptx";
+
+ // Export to pptx had dropped the subpaths.
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // File should have four subpaths with increasing path size
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "w", "20");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "h", "20");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "w", "40");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "h", "40");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf100391TextAreaRect)
+{
+ // The document has a custom shape of type "non-primitive" to trigger the custGeom export
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf100391_TextAreaRect.odp";
+ // When saving to PPTX the textarea rect was set to default instead of using the actual area
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup. Without fix the values were l="l", t="t", r="r", b="b"
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "l", "textAreaLeft");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "t", "textAreaTop");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "r", "textAreaRight");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "b", "textAreaBottom");
+ // The values are calculated in guides, for example
+ assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "name", "textAreaLeft");
+ assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "fmla", "*/ 1440000 w 2880000");
+ // The test reflects the state of Apr 2022. It needs to be adapted when export of handles and
+ // guides is implemented.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf109169_OctagonBevel)
+{
+ // The odp file contains an "Octagon Bevel" shape. Such has shading not in commands H,I,J,K
+ // but shading is generated in ctor of EnhancedCustomShape2d from the Type value.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf109169_OctagonBevel.odt";
+
+ // Export to docx had not written a:fill or a:stroke attributes at all.
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // File should have six subpaths, one with stroke and five with fill
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "stroke", "0");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "darkenLess");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "fill", "darken");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "fill", "darken");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "lightenLess");
+ assertXPath(pXmlDoc, "//a:pathLst/a:path[6]", "fill", "lighten");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testFaultyPathCommandsAWT)
+{
+ // The odp file contains shapes whose path starts with command A, W, T or L. That is a faulty
+ // path. LO is tolerant and renders it so that is makes a moveTo to the start point of the arc or
+ // the end of the line respectively. Export to OOXML does the same now and writes a moveTo
+ // instead of the normally used lnTo. If a lnTo is written, MS Office shows nothing of the shape.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "FaultyPathStart.odp";
+
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup:
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // First child of a:path should be a moveTo in all four shapes.
+ assertXPath(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo");
+ assertXPath(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo");
+ assertXPath(pXmlDoc, "//p:spTree/p:sp[3]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo");
+ assertXPath(pXmlDoc, "//p:spTree/p:sp[4]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf148784StretchXY)
+{
+ // The document has a custom shapes of type "non-primitive" to trigger the custGeom export.
+ // They use formulas with 'right' and 'bottom'.
+ // When saving to PPTX the attributes stretchpoint-x and stretchpoint-y were not considered. The
+ // line at right and bottom edge were positioned inside as if the shape had a square size.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf148784_StretchXY.odp";
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // x-position of last segment should be same as path width. It was 21600 without fix.
+ sal_Int32 nWidth
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/@w")
+ .toInt32();
+ sal_Int32 nPosX
+ = getXPathContent(
+ pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo[4]/a:pt/@x")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchX", nWidth, nPosX);
+
+ // y-position of last segment should be same as path height. It was 21600 without fix.
+ sal_Int32 nHeight
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/@h")
+ .toInt32();
+ sal_Int32 nPosY
+ = getXPathContent(
+ pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo[4]/a:pt/@y")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchY", nHeight, nPosY);
+
+ // The test reflects the state of Apr 2022. It needs to be adapted when export of handles and
+ // guides is implemented.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf148784StretchCommandQ)
+{
+ // The document has a custom shapes of type "non-primitive" to trigger the custGeom export.
+ // They use formulas with 'right' and 'bottom'.
+ // When saving to PPTX the attributes stretchpoint-x and stretchpoint-y were not considered.
+ // That results in wrong arcs on the right or bottom side of the shape.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf148784_StretchCommandQ.odp";
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // x-position of second quadBezTo control should be same as path width. It was 21600 without fix.
+ sal_Int32 nWidth
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/@w")
+ .toInt32();
+ sal_Int32 nPosX
+ = getXPathContent(
+ pXmlDoc,
+ "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:quadBezTo[2]/a:pt/@x")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchX", nWidth, nPosX);
+
+ // y-position of third quadBezTo control should be same as path height. It was 21600 without fix.
+ sal_Int32 nHeight
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/@h")
+ .toInt32();
+ sal_Int32 nPosY
+ = getXPathContent(
+ pXmlDoc,
+ "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:quadBezTo[3]/a:pt/@y")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchY", nHeight, nPosY);
+
+ // The test reflects the state of Apr 2022. It needs to be adapted when export of handles and
+ // guides is implemented.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf148784StretchCommandVW)
+{
+ // The document has a custom shapes of type "non-primitive" to trigger the custGeom export.
+ // It should not need adaption when export of handles and guides is implemented because it
+ // has only fixed values in the path.
+ // When saving to PPTX the attributes stretchpoint-x and stretchpoint-y were not considered.
+ // That results in circles instead of ellipses.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf148784_StretchCommandVW.odp";
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup.
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // wR of first ArcTo in first shape should be same as path width/2. It was 10800 without fix.
+ sal_Int32 nHalfWidth
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/@w")
+ .toInt32()
+ / 2;
+ sal_Int32 nWR
+ = getXPathContent(pXmlDoc,
+ "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:arcTo[1]/@wR")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchX", nHalfWidth, nWR);
+
+ // hR of first ArcTo in second shape should be same as path height /2. It was 10800 without fix.
+ sal_Int32 nHalfHeight
+ = getXPathContent(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/@h")
+ .toInt32()
+ / 2;
+ sal_Int32 nHR
+ = getXPathContent(pXmlDoc,
+ "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:arcTo[1]/@hR")
+ .toInt32();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("StretchY", nHalfHeight, nHR);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/mathml.cxx b/oox/qa/unit/mathml.cxx
new file mode 100644
index 000000000..3ab87fcbc
--- /dev/null
+++ b/oox/qa/unit/mathml.cxx
@@ -0,0 +1,71 @@
+/* -*- 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 <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+using namespace ::com::sun::star;
+
+/// oox mathml tests.
+class OoxMathmlTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+void OoxMathmlTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void OoxMathmlTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+
+CPPUNIT_TEST_FIXTURE(OoxMathmlTest, testImportCharacters)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "import-characters.pptx";
+ // Without the accompanying fix in place, this failed with an assertion failure on import.
+ getComponent() = loadFromDesktop(aURL);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxMathmlTest, testImportMce)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "import-mce.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);
+
+ // Without the accompanying fix in place, this failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // i.e. both the math object and its replacement image was imported, as two separate objects.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xDrawPage->getCount());
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/shape.cxx b/oox/qa/unit/shape.cxx
new file mode 100644
index 000000000..345ebb215
--- /dev/null
+++ b/oox/qa/unit/shape.cxx
@@ -0,0 +1,180 @@
+/* -*- 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 <string_view>
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <rtl/math.hxx>
+#include <svx/svdoashp.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Gets one child of xShape, which one is specified by nIndex.
+uno::Reference<drawing::XShape> getChildShape(const uno::Reference<drawing::XShape>& xShape,
+ sal_Int32 nIndex)
+{
+ uno::Reference<container::XIndexAccess> xGroup(xShape, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGroup.is());
+
+ CPPUNIT_ASSERT(xGroup->getCount() > nIndex);
+
+ uno::Reference<drawing::XShape> xRet(xGroup->getByIndex(nIndex), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xRet.is());
+
+ return xRet;
+}
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+
+/// oox shape tests.
+class OoxShapeTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+ void load(std::u16string_view rURL);
+};
+
+void OoxShapeTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void OoxShapeTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void OoxShapeTest::load(std::u16string_view rFileName)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName;
+ mxComponent = loadFromDesktop(aURL);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxShapeTest, testGroupTransform)
+{
+ load(u"tdf141463_GroupTransform.pptx");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(getChildShape(xGroup, 0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed in several properties.
+
+ sal_Int32 nAngle;
+ xPropSet->getPropertyValue("ShearAngle") >>= nAngle;
+ // Failed with - Expected: 0
+ // - Actual : -810
+ // i.e. the shape was sheared although shearing does not exist in oox
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nAngle);
+
+ xPropSet->getPropertyValue("RotateAngle") >>= nAngle;
+ // Failed with - Expected: 26000 (is in 1/100deg)
+ // - Actual : 26481 (is in 1/100deg)
+ // 100deg in PowerPoint UI = 360deg - 100deg in LO.
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(26000), nAngle);
+
+ sal_Int32 nActual = xShape->getSize().Width;
+ // The group has ext.cy=2880000 and chExt.cy=4320000 resulting in Y-scale=2/3.
+ // The child has ext 2880000 x 1440000. Because of rotation angle 80deg, the Y-scale has to be
+ // applied to the width, resulting in 2880000 * 2/3 = 1920000EMU = 5333Hmm
+ // ToDo: Expected value currently 1 off.
+ // Failed with - Expected: 5332
+ // - Actual : 5432
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5332), nActual);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxShapeTest, testMultipleGroupShapes)
+{
+ load(u"multiple-group-shapes.docx");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 1
+ // i.e. the 2 group shapes from the document were imported as a single one.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xDrawPage->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxShapeTest, testCustomshapePosition)
+{
+ load(u"customshape-position.docx");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+
+ sal_Int32 nY{};
+ xShape->getPropertyValue("VertOrientPosition") >>= nY;
+ // <wp:posOffset>581025</wp:posOffset> in the document.
+ sal_Int32 nExpected = rtl::math::round(581025.0 / 360);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1614
+ // - Actual : 0
+ // i.e. the position of the shape was lost on import due to the rounded corners.
+ CPPUNIT_ASSERT_EQUAL(nExpected, nY);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxShapeTest, testTdf125582_TextOnCircle)
+{
+ // The document contains a shape with a:prstTxWarp="textCircle" with two paragraphs.
+ // PowerPoint aligns the bottom of the text with the path, LO had aligned the middle of the
+ // text with the path, which resulted in smaller text.
+ load(u"tdf125582_TextOnCircle.pptx");
+
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
+
+ // BoundRect of Fontwork shape depends on dpi.
+ if (IsDefaultDPI())
+ {
+ SdrObjCustomShape& rSdrCustomShape(
+ static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
+ // Without the fix in place width was 3639, but should be 4824 for 96dpi.
+ tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(4824), aBoundRect.GetWidth(), 5);
+ }
+
+ drawing::TextVerticalAdjust eAdjust;
+ xPropSet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("TextVerticalAdjust", drawing::TextVerticalAdjust_BOTTOM, eAdjust);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/vba_compression.cxx b/oox/qa/unit/vba_compression.cxx
new file mode 100644
index 000000000..906e30bfc
--- /dev/null
+++ b/oox/qa/unit/vba_compression.cxx
@@ -0,0 +1,255 @@
+/* -*- 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/plugin/TestPlugIn.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestFixture.h>
+#include <osl/file.hxx>
+#include <oox/ole/vbaexport.hxx>
+#include <rtl/bootstrap.hxx>
+#include <tools/stream.hxx>
+#include <unotest/directories.hxx>
+#include <algorithm>
+
+class TestVbaCompression : public CppUnit::TestFixture
+{
+public:
+ // just a sequence of bytes that should not be compressed at all
+ void testSimple1();
+
+ // a sequence containing one subsequence that can be compressed
+ void testSimple2();
+
+ void testSimple3();
+
+ // real stream from a document
+ void testComplex1();
+
+ // tests taken from the VBA specification
+ // section 3.2
+
+ // section 3.2.1
+ void testSpec321();
+ // section 3.2.2
+ void testSpec322();
+ // section 3.2.3
+ void testSpec323();
+
+ CPPUNIT_TEST_SUITE(TestVbaCompression);
+ CPPUNIT_TEST(testSimple1);
+ CPPUNIT_TEST(testSimple2);
+ CPPUNIT_TEST(testSimple3);
+ CPPUNIT_TEST(testComplex1);
+ CPPUNIT_TEST(testSpec321);
+ CPPUNIT_TEST(testSpec322);
+ CPPUNIT_TEST(testSpec323);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ static OUString const & getDebugDirUrl() {
+ struct DebugDirUrl {
+ DebugDirUrl() : url("$UserInstallation/debug/") {
+ rtl::Bootstrap::expandMacros(url);
+ //TODO: provide an OUString -> OUString expansion function, and which throws on
+ // failure
+ auto e = osl::Directory::create(url);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, e);
+ }
+ OUString url;
+ };
+ static DebugDirUrl url;
+ return url.url;
+ }
+
+ test::Directories m_directories;
+};
+
+namespace {
+
+void ReadFiles(const OUString& rTestFile, const OUString& rReference,
+ SvMemoryStream& rOutputMemoryStream, SvMemoryStream& rReferenceMemoryStream,
+ const OUString& rDebugPath)
+{
+ SvFileStream aInputStream(rTestFile, StreamMode::READ);
+ SvMemoryStream aInputMemoryStream(4096, 4096);
+ aInputMemoryStream.WriteStream(aInputStream);
+
+ VBACompression aCompression(rOutputMemoryStream, aInputMemoryStream);
+ aCompression.write();
+
+ SvFileStream aReferenceStream(rReference, StreamMode::READ);
+ rReferenceMemoryStream.WriteStream(aReferenceStream);
+
+ rOutputMemoryStream.Seek(0);
+ SvFileStream aDebugStream(rDebugPath, StreamMode::WRITE);
+ aDebugStream.WriteStream(rOutputMemoryStream);
+}
+
+}
+
+void TestVbaCompression::testSimple1()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/simple1.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/simple1.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream,
+ aReferenceMemoryStream, getDebugDirUrl() + "vba_debug.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testSimple2()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/simple2.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/simple2.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug2.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testSimple3()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/simple3.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/simple3.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug3.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testComplex1()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/complex1.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/complex1.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug_complex1.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testSpec321()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/spec321.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/spec321.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug_spec321.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testSpec322()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/spec322.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/spec322.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug_spec322.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+void TestVbaCompression::testSpec323()
+{
+ OUString aTestFile = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/spec323.bin");
+ OUString aReference = m_directories.getPathFromSrc(u"/oox/qa/unit/data/vba/reference/spec323.bin");
+
+ SvMemoryStream aOutputMemoryStream(4096, 4096);
+ SvMemoryStream aReferenceMemoryStream(4096, 4096);
+ ReadFiles(aTestFile, aReference, aOutputMemoryStream, aReferenceMemoryStream, getDebugDirUrl() + "vba_debug_spec323.bin");
+
+ CPPUNIT_ASSERT_EQUAL(aReferenceMemoryStream.GetSize(), aOutputMemoryStream.GetSize());
+
+ const sal_uInt8* pReferenceData = static_cast<const sal_uInt8*>( aReferenceMemoryStream.GetData() );
+ const sal_uInt8* pData = static_cast<const sal_uInt8*>( aOutputMemoryStream.GetData() );
+
+ const sal_uInt64 nSize = std::min(aReferenceMemoryStream.GetSize(),
+ aOutputMemoryStream.GetSize());
+ for (sal_uInt64 i = 0; i < nSize; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(pReferenceData[i]), static_cast<int>(pData[i]));
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestVbaCompression);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/vba_encryption.cxx b/oox/qa/unit/vba_encryption.cxx
new file mode 100644
index 000000000..023cc197b
--- /dev/null
+++ b/oox/qa/unit/vba_encryption.cxx
@@ -0,0 +1,82 @@
+/* -*- 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/plugin/TestPlugIn.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestFixture.h>
+
+#include <oox/ole/vbaexport.hxx>
+
+class TestVbaEncryption : public CppUnit::TestFixture
+{
+public:
+#if 0
+ // an initial test for the encryption taken from the spec
+ void testSimple1();
+
+ void testSimple2();
+#endif
+
+ void testProjKey1();
+
+ CPPUNIT_TEST_SUITE(TestVbaEncryption);
+ // CPPUNIT_TEST(testSimple1);
+ // CPPUNIT_TEST(testSimple2);
+ CPPUNIT_TEST(testProjKey1);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+#if 0
+void TestVbaEncryption::testSimple1()
+{
+ sal_uInt8 nSeed = 0x07;
+ sal_uInt8 nProjKey = 0xDF;
+ sal_uInt16 nLength = 0x04;
+ sal_uInt8 pData[] = { 0x00, 0x00, 0x00, 0x00 };
+
+ SvMemoryStream aEncryptedStream(4096, 4096);
+ VBAEncryption aEncryption(pData, nLength, aEncryptedStream,
+ &nSeed, nProjKey);
+ aEncryption.write();
+}
+
+void TestVbaEncryption::testSimple2()
+{
+ sal_uInt8 nSeed = 0x15;
+ sal_uInt8 nProjKey = 0xDF;
+ sal_uInt16 nLength = 0x01;
+ sal_uInt8 pData[] = { 0xFF };
+
+ SvMemoryStream aEncryptedStream(4096, 4096);
+ VBAEncryption aEncryption(pData, nLength, aEncryptedStream,
+ &nSeed, nProjKey);
+ aEncryption.write();
+ sal_uInt8 pExpectedData[] = "1517CAF1D6F9D7F9D706";
+ size_t length = sizeof(pExpectedData);
+ aEncryptedStream.Seek(0);
+ for (size_t i = 0; i < length; ++i)
+ {
+ unsigned char val = 0;
+ aEncryptedStream.ReadUChar(val);
+ CPPUNIT_ASSERT_EQUAL((int)pExpectedData[i], (int)sal_uInt8(val));
+ }
+}
+#endif
+
+void TestVbaEncryption::testProjKey1()
+{
+ sal_uInt8 nProjKey = VBAEncryption::calculateProjKey("{917DED54-440B-4FD1-A5C1-74ACF261E600}");
+ CPPUNIT_ASSERT_EQUAL(int(0xdf), static_cast<int>(nProjKey));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestVbaEncryption);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/qa/unit/vml.cxx b/oox/qa/unit/vml.cxx
new file mode 100644
index 000000000..61730c744
--- /dev/null
+++ b/oox/qa/unit/vml.cxx
@@ -0,0 +1,236 @@
+/* -*- 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 <string_view>
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/PolygonKind.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/ColorMode.hpp>
+
+using namespace ::com::sun::star;
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+
+/// oox vml tests.
+class OoxVmlTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+ void load(std::u16string_view rURL);
+};
+
+void OoxVmlTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void OoxVmlTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void OoxVmlTest::load(std::u16string_view rFileName)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName;
+ mxComponent = loadFromDesktop(aURL);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, tdf112450_vml_polyline)
+{
+ // Load a document with v:polyline shapes. Error was, that the size was set to zero and the
+ // points were zero because of missing decode from length with unit.
+ load(u"tdf112450_vml_polyline.docx");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ {
+ // This tests a polyline shape which is not closed.
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ // Without fix in place, Geometry had 2 points, both 0|0.
+ drawing::PointSequenceSequence aGeometry;
+ xShapeProps->getPropertyValue("Geometry") >>= aGeometry;
+ drawing::PointSequence aPolygon = aGeometry[0];
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(6879), aPolygon[3].X, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(487), aPolygon[3].Y, 1);
+ // Without fix in place, width and height were zero
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(6879), xShape->getSize().Width, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1926), xShape->getSize().Height, 1);
+ // After the fix the shape has still to be PolygonKind_PLIN
+ drawing::PolygonKind ePolygonKind;
+ xShapeProps->getPropertyValue("PolygonKind") >>= ePolygonKind;
+ CPPUNIT_ASSERT_EQUAL(drawing::PolygonKind_PLIN, ePolygonKind);
+ }
+ {
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ // Without fix in place, Geometry had 2 points, both 0|0.
+ drawing::PointSequenceSequence aGeometry;
+ xShapeProps->getPropertyValue("Geometry") >>= aGeometry;
+ drawing::PointSequence aPolygon = aGeometry[0];
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(5062), aPolygon[2].X, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(2247), aPolygon[2].Y, 1);
+ // Without fix in place, width and height were zero
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(6163), xShape->getSize().Width, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(2247), xShape->getSize().Height, 1);
+ // Without fix in place the shape was not closed, it had PolygonKind_PLIN
+ drawing::PolygonKind ePolygonKind;
+ xShapeProps->getPropertyValue("PolygonKind") >>= ePolygonKind;
+ CPPUNIT_ASSERT_EQUAL(drawing::PolygonKind_POLY, ePolygonKind);
+ }
+ {
+ // This tests a filled shape where v:polyline does not have attribute coordsize
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(2), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ // Without fix in place, Geometry had 2 points, both 0|0.
+ drawing::PointSequenceSequence aGeometry;
+ xShapeProps->getPropertyValue("Geometry") >>= aGeometry;
+ drawing::PointSequence aPolygon = aGeometry[0];
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(2095), aPolygon[3].X, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(608), aPolygon[3].Y, 1);
+ // Without fix in place, width and height were zero
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(5634), xShape->getSize().Width, 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(2485), xShape->getSize().Height, 1);
+ // Without fix in place the shape was not closed, it had PolygonKind_PLIN
+ drawing::PolygonKind ePolygonKind;
+ xShapeProps->getPropertyValue("PolygonKind") >>= ePolygonKind;
+ CPPUNIT_ASSERT_EQUAL(drawing::PolygonKind_POLY, ePolygonKind);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, tdf137314_vml_rotation_unit_fd)
+{
+ // Load a document with a 30deg rotated arc on a drawing canvas. Rotation is given
+ // as 1966080fd. Error was, that the vml angle unit "fd" was not converted to Degree100.
+ load(u"tdf137314_vml_rotation_unit_fd.docx");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xGroup->getByIndex(1), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ drawing::PolyPolygonBezierCoords aPolyPolygonBezierCoords;
+ xShapeProps->getPropertyValue("PolyPolygonBezier") >>= aPolyPolygonBezierCoords;
+ drawing::PointSequence aPolygon = aPolyPolygonBezierCoords.Coordinates[1];
+ // Without fix in place, the vector was -1441|1490.
+ // [1] and [2] are Bezier-curve control points.
+ sal_Int32 nDiffX = aPolygon[3].X - aPolygon[0].X;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1490), nDiffX, 1);
+ sal_Int32 nDiffY = aPolygon[3].Y - aPolygon[0].Y;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1441), nDiffY, 1);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, testSpt202ShapeType)
+{
+ // Load a document with a groupshape, 2nd child is a <v:shape>, its type has o:spt set to 202
+ // (TextBox).
+ load(u"group-spt202.docx");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xGroup->getByIndex(1), uno::UNO_QUERY);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: com.sun.star.drawing.TextShape
+ // - Actual : com.sun.star.drawing.CustomShape
+ // and then the size of the group shape was incorrect, e.g. its right edge was outside the page
+ // boundaries.
+ CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.drawing.TextShape"), xShape->getShapeType());
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, testShapeNonAutosizeWithText)
+{
+ // Load a document which has a group shape, containing a single child.
+ // 17.78 cm is the full group shape width, 19431/64008 is the child shape's relative width inside
+ // that, so 5.3975 cm should be the shape width.
+ load(u"shape-non-autosize-with-text.docx");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(xGroup->getByIndex(0), uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Actual : 1115
+ // - Expected: 5398
+ // because the width was determined using its text size, not using the explicit size.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5398), xShape->getSize().Width);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, testGraphicStroke)
+{
+ load(u"graphic-stroke.pptx");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xDrawPage->getCount());
+
+ uno::Reference<beans::XPropertySet> xShape;
+ uno::Reference<lang::XServiceInfo> xInfo(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ if (xInfo->supportsService("com.sun.star.drawing.OLE2Shape"))
+ xShape.set(xInfo, uno::UNO_QUERY);
+
+ CPPUNIT_ASSERT(xShape.is());
+
+ drawing::LineStyle eLineStyle{};
+ xShape->getPropertyValue("LineStyle") >>= eLineStyle;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. line style was NONE, not SOLID.
+ CPPUNIT_ASSERT_EQUAL(drawing::LineStyle_SOLID, eLineStyle);
+}
+
+CPPUNIT_TEST_FIXTURE(OoxVmlTest, testWatermark)
+{
+ // Given a document with a picture watermark, and the "washout" checkbox is ticked on the Word
+ // UI:
+ // When loading that document:
+ load(u"watermark.docx");
+
+ // Then make sure the watermark effect is not lost on import:
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ drawing::ColorMode eMode{};
+ xShape->getPropertyValue("GraphicColorMode") >>= eMode;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 3
+ // - Actual : 0
+ // i.e. the color mode was STANDARD, not WATERMARK.
+ CPPUNIT_ASSERT_EQUAL(drawing::ColorMode_WATERMARK, eMode);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */