diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /oox/qa | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.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 'oox/qa')
71 files changed, 2664 insertions, 0 deletions
diff --git a/oox/qa/token/tokenmap-test.cxx b/oox/qa/token/tokenmap-test.cxx new file mode 100644 index 000000000..058f7c16e --- /dev/null +++ b/oox/qa/token/tokenmap-test.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <oox/token/tokenmap.hxx> +#include <oox/token/tokens.hxx> + +using namespace com::sun::star::uno; + +namespace oox { + +class TokenmapTest: public CppUnit::TestFixture +{ +public: + void test_roundTrip(); + + CPPUNIT_TEST_SUITE(TokenmapTest); + + CPPUNIT_TEST(test_roundTrip); + CPPUNIT_TEST_SUITE_END(); + +private: + TokenMap tokenMap; +}; + +void TokenmapTest::test_roundTrip() +{ + for ( sal_Int32 nToken = 0; nToken < XML_TOKEN_COUNT; ++nToken ) + { + // check that the getIdentifier <-> getToken roundtrip works + Sequence< sal_Int8 > rUtf8Name = tokenMap.getUtf8TokenName(nToken); + sal_Int32 ret = tokenMap.getTokenFromUTF8( + reinterpret_cast< const char * >(rUtf8Name.getConstArray()), + rUtf8Name.getLength() ); + CPPUNIT_ASSERT_EQUAL(ret, nToken); + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(TokenmapTest); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); 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 Binary files differnew file mode 100644 index 000000000..219795ffa --- /dev/null +++ b/oox/qa/unit/data/FaultyPathStart.odp diff --git a/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx b/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx Binary files differnew file mode 100644 index 000000000..db26dc032 --- /dev/null +++ b/oox/qa/unit/data/camera-rotation-revolution-nonwps.pptx diff --git a/oox/qa/unit/data/camera-rotation-revolution.docx b/oox/qa/unit/data/camera-rotation-revolution.docx Binary files differnew file mode 100644 index 000000000..74054aa82 --- /dev/null +++ b/oox/qa/unit/data/camera-rotation-revolution.docx diff --git a/oox/qa/unit/data/chart-data-label-char-color.docx b/oox/qa/unit/data/chart-data-label-char-color.docx Binary files differnew file mode 100644 index 000000000..0e389f923 --- /dev/null +++ b/oox/qa/unit/data/chart-data-label-char-color.docx diff --git a/oox/qa/unit/data/chart-theme-override.pptx b/oox/qa/unit/data/chart-theme-override.pptx Binary files differnew file mode 100644 index 000000000..85243b678 --- /dev/null +++ b/oox/qa/unit/data/chart-theme-override.pptx diff --git a/oox/qa/unit/data/customshape-position.docx b/oox/qa/unit/data/customshape-position.docx Binary files differnew file mode 100644 index 000000000..227928f20 --- /dev/null +++ b/oox/qa/unit/data/customshape-position.docx diff --git a/oox/qa/unit/data/dml-groupshape-polygon.docx b/oox/qa/unit/data/dml-groupshape-polygon.docx Binary files differnew file mode 100644 index 000000000..6d20b0342 --- /dev/null +++ b/oox/qa/unit/data/dml-groupshape-polygon.docx diff --git a/oox/qa/unit/data/endParaRPr-newline-textsize.pptx b/oox/qa/unit/data/endParaRPr-newline-textsize.pptx Binary files differnew file mode 100644 index 000000000..109f818ec --- /dev/null +++ b/oox/qa/unit/data/endParaRPr-newline-textsize.pptx diff --git a/oox/qa/unit/data/gradient-multistep-transparency.pptx b/oox/qa/unit/data/gradient-multistep-transparency.pptx Binary files differnew file mode 100644 index 000000000..83946c71b --- /dev/null +++ b/oox/qa/unit/data/gradient-multistep-transparency.pptx diff --git a/oox/qa/unit/data/graphic-stroke.pptx b/oox/qa/unit/data/graphic-stroke.pptx Binary files differnew file mode 100644 index 000000000..f4465476f --- /dev/null +++ b/oox/qa/unit/data/graphic-stroke.pptx diff --git a/oox/qa/unit/data/group-spt202.docx b/oox/qa/unit/data/group-spt202.docx Binary files differnew file mode 100644 index 000000000..14bf00b50 --- /dev/null +++ b/oox/qa/unit/data/group-spt202.docx diff --git a/oox/qa/unit/data/import-characters.pptx b/oox/qa/unit/data/import-characters.pptx Binary files differnew file mode 100644 index 000000000..293380236 --- /dev/null +++ b/oox/qa/unit/data/import-characters.pptx diff --git a/oox/qa/unit/data/import-mce.pptx b/oox/qa/unit/data/import-mce.pptx Binary files differnew file mode 100644 index 000000000..65cf1cbab --- /dev/null +++ b/oox/qa/unit/data/import-mce.pptx diff --git a/oox/qa/unit/data/multiple-group-shapes.docx b/oox/qa/unit/data/multiple-group-shapes.docx Binary files differnew file mode 100644 index 000000000..c751bcdf6 --- /dev/null +++ b/oox/qa/unit/data/multiple-group-shapes.docx diff --git a/oox/qa/unit/data/preset-adjust-value.pptx b/oox/qa/unit/data/preset-adjust-value.pptx Binary files differnew file mode 100644 index 000000000..d1d570a19 --- /dev/null +++ b/oox/qa/unit/data/preset-adjust-value.pptx diff --git a/oox/qa/unit/data/refer-to-theme-shape-fill.odp b/oox/qa/unit/data/refer-to-theme-shape-fill.odp Binary files differnew file mode 100644 index 000000000..3a32aa716 --- /dev/null +++ b/oox/qa/unit/data/refer-to-theme-shape-fill.odp diff --git a/oox/qa/unit/data/refer-to-theme.pptx b/oox/qa/unit/data/refer-to-theme.pptx Binary files differnew file mode 100644 index 000000000..9f05bf7b0 --- /dev/null +++ b/oox/qa/unit/data/refer-to-theme.pptx diff --git a/oox/qa/unit/data/shape-non-autosize-with-text.docx b/oox/qa/unit/data/shape-non-autosize-with-text.docx Binary files differnew file mode 100644 index 000000000..b9ae501f4 --- /dev/null +++ b/oox/qa/unit/data/shape-non-autosize-with-text.docx diff --git a/oox/qa/unit/data/shape-text-adjust-left.pptx b/oox/qa/unit/data/shape-text-adjust-left.pptx Binary files differnew file mode 100644 index 000000000..d197425b9 --- /dev/null +++ b/oox/qa/unit/data/shape-text-adjust-left.pptx diff --git a/oox/qa/unit/data/shape-text-alignment.pptx b/oox/qa/unit/data/shape-text-alignment.pptx Binary files differnew file mode 100644 index 000000000..ff4ff06f2 --- /dev/null +++ b/oox/qa/unit/data/shape-text-alignment.pptx diff --git a/oox/qa/unit/data/smartart-groupshape.pptx b/oox/qa/unit/data/smartart-groupshape.pptx Binary files differnew file mode 100644 index 000000000..81dcee1e5 --- /dev/null +++ b/oox/qa/unit/data/smartart-groupshape.pptx diff --git a/oox/qa/unit/data/table-shadow.pptx b/oox/qa/unit/data/table-shadow.pptx Binary files differnew file mode 100644 index 000000000..3247404cb --- /dev/null +++ b/oox/qa/unit/data/table-shadow.pptx diff --git a/oox/qa/unit/data/tdf100391_TextAreaRect.odp b/oox/qa/unit/data/tdf100391_TextAreaRect.odp Binary files differnew file mode 100644 index 000000000..b9b9e5b39 --- /dev/null +++ b/oox/qa/unit/data/tdf100391_TextAreaRect.odp diff --git a/oox/qa/unit/data/tdf109169_OctagonBevel.odt b/oox/qa/unit/data/tdf109169_OctagonBevel.odt Binary files differnew file mode 100644 index 000000000..2ba39b102 --- /dev/null +++ b/oox/qa/unit/data/tdf109169_OctagonBevel.odt diff --git a/oox/qa/unit/data/tdf112450_vml_polyline.docx b/oox/qa/unit/data/tdf112450_vml_polyline.docx Binary files differnew file mode 100644 index 000000000..a31124ee7 --- /dev/null +++ b/oox/qa/unit/data/tdf112450_vml_polyline.docx diff --git a/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx b/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx Binary files differnew file mode 100644 index 000000000..1862c0257 --- /dev/null +++ b/oox/qa/unit/data/tdf113187_arcTo_withoutReferences.pptx diff --git a/oox/qa/unit/data/tdf125582_TextOnCircle.pptx b/oox/qa/unit/data/tdf125582_TextOnCircle.pptx Binary files differnew file mode 100644 index 000000000..b2c09e515 --- /dev/null +++ b/oox/qa/unit/data/tdf125582_TextOnCircle.pptx diff --git a/oox/qa/unit/data/tdf131082.pptx b/oox/qa/unit/data/tdf131082.pptx Binary files differnew file mode 100644 index 000000000..dbe88126f --- /dev/null +++ b/oox/qa/unit/data/tdf131082.pptx diff --git a/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx b/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx Binary files differnew file mode 100644 index 000000000..17c8cabc0 --- /dev/null +++ b/oox/qa/unit/data/tdf137314_vml_rotation_unit_fd.docx diff --git a/oox/qa/unit/data/tdf141463_GroupTransform.pptx b/oox/qa/unit/data/tdf141463_GroupTransform.pptx Binary files differnew file mode 100644 index 000000000..36c506262 --- /dev/null +++ b/oox/qa/unit/data/tdf141463_GroupTransform.pptx diff --git a/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt Binary files differnew file mode 100644 index 000000000..d40dccac8 --- /dev/null +++ b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt diff --git a/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt Binary files differnew file mode 100644 index 000000000..e932cf47b --- /dev/null +++ b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt diff --git a/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt Binary files differnew file mode 100644 index 000000000..bc0357a1a --- /dev/null +++ b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt diff --git a/oox/qa/unit/data/tdf142605_CurveSize.odp b/oox/qa/unit/data/tdf142605_CurveSize.odp Binary files differnew file mode 100644 index 000000000..c797d378f --- /dev/null +++ b/oox/qa/unit/data/tdf142605_CurveSize.odp diff --git a/oox/qa/unit/data/tdf147978_endsubpath.odp b/oox/qa/unit/data/tdf147978_endsubpath.odp Binary files differnew file mode 100644 index 000000000..2dfd55de1 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_endsubpath.odp diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp Binary files differnew file mode 100644 index 000000000..99ddda7c1 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp Binary files differnew file mode 100644 index 000000000..49e01bc09 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp Binary files differnew file mode 100644 index 000000000..3dcd0d567 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp Binary files differnew file mode 100644 index 000000000..611225178 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx Binary files differnew file mode 100644 index 000000000..bbedc7ab9 --- /dev/null +++ b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx diff --git a/oox/qa/unit/data/tdf148784_StretchCommandQ.odp b/oox/qa/unit/data/tdf148784_StretchCommandQ.odp Binary files differnew file mode 100644 index 000000000..3da092b2c --- /dev/null +++ b/oox/qa/unit/data/tdf148784_StretchCommandQ.odp diff --git a/oox/qa/unit/data/tdf148784_StretchCommandVW.odp b/oox/qa/unit/data/tdf148784_StretchCommandVW.odp Binary files differnew file mode 100644 index 000000000..bf1054a20 --- /dev/null +++ b/oox/qa/unit/data/tdf148784_StretchCommandVW.odp diff --git a/oox/qa/unit/data/tdf148784_StretchXY.odp b/oox/qa/unit/data/tdf148784_StretchXY.odp Binary files differnew file mode 100644 index 000000000..f9df40570 --- /dev/null +++ b/oox/qa/unit/data/tdf148784_StretchXY.odp diff --git a/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx b/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx Binary files differnew file mode 100644 index 000000000..4dbf3717d --- /dev/null +++ b/oox/qa/unit/data/testTdf132557_footerCustomShapes.pptx diff --git a/oox/qa/unit/data/theme-tint.pptx b/oox/qa/unit/data/theme-tint.pptx Binary files differnew file mode 100644 index 000000000..23ab7589d --- /dev/null +++ b/oox/qa/unit/data/theme-tint.pptx diff --git a/oox/qa/unit/data/theme.pptx b/oox/qa/unit/data/theme.pptx Binary files differnew file mode 100644 index 000000000..5268e4041 --- /dev/null +++ b/oox/qa/unit/data/theme.pptx diff --git a/oox/qa/unit/data/transparent-text.pptx b/oox/qa/unit/data/transparent-text.pptx Binary files differnew file mode 100644 index 000000000..b7b3ede4d --- /dev/null +++ b/oox/qa/unit/data/transparent-text.pptx diff --git a/oox/qa/unit/data/vba/complex1.bin b/oox/qa/unit/data/vba/complex1.bin Binary files differnew file mode 100644 index 000000000..390208290 --- /dev/null +++ b/oox/qa/unit/data/vba/complex1.bin diff --git a/oox/qa/unit/data/vba/reference/complex1.bin b/oox/qa/unit/data/vba/reference/complex1.bin Binary files differnew file mode 100644 index 000000000..335b8e808 --- /dev/null +++ b/oox/qa/unit/data/vba/reference/complex1.bin diff --git a/oox/qa/unit/data/vba/reference/simple1.bin b/oox/qa/unit/data/vba/reference/simple1.bin Binary files differnew file mode 100644 index 000000000..a4644fbb7 --- /dev/null +++ b/oox/qa/unit/data/vba/reference/simple1.bin diff --git a/oox/qa/unit/data/vba/reference/simple2.bin b/oox/qa/unit/data/vba/reference/simple2.bin Binary files differnew file mode 100644 index 000000000..5de3f5ae0 --- /dev/null +++ b/oox/qa/unit/data/vba/reference/simple2.bin 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 Binary files differnew file mode 100644 index 000000000..3120c7f50 --- /dev/null +++ b/oox/qa/unit/data/vba/reference/spec321.bin diff --git a/oox/qa/unit/data/vba/reference/spec322.bin b/oox/qa/unit/data/vba/reference/spec322.bin Binary files differnew file mode 100644 index 000000000..7724b62a4 --- /dev/null +++ b/oox/qa/unit/data/vba/reference/spec322.bin diff --git a/oox/qa/unit/data/vba/reference/spec323.bin b/oox/qa/unit/data/vba/reference/spec323.bin Binary files differnew file mode 100644 index 000000000..51104ee0b --- /dev/null +++ b/oox/qa/unit/data/vba/reference/spec323.bin diff --git a/oox/qa/unit/data/vba/simple1.bin b/oox/qa/unit/data/vba/simple1.bin Binary files differnew file mode 100644 index 000000000..d59c1d5ef --- /dev/null +++ b/oox/qa/unit/data/vba/simple1.bin diff --git a/oox/qa/unit/data/vba/simple2.bin b/oox/qa/unit/data/vba/simple2.bin Binary files differnew file mode 100644 index 000000000..1b9b88b16 --- /dev/null +++ b/oox/qa/unit/data/vba/simple2.bin 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 Binary files differnew file mode 100644 index 000000000..c9eacff9a --- /dev/null +++ b/oox/qa/unit/data/watermark.docx 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: */ |