/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office 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 <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/text/XTextTable.hpp>

#include <comphelper/configuration.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/propertyvalue.hxx>
#include <officecfg/Office/Common.hxx>

#include <pam.hxx>
#include <unotxdoc.hxx>
#include <docsh.hxx>
#include <IDocumentSettingAccess.hxx>
#include <wrtsh.hxx>

using namespace css;
using namespace css::uno;

namespace
{
class Test : public SwModelTestBase
{
public:
    Test()
        : SwModelTestBase(u"/sw/qa/extras/ooxmlexport/data/"_ustr)
    {
    }
};

DECLARE_OOXMLEXPORT_TEST(testTdf137335_whitespaceLineHeight, "tdf137335_whitespaceLineHeight.docx")
{
    // given a one-paragraph document where the last two lines have only tabstops and spaces
    auto pXmlDoc = parseLayoutDump();

    SwTwips Height
        = getXPath(pXmlDoc, "//page/body/txt/SwParaPortion/SwLineLayout[3]", "height").toInt32();
    // Without the fix, the text height was based on the largest tabstop (48pt/1104 twips)
    // instead of the largest space (72pt)
    CPPUNIT_ASSERT_EQUAL(SwTwips(1656), Height);

    // However, the last line (with the carriage return symbol) is not based on the whitespace size,
    // but on the paragraph's default size (12pt in this case).
    Height = getXPath(pXmlDoc, "//page/body/txt/SwParaPortion/SwLineLayout[4]", "height").toInt32();
    CPPUNIT_ASSERT_EQUAL(SwTwips(276), Height);
}

DECLARE_OOXMLEXPORT_TEST(testTdf164835_nonDummyLineHeight, "tdf164835_nonDummyLineHeight.docx")
{
    // given a document where whitespace-only lines wrap beside an image
    auto pXmlDoc = parseLayoutDump();

    SwTwips nImageBottom = getXPath(pXmlDoc, "//fly/infos/bounds", "bottom").toInt32();
    // CPPUNIT_ASSERT_EQUAL(1, getPages()); // should all be on a single page
    assertXPathContent(pXmlDoc, "//page[2]/body/txt", u"Text below the picture");
    SwTwips nTextTop = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "top").toInt32();
    // Without the fix, the text in paragraph 5 was beside the image.
    CPPUNIT_ASSERT_GREATER(nImageBottom, nTextTop); // text is below the image
}

DECLARE_OOXMLEXPORT_TEST(testTdf172113_linespacingComment, "tdf172113_linespacingComment.odt")
{
    // given a two page document - triple spaced with a comment in an otherwise empty paragraph
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

DECLARE_OOXMLEXPORT_TEST(testTdf172169_linespacingFootnote, "tdf172169_linespacingFootnote.odt")
{
    // given what should be a single page, double spaced document
    // where a line starts with a large non-text item and then later has a footnote marker.

    CPPUNIT_ASSERT_EQUAL(1, getPages());
}

DECLARE_OOXMLEXPORT_TEST(testTdf172169_linespacingSecondPortion,
                         "tdf172169_linespacingSecondPortion.odt")
{
    // given a two page, double spaced document where enlarged text follows an even larger image
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

CPPUNIT_TEST_FIXTURE(Test, testTdf172169_intraLinespacing)
{
    // given a six page, double-spaced document
    // with various different 60pt SwLinePortions affecting intra-paragraph line spacing
    createSwDoc("tdf172169_intraLinespacing.odt");

    // NOTE that before LO 7.1 this was 8 pages, but it changed without a compatibility flag...
    CPPUNIT_ASSERT_EQUAL(6, getPages());

    // Test that the 'broken' layout remains broken in older ODT files
    auto pXmlDoc = parseLayoutDump();

    OUString sText = getXPathContent(pXmlDoc, "//page[2]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("The change is not limited to footnotes.")); // testing field
    SwTwips nTextHeight = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills half the page.
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2484, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[3]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("How about hidden stuff."));
    nTextHeight = getXPath(pXmlDoc, "//page[3]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills about a third of the page
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1380, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[4]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("How about FieldMark stuff."));
    nTextHeight = getXPath(pXmlDoc, "//page[4]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills half the page.
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2484, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[5]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("Also explicitly test for content controls,"));
    nTextHeight = getXPath(pXmlDoc, "//page[5]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills half the page.
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2484, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[6]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("Let us also explicitly test for tabstops."));
    nTextHeight = getXPath(pXmlDoc, "//page[6]/body/txt/infos/bounds", "height").toInt32();
    // The text basically fills the entire page
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3588, nTextHeight, 100);

    saveAndReload(TestFilter::DOCX);

    CPPUNIT_ASSERT_EQUAL(6, getPages());

    saveAndReload(TestFilter::ODT);

    CPPUNIT_ASSERT_EQUAL(6, getPages());

    // This formatting should be the same for DOCX and ODT - only testing ODT
    pXmlDoc = parseLayoutDump();

    sText = getXPathContent(pXmlDoc, "//page[2]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("The change is not limited to footnotes."));
    nTextHeight = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "height").toInt32();
    // The text basically fills the entire page. (was 2484)
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3588, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[3]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("How about hidden stuff."));
    nTextHeight = getXPath(pXmlDoc, "//page[3]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills about a third of the page
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1380, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[4]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("How about FieldMark stuff."));
    nTextHeight = getXPath(pXmlDoc, "//page[4]/body/txt/infos/bounds", "height").toInt32();
    // The text basically fills the entire page. (was 2484)
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3588, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[5]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("Also explicitly test for content controls,"));
    nTextHeight = getXPath(pXmlDoc, "//page[5]/body/txt/infos/bounds", "height").toInt32();
    // The text basically fills the entire page. (was 2484)
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3588, nTextHeight, 100);

    sText = getXPathContent(pXmlDoc, "//page[6]/body/txt");
    CPPUNIT_ASSERT(sText.startsWith("Let us also explicitly test for tabstops."));
    nTextHeight = getXPath(pXmlDoc, "//page[6]/body/txt/infos/bounds", "height").toInt32();
    // The text only fills about two thirds of the page
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2484, nTextHeight, 100);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf172169_linespacingBidi)
{
    // given a double spaced document containing multiportion bidi characters
    createSwDoc("tdf172169_linespacingBidi.odt");

    // NOTE that before LO 7.1 this was 2 pages, but it changed without a compatibility flag...
    // Ensure the current implementation doesn't change unintentionally.
    CPPUNIT_ASSERT_EQUAL(1, getPages());

    auto pXmlDoc = parseLayoutDump();

    SwTwips nTextHeight = getXPath(pXmlDoc, "//page/body/txt/infos/bounds", "height").toInt32();
    // The paragraph fills the page
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3634, nTextHeight, 100);

    saveAndReload(TestFilter::DOCX);
    pXmlDoc = parseLayoutDump();

    // The ODT behaviour is buggy. Line spacing should be based on the tallest, not smallest text.
    CPPUNIT_ASSERT_EQUAL(2, getPages());

    nTextHeight = getXPath(pXmlDoc, "//page[1]/body/txt/infos/bounds", "height").toInt32();
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3312, nTextHeight, 100);
    nTextHeight = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "height").toInt32();
    CPPUNIT_ASSERT_EQUAL(SwTwips(552), nTextHeight);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf172169_linespacingDoubleLineText)
{
    // given a double spaced document containing Asian double-line text as a mid-portion

    createSwDoc("tdf172169_linespacingDoubleLineText.odt");

    // NOTE that before LO 7.1 this was 2 pages, but it changed without a compatibility flag...
    // Ensure the current implementation doesn't change unintentionally.
    CPPUNIT_ASSERT_EQUAL(1, getPages());

    saveAndReload(TestFilter::DOCX);

    // DOCX does use the height of the double-line text to inform the line-spacing
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

CPPUNIT_TEST_FIXTURE(Test, testTdf172169_linespacingVerticalText)
{
    // given a double spaced document containing vertical text (as first portion and mid-portion)
    createSwDoc("tdf172169_linespacingVerticalText.odt");

    // NOTE that before LO 7.1 this was 3 pages, but it changed without a compatibility flag...
    // Ensure the current implementation doesn't change unintentionally.
    CPPUNIT_ASSERT_EQUAL(2, getPages());

    auto pXmlDoc = parseLayoutDump();

    SwTwips nTextHeight = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "height").toInt32();
    // No extra line-spacing gap is produced by the rotated text in ODT
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2543, nTextHeight, 100);

    saveAndReload(TestFilter::DOCX);
    pXmlDoc = parseLayoutDump();

    // DOCX does use the height of the rotated text to inform the line-spacing!
    CPPUNIT_ASSERT_EQUAL(3, getPages());

    nTextHeight = getXPath(pXmlDoc, "//page[2]/body/txt/infos/bounds", "height").toInt32();
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3154, nTextHeight, 100); // will be 1991
}

DECLARE_OOXMLEXPORT_TEST(testTdf148057_columnBreak, "tdf148057_columnBreak.docx")
{
    // given a document with a linefeed immediately following a column break (in non-column section)

    // don't lose the column break to the paragraph's no-inherited-page-break property.
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

DECLARE_OOXMLEXPORT_TEST(testTdf171025_pageAfter, "tdf171025_pageAfter.docx")
{
    // given a document with a LO specialty pageAfter break
    // coupled with IsPlausableSingleWordSection
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

CPPUNIT_TEST_FIXTURE(Test, testTdf171038_pageAfter)
{
    // given a document with a LO-specialty PageAfter break
    // coupled with !IsPlausableSingleWordSection
    createSwDoc("tdf171038_pageAfter.docx");

    saveAndReload(TestFilter::DOCX);

    CPPUNIT_ASSERT_EQUAL(2, getPages());

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    assertXPath(pXmlDoc, "//w:sectPr", 2); // there are two section breaks
    assertXPath(pXmlDoc, "//w:body/w:p/w:pPr/w:p/w:pPr/w:sectPr", 0); // would be corrupt document
    assertXPath(pXmlDoc, "//w:body/w:p/w:pPr/w:sectPr", 1); // one is in the paragraph rPr
    assertXPath(pXmlDoc, "//w:body/w:sectPr", 1); // the other is at the end of the document
}

DECLARE_OOXMLEXPORT_TEST(testTdf166544_noTopMargin_fields, "tdf166544_noTopMargin_fields.docx")
{
    // given a document with a hyperlink field containing a page break
    auto pXmlDoc = parseLayoutDump();

    // The top margin is applied before the page break - since the page break follows the field end
    sal_Int32 nHeight = getXPath(pXmlDoc, "//page[2]//txt/infos/bounds", "height").toInt32();
    // Without the fix, the text height (showing a large top margin) was 569
    CPPUNIT_ASSERT_EQUAL(sal_Int32(269), nHeight);
}

DECLARE_OOXMLEXPORT_TEST(testTdf171383_refFieldInCell, "tdf171383_refFieldInCell.docx")
{
    // given a document where the table contains a multi-paragraph reference field

    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
                                                    uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<text::XTextRange> xCell(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"Xyz"_ustr, xCell->getString());
    uno::Reference<beans::XPropertySet> xPara(getParagraphOfText(1, xCell->getText()),
                                              uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"1"_ustr, getProperty<OUString>(xPara, u"ListLabelString"_ustr));

    xCell.set(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCell->getString().startsWith("Issue 1"));
    xPara.set(getParagraphOfText(1, xCell->getText()), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u""_ustr, getProperty<OUString>(xPara, u"ListLabelString"_ustr));
}

DECLARE_OOXMLEXPORT_TEST(testTdf171299_tableInField, "tdf171299_tableInField.docx")
{
    // given a document where the field contains a table

    // there is some text prior to the table
    getParagraph(1, u"Zusammenfassung:"_ustr);
    CPPUNIT_ASSERT_EQUAL(4, getParagraphs());

    // The table cells also all have text content
    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
                                                    uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<text::XTextRange> xCell(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"ID"_ustr, xCell->getString());
}

DECLARE_OOXMLEXPORT_TEST(testTdf138020_undefined_firstCol, "tdf138020_undefined_firstCol.docx")
{
    // Given a table where the vertical band and first column are both defined
    // but there is no firstColumn style definition provided

    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
                                                    uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);

    // w:firstColumn=1 but no style defined, so it is treated as if it was turned off.
    // Odd-VBand's green color applies
    uno::Reference<text::XTextRange> xCell(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xd9f2d0), getProperty<Color>(xCell, u"BackColor"_ustr)); // green

    // second column applies Even-VBand's automatic color
    xCell.set(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(COL_AUTO, getProperty<Color>(xCell, u"BackColor"_ustr));
}

DECLARE_OOXMLEXPORT_TEST(testTdf138020_all_rows_tblHeader, "tdf138020_all_rows_tblHeader.docx")
{
    // Given a multi-page table where all rows are marked as tblHeader

    xmlDocUniquePtr pDump = parseLayoutDump();
    CPPUNIT_ASSERT_EQUAL(3, getPages());

    // table starts on page 2 and ends on page 3, with no repeated header (MS Word ignores it)
    assertXPath(pDump, "/root/page[1]/body/tab", 0);
    assertXPath(pDump, "/root/page[2]/body/tab", 1);
    assertXPath(pDump, "/root/page[3]/body/tab", 1);

    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
                                                    uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);

    // w:firstRow=0, but has tblHeader, so that automatically forces firstRow style
    uno::Reference<text::XTextRange> xCell(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xffc000), getProperty<Color>(xCell, u"BackColor"_ustr)); // gold

    // Since all rows were marked as tblHeader, in emulation we turned off the header
    // since MS Word doesn't repeat the header in this situation
    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xTextTable, u"HeaderRowCount"_ustr));

    // second last row is still tblHeader, so firstRow
    xCell.set(xTextTable->getCellByName(u"C7"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xffc000), getProperty<Color>(xCell, u"BackColor"_ustr)); // gold

    // w:lastRow not provided, so it would be on by default, but it too is tblHeader, so firstRow
    xCell.set(xTextTable->getCellByName(u"A8"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xffc000), getProperty<Color>(xCell, u"BackColor"_ustr)); // gold
}

DECLARE_OOXMLEXPORT_TEST(testTdf167843_tblLook_firstRow_tblHeader,
                         "tdf167843_tblLook_firstRow_tblHeader.docx")
{
    // Given a table with a colorful table style defined
    // but a hand-mangled tblLook where the legacy w:val does not match the explicit elements.
    //     <w:tblLook w:val="04A0" w:firstRow="0"/>

    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
                                                    uno::UNO_QUERY);
    uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);

    // w:firstRow=0, so firstRow style should not be applied - i.e. no formatting.
    // w:val disagrees, but it is ignored since a w:firstRow has been explicitly provided
    uno::Reference<text::XTextRange> xCell(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(COL_AUTO, getProperty<Color>(xCell, u"BackColor"_ustr));

    // since firstRow style is not applied, do not inherit the style's tblHeader
    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xTextTable, u"HeaderRowCount"_ustr));

    // w:firstColumn not provided, so it is on by default (regardless of w:val, although it agrees)
    xCell.set(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xd9d9d9), getProperty<Color>(xCell, u"BackColor"_ustr)); // gray 1

    // w:lastColumn not provided, so it is on by default (regardless of w:val, which disagrees)
    xCell.set(xTextTable->getCellByName(u"G1"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xbfbfbf), getProperty<Color>(xCell, u"BackColor"_ustr)); // gray 2

    // w:lastRow not provided, so it is on by default (regardless of w:val, which disagrees)
    xCell.set(xTextTable->getCellByName(u"A8"_ustr), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(Color(0xc1e4f5), getProperty<Color>(xCell, u"BackColor"_ustr)); // blue
}

DECLARE_OOXMLEXPORT_TEST(testTdf166141_linkedStyles, "tdf166141_linkedStyles.docx")
{
    // Given a document with settings.xml containing both linkStyles and attachedTemplate

    // The problem was that there should not be any spacing between the two paragraphs,
    // but there was 200 twips of below spacing.

    uno::Reference<beans::XPropertySet> xStyle(
        getStyles(u"ParagraphStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
    // Since the "template" to update the styles from doesn't exist, make no changes
    // This was being forced to 200 twips by linkedStyles...
    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xStyle, u"ParaBottomMargin"_ustr));
}

DECLARE_OOXMLEXPORT_TEST(testTdf166141_linkedStyles2, "tdf166141_linkedStyles2.docx")
{
    // Given a document with settings.xml containing only linkStyles - no attachedTemplate

    // Since no "template" was provided to update the styles from,
    // the styles must be updated using the application defaults.
    uno::Reference<beans::XPropertySet> xStyle(
        getStyles(u"ParagraphStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
    // This must be 200 twips / 0.35cm.
    CPPUNIT_ASSERT_EQUAL(sal_Int32(353), getProperty<sal_Int32>(xStyle, u"ParaBottomMargin"_ustr));
}

DECLARE_OOXMLEXPORT_TEST(testTdf166510_sectPr_bottomSpacing, "tdf166510_sectPr_bottomSpacing.docx")
{
    // given with a sectPr with different bottom spacing (undefined in this case - i.e. zero)
    auto pXmlDoc = parseLayoutDump();

    // The last paragraph (sectPr) has 0 below spacing, so no reduction of page 2's 200pt top margin
    sal_Int32 nHeight = getXPath(pXmlDoc, "//page[2]//body/txt/infos/bounds", "height").toInt32();
    // Without the fix, the text height (showing no top margin at all) was 253
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4253), nHeight);
}

DECLARE_OOXMLEXPORT_TEST(testTdf169986_bottomSpacing, "tdf169986_bottomSpacing.docx")
{
    // given with a continuous section break with a lot of below spacing, and several footnotes...
    CPPUNIT_ASSERT_EQUAL(1, getPages());
}

DECLARE_OOXMLEXPORT_TEST(testTdf170003_bottomSpacing, "tdf170003_bottomSpacing.docx")
{
    // Given a document with a table before the page break, and a sectPr with a huge bottom spacing

    // This must be 200 twips / 0.35cm / 353 mm100, not sectPr's 2000 twips / 3.53 cm / 3530 mm100
    CPPUNIT_ASSERT_EQUAL(sal_Int32(353),
                         getProperty<sal_Int32>(getParagraph(1), u"ParaBottomMargin"_ustr));
}

DECLARE_OOXMLEXPORT_TEST(testTdf170119_bottomSpacing, "tdf170119_bottomSpacing.docx")
{
    // Given a document with a page break and a sectPr with a huge bottom spacing

    // Without the fix, page 2 started with a 150pt gap, pushing content to the third page.
    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

DECLARE_OOXMLEXPORT_TEST(testTdf167657_sectPr_bottomSpacing, "tdf167657_sectPr_bottomSpacing.docx")
{
    // given with a continuous break sectPr with no belowSpacing
    CPPUNIT_ASSERT_EQUAL(1, getPages());

    auto pXmlDoc = parseLayoutDump();

    // Since there is no page break, the prior paragraph's belowSpacing should not be zero'd out.
    // NOTE: apparently layout assigns the belowSpacing to the following section, not body/txt[1],
    sal_Int32 nHeight = getXPath(pXmlDoc, "//section/infos/bounds", "height").toInt32();
    // Without the fix, the section's height (showing no bottom margin for the 1st para) was 309
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1409), nHeight);
}

DECLARE_OOXMLEXPORT_TEST(testTdf165478_bottomAligned, "tdf165478_bottomAligned.docx")
{
    // given a layoutInCell, wrap-through image, paragraph-anchored to a bottom-aligned cell
    auto pXmlDoc = parseLayoutDump();

    // The text in the cell should be at the bottom of the cell
    assertXPathContent(pXmlDoc, "//cell[2]/txt", u"Bottom aligned");
    sal_Int32 nTextBottom = getXPath(pXmlDoc, "//cell[2]/txt/infos/bounds", "bottom").toInt32();
    sal_Int32 nCellBottom = getXPath(pXmlDoc, "//cell[2]/infos/bounds", "bottom").toInt32();

    // Without the fix, the text was at the top of the cell (2002) instead of at the bottom (4423)
    CPPUNIT_ASSERT_EQUAL(nCellBottom, nTextBottom);

    // The image is inside of the table
    sal_Int32 nFlyTop = getXPath(pXmlDoc, "//cell[2]//fly/infos/bounds", "top").toInt32();
    sal_Int32 nCellTop = getXPath(pXmlDoc, "//cell[2]/infos/bounds", "top").toInt32();
    // Without the fix, the image was above the table (284) instead of inside the cell (1887)
    CPPUNIT_ASSERT_GREATER(nCellTop, nFlyTop); // image is below the cell top
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1887), nFlyTop);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithInline)
{
    createSwDoc("tdf165359_SdtWithInline.docx");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // MS Word reports document as corrupt if a picture is inside of a plainText content control
    assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithDrawing)
{
    createSwDoc("tdf165359_SdtWithDrawing.docx");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // MS Word reports document as corrupt if a picture is inside of a plainText content control
    assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170438_dropdown)
{
    createSwDoc("tdf170438_dropdown.odt");

    saveAndReload(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // MS Word reports document as corrupt if displayText is empty
    assertXPath(pXmlDoc, "//w:listItem[1]", "displayText", u" ");
    assertXPath(pXmlDoc, "//w:listItem[1]", "value", u""); // value may be empty
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170516_drawingBeforePlainText)
{
    createSwDoc("tdf170516_drawingBeforePlainText.docx");

    saveAndReload(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
    assertXPath(pXmlDoc, "//w:tc/w:p/w:sdt", 1); // and it is inside a cell

    assertXPath(pXmlDoc, "//mc:AlternateContent", 1); // only one drawing
    assertXPath(pXmlDoc, "//w:tc/w:p/w:r/mc:AlternateContent", 1); // and it is not inside the sdt
}

CPPUNIT_TEST_FIXTURE(Test, testTdf168988_grabbagDatePicker)
{
    createSwDoc("tdf168988_grabbagDatePicker.docx");

    save(TestFilter::DOCX);
    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
    assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", 1); // it is a date content control
    // there is no valid date, so fullDate must not be provided (or MS Word says 'corrupt file')
    assertXPathNoAttribute(pXmlDoc, "//w:sdt/w:sdtPr/w:date", "fullDate");
    assertXPathContent(pXmlDoc, "//w:sdt/w:sdtContent/w:r/w:t",
                       u"                                     2012./2013.");
}

CPPUNIT_TEST_FIXTURE(Test, testTdf169101_datePicker)
{
    createSwDoc("tdf169101_datePicker.docx");

    save(TestFilter::DOCX);
    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
    assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", 1); // it is a date content control
    // there is no valid date, so fullDate must not be provided (or MS Word says 'corrupt file')
    assertXPathNoAttribute(pXmlDoc, "//w:sdt/w:sdtPr/w:date", "fullDate");
    assertXPathContent(pXmlDoc, "//w:sdt/w:sdtContent/w:r/w:t", u"Week #");

    // if the relationship is missing, then MS Word shows placeholder text instead of customXml date
    xmlDocUniquePtr pXmlCustomXmlRels = parseExport(u"customXml/_rels/item1.xml.rels"_ustr);
    assertXPath(pXmlCustomXmlRels, "//rels:Relationship", "Target", u"itemProps1.xml");
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
{
    createSwDoc("tdf170389_manyTabstops.odt");

    saveAndReload(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // MS Word reports document as corrupt if it has more than 64 tabstops defined
    // The paragraph itself defines 40, and inherits 40. Without the fixes, this was 80 or 64
    assertXPath(pXmlDoc, "//w:tabs/w:tab", 40);

    xmlDocUniquePtr pLayout = parseLayoutDump();
    sal_Int32 nSize
        = getXPath(pLayout, "//SwFixPortion[@type='PortionType::TabLeft']", "width").toInt32();
    // The word 'tabstop' should be almost at the very end of the line, starting at 6 inches.
    // Without the fix, the tabstop's width was a tiny 247, now it is 1797.
    CPPUNIT_ASSERT_GREATER(sal_Int32(1500), nSize); // nSize > 1500
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170602_checkbox_bookmarkEnd)
{
    createSwDoc("tdf170602_checkbox_bookmarkEnd.docx");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // MS Word reports document as corrupt if a plainText blockSdt contains a bookmarkEnd
    assertXPath(pXmlDoc, "//w:body/w:tbl/w:tr[2]/w:tc[1]/w:bookmarkEnd", 1); // Tempestades
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170602_exportContext)
{
    // given a document where both the body text and the interrupting header stream contain an sdt
    createSwDoc("tdf170602_exportContext.docx");

    // without the fix, the document would crash on save, b/c m_oSdtPrToken wasn't preserved
    save(TestFilter::DOCX);
}

CPPUNIT_TEST_FIXTURE(Test, testInvalidDatetimeInProps)
{
    createSwDoc("invalidDatetimeInProps.fodt");

    // Without the fix in place, this test would have failed with
    // - Expected: 0
    // - Actual  : 2
    // - validation error in OOXML export: Errors: 2
    saveAndReload(TestFilter::DOCX);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf169413_asciiTheme)
{
    // the document failed to reload without errors after a round-trip
    createSwDoc("tdf169413_asciiTheme.docx");

    saveAndReload(TestFilter::DOCX);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf166620)
{
    createSwDoc();
    {
        SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
        pWrtShell->Insert(u"Body text"_ustr);
        pWrtShell->InsertFootnote({}, /*bEndNote=*/true, /*bEdit=*/true);
        pWrtShell->Insert(u"Endnote text"_ustr);
    }

    // Exporting to a Word format, a tab is prepended to the endnote text. When imported, the
    // NoGapAfterNoteNumber compatibility flag is enabled; and the exported tab is the only thing
    // that separates the number and the text. The tab must not be stripped away on import.
    saveAndReload(TestFilter::DOCX);
    {
        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
        auto xSettings = xFactory->createInstance(u"com.sun.star.document.Settings"_ustr);
        CPPUNIT_ASSERT(getProperty<bool>(xSettings, u"NoGapAfterNoteNumber"_ustr));

        auto xSupplier = mxComponent.queryThrow<text::XEndnotesSupplier>();
        auto xEndnotes = xSupplier->getEndnotes();
        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xEndnotes->getCount());
        auto xEndnoteText = xEndnotes->getByIndex(0).queryThrow<text::XText>();
        CPPUNIT_ASSERT_EQUAL(u"\tEndnote text"_ustr, xEndnoteText->getString());
    }
    // Do a second round-trip. It must not duplicate the tab.
    saveAndReload(TestFilter::DOCX);
    {
        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
        auto xSettings = xFactory->createInstance(u"com.sun.star.document.Settings"_ustr);
        CPPUNIT_ASSERT(getProperty<bool>(xSettings, u"NoGapAfterNoteNumber"_ustr));

        auto xSupplier = mxComponent.queryThrow<text::XEndnotesSupplier>();
        auto xEndnotes = xSupplier->getEndnotes();
        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xEndnotes->getCount());
        auto xEndnoteText = xEndnotes->getByIndex(0).queryThrow<text::XText>();
        CPPUNIT_ASSERT_EQUAL(u"\tEndnote text"_ustr, xEndnoteText->getString());

        // Remove the tab
        xEndnoteText->setString(u"Endnote text"_ustr);
    }
    // Do a third round-trip. It must not introduce the tab, because of the compatibility flag.
    saveAndReload(TestFilter::DOCX);
    {
        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
        auto xSettings = xFactory->createInstance(u"com.sun.star.document.Settings"_ustr);
        CPPUNIT_ASSERT(getProperty<bool>(xSettings, u"NoGapAfterNoteNumber"_ustr));

        auto xSupplier = mxComponent.queryThrow<text::XEndnotesSupplier>();
        auto xEndnotes = xSupplier->getEndnotes();
        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xEndnotes->getCount());
        auto xEndnoteText = xEndnotes->getByIndex(0).queryThrow<text::XText>();
        CPPUNIT_ASSERT_EQUAL(u"Endnote text"_ustr, xEndnoteText->getString());
    }
}

CPPUNIT_TEST_FIXTURE(Test, testTdf167082)
{
    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: Heading 1
    // - Actual  : Standard

    createSwDoc("tdf167082.docx");

    saveAndReload(TestFilter::DOCX);

    OUString aStyleName = getProperty<OUString>(getParagraph(3), u"ParaStyleName"_ustr);

    CPPUNIT_ASSERT_EQUAL(u"Heading 1"_ustr, aStyleName);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf145716_nonHTMLspacing)
{
    // Given a two page document with the compat flag doNotUseHTMLParagraphAutoSpacing
    createSwDoc("tdf145716_nonHTMLspacing.docx");

    saveAndReload(TestFilter::DOCX);

    CPPUNIT_ASSERT_EQUAL(2, getPages());
}

CPPUNIT_TEST_FIXTURE(Test, testTdf148294_crossReferences)
{
    // Given a file with several cross-references in various edge cases
    createSwDoc("tdf148294_crossReferences.odt");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    assertXPath(pXmlDoc, "//w:bookmarkStart", 4);
    assertXPath(pXmlDoc, "//w:bookmarkEnd", 4);

    // The second bookmark should be stradling the text 'DOCX'
    assertXPathContent(pXmlDoc, "//w:p[3]/w:r/w:t", u"DOCX");
    int n = countXPathNodes(pXmlDoc, "//w:p[3]/w:bookmarkStart/preceding::w:t");
    CPPUNIT_ASSERT_EQUAL(n + 1, countXPathNodes(pXmlDoc, "//w:p[3]/w:bookmarkEnd/preceding::w:t"));
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170908_delText)
{
    // Given a file with deleted text at the end of the paragraph just before anchored stuff
    createSwDoc("tdf170908_delText.docx");

    // When exporting that to DOCX:
    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // delText must be inside a w:del or MS Word considers the document to be corrupt
    assertXPath(pXmlDoc, "//w:delText", 1); // there is a delTree element
    CPPUNIT_ASSERT_EQUAL(1, countXPathNodes(pXmlDoc, "//w:del/w:r/w:delText")); // must be in w:del

    // tdf#170516 drawing after plainText control
    assertXPath(pXmlDoc, "//mc:AlternateContent", 1); // only one drawing
    const int nRunsBeforeDelText = countXPathNodes(pXmlDoc, "//w:delText/preceding::w:r");
    const int nRunsBeforeAnchor = countXPathNodes(pXmlDoc, "//mc:AlternateContent/preceding::w:r");

    // The text should be in a separate (preceding) run from the floating stuff
    CPPUNIT_ASSERT_GREATER(nRunsBeforeDelText, nRunsBeforeAnchor);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170908_delText_sdt)
{
    // Given a document with a deleted, exported-with-WritePendingPlaceholder sdt

    createSwDoc("tdf170908_delText_sdt.odt");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // delText must be inside a w:del or MS Word considers the document to be corrupt
    assertXPath(pXmlDoc, "//w:delText", 1); // there is only one delTree element
    assertXPath(pXmlDoc, "//w:del/w:sdt/w:sdtContent/w:r/w:delText", 1); // the whole sdt is deleted
}

CPPUNIT_TEST_FIXTURE(Test, testTdf170952_delText)
{
    // Given a document with deleted text at the end of the paragraph

    skipValidation(); // ERROR: The content of element 'w:rPrChange' is not complete. One of '{"http://schemas.openxmlformats.org/wordprocessingml/2006/main":rPr}' is expected.
    createSwDoc("tdf170952_delText.docx");

    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // delText must be inside a w:del or MS Word considers the document to be corrupt
    assertXPath(pXmlDoc, "//w:delText", 3); // there are three delTree elements
    CPPUNIT_ASSERT_EQUAL(3, countXPathNodes(pXmlDoc, "//w:del/w:r/w:delText")); // all are in w:del
}

CPPUNIT_TEST_FIXTURE(Test, testRangeCommentInDeleteDocxExport)
{
    // Given a document with a comment that is inside a delete redline:
    createSwDoc("redline-range-comment.docx");

    // When exporting that to DOCX:
    save(TestFilter::DOCX);

    // Then make sure that the comment reference is inside the delete redline:
    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: 1
    // - Actual  : 0
    // i.e. the comment anchor was outside the redline.
    CPPUNIT_ASSERT_EQUAL(1, countXPathNodes(pXmlDoc, "//w:del/w:r/w:commentReference"));
}

CPPUNIT_TEST_FIXTURE(Test, testFloatingTableAnchorPosExport)
{
    // Given a document with two floating tables after each other:
    // When saving that document to DOCX:
    createSwDoc("floattable-anchorpos.docx");
    save(TestFilter::DOCX);

    // Then make sure that the dummy anchor of the first floating table is not written to the export
    // result:
    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    // Check the order of the floating tables: C is from the previous node, A is normal floating
    // table.
    CPPUNIT_ASSERT_EQUAL(u"C"_ustr,
                         getXPathContent(pXmlDoc, "//w:body/w:tbl[1]/w:tr/w:tc/w:p/w:r/w:t"));
    CPPUNIT_ASSERT_EQUAL(u"A"_ustr,
                         getXPathContent(pXmlDoc, "//w:body/w:tbl[2]/w:tr/w:tc/w:p/w:r/w:t"));
    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: 1
    // - Actual  : 2
    // i.e. the dummy anchor node was written to DOCX, leading to a Writer vs Word layout
    // difference.
    CPPUNIT_ASSERT_EQUAL(1, countXPathNodes(pXmlDoc, "//w:body/w:p"));
    CPPUNIT_ASSERT_EQUAL(u"D"_ustr, getXPathContent(pXmlDoc, "//w:body/w:p/w:r/w:t"));
}

CPPUNIT_TEST_FIXTURE(Test, testTdf167297)
{
    createSwDoc("tdf167297.fodt");

    auto fnVerify = [this] {
        auto pXmlDoc = parseLayoutDump();

        // The test document uses style:script-type to replace 12pt characters
        // with 96pt characters. Round-trip is confirmed by checking height.
        sal_Int32 nHeight = getXPath(pXmlDoc, "//txt/infos/bounds", "height").toInt32();
        CPPUNIT_ASSERT_GREATER(sal_Int32(2000), nHeight);
    };

    fnVerify();
    saveAndReload(TestFilter::DOCX);
    fnVerify();
}

CPPUNIT_TEST_FIXTURE(Test, testTdf167583)
{
    createSwDoc();

    auto fnVerify = [&](bool bExpected) {
        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
        auto xSettings = xFactory->createInstance(u"com.sun.star.document.Settings"_ustr);
        CPPUNIT_ASSERT_EQUAL(
            bExpected, getProperty<bool>(xSettings, u"AdjustTableLineHeightsToGridHeight"_ustr));
    };

    // By default, a Writer doc has the compat option set
    fnVerify(true);

    // Check that the value is persisted across save-reload
    saveAndReload(TestFilter::DOCX);
    fnVerify(true);

    // Unset the compat flag
    {
        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
        uno::Reference<beans::XPropertySet> xSettings(
            xFactory->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
        xSettings->setPropertyValue(u"AdjustTableLineHeightsToGridHeight"_ustr,
                                    cpo::uno::Any(false));
    }

    fnVerify(false);
    saveAndReload(TestFilter::DOCX);
    fnVerify(false);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf150822)
{
    createSwDoc("tdf150822.docx");

    auto fnVerify = [this] {
        auto pXmlDoc = parseLayoutDump();

        // Without the fix, vertical layout won't be parsed
        assertXPath(pXmlDoc, "//txt[@WritingMode='Vertical']", 3);
    };

    fnVerify();
    saveAndReload(TestFilter::DOCX);
    fnVerify();
}

CPPUNIT_TEST_FIXTURE(Test, testWNumDuplication)
{
    // Given a document that changes a lot between a few numbering styles and overrides:
    createSwDoc("mixednumberings.docx");
    save(TestFilter::DOCX);

    // Then make sure that we export a reasonable number of "w:num" elements:
    xmlDocUniquePtr pXmlNum = parseExport(u"word/numbering.xml"_ustr);
    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: 10
    // - Actual  : 61
    CPPUNIT_ASSERT_EQUAL(10, countXPathNodes(pXmlNum, "//w:numbering/w:num"));
}

CPPUNIT_TEST_FIXTURE(Test, testTdf169274)
{
    createSwDoc("tdf169274.docx");
    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
    const OString sPath = "//w:body/w:tbl/w:tr/w:tc/w:p/w:sdt/"_ostr;

    // Verify SDT exists with dataBinding property
    assertXPath(pXmlDoc, sPath + "w:sdtPr/w:dataBinding", 1);
    // Verify there are no nested SDTs
    assertXPath(pXmlDoc, sPath + "w:sdtContent/w:sdt", 0);
}

CPPUNIT_TEST_FIXTURE(Test, testQFormatPreservation)
{
    createSwDoc("nospacing_hidden.docx");
    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlStyles = parseExport(u"word/styles.xml"_ustr);

    // "Heading" is the LO pool style auto-created as parent of Heading 1-9.
    // The source DOCX has no such style, so the export must not invent qFormat
    // for it via the lcl_guessQFormat fallback - import silence is preserved.
    assertXPath(pXmlStyles, "//w:style[@w:styleId='Heading']/w:qFormat", 0);
    // "No spacing" had w:qFormat w:val="0" in the source - that explicit
    // "no qFormat" must round-trip as no qFormat element on export.
    assertXPath(pXmlStyles, "//w:style[@w:styleId='No spacing']/w:qFormat", 0);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf168964)
{
    createSwDoc("tdf168964.odt");
    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);

    OUString sStyle
        = getXPath(pXmlDoc, "//wps:txbx/w:txbxContent/w:p[2]/w:r/w:object/v:shape", "style");

    // Test that OLEs inside a textbox are anchored as-char
    // Before the fix the 'style' attribute contained "position:absolute", making it to-char
    CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), sStyle.indexOf("position:absolute"));
}

CPPUNIT_TEST_FIXTURE(Test, testStylePaneFormatFilterRoundTrip)
{
    createSwDoc("style-pane-format-filter.docx");
    save(TestFilter::DOCX);

    xmlDocUniquePtr pXmlSettings = parseExport(u"word/settings.xml"_ustr);
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "val", u"3F01");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "allStyles", u"1");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "customStyles", u"0");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "latentStyles", u"0");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "stylesInUse", u"0");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "headingStyles", u"1");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "numberingStyles", u"0");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "tableStyles", u"0");
    assertXPath(pXmlSettings, "/w:settings/w:stylePaneFormatFilter", "visibleStyles", u"0");
}

} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
