diff options
Diffstat (limited to '')
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 1087 |
1 files changed, 1087 insertions, 0 deletions
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx new file mode 100644 index 000000000..11bbfa865 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -0,0 +1,1087 @@ +/* -*- 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 <queue> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/text/XFootnotesSupplier.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/text/TextGridMode.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontWeight.hpp> + +#include <comphelper/scopeguard.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <officecfg/Office/Common.hxx> +#include <o3tl/string_view.hxx> +#include <comphelper/propertyvalue.hxx> + +#include <unotxdoc.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> + +constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/extras/ooxmlexport/data/"; + +class Test : public SwModelTestBase +{ +public: + Test() : SwModelTestBase(DATA_DIRECTORY, "Office Open XML Text") {} + +protected: + /** + * Denylist handling + */ + bool mustTestImportOf(const char* filename) const override { + // If the testcase is stored in some other format, it's pointless to test. + return o3tl::ends_with(filename, ".docx"); + } +}; + +DECLARE_OOXMLEXPORT_TEST(testTdf135164_cancelledNumbering, "tdf135164_cancelledNumbering.docx") +{ + uno::Reference<beans::XPropertySet> xPara(getParagraph(1, u"TBMM DÖNEMİ"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty<OUString>(xPara, "ListLabelString")); + + xPara.set(getParagraph(2, "Numbering explicitly cancelled"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty<OUString>(xPara, "ListLabelString")); + + xPara.set(getParagraph(6, "Default style has roman numbering"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("i"), getProperty<OUString>(xPara, "ListLabelString")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf147861_customField, "tdf147861_customField.docx") +{ + // These should each be specific values, not a shared DocProperty + getParagraph(1, "CustomEditedTitle"); // edited + // A couple of nulls at the end of the string thwarted all attempts at an "equals" comparison. + CPPUNIT_ASSERT(getParagraph(2)->getString().startsWith(" INSERT Custom Title here")); + getParagraph(3, "My Title"); // edited + + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("CustomEditedTitle"), xField->getPresentation(false)); + // The " (fixed)" part is unnecessary, but it must be consistent across a round-trip + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Title (fixed)"), xField->getPresentation(true)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148380_createField, "tdf148380_createField.docx") +{ + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + // This should NOT be "Lorenzo Chavez", or a real date since the user hand-modified the result. + CPPUNIT_ASSERT_EQUAL(OUString("Myself - that's who"), xField->getPresentation(false)); + xField.set(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("yesterday at noon"), xField->getPresentation(false)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148380_fldLocked, "tdf148380_fldLocked.docx") +{ + getParagraph(2, "4/5/2022 4:29:00 PM"); + getParagraph(4, "1/23/4567 8:9:10 PM"); + + // Verify that these are fields, and not just plain text + // (import only, since export thankfully just dumps these fixed fields as plain text + if (mbExported) + return; + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + // This should NOT be updated at FILEOPEN to match the last modified time - it is locked. + CPPUNIT_ASSERT_EQUAL(OUString("4/5/2022 4:29:00 PM"), xField->getPresentation(false)); + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Modified (fixed)"), xField->getPresentation(true)); + xField.set(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1/23/4567 8:9:10 PM"), xField->getPresentation(false)); + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Last printed (fixed)"), xField->getPresentation(true)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148380_usernameField, "tdf148380_usernameField.docx") +{ + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + // These should match the as-last-seen-in-the-text name, and not the application's user name + CPPUNIT_ASSERT_EQUAL(OUString("Charlie Brown"), xField->getPresentation(false)); + xField.set(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("CB"), xField->getPresentation(false)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148380_modifiedField, "tdf148380_modifiedField.docx") +{ + getParagraph(2, "4/5/2022 3:29:00 PM"); // default (unspecified) date format + + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + // unspecified SAVEDATE gets default US formatting because style.xml has w:lang w:val="en-US" + CPPUNIT_ASSERT_EQUAL(OUString("4/5/2022 3:29:00 PM"), xField->getPresentation(false)); + xField.set(xFields->nextElement(), uno::UNO_QUERY); + // This was hand-modified and really should be Charlie Brown, not Charles ... + CPPUNIT_ASSERT_EQUAL(OUString("Charlie Brown"), xField->getPresentation(false)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148380_printField, "tdf148380_printField.docx") +{ + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + // unspecified SAVEDATE gets default GB formatting because style.xml has w:lang w:val="en-GB" + CPPUNIT_ASSERT_EQUAL(OUString("08/04/2022 07:10:00 AM"), xField->getPresentation(false)); + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Modified"), xField->getPresentation(true)); + xField.set(xFields->nextElement(), uno::UNO_QUERY); + // MS Word actually shows "8 o'clock-ish" until the document is reprinted, + // but it seems best to actually show the real last-printed date since it can't be FIXEDFLD + CPPUNIT_ASSERT_EQUAL(OUString("08/04/2022 06:47:00 AM"), xField->getPresentation(false)); + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Last printed"), xField->getPresentation(true)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf132475_printField, "tdf132475_printField.docx") +{ + // The last printed date field: formatted two different ways + getParagraph(2, "Thursday, March 17, 2022"); + getParagraph(3, "17-Mar-22"); + // Time zone affects the displayed time in MS Word. LO shows GMT time. Word only updated by F9 + getParagraph(5, "12:49"); + getParagraph(6, "12:49:00 PM"); + + // Verify that these are fields, and not just plain text + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Thursday, March 17, 2022"), xField->getPresentation(false)); + CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Last printed"), xField->getPresentation(true)); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf135906) +{ + loadAndReload("tdf135906.docx"); + // just test round-tripping. The document was exported as corrupt and didn't re-load. +} + +CPPUNIT_TEST_FIXTURE(Test, TestTdf146802) +{ + load(DATA_DIRECTORY, "tdf146802.docx"); + + // First check if the load failed, as before the fix. + CPPUNIT_ASSERT(mxComponent); + + // There is a group shape with text box inside having an embedded VML formula, + // check if something missing. + CPPUNIT_ASSERT_EQUAL_MESSAGE("Where is the formula?", 2, getShapes()); + // Before the fix the bugdoc failed to load or the formula was missing. +} + +CPPUNIT_TEST_FIXTURE(Test, testParaStyleNumLevel) +{ + loadAndSave("para-style-num-level.docx"); + xmlDocUniquePtr pXmlDoc = parseExport("word/styles.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // i.e. a custom list level in a para style was lost on import+export. + assertXPath(pXmlDoc, "/w:styles/w:style[@w:styleId='Mystyle']/w:pPr/w:numPr/w:ilvl", "val", "1"); +} + +CPPUNIT_TEST_FIXTURE(Test, testClearingBreak) +{ + // Given a document with a clearing break, when saving to DOCX: + loadAndSave("clearing-break.docx"); + + // Then make sure that the clearing break is not lost: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - XPath '/w:document/w:body/w:p/w:r/w:br' number of nodes is incorrect + // i.e. first the clearing break was turned into a plain break, then it was completely lost. + assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r/w:br", "clear", "all"); +} + +CPPUNIT_TEST_FIXTURE(Test, testContentControlExport) +{ + // Given a document with a content control around one or more text portions: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // XPath '//w:sdt/w:sdtPr/w:showingPlcHdr' number of nodes is incorrect + // i.e. the SDT elements were missing on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:showingPlcHdr", 1); + assertXPath(pXmlDoc, "//w:sdt/w:sdtContent", 1); +} + +CPPUNIT_TEST_FIXTURE(Test, testCheckboxContentControlExport) +{ + // Given a document with a checkbox content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, OUString(u"☐"), /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Checkbox", uno::Any(true)); + xContentControlProps->setPropertyValue("Checked", uno::Any(true)); + xContentControlProps->setPropertyValue("CheckedState", uno::Any(OUString(u"☒"))); + xContentControlProps->setPropertyValue("UncheckedState", uno::Any(OUString(u"☐"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//w:sdt/w:sdtPr/w14:checkbox/w14:checked' number of nodes is incorrect + // i.e. <w14:checkbox> and its child elements were lost. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w14:checkbox/w14:checked", "val", "1"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w14:checkbox/w14:checkedState", "val", "2612"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w14:checkbox/w14:uncheckedState", "val", "2610"); +} + +CPPUNIT_TEST_FIXTURE(Test, testDropdownContentControlExport) +{ + // Given a document with a dropdown content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "choose an item", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + { + uno::Sequence<beans::PropertyValues> aListItems = { + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("R"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("G"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("B"))), + }, + }; + xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems)); + } + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//w:sdt/w:sdtPr/w:dropDownList/w:listItem[1]' number of nodes is incorrect + // i.e. the list items were lost on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[1]", "displayText", "red"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[1]", "value", "R"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[2]", "displayText", "green"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[2]", "value", "G"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[3]", "displayText", "blue"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem[3]", "value", "B"); +} + +CPPUNIT_TEST_FIXTURE(Test, testPictureContentControlExport) +{ + // Given a document with a picture content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> xTextGraphic( + xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + xTextGraphic->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AS_CHARACTER)); + uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xTextContent, false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Picture", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // i.e. <w:picture> was lost on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:picture", 1); +} + +CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport) +{ + // Given a document with a date content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Date", uno::Any(true)); + xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("M/d/yyyy"))); + xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xContentControlProps->setPropertyValue("CurrentDate", uno::Any(OUString("2022-05-26T00:00:00Z"))); + xContentControlProps->setPropertyValue("PlaceholderDocPart", uno::Any(OUString("DefaultPlaceholder_-1854013437"))); + xContentControlProps->setPropertyValue("DataBindingPrefixMappings", uno::Any(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "))); + xContentControlProps->setPropertyValue("DataBindingXpath", uno::Any(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"))); + xContentControlProps->setPropertyValue("DataBindingStoreItemID", uno::Any(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"))); + xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//w:sdt/w:sdtPr/w:date/w:dateFormat' number of nodes is incorrect + // i.e. the <w:date> was lost on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date/w:dateFormat", "val", "M/d/yyyy"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date/w:lid", "val", "en-US"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", "fullDate", "2022-05-26T00:00:00Z"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:placeholder/w:docPart", "val", "DefaultPlaceholder_-1854013437"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "prefixMappings", "xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "xpath", "/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "storeItemID", "{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:color", "val", "008000"); +} + +CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder) +{ + // Given a document with a negative border distance: + createSwDoc(); + SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + SwDocShell* pDocShell = pTextDoc->GetDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->Insert("test"); + uno::Reference<beans::XPropertySet> xPageStyle(getStyles("PageStyles")->getByName("Standard"), + uno::UNO_QUERY); + xPageStyle->setPropertyValue("TopMargin", uno::Any(static_cast<sal_Int32>(501))); + table::BorderLine2 aBorder; + aBorder.LineWidth = 159; + aBorder.OuterLineWidth = 159; + xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder)); + sal_Int32 nTopBorderDistance = -646; + xPageStyle->setPropertyValue("TopBorderDistance", uno::Any(nTopBorderDistance)); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure that the page edge -> border space is correct: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + assertXPath(pXmlDoc, "//w:pgMar", "top", "284"); + assertXPath(pXmlDoc, "//w:pgBorders/w:top", "sz", "36"); + // Without the fix in place, this test would have failed with: + // - Expected: 28 + // - Actual : 0 + // i.e. editeng::BorderDistancesToWord() mis-handled negative border distances. + assertXPath(pXmlDoc, "//w:pgBorders/w:top", "space", "28"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf148494) +{ + loadAndSave("tdf148494.docx"); + + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + + // Without the fix in place, this test would have failed with + // - Expected: MACROBUTTON AllCaps Hello World + // - Actual : MACROBUTTONAllCaps Hello World + assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[3]/w:instrText", " MACROBUTTON AllCaps Hello World "); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf137466, "tdf137466.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + if (!pXmlDoc) + return; // initial import, no further checks + + // Ensure that we have <w:placeholder><w:docPart v:val="xxxx"/></w:placeholder> + OUString sDocPart = getXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt/w:sdtPr/w:placeholder/w:docPart", "val"); + CPPUNIT_ASSERT_EQUAL(OUString("DefaultPlaceholder_-1854013440"), sDocPart); + + // Ensure that we have <w15:color v:val="xxxx"/> + OUString sColor = getXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt/w:sdtPr/w15:color", "val"); + CPPUNIT_ASSERT_EQUAL(OUString("FF0000"), sColor); +} + +DECLARE_OOXMLEXPORT_TEST(testParaListRightIndent, "testParaListRightIndent.docx") +{ + CPPUNIT_ASSERT_EQUAL(1, getPages()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(2000), getProperty<sal_Int32>(getParagraph(1), "ParaRightMargin")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(5001), getProperty<sal_Int32>(getParagraph(2), "ParaRightMargin")); +} + +CPPUNIT_TEST_FIXTURE(Test, testDontAddNewStyles) +{ + // Given a document that lacks builtin styles, and addition of them is disabled: + { + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Load::DisableBuiltinStyles::set(true, pBatch); + pBatch->commit(); + } + comphelper::ScopeGuard g([] { + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Load::DisableBuiltinStyles::set(false, pBatch); + pBatch->commit(); + }); + + // When saving that document: + loadAndSave("dont-add-new-styles.docx"); + + // Then make sure that export doesn't have additional styles, Caption was one of them: + xmlDocUniquePtr pXmlDoc = parseExport("word/styles.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 + // - Actual : 1 + // i.e. builtin styles were added to the export result, even if we opted out. + assertXPath(pXmlDoc, "/w:styles/w:style[@w:styleId='Caption']", 0); +} + +DECLARE_OOXMLEXPORT_TEST(TestWPGZOrder, "testWPGZOrder.docx") +{ + // Check if the load failed. + CPPUNIT_ASSERT(mxComponent); + + // Get the WPG + uno::Reference<drawing::XShapes> xGroup(getShape(1), uno::UNO_QUERY_THROW); + uno::Reference<beans::XPropertySet> xGroupProperties(xGroup, uno::UNO_QUERY_THROW); + + // Initialize a queue for subgroups + std::queue<uno::Reference<drawing::XShapes>> xGroupList; + xGroupList.push(xGroup); + + // Every textbox shall be visible. + while (xGroupList.size()) + { + // Get the first group + xGroup = xGroupList.front(); + xGroupList.pop(); + for (sal_Int32 i = 0; i < xGroup->getCount(); ++i) + { + // Get the child shape + uno::Reference<beans::XPropertySet> xChildShapeProperties(xGroup->getByIndex(i), + uno::UNO_QUERY_THROW); + // Check for textbox + if (!xChildShapeProperties->getPropertyValue("TextBox").get<bool>()) + { + // Is this a Group Shape? Put it into the queue. + uno::Reference<drawing::XShapes> xInnerGroup(xGroup->getByIndex(i), uno::UNO_QUERY); + if (xInnerGroup) + xGroupList.push(xInnerGroup); + continue; + } + + // Get the textbox properties + uno::Reference<beans::XPropertySet> xTextBoxFrameProperties( + xChildShapeProperties->getPropertyValue("TextBoxContent"), uno::UNO_QUERY_THROW); + + // Assert that the textbox ZOrder greater than the groupshape + CPPUNIT_ASSERT_GREATER(xGroupProperties->getPropertyValue("ZOrder").get<long>(), + xTextBoxFrameProperties->getPropertyValue("ZOrder").get<long>()); + // Before the fix, this failed because that was less, and the textboxes were covered. + } + + } +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148720, "tdf148720.odt") +{ + const auto& pLayout = parseLayoutDump(); + + const OString sShapeXPaths[] = + { + OString("/root/page/body/txt/anchored/SwAnchoredDrawObject/SdrObjGroup/SdrObjList/SdrObject[1]"), + OString("/root/page/body/txt/anchored/SwAnchoredDrawObject/SdrObjGroup/SdrObjList/SdrObjGroup/SdrObjList/SdrObjGroup/SdrObjList/SdrObject[1]"), + OString("/root/page/body/txt/anchored/SwAnchoredDrawObject/SdrObjGroup/SdrObjList/SdrObjGroup/SdrObjList/SdrObjGroup/SdrObjList/SdrObject[2]"), + OString("/root/page/body/txt/anchored/SwAnchoredDrawObject/SdrObjGroup/SdrObjList/SdrObject[2]") + }; + + const OString sTextXPaths[] = + { + OString("/root/page/body/txt/anchored/fly[1]/infos/bounds"), + OString("/root/page/body/txt/anchored/fly[2]/infos/bounds"), + OString("/root/page/body/txt/anchored/fly[3]/infos/bounds"), + OString("/root/page/body/txt/anchored/fly[4]/infos/bounds") + }; + + const OString sAttribs[] = + { + OString("left"), + OString("top"), + OString("width"), + OString("height") + }; + + for (sal_Int32 i = 0; i < 4; ++i) + { + OUString aShapeVals[4]; + int aTextVals[4] = {0, 0, 0, 0}; + + const auto aOutRect = getXPath(pLayout, sShapeXPaths[i], "aOutRect"); + + sal_uInt16 nCommaPos[4] = {0, 0, 0, 0}; + nCommaPos[1] = aOutRect.indexOf(","); + nCommaPos[2] = aOutRect.indexOf(",", nCommaPos[1] + 1); + nCommaPos[3] = aOutRect.indexOf(",", nCommaPos[2] + 1); + + + aShapeVals[0] = aOutRect.copy(nCommaPos[0], nCommaPos[1] - nCommaPos[0]); + aShapeVals[1] = aOutRect.copy(nCommaPos[1] + 2, nCommaPos[2] - nCommaPos[1] - 2); + aShapeVals[2] = aOutRect.copy(nCommaPos[2] + 2, nCommaPos[3] - nCommaPos[2] - 2); + aShapeVals[3] = aOutRect.copy(nCommaPos[3] + 2, aOutRect.getLength() - nCommaPos[3] - 2); + + for (int ii = 0; ii < 4; ++ii) + { + aTextVals[ii] = getXPath(pLayout, sTextXPaths[i], sAttribs[ii]).toInt32(); + } + + tools::Rectangle ShapeArea(Point(aShapeVals[0].toInt32(), aShapeVals[1].toInt32()), Size(aShapeVals[2].toInt32() + 5, aShapeVals[3].toInt32() + 5)); + + tools::Rectangle TextArea(Point(aTextVals[0], aTextVals[1]), Size(aTextVals[2], aTextVals[3])); + + CPPUNIT_ASSERT(ShapeArea.Contains(TextArea)); + } +} + +DECLARE_OOXMLEXPORT_TEST(testTdf126287, "tdf126287.docx") +{ + CPPUNIT_ASSERT_EQUAL(2, getPages()); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf123642_BookmarkAtDocEnd, "tdf123642.docx") +{ + // get bookmark interface + uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xBookmarksByIdx(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xBookmarksByName = xBookmarksSupplier->getBookmarks(); + + // check: we have 1 bookmark (previously there were 0) + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xBookmarksByIdx->getCount()); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Bookmark1")); + + // and it is really in exported DOCX (let's ensure) + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + if (!pXmlDoc) + return; // initial import, no further checks + + CPPUNIT_ASSERT_EQUAL(OUString("Bookmark1"), getXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:bookmarkStart[1]", "name")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148361, "tdf148361.docx") +{ + // Refresh fields and ensure cross-reference to numbered para is okay + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); + + uno::Reference<text::XTextField> xTextField1(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("itadmin"), xTextField1->getPresentation(false)); + + OUString aActual = getParagraph(2)->getString(); + // This was "itadmin". + CPPUNIT_ASSERT_EQUAL(OUString("[Type text]"), aActual); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf142407, "tdf142407.docx") +{ + uno::Reference<container::XNameAccess> xPageStyles = getStyles("PageStyles"); + uno::Reference<beans::XPropertySet> xPageStyle(xPageStyles->getByName("Standard"), uno::UNO_QUERY); + sal_Int16 nGridLines; + xPageStyle->getPropertyValue("GridLines") >>= nGridLines; + CPPUNIT_ASSERT_EQUAL( sal_Int16(36), nGridLines); // was 23, left large space before text. +} + +DECLARE_OOXMLEXPORT_TEST(testWPGBodyPr, "WPGbodyPr.docx") +{ + // Is load successful? + CPPUNIT_ASSERT(mxComponent); + + // There are a WPG shape and a picture + CPPUNIT_ASSERT_EQUAL(2, getShapes()); + + // Get the WPG shape + uno::Reference<drawing::XShapes> xGroup(getShape(1), uno::UNO_QUERY); + // And the embed WPG + uno::Reference<drawing::XShapes> xEmbedGroup(xGroup->getByIndex(1), uno::UNO_QUERY); + + // Get the properties of the shapes + uno::Reference<beans::XPropertySet> xOuterShape(xGroup->getByIndex(0), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xMiddleShape(xEmbedGroup->getByIndex(0), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xInnerShape(xEmbedGroup->getByIndex(1), uno::UNO_QUERY); + + // Get the properties of the textboxes too + uno::Reference<beans::XPropertySet> xOuterTextBox( + xOuterShape->getPropertyValue("TextBoxContent"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xMiddleTextBox( + xMiddleShape->getPropertyValue("TextBoxContent"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xInnerTextBox( + xInnerShape->getPropertyValue("TextBoxContent"), uno::UNO_QUERY); + + // Check the alignments + CPPUNIT_ASSERT_EQUAL(css::drawing::TextVerticalAdjust::TextVerticalAdjust_TOP, + xOuterTextBox->getPropertyValue("TextVerticalAdjust") + .get<css::drawing::TextVerticalAdjust>()); + CPPUNIT_ASSERT_EQUAL(css::drawing::TextVerticalAdjust::TextVerticalAdjust_TOP, + xMiddleTextBox->getPropertyValue("TextVerticalAdjust") + .get<css::drawing::TextVerticalAdjust>()); + CPPUNIT_ASSERT_EQUAL(css::drawing::TextVerticalAdjust::TextVerticalAdjust_CENTER, + xInnerTextBox->getPropertyValue("TextVerticalAdjust") + .get<css::drawing::TextVerticalAdjust>()); + + // Check the inset margins, all were 0 before the fix + CPPUNIT_ASSERT_EQUAL(sal_Int32(499), + xInnerShape->getPropertyValue("TextLowerDistance").get<sal_Int32>()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(499), + xInnerShape->getPropertyValue("TextUpperDistance").get<sal_Int32>()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1000), + xInnerShape->getPropertyValue("TextLeftDistance").get<sal_Int32>()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(254), + xInnerShape->getPropertyValue("TextRightDistance").get<sal_Int32>()); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf146851_1, "tdf146851_1.docx") +{ + uno::Reference<beans::XPropertySet> xPara; + + xPara.set(getParagraph(1, "qwerty"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1."), getProperty<OUString>(xPara, "ListLabelString")); + + xPara.set(getParagraph(2, "asdfg"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1/"), getProperty<OUString>(xPara, "ListLabelString")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf146851_2, "tdf146851_2.docx") +{ + // Ensure numbering on second para + uno::Reference<beans::XPropertySet> xPara; + xPara.set(getParagraph(2, "."), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Schedule"), getProperty<OUString>(xPara, "ListLabelString")); + + // Refresh fields and ensure cross-reference to numbered para is okay + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + uno::Reference<util::XRefreshable>(xFieldsAccess, uno::UNO_QUERY_THROW)->refresh(); + + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); + uno::Reference<text::XTextField> xTextField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Schedule"), xTextField->getPresentation(false)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148052, "tdf148052.docx") +{ + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); + + uno::Reference<text::XTextField> xTextField(xFields->nextElement(), uno::UNO_QUERY); + + // Without the fix in place, this test would have failed with + // - Expected: 14. Aug 18 + // - Actual : 11. Apr 22 + CPPUNIT_ASSERT_EQUAL(OUString("14. Aug 18"), xTextField->getPresentation(false)); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148111, "tdf148111.docx") +{ + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + std::vector<OUString> aExpectedValues = { + // These field values are NOT in order in document: getTextFields did provide + // fields in a strange but fixed order + "Title", "Placeholder", "Placeholder", "Placeholder", + "Placeholder", "Placeholder", "Placeholder", "Placeholder", + "Placeholder", "Placeholder", "Placeholder", "Placeholder", + "Placeholder", "Placeholder", "Placeholder", "Placeholder", + "Placeholder", "Title", "Title", "Title", + "Title", "Title", "Title", "Title" + }; + + sal_uInt16 nIndex = 0; + while (xFields->hasMoreElements()) + { + uno::Reference<text::XTextField> xTextField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(aExpectedValues[nIndex++], xTextField->getPresentation(false)); + } + + // No more fields + CPPUNIT_ASSERT(!xFields->hasMoreElements()); +} + +DECLARE_OOXMLEXPORT_TEST(TestTdf73499, "tdf73499.docx") +{ + // Ensure, the bugdoc is opened + CPPUNIT_ASSERT(mxComponent); + // Get the groupshape + uno::Reference<drawing::XShapes> xGroup(getShape(1), uno::UNO_QUERY_THROW); + + // Get the textboxes of the groupshape + uno::Reference<text::XText> xTextBox1(xGroup->getByIndex(0), uno::UNO_QUERY_THROW); + uno::Reference<text::XText> xTextBox2(xGroup->getByIndex(1), uno::UNO_QUERY_THROW); + + // Get the properties of the textboxes + uno::Reference<beans::XPropertySet> xTextBox1Properties(xTextBox1, uno::UNO_QUERY_THROW); + uno::Reference<beans::XPropertySet> xTextBox2Properties(xTextBox2, uno::UNO_QUERY_THROW); + + // Get the name of the textboxes + uno::Reference<container::XNamed> xTextBox1Name(xTextBox1, uno::UNO_QUERY_THROW); + uno::Reference<container::XNamed> xTextBox2Name(xTextBox2, uno::UNO_QUERY_THROW); + + // Check for the links, before the fix that were missing + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Link name missing!", xTextBox2Name->getName(), + xTextBox1Properties->getPropertyValue("ChainNextName").get<OUString>()); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Link name missing!", xTextBox1Name->getName(), + xTextBox2Properties->getPropertyValue("ChainPrevName").get<OUString>()); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf81507, "tdf81507.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + if (!pXmlDoc) + return; // initial import, no further checks + + // Ensure that we have <w:text w:multiLine="1"/> + CPPUNIT_ASSERT_EQUAL(OUString("1"), getXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:sdt/w:sdtPr/w:text", "multiLine")); + + // Ensure that we have <w:text w:multiLine="0"/> + CPPUNIT_ASSERT_EQUAL(OUString("0"), getXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:sdt/w:sdtPr/w:text", "multiLine")); + + // Ensure that we have <w:text/> + getXPath(pXmlDoc, "/w:document/w:body/w:p[3]/w:sdt/w:sdtPr/w:text", ""); + + // Ensure that we have no <w:text/> (not quite correct case, but to ensure import/export are okay) + xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "/w:document/w:body/w:p[4]/w:sdt/w:sdtPr/w:text"); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), + static_cast<sal_Int32>(xmlXPathNodeSetGetLength(pXmlObj->nodesetval))); + xmlXPathFreeObject(pXmlObj); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf139948, "tdf139948.docx") +{ + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(1, "No border"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(2, "Border below"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(3, "Borders below and above"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(4, "Border above"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(5, "No border"), "TopBorder").LineWidth); + + + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(1), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(2), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(3), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(4), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(5), "BottomBorder").LineWidth); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf144563, "tdf144563.docx") +{ + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + // Refresh all cross-reference fields + uno::Reference<util::XRefreshable>(xFieldsAccess, uno::UNO_QUERY_THROW)->refresh(); + + // Verify values + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + + std::vector<OUString> aExpectedValues = { + // These field values are NOT in order in document: getTextFields did provide + // fields in a strange but fixed order + "1", "1", "1", "1", "1/", "1/", "1/", "1)", "1)", "1)", "1.)", + "1.)", "1.)", "1..", "1..", "1..", "1.", "1.", "1.", "1", "1" + }; + + sal_uInt16 nIndex = 0; + while (xFields->hasMoreElements()) + { + uno::Reference<text::XTextField> xTextField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(aExpectedValues[nIndex++], xTextField->getPresentation(false)); + } +} + +// broken test document? +#if !defined(_WIN32) +DECLARE_OOXMLEXPORT_TEST(testTdf146955, "tdf146955.odt") +{ + // import of a (broken?) DOCX export with dozens of frames raised a SAX exception, + // when the code tried to access to a non-existent footnote + uno::Reference<text::XFootnotesSupplier> xNotes(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xNotes->getFootnotes()->getCount()); +} +#endif + +DECLARE_OOXMLEXPORT_TEST(testTdf144668, "tdf144668.odt") +{ + uno::Reference<beans::XPropertySet> xPara1(getParagraph(1, u"level1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("[0001]"), getProperty<OUString>(xPara1, "ListLabelString")); + + uno::Reference<beans::XPropertySet> xPara2(getParagraph(2, u"level2"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("[001]"), getProperty<OUString>(xPara2, "ListLabelString")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148455_1, "tdf148455_1.docx") +{ + uno::Reference<beans::XPropertySet> xPara2(getParagraph(3, u"1.1.1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1.1.1."), getProperty<OUString>(xPara2, "ListLabelString")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148455_2, "tdf148455_2.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + if (!pXmlDoc) + return; // initial import, no further checks + + // Find list id for restarted list + sal_Int32 nListId = getXPath(pXmlDoc, "/w:document/w:body/w:p[3]/w:pPr/w:numPr/w:numId", "val").toInt32(); + + xmlDocUniquePtr pNumberingDoc = parseExport("word/numbering.xml"); + + // Ensure we have empty lvlOverride for levels 0 - 1 + getXPath(pNumberingDoc, "/w:numbering/w:num[@w:numId='" + OString::number(nListId) +"']/w:lvlOverride[@w:ilvl='0']", ""); + getXPath(pNumberingDoc, "/w:numbering/w:num[@w:numId='" + OString::number(nListId) +"']/w:lvlOverride[@w:ilvl='1']", ""); + // And normal override for level 2 + getXPath(pNumberingDoc, "/w:numbering/w:num[@w:numId='" + OString::number(nListId) +"']/w:lvlOverride[@w:ilvl='2']/w:startOverride", "val"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978enhancedPathABVW) +{ + load(DATA_DIRECTORY, "tdf147978_enhancedPath_commandABVW.odt"); + CPPUNIT_ASSERT(mxComponent); + save("Office Open XML Text", maTempFile); + mxComponent->dispose(); + mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument"); + // Make sure the new implemented export for commands A,B,V and W use the correct arc between + // the given two points, here the short one. + for (sal_Int16 i = 1 ; i <= 4; ++i) + { + uno::Reference<drawing::XShape> xShape = getShape(i); + CPPUNIT_ASSERT_EQUAL(sal_Int32(506), getProperty<awt::Rectangle>(xShape, "BoundRect").Height); + } +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148132, "tdf148132.docx") +{ + { + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); + auto xLevels = getProperty< uno::Reference<container::XIndexAccess> >(xParagraph, "NumberingRules"); + // Get level 2 char style + comphelper::SequenceAsHashMap levelProps(xLevels->getByIndex(1)); + OUString aCharStyleName = levelProps["CharStyleName"].get<OUString>(); + // Ensure that numbering in this paragraph is 24pt bold italic + // Previously it got overridden by paragraph properties and became 6pt, no bold, no italic + uno::Reference<beans::XPropertySet> xStyle(getStyles("CharacterStyles")->getByName(aCharStyleName), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(24.f, getProperty<float>(xStyle, "CharHeight")); + CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD, getProperty<float>(xStyle, "CharWeight")); + CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC, getProperty<awt::FontSlant>(xStyle, "CharPosture")); + } + // And do the same for second paragraph. Numbering should be identical + { + uno::Reference<text::XTextRange> xParagraph = getParagraph(2); + auto xLevels = getProperty< uno::Reference<container::XIndexAccess> >(xParagraph, "NumberingRules"); + comphelper::SequenceAsHashMap levelProps(xLevels->getByIndex(1)); + OUString aCharStyleName = levelProps["CharStyleName"].get<OUString>(); + + uno::Reference<beans::XPropertySet> xStyle(getStyles("CharacterStyles")->getByName(aCharStyleName), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(24.f, getProperty<float>(xStyle, "CharHeight")); + CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD, getProperty<float>(xStyle, "CharWeight")); + CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC, getProperty<awt::FontSlant>(xStyle, "CharPosture")); + } +} + +DECLARE_OOXMLEXPORT_TEST(testTdf154481, "tdf154481.docx") +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("Missing pages!", 7, getPages()); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf149200) +{ + loadAndSave("tdf149200.docx"); + CPPUNIT_ASSERT_EQUAL(1, getPages()); + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + + // Ensure there is no unexpected invalid structure <w14:textFill> + // There is just one run property + xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "count(/w:document/w:body/w:p[1]/w:r[1]/w:rPr/*)"); + CPPUNIT_ASSERT(pXmlObj); + CPPUNIT_ASSERT_EQUAL(double(1), pXmlObj->floatval); + // And it is a color definition with themeColor + CPPUNIT_ASSERT_EQUAL(OUString("dark1"), getXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r[1]/w:rPr/w:color", "themeColor")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf149313, "tdf149313.docx") +{ + // only 2, but not 3 pages in document + CPPUNIT_ASSERT_EQUAL(2, getPages()); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // And ensure that pages are with correct sections (have correct dimensions) + CPPUNIT_ASSERT_EQUAL(sal_Int32(4989), getXPath(pXmlDoc, "/root/page[1]/infos/bounds", "height").toInt32()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4989), getXPath(pXmlDoc, "/root/page[1]/infos/bounds", "width").toInt32()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4989), getXPath(pXmlDoc, "/root/page[2]/infos/bounds", "height").toInt32()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8000), getXPath(pXmlDoc, "/root/page[2]/infos/bounds", "width").toInt32()); +} + +#include <docsh.hxx> +#include <unotxdoc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> + +DECLARE_OOXMLEXPORT_TEST(testTdf148360, "tdf148360.docx") +{ + const auto& pLayout = parseLayoutDump(); + + // Ensure first element is a tab + assertXPath(pLayout, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::TabLeft"); + // and only then goes content + assertXPath(pLayout, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text"); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf135923, "tdf135923-min.docx") +{ + uno::Reference<text::XText> xShape(getShape(1), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(1, xShape); + + CPPUNIT_ASSERT_EQUAL(COL_WHITE, getProperty<Color>(getRun(xParagraph, 1), "CharColor")); + CPPUNIT_ASSERT_EQUAL(COL_BLACK, getProperty<Color>(getRun(xParagraph, 2), "CharColor")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf148273_sectionBulletFormatLeak, "tdf148273_sectionBulletFormatLeak.docx") +{ + // get a paragraph with bullet point after section break + uno::Reference<text::XTextRange> xParagraph = getParagraph(4); + uno::Reference<beans::XPropertySet> xProps(xParagraph, uno::UNO_QUERY); + + // Make sure that the bullet has no ListAutoFormat inherited from + // the empty paragraph before the section break + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 + // - Actual : 1 + // i.e. empty paragraph formats from the first section leaked to the bullet's formatting + uno::Any aValue = xProps->getPropertyValue("ListAutoFormat"); + CPPUNIT_ASSERT_EQUAL(false, aValue.hasValue()); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf149089, "tdf149089.docx") +{ + uno::Reference<container::XNameAccess> xPageStyles = getStyles("PageStyles"); + uno::Reference<beans::XPropertySet> xPageStyle(xPageStyles->getByName("Standard"), uno::UNO_QUERY); + sal_Int16 nGridMode; + xPageStyle->getPropertyValue("GridMode") >>= nGridMode; + CPPUNIT_ASSERT_EQUAL( sal_Int16(text::TextGridMode::LINES), nGridMode); // was LINES_AND_CHARS +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf139128) +{ + loadAndReload("tdf139128.odt"); + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + CPPUNIT_ASSERT(pXmlDoc); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 0 + // i.e. the line break was lost on export. + assertXPath(pXmlDoc, "//w:br", 2); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |