Kaydet (Commit) 106c642b authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Tomaž Vajngerl

sw: roundtrip test of OOXML decryption/encryption

Change-Id: Idea2a46a692aed666eb8dbc6185ae001d30757c2
Reviewed-on: https://gerrit.libreoffice.org/33228Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarTomaž Vajngerl <quikee@gmail.com>
üst 9ac64496
/* -*- 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/.
*/
#ifndef INCLUDED_TEST_TESTINTERACTIONHANDLER_HXX
#define INCLUDED_TEST_TESTINTERACTIONHANDLER_HXX
#include <sal/config.h>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/task/XInteractionAbort.hpp>
#include <com/sun/star/task/XInteractionApprove.hpp>
#include <com/sun/star/task/XInteractionPassword2.hpp>
#include <com/sun/star/task/DocumentPasswordRequest2.hpp>
#include <com/sun/star/task/DocumentMSPasswordRequest2.hpp>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/sequence.hxx>
class TestInteractionHandler : public cppu::WeakImplHelper<css::lang::XServiceInfo,
css::lang::XInitialization,
css::task::XInteractionHandler2>
{
OUString msPassword;
bool mbPasswordRequested;
TestInteractionHandler(const TestInteractionHandler&) = delete;
TestInteractionHandler& operator=(const TestInteractionHandler&) = delete;
public:
TestInteractionHandler(const OUString& sPassword)
: msPassword(sPassword)
{}
virtual ~TestInteractionHandler() override
{}
bool wasPasswordRequested()
{
return mbPasswordRequested;
}
virtual OUString SAL_CALL getImplementationName()
throw (css::uno::RuntimeException, std::exception) override
{
return OUString("com.sun.star.comp.uui.TestInteractionHandler");
}
virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName)
throw (css::uno::RuntimeException, std::exception) override
{
return cppu::supportsService(this, rServiceName);
}
virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames()
throw (css::uno::RuntimeException, std::exception) override
{
css::uno::Sequence<OUString> aNames(3);
aNames[0] = "com.sun.star.task.InteractionHandler";
// added to indicate support for configuration.backend.MergeRecoveryRequest
aNames[1] = "com.sun.star.configuration.backend.InteractionHandler";
aNames[2] = "com.sun.star.uui.InteractionHandler";
// for backwards compatibility
return aNames;
}
virtual void SAL_CALL initialize(css::uno::Sequence<css::uno::Any> const & /*rArguments*/)
throw (css::uno::Exception, std::exception) override
{}
virtual void SAL_CALL handle(css::uno::Reference<css::task::XInteractionRequest> const & rRequest)
throw (css::uno::RuntimeException, std::exception) override
{
handleInteractionRequest(rRequest);
}
virtual sal_Bool SAL_CALL handleInteractionRequest(const css::uno::Reference<css::task::XInteractionRequest>& rRequest)
throw (css::uno::RuntimeException, std::exception) override
{
mbPasswordRequested = false;
css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> const &rContinuations = rRequest->getContinuations();
css::uno::Any const aRequest(rRequest->getRequest());
if (handlePasswordRequest(rContinuations, aRequest))
return true;
for (sal_Int32 i = 0; i < rContinuations.getLength(); ++i)
{
css::uno::Reference<css::task::XInteractionApprove> xApprove(rContinuations[i], css::uno::UNO_QUERY);
if (xApprove.is())
xApprove->select();
}
return true;
}
bool handlePasswordRequest(const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> &rContinuations,
const css::uno::Any& rRequest)
{
bool bPasswordRequestFound = false;
bool bIsRequestPasswordToModify = false;
OString sUrl;
css::task::DocumentPasswordRequest2 passwordRequest2;
if (rRequest >>= passwordRequest2)
{
bIsRequestPasswordToModify = passwordRequest2.IsRequestPasswordToModify;
sUrl = passwordRequest2.Name.toUtf8();
bPasswordRequestFound = true;
}
css::task::DocumentMSPasswordRequest2 passwordMSRequest2;
if (rRequest >>= passwordMSRequest2)
{
bIsRequestPasswordToModify = passwordMSRequest2.IsRequestPasswordToModify;
sUrl = passwordMSRequest2.Name.toUtf8();
bPasswordRequestFound = true;
}
if (!bPasswordRequestFound)
{
mbPasswordRequested = false;
return false;
}
mbPasswordRequested = true;
for (sal_Int32 i = 0; i < rContinuations.getLength(); ++i)
{
if (bIsRequestPasswordToModify)
{
css::uno::Reference<css::task::XInteractionPassword2> const xIPW2(rContinuations[i], css::uno::UNO_QUERY);
xIPW2->setPasswordToModify(msPassword);
xIPW2->select();
}
else
{
css::uno::Reference<css::task::XInteractionPassword> const xIPW(rContinuations[i], css::uno::UNO_QUERY);
if (xIPW.is())
{
xIPW->setPassword(msPassword);
xIPW->select();
}
}
}
return true;
}
};
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#*************************************************************************
#
# 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/.
#
#*************************************************************************
$(eval $(call gb_CppunitTest_CppunitTest,sw_ooxmlencryption))
$(eval $(call gb_CppunitTest_add_exception_objects,sw_ooxmlencryption, \
sw/qa/extras/ooxmlexport/ooxmlencryption \
))
$(eval $(call gb_CppunitTest_use_libraries,sw_ooxmlencryption, \
$(sw_ooxmlexport_libraries) \
))
$(eval $(call gb_CppunitTest_use_externals,sw_ooxmlencryption,\
boost_headers \
libxml2 \
))
$(eval $(call gb_CppunitTest_set_include,sw_ooxmlencryption,\
-I$(SRCDIR)/sw/inc \
-I$(SRCDIR)/sw/source/core/inc \
-I$(SRCDIR)/sw/qa/extras/inc \
$$(INCLUDE) \
))
$(eval $(call gb_CppunitTest_use_sdk_api,sw_ooxmlencryption))
$(eval $(call gb_CppunitTest_use_ure,sw_ooxmlencryption))
$(eval $(call gb_CppunitTest_use_vcl,sw_ooxmlencryption))
$(eval $(call gb_CppunitTest_use_components,sw_ooxmlencryption,\
$(sw_ooxmlexport_components) \
filter/source/storagefilterdetect/storagefd \
))
$(eval $(call gb_CppunitTest_use_configuration,sw_ooxmlencryption))
$(eval $(call gb_CppunitTest_use_packages,sw_ooxmlencryption,\
oox_generated \
))
# vim: set noet sw=4 ts=4:
......@@ -65,6 +65,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\
CppunitTest_sw_ooxmlexport9 \
CppunitTest_sw_ooxmlfieldexport \
CppunitTest_sw_ooxmlw14export \
CppunitTest_sw_ooxmlencryption \
CppunitTest_sw_ooxmlimport \
CppunitTest_sw_ww8export \
CppunitTest_sw_ww8export2 \
......
......@@ -59,6 +59,7 @@ define sw_ooxmlexport_components
sw/util/swd \
sw/util/msword \
sfx2/util/sfx \
sot/util/sot \
starmath/util/sm \
svl/source/fsstor/fsstorage \
svl/util/svl \
......
......@@ -23,7 +23,7 @@ public:
{}
};
#define DECLARE_DOCBOOKEXPORT_TEST(TestName, filename) DECLARE_SW_EXPORT_TEST(TestName, filename, DocbookExportTest)
#define DECLARE_DOCBOOKEXPORT_TEST(TestName, filename) DECLARE_SW_EXPORT_TEST(TestName, filename, nullptr, DocbookExportTest)
DECLARE_DOCBOOKEXPORT_TEST(testsimple, "simple.docx")
{
......
......@@ -67,7 +67,7 @@ private:
};
#define DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, HtmlExportTest)
#define DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, HtmlExportTest)
DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testFdo81276, "fdo81276.html")
{
......@@ -126,7 +126,7 @@ DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testCharacterBorder, "charborder.odt")
// No shadow
}
#define DECLARE_HTMLEXPORT_TEST(TestName, filename) DECLARE_SW_EXPORT_TEST(TestName, filename, HtmlExportTest)
#define DECLARE_HTMLEXPORT_TEST(TestName, filename) DECLARE_SW_EXPORT_TEST(TestName, filename, nullptr, HtmlExportTest)
DECLARE_HTMLEXPORT_TEST(testExportOfImages, "textAndImage.docx")
{
......
......@@ -26,7 +26,7 @@ class HtmlImportTest : public SwModelTestBase
HtmlImportTest() : SwModelTestBase("sw/qa/extras/htmlimport/data/", "HTML (StarWriter)") {}
};
#define DECLARE_HTMLIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, HtmlImportTest)
#define DECLARE_HTMLIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, nullptr, HtmlImportTest)
DECLARE_HTMLIMPORT_TEST(testPictureImport, "picture.html")
{
......
......@@ -32,6 +32,7 @@
#include <test/bootstrapfixture.hxx>
#include <test/xmltesttools.hxx>
#include <test/testinteractionhandler.hxx>
#include <unotest/macros_test.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <rtl/strbuf.hxx>
......@@ -64,7 +65,7 @@ using namespace css;
* }
*
*/
#define DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, BaseClass) \
#define DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, password, BaseClass) \
class TestName : public BaseClass { \
protected:\
virtual OUString getTestName() override { return OUString(#TestName); } \
......@@ -75,25 +76,25 @@ using namespace css;
CPPUNIT_TEST_SUITE_END(); \
\
void Import() { \
executeImportTest(filename);\
executeImportTest(filename, password);\
}\
void Import_Export_Import() {\
executeImportExportImportTest(filename);\
executeImportExportImportTest(filename, password);\
}\
void verify() override;\
}; \
CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
void TestName::verify()
#define DECLARE_OOXMLIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
#define DECLARE_OOXMLEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
#define DECLARE_RTFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
#define DECLARE_RTFEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
#define DECLARE_ODFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
#define DECLARE_ODFEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
#define DECLARE_WW8EXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
#define DECLARE_OOXMLIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, nullptr, Test)
#define DECLARE_OOXMLEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, Test)
#define DECLARE_RTFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, nullptr, Test)
#define DECLARE_RTFEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, Test)
#define DECLARE_ODFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, nullptr, Test)
#define DECLARE_ODFEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, Test)
#define DECLARE_WW8EXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, Test)
#define DECLARE_SW_IMPORT_TEST(TestName, filename, BaseClass) \
#define DECLARE_SW_IMPORT_TEST(TestName, filename, password, BaseClass) \
class TestName : public BaseClass { \
protected:\
virtual OUString getTestName() override { return OUString(#TestName); } \
......@@ -103,14 +104,14 @@ using namespace css;
CPPUNIT_TEST_SUITE_END(); \
\
void Import() { \
executeImportTest(filename);\
executeImportTest(filename, password);\
}\
void verify() override;\
}; \
CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
void TestName::verify()
#define DECLARE_SW_EXPORT_TEST(TestName, filename, BaseClass) \
#define DECLARE_SW_EXPORT_TEST(TestName, filename, password, BaseClass) \
class TestName : public BaseClass { \
protected:\
virtual OUString getTestName() override { return OUString(#TestName); } \
......@@ -120,7 +121,7 @@ using namespace css;
CPPUNIT_TEST_SUITE_END(); \
\
void Import_Export() {\
executeImportExport(filename);\
executeImportExport(filename, password);\
}\
void verify() override;\
}; \
......@@ -136,6 +137,8 @@ private:
protected:
uno::Reference< lang::XComponent > mxComponent;
rtl::Reference<TestInteractionHandler> xInteractionHandler;
xmlBufferPtr mpXmlBuffer;
const char* mpTestDocumentPath;
const char* mpFilter;
......@@ -211,7 +214,7 @@ protected:
* Helper func used by each unit test to test the 'import' code.
* (Loads the requested file and then calls 'verify' method)
*/
void executeImportTest(const char* filename)
void executeImportTest(const char* filename, const char* pPassword = nullptr)
{
// If the testcase is stored in some other format, it's pointless to test.
if (mustTestImportOf(filename))
......@@ -219,7 +222,7 @@ protected:
maTempFile.EnableKillingFile(false);
header();
std::unique_ptr<Resetter> const pChanges(preTest(filename));
load(mpTestDocumentPath, filename);
load(mpTestDocumentPath, filename, pPassword);
verify();
finish();
maTempFile.EnableKillingFile();
......@@ -231,14 +234,14 @@ protected:
* (Loads the requested file, save it to temp file, load the
* temp file and then calls 'verify' method)
*/
void executeImportExportImportTest(const char* filename)
void executeImportExportImportTest(const char* filename, const char* pPassword = nullptr)
{
maTempFile.EnableKillingFile(false);
header();
std::unique_ptr<Resetter> const pChanges(preTest(filename));
load(mpTestDocumentPath, filename);
load(mpTestDocumentPath, filename, pPassword);
postLoad(filename);
reload(mpFilter, filename);
reload(mpFilter, filename, pPassword);
verify();
finish();
maTempFile.EnableKillingFile();
......@@ -250,12 +253,12 @@ protected:
* the initial document condition), exports with the desired
* export filter and then calls 'verify' method)
*/
void executeImportExport(const char* filename)
void executeImportExport(const char* filename, const char* pPassword = nullptr)
{
maTempFile.EnableKillingFile(false);
header();
std::unique_ptr<Resetter> const pChanges(preTest(filename));
load(mpTestDocumentPath, filename);
load(mpTestDocumentPath, filename, pPassword);
save(OUString::createFromAscii(mpFilter), maTempFile);
maTempFile.EnableKillingFile(false);
verify();
......@@ -595,26 +598,50 @@ protected:
std::cout << "File tested,Execution Time (ms)" << std::endl;
}
void load(const char* pDir, const char* pName)
void load(const char* pDir, const char* pName, const char* pPassword = nullptr)
{
return loadURL(m_directories.getURLFromSrc(pDir) + OUString::createFromAscii(pName), pName);
return loadURL(m_directories.getURLFromSrc(pDir) + OUString::createFromAscii(pName), pName, pPassword);
}
void loadURL(OUString const& rURL, const char* pName)
void setTestInteractionHandler(const char* pPassword, std::vector<beans::PropertyValue>& rFilterOptions)
{
OUString sPassword = OUString::createFromAscii(pPassword);
rFilterOptions.resize(rFilterOptions.size() + 1);
xInteractionHandler = rtl::Reference<TestInteractionHandler>(new TestInteractionHandler(sPassword));
uno::Reference<task::XInteractionHandler2> const xInteraction(xInteractionHandler.get());
rFilterOptions[0].Name = "InteractionHandler";
rFilterOptions[0].Value <<= xInteraction;
}
void loadURL(OUString const& rURL, const char* pName, const char* pPassword = nullptr)
{
if (mxComponent.is())
mxComponent->dispose();
std::vector<beans::PropertyValue> aFilterOptions;
if (pPassword)
{
setTestInteractionHandler(pPassword, aFilterOptions);
}
// Output name early, so in the case of a hang, the name of the hanging input file is visible.
if (pName)
std::cout << pName << ":\n";
mnStartTime = osl_getGlobalTimer();
mxComponent = loadFromDesktop(rURL, "com.sun.star.text.TextDocument");
mxComponent = loadFromDesktop(rURL, "com.sun.star.text.TextDocument", comphelper::containerToSequence(aFilterOptions));
if (pPassword)
{
CPPUNIT_ASSERT_MESSAGE("Password set but not requested", xInteractionHandler->wasPasswordRequested());
}
discardDumpedLayout();
if (pName && mustCalcLayoutOf(pName))
calcLayout();
}
void reload(const char* pFilter, const char* filename)
void reload(const char* pFilter, const char* filename, const char* pPassword = nullptr)
{
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
OUString aFilterName = OUString::createFromAscii(pFilter);
......@@ -622,11 +649,29 @@ protected:
aMediaDescriptor["FilterName"] <<= aFilterName;
if (!maFilterOptions.isEmpty())
aMediaDescriptor["FilterOptions"] <<= maFilterOptions;
if (pPassword)
{
OUString sPassword = OUString::createFromAscii(pPassword);
css::uno::Sequence<css::beans::NamedValue> aEncryptionData {
{ "OOXPassword", css::uno::makeAny(sPassword) }
};
aMediaDescriptor[utl::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData;
}
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
xComponent->dispose();
mbExported = true;
mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument");
std::vector<beans::PropertyValue> aFilterOptions;
if (pPassword)
{
setTestInteractionHandler(pPassword, aFilterOptions);
}
mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument", comphelper::containerToSequence(aFilterOptions));
if (pPassword)
{
CPPUNIT_ASSERT_MESSAGE("Password set but not requested", xInteractionHandler->wasPasswordRequested());
}
if (mustValidate(filename))
{
if(aFilterName == "Office Open XML Text")
......
/* -*- 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 <swmodeltestbase.hxx>
#include <string>
class Test : public SwModelTestBase
{
public:
Test() : SwModelTestBase("/sw/qa/extras/ooxmlexport/data/", "Office Open XML Text") {}
protected:
bool mustTestImportOf(const char* filename) const override {
return (OString(filename).endsWith(".docx"));
}
};
DECLARE_SW_ROUNDTRIP_TEST(testPassword2007, "Encrypted_MSO2007_abc.docx", "abc", Test)
{
// Standard encryption format, AES 128, SHA1
uno::Reference<text::XTextRange> xParagraph(getParagraph(1));
CPPUNIT_ASSERT_EQUAL(OUString("abc"), xParagraph->getString());
}
DECLARE_SW_ROUNDTRIP_TEST(testPassword2010, "Encrypted_MSO2010_abc.docx", "abc", Test)
{
// Agile encryption format, AES 128, CBC, SHA1
uno::Reference<text::XTextRange> xParagraph(getParagraph(1));
CPPUNIT_ASSERT_EQUAL(OUString("abc"), xParagraph->getString());
}
DECLARE_SW_ROUNDTRIP_TEST(testPassword2013, "Encrypted_MSO2013_abc.docx", "abc", Test)
{
// Agile encryption format, AES 256, CBC, SHA512
uno::Reference<text::XTextRange> xParagraph(getParagraph(1));
CPPUNIT_ASSERT_EQUAL(OUString("ABC"), xParagraph->getString());
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -86,7 +86,7 @@ class FailTest : public Test
{
public:
// UGLY: hacky manual override of MacrosTest::loadFromDesktop
void executeImportTest(const char* filename)
void executeImportTest(const char* filename, const char* /*password*/)
{
header();
preTest(filename);
......@@ -125,7 +125,7 @@ DECLARE_OOXMLIMPORT_TEST(testImageHyperlink, "image-hyperlink.docx")
#if !defined(_WIN32)
DECLARE_SW_IMPORT_TEST(testMathMalformedXml, "math-malformed_xml.docx", FailTest)
DECLARE_SW_IMPORT_TEST(testMathMalformedXml, "math-malformed_xml.docx", nullptr, FailTest)
{
CPPUNIT_ASSERT(!mxComponent.is());
}
......@@ -1226,8 +1226,7 @@ protected:
}
};
DECLARE_SW_IMPORT_TEST(testHFLinkToPrev, "headerfooter-link-to-prev.docx",
testHFBase)
DECLARE_SW_IMPORT_TEST(testHFLinkToPrev, "headerfooter-link-to-prev.docx", nullptr, testHFBase)
{
uno::Reference<container::XNameAccess> xPageStyles = getStyles("PageStyles");
......
......@@ -19,7 +19,7 @@ public:
}
};
#define DECLARE_WW8IMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
#define DECLARE_WW8IMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, nullptr, Test)
DECLARE_WW8IMPORT_TEST(testFloatingTableSectionMargins, "floating-table-section-margins.doc")
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment