summaryrefslogtreecommitdiffstats
path: root/sw/qa/extras/uiwriter/uiwriter6.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/qa/extras/uiwriter/uiwriter6.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/qa/extras/uiwriter/uiwriter6.cxx')
-rw-r--r--sw/qa/extras/uiwriter/uiwriter6.cxx3037
1 files changed, 3037 insertions, 0 deletions
diff --git a/sw/qa/extras/uiwriter/uiwriter6.cxx b/sw/qa/extras/uiwriter/uiwriter6.cxx
new file mode 100644
index 0000000000..5b4140e029
--- /dev/null
+++ b/sw/qa/extras/uiwriter/uiwriter6.cxx
@@ -0,0 +1,3037 @@
+/* -*- 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 <com/sun/star/drawing/FillStyle.hpp>
+#include <swmodeltestbase.hxx>
+#include <cntfrm.hxx>
+#include <itabenum.hxx>
+#include <ndtxt.hxx>
+#include <wrtsh.hxx>
+#include <drawdoc.hxx>
+#include <view.hxx>
+#include <com/sun/star/text/XTextColumns.hpp>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+#include <svl/itemiter.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
+#include <dbfld.hxx>
+#include <txatbase.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <UndoManager.hxx>
+#include <unotools/syslocaleoptions.hxx>
+
+#include <svl/stritem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <cmdid.h>
+#include <tools/json_writer.hxx>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
+#include <o3tl/cppunittraitshelper.hxx>
+#include <swdtflvr.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <editeng/swafopt.hxx>
+#include <editeng/unolingu.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <vcl/scheduler.hxx>
+#include <config_fonts.h>
+#include <test/htmltesttools.hxx>
+#include <wrthtml.hxx>
+#include <dbmgr.hxx>
+#include <rootfrm.hxx>
+#include <unotxdoc.hxx>
+#include <wrong.hxx>
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+#include <com/sun/star/linguistic2/XLinguProperties.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+#include <linguistic/misc.hxx>
+
+#include <workctrl.hxx>
+
+using namespace osl;
+using namespace com::sun::star;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::linguistic2;
+using namespace linguistic;
+
+namespace
+{
+sal_Int32 lcl_getAttributeIDFromHints(const SwpHints& hints)
+{
+ for (size_t i = 0; i < hints.Count(); ++i)
+ {
+ const SwTextAttr* hint = hints.Get(i);
+ if (hint->Which() == RES_TXTATR_AUTOFMT)
+ {
+ const SwFormatAutoFormat& rFmt = hint->GetAutoFormat();
+ SfxItemIter aIter(*rFmt.GetStyleHandle());
+ return aIter.GetCurItem()->Which();
+ }
+ }
+ return -1;
+}
+
+uno::Reference<XLinguServiceManager2> GetLngSvcMgr_Impl()
+{
+ uno::Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<XLinguServiceManager2> xRes = LinguServiceManager::create(xContext);
+ return xRes;
+}
+} //namespace
+
+class SwUiWriterTest6 : public SwModelTestBase, public HtmlTestTools
+{
+public:
+ SwUiWriterTest6()
+ : SwModelTestBase("/sw/qa/extras/uiwriter/data/")
+ {
+ }
+};
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf116640)
+{
+ createSwDoc();
+
+ uno::Sequence<beans::PropertyValue> aArgs(
+ comphelper::InitPropertySequence({ { "Columns", uno::Any(sal_Int32(2)) } }));
+
+ dispatchCommand(mxComponent, ".uno:InsertSection", aArgs);
+
+ uno::Reference<text::XTextSectionsSupplier> xTextSectionsSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xSections(xTextSectionsSupplier->getTextSections(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xTextSection(xSections->getByIndex(0), uno::UNO_QUERY);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+
+ uno::Reference<text::XTextColumns> xTextColumns
+ = getProperty<uno::Reference<text::XTextColumns>>(xTextSection, "TextColumns");
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(2), xTextColumns->getColumnCount());
+
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount());
+
+ dispatchCommand(mxComponent, ".uno:Redo", {});
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf108524)
+{
+ createSwDoc("tdf108524.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // In total we expect two cells containing a section.
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/section"_ostr, 2);
+
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/section"_ostr, 1);
+ // This was 0, section wasn't split, instead it was only on the first page
+ // and it was cut off.
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab/row/cell/section"_ostr, 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testLinesInSectionInTable)
+{
+ // This is similar to testTdf108524(), but the page boundary now is not in
+ // the middle of a multi-line paragraph: the section only contains oneliner
+ // paragraphs instead.
+ createSwDoc("lines-in-section-in-table.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // In total we expect two cells containing a section.
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/section"_ostr, 2);
+
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/section"_ostr, 1);
+ // This was 0, section wasn't split, instead it was only on the first page
+ // and it was cut off.
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab/row/cell/section"_ostr, 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testLinesMoveBackwardsInSectionInTable)
+{
+#if HAVE_MORE_FONTS
+ // Assert that paragraph "4" is on page 1 and "5" is on page 2.
+ createSwDoc("lines-in-section-in-table.odt");
+ SwDoc* pDoc = getSwDoc();
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page"_ostr, 2);
+ SwNodeOffset nPara4Node(getXPath(pXmlDoc,
+ "/root/page[1]/body/tab/row/cell[1]/section/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("4"), pDoc->GetNodes()[nPara4Node]->GetTextNode()->GetText());
+ SwNodeOffset nPara5Node(getXPath(pXmlDoc,
+ "/root/page[2]/body/tab/row/cell[1]/section/txt[1]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("5"), pDoc->GetNodes()[nPara5Node]->GetTextNode()->GetText());
+
+ // Remove paragraph "4".
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ while (pWrtShell->GetCursor()->GetPointNode().GetIndex() < nPara4Node)
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->EndPara();
+ pWrtShell->Up(/*bSelect=*/true);
+ pWrtShell->DelLeft();
+
+ // Assert that paragraph "5" is now moved back to page 1 and is the last paragraph there.
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ SwNodeOffset nPage1LastNode(
+ getXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell[1]/section/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ // This was "3", paragraph "4" was deleted, but "5" was not moved backwards from page 2.
+ CPPUNIT_ASSERT_EQUAL(OUString("5"), pDoc->GetNodes()[nPage1LastNode]->GetTextNode()->GetText());
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTableInSection)
+{
+#if HAVE_MORE_FONTS
+ // The document has a section, containing a table that spans over 2 pages.
+ createSwDoc("table-in-sect.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // In total we expect 4 cells.
+ assertXPath(pXmlDoc, "/root/page/body/section/tab/row/cell"_ostr, 4);
+
+ // Assert that on both pages the section contains 2 cells.
+ assertXPath(pXmlDoc, "/root/page[1]/body/section/tab/row/cell"_ostr, 2);
+ assertXPath(pXmlDoc, "/root/page[2]/body/section/tab/row/cell"_ostr, 2);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTableInNestedSection)
+{
+#if HAVE_MORE_FONTS
+ // The document has a nested section, containing a table that spans over 2 pages.
+ // This crashed the layout.
+ createSwDoc("rhbz739252-3.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Make sure the table is inside a section and spans over 2 pages.
+ assertXPath(pXmlDoc, "//page[1]//section/tab"_ostr, 1);
+ assertXPath(pXmlDoc, "//page[2]//section/tab"_ostr, 1);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf112741)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("tdf112741.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 5 pages.
+ assertXPath(pXmlDoc, "//page"_ostr, 4);
+ assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/row/cell/section"_ostr, 1);
+ assertXPath(pXmlDoc, "//page[2]/body/tab/row/cell/tab/row/cell/section"_ostr, 1);
+ // This failed, 3rd page contained no sections.
+ assertXPath(pXmlDoc, "//page[3]/body/tab/row/cell/tab/row/cell/section"_ostr, 1);
+ assertXPath(pXmlDoc, "//page[4]/body/tab/row/cell/tab/row/cell/section"_ostr, 1);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf112860)
+{
+#if HAVE_MORE_FONTS
+ // The document has a split section inside a nested table, and also a table
+ // in the footer.
+ // This crashed the layout.
+ createSwDoc("tdf112860.fodt");
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf113287)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("tdf113287.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//page"_ostr, 2);
+ sal_uInt32 nCellTop
+ = getXPath(pXmlDoc, "//page[2]/body/tab/row/cell[1]/infos/bounds"_ostr, "top"_ostr)
+ .toUInt32();
+ sal_uInt32 nSectionTop
+ = getXPath(pXmlDoc, "//page[2]/body/tab/row/cell[1]/section/infos/bounds"_ostr, "top"_ostr)
+ .toUInt32();
+ // Make sure section frame is inside the cell frame.
+ // Expected greater than 4593, was only 3714.
+ CPPUNIT_ASSERT_GREATER(nCellTop, nSectionTop);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf113445)
+{
+#if HAVE_MORE_FONTS
+ // Force multiple-page view.
+ createSwDoc("tdf113445.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwView* pView = pDocShell->GetView();
+ pView->SetViewLayout(/*nColumns=*/2, /*bBookMode=*/false);
+ calcLayout();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//page"_ostr, 2);
+ sal_uInt32 nPage1Left
+ = getXPath(pXmlDoc, "//page[1]/infos/bounds"_ostr, "left"_ostr).toUInt32();
+ sal_uInt32 nPage2Left
+ = getXPath(pXmlDoc, "//page[2]/infos/bounds"_ostr, "left"_ostr).toUInt32();
+ // Make sure that page 2 is on the right hand side of page 1, not below it.
+ CPPUNIT_ASSERT_GREATER(nPage1Left, nPage2Left);
+
+ // Insert a new paragraph at the start of the document.
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->StartOfSection();
+ pWrtShell->SplitNode();
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+
+ // Make sure that Table2:C5 and Table2:D5 has its section frame inside the cell frame.
+ sal_uInt32 nCell3Top
+ = getXPath(pXmlDoc, "//page[2]/body/tab/row/cell/tab/row[4]/cell[3]/infos/bounds"_ostr,
+ "top"_ostr)
+ .toUInt32();
+ sal_uInt32 nSection3Top
+ = getXPath(pXmlDoc,
+ "//page[2]/body/tab/row/cell/tab/row[4]/cell[3]/section/infos/bounds"_ostr,
+ "top"_ostr)
+ .toUInt32();
+ CPPUNIT_ASSERT_GREATER(nCell3Top, nSection3Top);
+ sal_uInt32 nCell4Top
+ = getXPath(pXmlDoc, "//page[2]/body/tab/row/cell/tab/row[4]/cell[4]/infos/bounds"_ostr,
+ "top"_ostr)
+ .toUInt32();
+ sal_uInt32 nSection4Top
+ = getXPath(pXmlDoc,
+ "//page[2]/body/tab/row/cell/tab/row[4]/cell[4]/section/infos/bounds"_ostr,
+ "top"_ostr)
+ .toUInt32();
+ CPPUNIT_ASSERT_GREATER(nCell4Top, nSection4Top);
+ // Also check if the two cells in the same row have the same top position.
+ // This was 4818, expected only 1672.
+ CPPUNIT_ASSERT_EQUAL(nCell3Top, nCell4Top);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf113686)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("tdf113686.fodt");
+ SwDoc* pDoc = getSwDoc();
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page"_ostr, 2);
+ SwNodeOffset nPage1LastNode(
+ getXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell[1]/tab/row/cell[1]/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("Table2:A1-P10"),
+ pDoc->GetNodes()[nPage1LastNode]->GetTextNode()->GetText());
+ SwNodeOffset nPage2FirstNode(getXPath(pXmlDoc,
+ "/root/page[2]/body/tab/row/cell[1]/section/txt[1]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("Table1:A1"),
+ pDoc->GetNodes()[nPage2FirstNode]->GetTextNode()->GetText());
+
+ // Remove page 2.
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ while (pWrtShell->GetCursor()->Start()->GetNodeIndex() < nPage1LastNode)
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->EndPara();
+ for (int i = 0; i < 3; ++i)
+ pWrtShell->Up(/*bSelect=*/true);
+ pWrtShell->DelLeft();
+
+ // Assert that the second page is removed.
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ // This was still 2, content from 2nd page was not moved.
+ assertXPath(pXmlDoc, "/root/page"_ostr, 1);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTableInSectionInTable)
+{
+#if HAVE_MORE_FONTS
+ // The document has a table, containing a section, containing a nested
+ // table.
+ // This crashed the layout.
+ createSwDoc("i95698.odt");
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testSectionInTableInTable)
+{
+#if HAVE_MORE_FONTS
+ // The document has a nested table, containing a multi-line section at a
+ // page boundary.
+ // This crashed the layout later in SwFrame::IsFootnoteAllowed().
+ createSwDoc("tdf112109.fodt");
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testSectionInTableInTable2)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("split-section-in-nested-table.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ sal_uInt32 nSection1
+ = getXPath(pXmlDoc, "//page[1]//body/tab/row/cell/tab/row/cell/section"_ostr, "id"_ostr)
+ .toUInt32();
+ sal_uInt32 nSection1Follow
+ = getXPath(pXmlDoc, "//page[1]//body/tab/row/cell/tab/row/cell/section"_ostr, "follow"_ostr)
+ .toUInt32();
+ // This failed, the section wasn't split inside a nested table.
+ sal_uInt32 nSection2
+ = getXPath(pXmlDoc, "//page[2]//body/tab/row/cell/tab/row/cell/section"_ostr, "id"_ostr)
+ .toUInt32();
+ sal_uInt32 nSection2Precede
+ = getXPath(pXmlDoc, "//page[2]//body/tab/row/cell/tab/row/cell/section"_ostr,
+ "precede"_ostr)
+ .toUInt32();
+
+ // Make sure that the first's follow and the second's precede is correct.
+ CPPUNIT_ASSERT_EQUAL(nSection2, nSection1Follow);
+ CPPUNIT_ASSERT_EQUAL(nSection1, nSection2Precede);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testSectionInTableInTable3)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("tdf113153.fodt");
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xTable(xTables->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("Table16"), xTable->getName());
+
+ uno::Reference<text::XTextTable> xRowSupplier(xTable, uno::UNO_QUERY);
+ uno::Reference<table::XTableRows> xRows = xRowSupplier->getRows();
+ uno::Reference<beans::XPropertySet> xRow(xRows->getByIndex(1), uno::UNO_QUERY);
+ xRow->setPropertyValue("IsSplitAllowed", uno::Any(true));
+ // This never returned.
+ calcLayout();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ sal_uInt32 nTable1 = getXPath(pXmlDoc, "//page[1]//body/tab"_ostr, "id"_ostr).toUInt32();
+ sal_uInt32 nTable1Follow
+ = getXPath(pXmlDoc, "//page[1]//body/tab"_ostr, "follow"_ostr).toUInt32();
+ sal_uInt32 nTable2 = getXPath(pXmlDoc, "//page[2]//body/tab"_ostr, "id"_ostr).toUInt32();
+ sal_uInt32 nTable2Precede
+ = getXPath(pXmlDoc, "//page[2]//body/tab"_ostr, "precede"_ostr).toUInt32();
+ sal_uInt32 nTable2Follow
+ = getXPath(pXmlDoc, "//page[2]//body/tab"_ostr, "follow"_ostr).toUInt32();
+ sal_uInt32 nTable3 = getXPath(pXmlDoc, "//page[3]//body/tab"_ostr, "id"_ostr).toUInt32();
+ sal_uInt32 nTable3Precede
+ = getXPath(pXmlDoc, "//page[3]//body/tab"_ostr, "precede"_ostr).toUInt32();
+
+ // Make sure the outer table frames are linked together properly.
+ CPPUNIT_ASSERT_EQUAL(nTable2, nTable1Follow);
+ CPPUNIT_ASSERT_EQUAL(nTable1, nTable2Precede);
+ CPPUNIT_ASSERT_EQUAL(nTable3, nTable2Follow);
+ CPPUNIT_ASSERT_EQUAL(nTable2, nTable3Precede);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testSectionInTableInTable4)
+{
+#if HAVE_MORE_FONTS
+ createSwDoc("tdf113520.fodt");
+ SwDoc* pDoc = getSwDoc();
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page"_ostr, 3);
+ SwNodeOffset nPage1LastNode(
+ getXPath(pXmlDoc,
+ "/root/page[1]/body/tab/row/cell[1]/tab/row/cell[1]/section/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("Section1:P10"),
+ pDoc->GetNodes()[nPage1LastNode]->GetTextNode()->GetText());
+ SwNodeOffset nPage3FirstNode(
+ getXPath(pXmlDoc, "/root/page[3]/body/tab/row/cell[1]/tab/row/cell[1]/section/txt[1]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("Section1:P23"),
+ pDoc->GetNodes()[nPage3FirstNode]->GetTextNode()->GetText());
+
+ // Remove page 2.
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ while (pWrtShell->GetCursor()->Start()->GetNodeIndex() < nPage1LastNode)
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->EndPara();
+ while (pWrtShell->GetCursor()->End()->GetNodeIndex() < nPage3FirstNode)
+ pWrtShell->Down(/*bSelect=*/true);
+ pWrtShell->EndPara(/*bSelect=*/true);
+ pWrtShell->DelLeft();
+
+ // Assert that the page is removed.
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ // This was 3, page 2 was emptied, but it wasn't removed.
+ assertXPath(pXmlDoc, "/root/page"_ostr, 2);
+
+ // Make sure the outer table frames are linked together properly.
+ sal_uInt32 nTable1 = getXPath(pXmlDoc, "//page[1]//body/tab"_ostr, "id"_ostr).toUInt32();
+ sal_uInt32 nTable1Follow
+ = getXPath(pXmlDoc, "//page[1]//body/tab"_ostr, "follow"_ostr).toUInt32();
+ sal_uInt32 nTable2 = getXPath(pXmlDoc, "//page[2]//body/tab"_ostr, "id"_ostr).toUInt32();
+ sal_uInt32 nTable2Precede
+ = getXPath(pXmlDoc, "//page[2]//body/tab"_ostr, "precede"_ostr).toUInt32();
+ CPPUNIT_ASSERT_EQUAL(nTable2, nTable1Follow);
+ CPPUNIT_ASSERT_EQUAL(nTable1, nTable2Precede);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf112160)
+{
+#if HAVE_MORE_FONTS
+ // Assert that the A2 cell is on page 1.
+ createSwDoc("tdf112160.fodt");
+ SwDoc* pDoc = getSwDoc();
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ SwNodeOffset nA2CellNode(
+ getXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/section/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32());
+ CPPUNIT_ASSERT_EQUAL(OUString("Table1.A2"),
+ pDoc->GetNodes()[nA2CellNode]->GetTextNode()->GetText());
+
+ // Append a new paragraph to the end of the A2 cell.
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ while (pWrtShell->GetCursor()->GetPointNode().GetIndex() < nA2CellNode)
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->EndPara();
+ pWrtShell->SplitNode();
+
+ // Assert that after A2 got extended, D2 stays on page 1.
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ sal_uInt32 nD2CellNode
+ = getXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[last()]/section/txt[last()]"_ostr,
+ "txtNodeIndex"_ostr)
+ .toUInt32();
+ // This was Table1.C2, Table1.D2 was moved to the next page, unexpected.
+ CPPUNIT_ASSERT_EQUAL(OUString("Table1.D2"),
+ pDoc->GetNodes()[SwNodeOffset(nD2CellNode)]->GetTextNode()->GetText());
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf114536)
+{
+ // This crashed in SwTextFormatter::MergeCharacterBorder() due to a
+ // use after free.
+ createSwDoc("tdf114536.odt");
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testParagraphOfTextRange)
+{
+ createSwDoc("paragraph-of-text-range.odt");
+ SwDoc* pDoc = getSwDoc();
+
+ // Enter the table.
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Down(/*bSelect=*/false);
+ CPPUNIT_ASSERT(pWrtShell->IsCursorInTable());
+ // Enter the section.
+ pWrtShell->Down(/*bSelect=*/false);
+ CPPUNIT_ASSERT(pWrtShell->IsDirectlyInSection());
+
+ // Assert that we get the right paragraph object.
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextViewCursorSupplier> xController(xModel->getCurrentController(),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextRange> xViewCursor = xController->getViewCursor();
+ // This failed as there were no TextParagraph property.
+ auto xParagraph
+ = getProperty<uno::Reference<text::XTextRange>>(xViewCursor->getStart(), "TextParagraph");
+ CPPUNIT_ASSERT_EQUAL(OUString("In section"), xParagraph->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf99689TableOfContents)
+{
+ createSwDoc("tdf99689.odt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoNextTOXBase();
+ const SwTOXBase* pTOXBase = pWrtShell->GetCurTOX();
+ pWrtShell->UpdateTableOf(*pTOXBase);
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pTitleNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+ SwNodeIndex aIdx(*pTitleNode);
+ // skip the title
+ pDoc->GetNodes().GoNext(&aIdx);
+
+ // skip the first header. No attributes there.
+ // next node should contain superscript
+ SwTextNode* pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ sal_uInt16 nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+
+ // next node should contain subscript
+ pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf99689TableOfFigures)
+{
+ createSwDoc("tdf99689_figures.odt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoNextTOXBase();
+ const SwTOXBase* pTOXBase = pWrtShell->GetCurTOX();
+ pWrtShell->UpdateTableOf(*pTOXBase);
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pTitleNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+ SwNodeIndex aIdx(*pTitleNode);
+
+ // skip the title
+ // next node should contain subscript
+ SwTextNode* pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ sal_uInt16 nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+
+ // next node should contain superscript
+ pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf99689TableOfTables)
+{
+ createSwDoc("tdf99689_tables.odt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoNextTOXBase();
+ const SwTOXBase* pTOXBase = pWrtShell->GetCurTOX();
+ pWrtShell->UpdateTableOf(*pTOXBase);
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pTitleNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+ SwNodeIndex aIdx(*pTitleNode);
+
+ // skip the title
+ // next node should contain superscript
+ SwTextNode* pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ sal_uInt16 nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+
+ // next node should contain subscript
+ pNext = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+ CPPUNIT_ASSERT(pNext->HasHints());
+ nAttrType = lcl_getAttributeIDFromHints(pNext->GetSwpHints());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(RES_CHRATR_ESCAPEMENT), nAttrType);
+}
+
+// tdf#112448: Fix: take correct line height
+//
+// When line metrics is not calculated we need to call CalcRealHeight()
+// before usage of the Height() and GetRealHeight().
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf112448)
+{
+ createSwDoc("tdf112448.odt");
+
+ // check actual number of line breaks in the paragraph
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout"_ostr, 2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf113790)
+{
+ createSwDoc("tdf113790.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ // Create the clipboard document.
+ SwDoc aClipboard;
+ aClipboard.SetClipBoard(true);
+
+ // Go to fourth line - to "ABCD" bulleted list item
+ pWrtShell->Down(/*bSelect=*/false, 4);
+ pWrtShell->SelPara(nullptr);
+ CPPUNIT_ASSERT_EQUAL(OUString("ABCD"), pWrtShell->GetSelText());
+ pWrtShell->Copy(aClipboard);
+
+ // Go down to next-to-last (empty) line above "Title3"
+ pWrtShell->Down(/*bSelect=*/false, 4);
+ pWrtShell->Paste(aClipboard);
+
+ // Save it as DOCX & load it again
+ saveAndReload("Office Open XML Text");
+ CPPUNIT_ASSERT(dynamic_cast<SwXTextDocument*>(mxComponent.get()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157937)
+{
+ createSwDoc("tdf130088.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // select paragraph
+ pWrtShell->SelPara(nullptr);
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // show changes
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ // cycle case with change tracking
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ // This resulted freezing
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157988)
+{
+ createSwDoc("tdf130088.docx");
+ SwDoc* pDoc = getSwDoc();
+
+ // select the second word
+ dispatchCommand(mxComponent, ".uno:GoToNextWord", {});
+ dispatchCommand(mxComponent, ".uno:SelectWord", {});
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // show changes
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ // cycle case with change tracking
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodalesSodales"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ // This was false (missing revert of the tracked change)
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodalesSODALES"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodalesSodales"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt"));
+
+ // tdf#141198 cycle case without selection: the word under the cursor
+
+ dispatchCommand(mxComponent, ".uno:Escape", {});
+
+ dispatchCommand(mxComponent, ".uno:GoRight", {});
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodalesSODALES"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodalesSodales"));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157667)
+{
+ createSwDoc("tdf130088.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // select the first three words
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 25, /*bBasicCall=*/false);
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // show changes
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ // cycle case with change tracking
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesSodales tinciduntTincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ // This was false (missing revert of the tracked change)
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesINTEGER SODALES tincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesSodales tinciduntTincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt tristique."));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf158039)
+{
+ createSwDoc("tdf130088.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // select the first sentence
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 26, /*bBasicCall=*/false);
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // show changes
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ // cycle case with change tracking
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesSodales tinciduntTincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ // This was false (missing revert of the tracked change)
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesINTEGER SODALES tincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith(
+ "Integer sodalesSodales tinciduntTincidunt tristique."));
+
+ dispatchCommand(mxComponent, ".uno:ChangeCaseRotateCase", {});
+
+ CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("Integer sodales tincidunt tristique."));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf108048)
+{
+ createSwDoc();
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence({
+ { "Kind", uno::Any(sal_Int16(3)) },
+ { "TemplateName", uno::Any(OUString("Default Page Style")) },
+ { "PageNumber", uno::Any(sal_uInt16(6)) }, // Even number to avoid auto-inserted blank page
+ { "PageNumberFilled", uno::Any(true) },
+ });
+ dispatchCommand(mxComponent, ".uno:InsertBreak", aPropertyValues);
+ CPPUNIT_ASSERT_EQUAL(2, getParagraphs());
+ CPPUNIT_ASSERT_EQUAL(2, getPages());
+
+ // The inserted page must have page number set to 6
+ uno::Reference<text::XTextRange> xPara = getParagraph(2);
+ sal_uInt16 nPageNumber = getProperty<sal_uInt16>(xPara, "PageNumberOffset");
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(6), nPageNumber);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf113481)
+{
+ createSwDoc("tdf113481-IVS.odt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // One backspace should completely remove the CJK ideograph variation sequence
+ pWrtShell->EndPara();
+ // Before: U+8FBA U+E0102. After: empty
+ pWrtShell->DelLeft();
+ const uno::Reference<text::XTextRange> xPara1 = getParagraph(1);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xPara1->getString().getLength());
+
+ // Also variation sequence of weak characters that are treated as CJK script
+ pWrtShell->Down(false);
+ pWrtShell->EndPara();
+ // Before: U+4E2D U+2205 U+FE00. After: U+4E2D U+2205
+ pWrtShell->DelLeft();
+ const uno::Reference<text::XTextRange> xPara2 = getParagraph(2);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xPara2->getString().getLength());
+ CPPUNIT_ASSERT_EQUAL(u'\x4E2D', xPara2->getString()[0]);
+
+ // Also variation sequence of other scripts
+ pWrtShell->Down(false);
+ pWrtShell->EndPara();
+ // Before: U+1820 U+180B. After: U+1820
+ pWrtShell->DelLeft();
+ const uno::Reference<text::XTextRange> xPara3 = getParagraph(3);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xPara3->getString().getLength());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf115013)
+{
+ constexpr OUString sColumnName(u"Name with spaces, \"quotes\" and \\backslashes"_ustr);
+
+ utl::TempFileNamed aTempDir(nullptr, true);
+ aTempDir.EnableKillingFile();
+ const OUString aWorkDir = aTempDir.GetURL();
+
+ //create new writer document
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+
+ {
+ // Load and register data source
+ OUString sDataSource
+ = SwDBManager::LoadAndRegisterDataSource(createFileURL(u"datasource.ods"), &aWorkDir);
+ CPPUNIT_ASSERT(!sDataSource.isEmpty());
+
+ // Insert a new field type for the mailmerge field
+ SwDBData aDBData;
+ aDBData.sDataSource = sDataSource;
+ aDBData.sCommand = "Sheet1";
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwDBFieldType* pFieldType = static_cast<SwDBFieldType*>(
+ pWrtShell->InsertFieldType(SwDBFieldType(pDoc, sColumnName, aDBData)));
+ CPPUNIT_ASSERT(pFieldType);
+
+ // Insert the field into document
+ SwDBField aField(pFieldType);
+ pWrtShell->InsertField2(aField);
+ }
+ // Save it as DOCX & load it again
+ saveAndReload("Office Open XML Text");
+ pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwPaM* pCursor = pDoc->GetEditShell()->GetCursor();
+ CPPUNIT_ASSERT(pCursor);
+
+ // Get the field at the beginning of the document
+ SwDBField* pField = dynamic_cast<SwDBField*>(SwCursorShell::GetFieldAtCursor(pCursor, true));
+ CPPUNIT_ASSERT(pField);
+ OUString sColumn = static_cast<SwDBFieldType*>(pField->GetTyp())->GetColumnName();
+ // The column name must come correct after round trip
+ CPPUNIT_ASSERT_EQUAL(sColumnName, sColumn);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf115065)
+{
+ // In the document, the tables have table style assigned
+ // Source table (first one) has two rows;
+ // destination (second one) has only one row
+ createSwDoc("tdf115065.odt");
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ pWrtShell->GotoTable("Table2");
+ SwRect aRect = pWrtShell->GetCurrFrame()->getFrameArea();
+ // Destination point is the middle of the first cell of second table
+ Point ptTo(aRect.Left() + aRect.Width() / 2, aRect.Top() + aRect.Height() / 2);
+
+ pWrtShell->GotoTable("Table1");
+ aRect = pWrtShell->GetCurrFrame()->getFrameArea();
+ // Source point is the middle of the first cell of first table
+ Point ptFrom(aRect.Left() + aRect.Width() / 2, aRect.Top() + aRect.Height() / 2);
+
+ pWrtShell->SelTableCol();
+ // The copy operation (or closing document after that) segfaulted
+ pWrtShell->Copy(*pWrtShell, ptFrom, ptTo);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf84806_MovingMultipleTableRows)
+{
+ // Moving of multiple table rows.
+ // Source table (first one) has two rows;
+ // destination (second one) has only one row
+ createSwDoc("tdf115065.odt");
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables->getCount());
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table2"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTable2(xTableNames->getByName("Table2"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable2->getRows()->getCount());
+
+ // without redlining
+ CPPUNIT_ASSERT_MESSAGE("redlining should be off",
+ !pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+
+ pWrtShell->GotoTable("Table2");
+ SwRect aRect = pWrtShell->GetCurrFrame()->getFrameArea();
+ // Destination point is the middle of the first cell of second table
+ Point ptTo(aRect.Left() + aRect.Width() / 2, aRect.Top() + aRect.Height() / 2);
+
+ // Move rows of the first table into the second table
+ pWrtShell->GotoTable("Table1");
+ pWrtShell->SelTable();
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ // This was 2 tables
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable2->getRows()->getCount());
+
+ // Undo results 2 tables
+ rUndoManager.Undo();
+ uno::Reference<container::XIndexAccess> xTables2(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables2->getCount());
+ uno::Reference<text::XTextTable> xTable1b(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTable2b(xTableNames->getByName("Table2"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1b->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable2b->getRows()->getCount());
+
+ // FIXME assert with Redo()
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf147181_TrackedMovingOfMultipleTableRows)
+{
+ // Tracked moving of multiple table rows.
+ // Source table (first one) has two rows;
+ // destination (second one) has only one row
+ createSwDoc("tdf115065.odt");
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables->getCount());
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table2"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTable2(xTableNames->getByName("Table2"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable2->getRows()->getCount());
+
+ // FIXME: doesn't work with empty rows, yet
+ pWrtShell->Insert("x");
+ pWrtShell->Down(false);
+ pWrtShell->Insert("x");
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // show changes
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+
+ pWrtShell->GotoTable("Table2");
+ SwRect aRect = pWrtShell->GetCurrFrame()->getFrameArea();
+ // Destination point is the middle of the first cell of second table
+ Point ptTo(aRect.Left() + aRect.Width() / 2, aRect.Top() + aRect.Height() / 2);
+
+ // Move rows of the first table into the second table
+ pWrtShell->GotoTable("Table1");
+ pWrtShell->SelTable();
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ // still 2 tables, but the second one has got 3 rows
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable2->getRows()->getCount());
+
+ // accept changes results 1 table (removing moved table)
+ dispatchCommand(mxComponent, ".uno:AcceptAllTrackedChanges", {});
+ uno::Reference<container::XIndexAccess> xTables2(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables2->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable2->getRows()->getCount());
+
+ // Undo results 2 tables
+ rUndoManager.Undo();
+ rUndoManager.Undo();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables2->getCount());
+ uno::Reference<text::XTextTable> xTable1b(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTable2b(xTableNames->getByName("Table2"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1b->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable2b->getRows()->getCount());
+
+ // reject changes results 2 table again, with the original row counts
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+ uno::Reference<container::XIndexAccess> xTables3(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables3->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1b->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable2b->getRows()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157492_TrackedMovingRow)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ // Create a table
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ (void)&pWrtShell->InsertTable(TableOpt, 4, 3);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable1->getColumns()->getCount());
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // fill table with data
+ for (int i = 0; i < 3; ++i)
+ {
+ pWrtShell->Insert("x");
+ pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
+ }
+
+ Scheduler::ProcessEventsToIdle();
+
+ uno::Reference<text::XTextRange> xCellA1(xTable1->getCellByName("A1"), uno::UNO_QUERY);
+ xCellA1->setString("A1");
+ uno::Reference<text::XTextRange> xCellB1(xTable1->getCellByName("B1"), uno::UNO_QUERY);
+ xCellB1->setString("B1");
+ uno::Reference<text::XTextRange> xCellC1(xTable1->getCellByName("C1"), uno::UNO_QUERY);
+ xCellC1->setString("C1");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[1]/cell[1]/txt"_ostr, "A1");
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[1]/cell[2]/txt"_ostr, "B1");
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[1]/cell[3]/txt"_ostr, "C1");
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // Move first column of the table before the third column by drag & drop
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPage = pLayout->Lower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pTable = pBody->GetLower();
+ SwFrame* pRow1 = pTable->GetLower();
+ SwFrame* pCellA1 = pRow1->GetLower();
+ SwFrame* pRow3 = pRow1->GetNext()->GetNext();
+ SwFrame* pCellA3 = pRow3->GetLower();
+ const SwRect& rCellA1Rect = pCellA1->getFrameArea();
+ const SwRect& rCellA3Rect = pCellA3->getFrameArea();
+ Point ptTo(rCellA3Rect.Left() + rCellA3Rect.Width() / 2,
+ rCellA3Rect.Top() + rCellA3Rect.Height() / 2);
+ // select first table row by using the middle point of the left border of row 1
+ Point ptRow(rCellA1Rect.Left() - 5, rCellA1Rect.Top() + rCellA1Rect.Height() / 2);
+ pWrtShell->SelectTableRowCol(ptRow);
+
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ // reject changes results 4 rows again, not 5
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable2(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable2->getColumns()->getCount());
+ // This was 5 (moving row without change tracking)
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable2->getRows()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf154599_MovingColumn)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ // Create a table with less columns than rows
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ (void)&pWrtShell->InsertTable(TableOpt, 4, 3);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable1->getColumns()->getCount());
+
+ // without redlining
+ CPPUNIT_ASSERT_MESSAGE("redlining should be off",
+ !pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // Move first column of the table before the third column by drag & drop
+
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPage = pLayout->Lower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pTable = pBody->GetLower();
+ SwFrame* pRow1 = pTable->GetLower();
+ SwFrame* pCellA1 = pRow1->GetLower();
+ SwFrame* pCellC1 = pCellA1->GetNext()->GetNext();
+ const SwRect& rCellA1Rect = pCellA1->getFrameArea();
+ const SwRect& rCellC1Rect = pCellC1->getFrameArea();
+ Point ptTo(rCellC1Rect.Left() + rCellC1Rect.Width() / 2,
+ rCellC1Rect.Top() + rCellC1Rect.Height() / 2);
+ // select first table column by using the middle point of the top border of column A
+ Point ptColumn(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() - 5);
+ pWrtShell->SelectTableRowCol(ptColumn);
+
+ // This crashed here before the fix.
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable1->getColumns()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf155846_MovingColumn)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ // Create a table
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ (void)&pWrtShell->InsertTable(TableOpt, 4, 3);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable1->getColumns()->getCount());
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // fill table with data
+ for (int i = 0; i < 4; ++i)
+ {
+ pWrtShell->Insert("x");
+ pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ }
+
+ Scheduler::ProcessEventsToIdle();
+
+ uno::Reference<text::XTextRange> xCellA1(xTable1->getCellByName("A1"), uno::UNO_QUERY);
+ xCellA1->setString("A1");
+ uno::Reference<text::XTextRange> xCellA2(xTable1->getCellByName("A2"), uno::UNO_QUERY);
+ xCellA2->setString("A2");
+ uno::Reference<text::XTextRange> xCellA3(xTable1->getCellByName("A3"), uno::UNO_QUERY);
+ xCellA3->setString("A3");
+ uno::Reference<text::XTextRange> xCellA4(xTable1->getCellByName("A4"), uno::UNO_QUERY);
+ xCellA4->setString("A4");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[1]/cell[1]/txt"_ostr, "A1");
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[2]/cell[1]/txt"_ostr, "A2");
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[3]/cell[1]/txt"_ostr, "A3");
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row[4]/cell[1]/txt"_ostr, "A4");
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // Move first column of the table before the third column by drag & drop
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPage = pLayout->Lower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pTable = pBody->GetLower();
+ SwFrame* pRow1 = pTable->GetLower();
+ SwFrame* pCellA1 = pRow1->GetLower();
+ SwFrame* pCellC1 = pCellA1->GetNext()->GetNext();
+ const SwRect& rCellA1Rect = pCellA1->getFrameArea();
+ const SwRect& rCellC1Rect = pCellC1->getFrameArea();
+ Point ptTo(rCellC1Rect.Left() + rCellC1Rect.Width() / 2,
+ rCellC1Rect.Top() + rCellC1Rect.Height() / 2);
+ // select first table column by using the middle point of the top border of column A
+ Point ptColumn(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() - 5);
+ pWrtShell->SelectTableRowCol(ptColumn);
+
+ // This crashed here before the fix.
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ // reject changes results 3 columns again, not 4
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable2(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable2->getRows()->getCount());
+ // This was 4 (moving column without change tracking)
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable2->getColumns()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf154771_MovingMultipleColumns)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ // Create a table with less columns than rows
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ (void)&pWrtShell->InsertTable(TableOpt, 5, 4);
+
+ uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTableNames = xTablesSupplier->getTextTables();
+ CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+ uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xTable1->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getColumns()->getCount());
+
+ // without redlining
+ CPPUNIT_ASSERT_MESSAGE("redlining should be off",
+ !pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ // Move first two columns of the table before column D by drag & drop
+
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPage = pLayout->Lower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pTable = pBody->GetLower();
+ SwFrame* pRow1 = pTable->GetLower();
+ SwFrame* pCellA1 = pRow1->GetLower();
+ SwFrame* pCellB1 = pCellA1->GetNext();
+ SwFrame* pCellD1 = pCellB1->GetNext()->GetNext();
+ const SwRect& rCellA1Rect = pCellA1->getFrameArea();
+ const SwRect& rCellB1Rect = pCellB1->getFrameArea();
+ const SwRect& rCellD1Rect = pCellD1->getFrameArea();
+ Point ptTo(rCellD1Rect.Left() + rCellD1Rect.Width() / 2,
+ rCellD1Rect.Top() + rCellD1Rect.Height() / 2);
+ // select first two table columns by using
+ // the middle point of the top border of column A
+ // and middle point of the top border of column B
+ Point ptColumnA(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() - 5);
+ const Point ptColumnB(rCellB1Rect.Left() + rCellB1Rect.Width() / 2, rCellB1Rect.Top() - 5);
+ pWrtShell->SelectTableRowCol(ptColumnA, &ptColumnB);
+
+ rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
+ xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xTable1->getRows()->getCount());
+ // This was 5 before the fix (only the first selected column was moved, the
+ // other ones were copied instead of moving)
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getColumns()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf115132)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ std::vector<OUString> vTestTableNames;
+
+ // Create an empty paragraph that will separate first table from the rest
+ pWrtShell->SplitNode();
+ pWrtShell->StartOfSection();
+ // Create a table at the start of document body
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ const SwTable* pTable = &pWrtShell->InsertTable(TableOpt, 2, 3);
+ const SwTableFormat* pFormat = pTable->GetFrameFormat();
+ CPPUNIT_ASSERT(pFormat);
+ vTestTableNames.push_back(pFormat->GetName());
+ pWrtShell->EndOfSection();
+ // Create a table after a paragraph
+ pTable = &pWrtShell->InsertTable(TableOpt, 2, 3);
+ pFormat = pTable->GetFrameFormat();
+ CPPUNIT_ASSERT(pFormat);
+ vTestTableNames.push_back(pFormat->GetName());
+ // Create a table immediately after the previous
+ pTable = &pWrtShell->InsertTable(TableOpt, 2, 3);
+ pFormat = pTable->GetFrameFormat();
+ CPPUNIT_ASSERT(pFormat);
+ vTestTableNames.push_back(pFormat->GetName());
+ // Create a nested table in the middle of last row
+ pWrtShell->GotoTable(vTestTableNames.back());
+ for (int i = 0; i < 4; ++i)
+ pWrtShell->GoNextCell(false);
+ pTable = &pWrtShell->InsertTable(TableOpt, 2, 3);
+ pFormat = pTable->GetFrameFormat();
+ CPPUNIT_ASSERT(pFormat);
+ vTestTableNames.push_back(pFormat->GetName());
+
+ // Now check that in any cell in all tables we don't go out of a cell
+ // using Delete or Backspace. We test cases when a table is the first node;
+ // when we are in a first/middle/last cell in a row; when there's a paragraph
+ // before/after this cell; when there's another table before/after this cell;
+ // in nested table.
+ for (const auto& rTableName : vTestTableNames)
+ {
+ pWrtShell->GotoTable(rTableName);
+ do
+ {
+ const SwStartNode* pNd = pWrtShell->GetCursor()->GetPointNode().FindTableBoxStartNode();
+ pWrtShell->DelRight();
+ CPPUNIT_ASSERT_EQUAL(pNd,
+ pWrtShell->GetCursor()->GetPointNode().FindTableBoxStartNode());
+ pWrtShell->DelLeft();
+ CPPUNIT_ASSERT_EQUAL(pNd,
+ pWrtShell->GetCursor()->GetPointNode().FindTableBoxStartNode());
+ } while (pWrtShell->GoNextCell(false));
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testXDrawPagesSupplier)
+{
+ createSwDoc();
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("XDrawPagesSupplier interface is unavailable", xDrawPagesSupplier.is());
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
+ CPPUNIT_ASSERT(xDrawPages.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("There must be only a single DrawPage in Writer documents",
+ sal_Int32(1), xDrawPages->getCount());
+ uno::Any aDrawPage = xDrawPages->getByIndex(0);
+ uno::Reference<drawing::XDrawPage> xDrawPageFromXDrawPages(aDrawPage, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xDrawPageFromXDrawPages.is());
+
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "The DrawPage accessed using XDrawPages must be the same as using XDrawPageSupplier",
+ xDrawPage.get(), xDrawPageFromXDrawPages.get());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf116403)
+{
+ createSwDoc("tdf116403-considerborders.odt");
+ SwDoc* pDoc = getSwDoc();
+ // Check that before ToX update, the tab stop position is the old one
+ uno::Reference<text::XTextRange> xParagraph = getParagraph(2, "1\t1");
+ auto aTabs = getProperty<uno::Sequence<style::TabStop>>(xParagraph, "ParaTabStops");
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aTabs.getLength());
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(17000), aTabs[0].Position);
+
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ const SwTOXBase* pTOX = pWrtShell->GetTOX(0);
+ CPPUNIT_ASSERT(pTOX);
+ pWrtShell->UpdateTableOf(*pTOX);
+
+ xParagraph = getParagraph(2, "1\t1");
+ aTabs = getProperty<uno::Sequence<style::TabStop>>(xParagraph, "ParaTabStops");
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aTabs.getLength());
+ // This was still 17000, refreshing ToX didn't take borders spacings and widths into account
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Page borders must be considered for right-aligned tabstop",
+ static_cast<sal_Int32>(17000 - 2 * 500 - 2 * 1),
+ aTabs[0].Position);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testHtmlCopyImages)
+{
+ // Load a document with an image.
+ createSwDoc("image.odt");
+ SwDoc* pDoc = getSwDoc();
+
+ // Trigger the copy part of HTML copy&paste.
+ WriterRef xWrt = new SwHTMLWriter(/*rBaseURL=*/OUString());
+ CPPUNIT_ASSERT(xWrt.is());
+
+ xWrt->m_bWriteClipboardDoc = true;
+ xWrt->m_bWriteOnlyFirstTable = false;
+ xWrt->SetShowProgress(false);
+ {
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::WRITE | StreamMode::TRUNC);
+ SwWriter aWrt(aStream, *pDoc);
+ aWrt.Write(xWrt);
+ }
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+
+ // This failed, image was lost during HTML copy.
+ OUString aImage = getXPath(pHtmlDoc, "/html/body/p/img"_ostr, "src"_ostr);
+ // Also make sure that the image is not embedded (e.g. Word doesn't handle
+ // embedded images).
+ CPPUNIT_ASSERT(aImage.startsWith("file:///"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf116789)
+{
+ createSwDoc("tdf116789.fodt");
+ uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText1;
+ uno::Reference<text::XText> xText2;
+ {
+ uno::Reference<text::XTextContent> xBookmark(
+ xBookmarksSupplier->getBookmarks()->getByName("Bookmark 1"), uno::UNO_QUERY);
+ xText1 = xBookmark->getAnchor()->getText();
+ }
+ {
+ uno::Reference<text::XTextContent> xBookmark(
+ xBookmarksSupplier->getBookmarks()->getByName("Bookmark 1"), uno::UNO_QUERY);
+ xText2 = xBookmark->getAnchor()->getText();
+ }
+ // This failed, we got two different SwXCell for the same bookmark anchor text.
+ CPPUNIT_ASSERT_EQUAL(xText1, xText2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf91801)
+{
+ // Tests calculation with several user field variables without prior user fields
+ createSwDoc("tdf91801.fodt");
+ uno::Reference<text::XTextTable> xTable(getParagraphOrTable(1), uno::UNO_QUERY);
+ uno::Reference<table::XCell> xCell(xTable->getCellByName("A1"));
+ CPPUNIT_ASSERT_EQUAL(555.0, xCell->getValue());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf51223)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ SwNodeOffset nIndex = pWrtShell->GetCursor()->GetPointNode().GetIndex();
+ pWrtShell->Insert("i");
+ pWrtShell->SplitNode(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("I"),
+ static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
+ rUndoManager.Undo();
+ CPPUNIT_ASSERT_EQUAL(OUString("i"),
+ static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testFontEmbedding)
+{
+#if HAVE_MORE_FONTS && !defined(MACOSX)
+ createSwDoc("testFontEmbedding.odt");
+
+ OString aContentBaseXpath("/office:document-content/office:font-face-decls"_ostr);
+ OString aSettingsBaseXpath(
+ "/office:document-settings/office:settings/config:config-item-set"_ostr);
+
+ xmlDocUniquePtr pXmlDoc;
+
+ // Get document settings
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xProps(
+ xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW);
+
+ // Check font embedding state
+ CPPUNIT_ASSERT_EQUAL(false, xProps->getPropertyValue("EmbedFonts").get<bool>());
+ CPPUNIT_ASSERT_EQUAL(false, xProps->getPropertyValue("EmbedOnlyUsedFonts").get<bool>());
+ // Font scripts should be enabled by default, however this has no effect unless "EmbedOnlyUsedFonts" is enabled
+ CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedLatinScriptFonts").get<bool>());
+ CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedAsianScriptFonts").get<bool>());
+ CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedComplexScriptFonts").get<bool>());
+
+ // CASE 1 - no font embedding enabled
+
+ // Save the document
+ save("writer8");
+ CPPUNIT_ASSERT(maTempFile.IsValid());
+
+ // Check setting - No font embedding should be enabled
+ pXmlDoc = parseExport("settings.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedFonts']", "false");
+
+ // Check content - No font-face-src nodes should be present
+ pXmlDoc = parseExport("content.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face", 6);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']");
+ assertXPath(
+ pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']/svg:font-face-src", 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Sans1']/svg:font-face-src",
+ 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif']/svg:font-face-src",
+ 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif1']/svg:font-face-src",
+ 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Carlito']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Carlito']/svg:font-face-src", 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Caladea']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Caladea']/svg:font-face-src", 0);
+
+ // CASE 2 - font embedding enabled, but embed used fonts disabled
+
+ // Enable font embedding, disable embedding used font only
+ xProps->setPropertyValue("EmbedFonts", uno::Any(true));
+ xProps->setPropertyValue("EmbedOnlyUsedFonts", uno::Any(false));
+
+ // Save the document again
+ save("writer8");
+ CPPUNIT_ASSERT(maTempFile.IsValid());
+
+ // Check setting - font embedding should be enabled + embed only used fonts and scripts
+ pXmlDoc = parseExport("settings.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedFonts']", "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedOnlyUsedFonts']",
+ "false");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedLatinScriptFonts']",
+ "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedAsianScriptFonts']",
+ "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedComplexScriptFonts']",
+ "true");
+
+ // Check content - font-face-src should be present only for "Liberation Sans" fonts
+
+ pXmlDoc = parseExport("content.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face", 6);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']");
+ assertXPath(
+ pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']/svg:font-face-src", 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Sans1']/svg:font-face-src",
+ 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif']/svg:font-face-src",
+ 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif1']/svg:font-face-src",
+ 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Carlito']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Carlito']/svg:font-face-src", 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Caladea']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Caladea']/svg:font-face-src", 1);
+
+ // CASE 3 - font embedding enabled, embed only used fonts enabled
+
+ // Enable font embedding and setting to embed used fonts only
+ xProps->setPropertyValue("EmbedFonts", uno::Any(true));
+ xProps->setPropertyValue("EmbedOnlyUsedFonts", uno::Any(true));
+ xProps->setPropertyValue("EmbedLatinScriptFonts", uno::Any(true));
+ xProps->setPropertyValue("EmbedAsianScriptFonts", uno::Any(true));
+ xProps->setPropertyValue("EmbedComplexScriptFonts", uno::Any(true));
+
+ // Save the document again
+ save("writer8");
+ CPPUNIT_ASSERT(maTempFile.IsValid());
+
+ // Check setting - font embedding should be enabled + embed only used fonts and scripts
+ pXmlDoc = parseExport("settings.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedFonts']", "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedOnlyUsedFonts']",
+ "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedLatinScriptFonts']",
+ "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedAsianScriptFonts']",
+ "true");
+ assertXPathContent(
+ pXmlDoc, aSettingsBaseXpath + "/config:config-item[@config:name='EmbedComplexScriptFonts']",
+ "true");
+
+ // Check content - font-face-src should be present only for "Liberation Sans" fonts
+
+ pXmlDoc = parseExport("content.xml");
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face", 6);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']");
+ assertXPath(
+ pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans']/svg:font-face-src", 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Sans1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Sans1']/svg:font-face-src",
+ 0);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif']/svg:font-face-src",
+ 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Liberation Serif1']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath
+ + "/style:font-face[@style:name='Liberation Serif1']/svg:font-face-src",
+ 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Carlito']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Carlito']/svg:font-face-src", 1);
+ assertXPath(pXmlDoc, aContentBaseXpath + "/style:font-face[@style:name='Caladea']");
+ assertXPath(pXmlDoc,
+ aContentBaseXpath + "/style:font-face[@style:name='Caladea']/svg:font-face-src", 0);
+#endif
+}
+
+// Unit test for fix inconsistent bookmark behavior around at-char/as-char anchored frames
+//
+// We have a placeholder character in the sw doc model for as-char anchored frames,
+// so it's possible to have a bookmark before/after the frame or a non-collapsed bookmark
+// which covers the frame. The same is not true for at-char anchored frames,
+// where the anchor points to a doc model position, but there is no placeholder character.
+// If a bookmark is created covering the start and end of the anchor of the frame,
+// internally we create a collapsed bookmark which has the same position as the anchor of the frame.
+// When this doc model is handled by SwXParagraph::createEnumeration(),
+// first the frame and then the bookmark is appended to the text portion enumeration,
+// so your bookmark around the frame is turned into a collapsed bookmark after the frame.
+// (The same happens when we roundtrip an ODT document representing this doc model.)
+//
+// Fix the problem by inserting collapsed bookmarks with affected anchor positions
+// (same position is the anchor for an at-char frame) into the enumeration in two stages:
+// first the start of them before frames and then the end of them + other bookmarks.
+// This way UNO API users get their non-collapsed bookmarks around at-char anchored frames,
+// similar to as-char ones.
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testInconsistentBookmark)
+{
+ // create test document with text and bookmark
+ {
+ createSwDoc("testInconsistentBookmark.ott");
+ SwDoc* pDoc = getSwDoc();
+ IDocumentMarkAccess& rIDMA(*pDoc->getIDocumentMarkAccess());
+ SwNodeIndex aIdx(pDoc->GetNodes().GetEndOfContent(), -1);
+ SwCursor aPaM(SwPosition(aIdx), nullptr);
+ aPaM.SetMark();
+ aPaM.MovePara(GoCurrPara, fnParaStart);
+ aPaM.MovePara(GoCurrPara, fnParaEnd);
+ rIDMA.makeMark(aPaM, "Mark", IDocumentMarkAccess::MarkType::BOOKMARK,
+ ::sw::mark::InsertMode::New);
+ aPaM.Exchange();
+ aPaM.DeleteMark();
+ }
+
+ // save document and verify the bookmark scoup
+ {
+ // save document
+ save("writer8");
+
+ // load only content.xml
+ xmlDocUniquePtr pXmlDoc = parseExport("content.xml");
+ static constexpr OString aPath(
+ "/office:document-content/office:body/office:text/text:p"_ostr);
+
+ const int pos1 = getXPathPosition(pXmlDoc, aPath, "bookmark-start");
+ const int pos2 = getXPathPosition(pXmlDoc, aPath, "control");
+ const int pos3 = getXPathPosition(pXmlDoc, aPath, "bookmark-end");
+
+ CPPUNIT_ASSERT_GREATER(pos1, pos2);
+ CPPUNIT_ASSERT_GREATER(pos2, pos3);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testSpellOnlineParameter)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ bool bSet = pOpt->IsOnlineSpell();
+
+ uno::Sequence<beans::PropertyValue> params
+ = comphelper::InitPropertySequence({ { "Enable", uno::Any(!bSet) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+ CPPUNIT_ASSERT_EQUAL(!bSet, pOpt->IsOnlineSpell());
+
+ // set the same state as now and we don't expect any change (no-toggle)
+ params = comphelper::InitPropertySequence({ { "Enable", uno::Any(!bSet) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+ CPPUNIT_ASSERT_EQUAL(!bSet, pOpt->IsOnlineSpell());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf124603)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ uno::Sequence<beans::PropertyValue> params
+ = comphelper::InitPropertySequence({ { "Enable", uno::Any(true) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+
+ // Automatic Spell Checking is enabled
+
+ CPPUNIT_ASSERT(pOpt->IsOnlineSpell());
+
+ // check available en_US dictionary and test spelling with it
+ uno::Reference<XLinguServiceManager2> xLngSvcMgr(GetLngSvcMgr_Impl());
+ uno::Reference<XSpellChecker1> xSpell;
+ xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY);
+ LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString()));
+ if (xSpell.is() && xSpell->hasLanguage(static_cast<sal_uInt16>(eLang)))
+ {
+ // Type a correct word
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ emulateTyping(*pTextDoc, u"the ");
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+ // no bad word
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwWrongList*>(nullptr), pNode->GetWrong());
+
+ // Create a bad word from the good: "the" -> "thex"
+
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ emulateTyping(*pTextDoc, u"x");
+ // tdf#92036 pending spell checking
+ bool bPending = !pNode->GetWrong() || !pNode->GetWrong()->Count();
+ CPPUNIT_ASSERT(bPending);
+
+ // Move right, leave the bad word
+
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ // tdf#92036 still pending spell checking
+ bPending = !pNode->GetWrong() || !pNode->GetWrong()->Count();
+ CPPUNIT_ASSERT(bPending);
+
+ // Move down to trigger spell checking
+
+ pWrtShell->Down(/*bSelect=*/false, 1);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(pNode->GetWrong());
+ // This was 0 (pending spell checking)
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pNode->GetWrong()->Count());
+ }
+}
+
+#if !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf45949)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ uno::Sequence<beans::PropertyValue> params
+ = comphelper::InitPropertySequence({ { "Enable", uno::Any(true) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+
+ // Automatic Spell Checking is enabled
+ CPPUNIT_ASSERT(pOpt->IsOnlineSpell());
+
+ // check available en_US dictionary and test spelling with it
+ uno::Reference<XLinguServiceManager2> xLngSvcMgr(GetLngSvcMgr_Impl());
+ uno::Reference<XSpellChecker1> xSpell;
+ xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY);
+ LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString()));
+ if (xSpell.is() && xSpell->hasLanguage(static_cast<sal_uInt16>(eLang)))
+ {
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ emulateTyping(*pTextDoc, u"baaad http://www.baaad.org baaad");
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+
+ // tdf#152492: Without the fix in place, this test would have failed with
+ // - Expected: 1
+ // - Actual : 3
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pNode->GetWrong()->Count());
+
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 10, /*bBasicCall=*/false);
+ emulateTyping(*pTextDoc, u" ");
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), pNode->GetWrong()->Count());
+
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 6, /*bBasicCall=*/false);
+ emulateTyping(*pTextDoc, u" ");
+
+ // Move down to trigger spell checking
+ pWrtShell->Down(/*bSelect=*/false, 1);
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 3
+ // - Actual : 2
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(3), pNode->GetWrong()->Count());
+ }
+}
+#endif
+
+#if !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157442)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ uno::Sequence<beans::PropertyValue> params
+ = comphelper::InitPropertySequence({ { "Enable", uno::Any(true) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+
+ // Automatic Spell Checking is enabled
+ CPPUNIT_ASSERT(pOpt->IsOnlineSpell());
+
+ // check available en_US dictionary and test spelling with it
+ uno::Reference<XLinguServiceManager2> xLngSvcMgr(GetLngSvcMgr_Impl());
+ uno::Reference<XSpellChecker1> xSpell;
+ xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY);
+ LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString()));
+ if (xSpell.is() && xSpell->hasLanguage(static_cast<sal_uInt16>(eLang)))
+ {
+ uno::Reference<linguistic2::XLinguProperties> xLinguProperties(
+ LinguMgr::GetLinguPropertySet());
+
+ // Spell with digits is disabled by default
+ CPPUNIT_ASSERT_EQUAL(sal_False, xLinguProperties->getIsSpellWithDigits());
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ emulateTyping(*pTextDoc, u"ErrorError Treee2 ");
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SwTextNode* pNode = pShell->GetCursor()->GetPointNode().GetTextNode();
+
+ // Without the fix in place, this test would have crashed because GetWrong() returns nullptr
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pNode->GetWrong()->Count());
+ }
+}
+#endif
+
+#if !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf65535)
+{
+ createSwDoc("tdf65535.fodt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ uno::Sequence<beans::PropertyValue> params
+ = comphelper::InitPropertySequence({ { "Enable", uno::Any(true) } });
+ dispatchCommand(mxComponent, ".uno:SpellOnline", params);
+
+ // Automatic Spell Checking is enabled
+
+ CPPUNIT_ASSERT(pOpt->IsOnlineSpell());
+
+ // check available en_US dictionary and test spelling with it
+ uno::Reference<XLinguServiceManager2> xLngSvcMgr(GetLngSvcMgr_Impl());
+ uno::Reference<XSpellChecker1> xSpell;
+ xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY);
+ LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString()));
+ if (xSpell.is() && xSpell->hasLanguage(static_cast<sal_uInt16>(eLang)))
+ {
+ // trigger online spell checking by (a few) spaces to be sure to get it
+
+ emulateTyping(*pTextDoc, u" ");
+
+ // FIXME: inserting a space before the bad word removes the red underline
+ // Insert a second space to get the red underline (back)
+
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ emulateTyping(*pTextDoc, u" ");
+
+ // Select the bad word (right to left, as during right click)
+
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false);
+
+ // choose the word "Baaed" from the spelling suggestions of the context menu
+
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ {
+ static constexpr OUStringLiteral sApplyRule(u"Spelling_Baaed");
+ SfxStringItem aApplyItem(FN_PARAM_1, sApplyRule);
+ pViewShell->GetViewFrame().GetDispatcher()->ExecuteList(
+ SID_SPELLCHECK_APPLY_SUGGESTION, SfxCallMode::SYNCHRON, { &aApplyItem });
+ }
+
+ // check the replacement in the text
+
+ CPPUNIT_ASSERT_EQUAL(OUString(" Baaed"), getParagraph(1)->getString());
+ }
+
+ // check the remaining comment
+
+ tools::JsonWriter aJsonWriter;
+ pTextDoc->getPostIts(aJsonWriter);
+ OString pChar = aJsonWriter.finishAndGetAsOString();
+ std::stringstream aStream((std::string(pChar)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sCommentText;
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("comments"))
+ {
+ const boost::property_tree::ptree& rComment = rValue.second;
+ sCommentText = OString(rComment.get<std::string>("text"));
+ }
+ // This was false (lost comment with spelling replacement)
+ CPPUNIT_ASSERT_EQUAL("with comment"_ostr, sCommentText);
+}
+#endif
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testRedlineAutoCorrect)
+{
+ createSwDoc("redline-autocorrect.fodt");
+ SwDoc* pDoc = getSwDoc();
+
+ dispatchCommand(mxComponent, ".uno:GoToEndOfDoc", {});
+
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // show tracked deletion with enabled change tracking
+ RedlineFlags const nMode(pWrtShell->GetRedlineFlags() | RedlineFlags::On);
+ CPPUNIT_ASSERT(nMode & (RedlineFlags::ShowDelete | RedlineFlags::ShowInsert));
+ pWrtShell->SetRedlineFlags(nMode);
+ CPPUNIT_ASSERT(nMode & RedlineFlags::ShowDelete);
+
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+ emulateTyping(*pTextDoc, u" ");
+
+ // tdf#83419 This was "Ts " removing the deletion of "t" silently by sentence capitalization
+ OUString sReplaced("ts ");
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // hide delete redlines
+ pWrtShell->SetRedlineFlags(nMode & ~RedlineFlags::ShowDelete);
+
+ // repeat it with not visible redlining
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ emulateTyping(*pTextDoc, u" ");
+
+ sReplaced = "S ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // show delete redlines
+ pWrtShell->SetRedlineFlags(nMode);
+
+ // This still keep the tracked deletion, capitalize only the visible text "s"
+ // with tracked deletion of the original character
+ sReplaced = "tsS ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // repeat it with visible redlining and word auto replacement of "tset"
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ emulateTyping(*pTextDoc, u"et ");
+ // This was "Ttest" removing the tracked deletion silently.
+ // Don't replace, if a redline starts or ends within the text.
+ sReplaced = "tset ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // Otherwise replace it
+ emulateTyping(*pTextDoc, u"tset ");
+ sReplaced = "tset test ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // Including capitalization
+ emulateTyping(*pTextDoc, u"end. word ");
+ sReplaced = "tset test end. Word ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // tracked deletions after the correction point doesn't affect autocorrect
+ dispatchCommand(mxComponent, ".uno:GoToStartOfDoc", {});
+ emulateTyping(*pTextDoc, u"a ");
+ sReplaced = "A tset test end. Word ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testRedlineAutoCorrect2)
+{
+ createSwDoc("redline-autocorrect2.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ dispatchCommand(mxComponent, ".uno:GoToEndOfDoc", {});
+
+ // show tracked deletion
+ RedlineFlags const nMode(pWrtShell->GetRedlineFlags() | RedlineFlags::On);
+ CPPUNIT_ASSERT(nMode & (RedlineFlags::ShowDelete | RedlineFlags::ShowInsert));
+ pWrtShell->SetRedlineFlags(nMode);
+ CPPUNIT_ASSERT(nMode & RedlineFlags::ShowDelete);
+
+ emulateTyping(*pTextDoc, u"... ");
+
+ // This was "LoremLorem,…," (duplicating the deleted comma, but without deletion)
+ // Don't replace, if a redline starts or ends within the text.
+ OUString sReplaced = "Lorem,... ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // Continue it:
+ emulateTyping(*pTextDoc, u"Lorem,... ");
+ sReplaced = u"Lorem,... Lorem,… "_ustr;
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testEmojiAutoCorrect)
+{
+ createSwDoc("redline-autocorrect3.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // Emoji replacement (:snowman: -> ☃)
+
+ // without change tracking
+ CPPUNIT_ASSERT(!(pWrtShell->GetRedlineFlags() & RedlineFlags::On));
+ emulateTyping(*pTextDoc, u":snowman:");
+ OUString sReplaced = u"☃Lorem,"_ustr;
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // with change tracking (showing redlines)
+ RedlineFlags const nMode(pWrtShell->GetRedlineFlags() | RedlineFlags::On);
+ CPPUNIT_ASSERT(nMode & (RedlineFlags::ShowDelete | RedlineFlags::ShowInsert));
+ pWrtShell->SetRedlineFlags(nMode);
+ CPPUNIT_ASSERT(nMode & RedlineFlags::On);
+ CPPUNIT_ASSERT(nMode & RedlineFlags::ShowDelete);
+
+ emulateTyping(*pTextDoc, u":snowman:");
+ sReplaced = u"☃☃Lorem,"_ustr;
+
+ // tdf#140674 This was ":snowman:" instead of autocorrect
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf108423)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // testing autocorrect of i' -> I' on start of first paragraph
+ emulateTyping(*pTextDoc, u"i'");
+ // The word "i" should be capitalized due to autocorrect, followed by a typographical apostrophe
+ OUString sIApostrophe(u"I\u2019"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sIApostrophe, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u" i'");
+ OUString sText(sIApostrophe + u" " + sIApostrophe);
+ CPPUNIT_ASSERT_EQUAL(sText, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf153423)
+{
+ createSwDoc();
+ SvxSwAutoFormatFlags flags(*SwEditShell::GetAutoFormatFlags());
+ comphelper::ScopeGuard const g([=]() { SwEditShell::SetAutoFormatFlags(&flags); });
+ flags.bSetNumRule = true;
+ SwEditShell::SetAutoFormatFlags(&flags);
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ emulateTyping(*pTextDoc, u"1. Item 1");
+
+ SwXTextDocument* pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pXTextDocument);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 1.
+ // - Actual : 10.
+ CPPUNIT_ASSERT_EQUAL(OUString("1."), getProperty<OUString>(getParagraph(1), "ListLabelString"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf106164)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // testing autocorrect of we're -> We're on start of first paragraph
+ emulateTyping(*pTextDoc, u"we're ");
+ CPPUNIT_ASSERT_EQUAL(u"We\u2019re "_ustr, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf54409)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // testing autocorrect of "tset -> "test with typographical double quotation mark U+201C
+ emulateTyping(*pTextDoc, u"\"test ");
+ OUString sReplaced(u"\u201Ctest "_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // testing autocorrect of test" -> test" with typographical double quotation mark U+201D
+ emulateTyping(*pTextDoc, u"and tset\" ");
+ OUString sReplaced2(sReplaced + u"and test\u201D ");
+ CPPUNIT_ASSERT_EQUAL(sReplaced2, getParagraph(1)->getString());
+ // testing autocorrect of "tset" -> "test" with typographical double quotation mark U+201C and U+201D
+ emulateTyping(*pTextDoc, u"\"tset\" ");
+ OUString sReplaced3(sReplaced2 + u"\u201Ctest\u201D ");
+ CPPUNIT_ASSERT_EQUAL(sReplaced3, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf38394)
+{
+ createSwDoc("tdf38394.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // testing autocorrect of French l'" -> l'« (instead of l'»)
+ emulateTyping(*pTextDoc, u"l'\"");
+ OUString sReplaced(u"l\u2019« "_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // tdf#132301 autocorrect of qu'«
+ emulateTyping(*pTextDoc, u" qu'\"");
+ sReplaced += u" qu\u2019« ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf59666)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // testing missing autocorrect of single Greek letters
+ emulateTyping(*pTextDoc, u"π ");
+ CPPUNIT_ASSERT_EQUAL(u"\u03C0 "_ustr, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf133524)
+{
+ createSwDoc("tdf133524.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // 1. Testing autocorrect of >> and <<
+ // Example: »word«
+ emulateTyping(*pTextDoc, u">>");
+ OUString sReplaced(u"»"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // <<
+ emulateTyping(*pTextDoc, u"word<<");
+ sReplaced += u"word«";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // 2. Testing autocorrect of " to >> and << inside „...”
+ // Example: „Sentence and »word«.”
+ // opening primary level quote
+ emulateTyping(*pTextDoc, u" \"");
+ sReplaced += u" „";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // opening second level quote
+ emulateTyping(*pTextDoc, u"Sentence and \"");
+ sReplaced += u"Sentence and »";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // closing second level quote
+ emulateTyping(*pTextDoc, u"word\"");
+ sReplaced += u"word«";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // closing primary level quote
+ emulateTyping(*pTextDoc, u".\"");
+ sReplaced += u".”";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // tdf#134940 avoid premature replacement of "--" in "-->"
+ emulateTyping(*pTextDoc, u" -->");
+ OUString sReplaced2(sReplaced + u" -->");
+ // This was "–>" instead of "-->"
+ CPPUNIT_ASSERT_EQUAL(sReplaced2, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u" ");
+ sReplaced += u" → ";
+ // This was "–>" instead of "→"
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // tdf#83037
+ emulateTyping(*pTextDoc, u"-> ");
+ sReplaced += u"→ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u"<- ");
+ sReplaced += u"← ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u"<-- ");
+ sReplaced += u"← ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u"<--> ");
+ sReplaced += u"↔ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf133524_Romanian)
+{
+ createSwDoc("tdf133524_ro.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // 1. Testing autocorrect of " to << and >> inside „...”
+ // Example: „Sentence and «word».”
+ // opening primary level quote
+ emulateTyping(*pTextDoc, u"\"");
+ OUString sReplaced(u"„"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // opening second level quote
+ emulateTyping(*pTextDoc, u"Sentence and \"");
+ sReplaced += u"Sentence and «";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // closing second level quote
+ emulateTyping(*pTextDoc, u"word\"");
+ sReplaced += u"word»";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // closing primary level quote
+ emulateTyping(*pTextDoc, u".\"");
+ sReplaced += u".”";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // 2. Testing recognition of closing double quotation mark ”
+ emulateTyping(*pTextDoc, u" \"");
+ sReplaced += u" „";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // 3. Testing recognition of alternative closing double quotation mark “
+ emulateTyping(*pTextDoc, u"Alternative.“ \"");
+ sReplaced += u"Alternative.“ „";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf128860)
+{
+ createSwDoc("tdf128860.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // Second level ending quote: ‚word' -> ,word‘
+ emulateTyping(*pTextDoc, u",word'");
+ OUString sReplaced(u",word\u2019"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // Us apostrophe without preceding starting quote: word' -> word’
+ emulateTyping(*pTextDoc, u" word'");
+ sReplaced += u" word\u2019";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // But only after letters: word.' -> word.‘
+ emulateTyping(*pTextDoc, u" word.'");
+ sReplaced += u" word.‘";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf123786)
+{
+ createSwDoc("tdf123786.fodt");
+ SwDoc* pDoc = getSwDoc();
+
+ // On Windows, it will detect that system input language is en-US (despite "typing" e.g. Cyrillic characters),
+ // and will change Russian into English (US); in the latter language,
+ // the replacement from single quote will not become “, but ’.
+ SvtSysLocaleOptions aOptions;
+ aOptions.SetIgnoreLanguageChange(true);
+ aOptions.Commit();
+
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // Second level ending quote: „word' -> „word“
+ emulateTyping(*pTextDoc, u"„слово'");
+ OUString sReplaced(u"„слово“"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // Us apostrophe without preceding starting quote: word' -> word’
+ emulateTyping(*pTextDoc, u" слово'");
+ sReplaced += u" слово’";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // But only after letters: word.' -> word.“
+ emulateTyping(*pTextDoc, u" слово.'");
+ sReplaced += u" слово.“";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf133589)
+{
+ // Hungarian test document with right-to-left paragraph setting
+ createSwDoc("tdf133589.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ // translitere words to Old Hungarian
+ emulateTyping(*pTextDoc, u"székely ");
+ OUString sReplaced(u"𐳥𐳋𐳓𐳉𐳗 "_ustr);
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // disambiguate consonants: asszony -> asz|szony
+ emulateTyping(*pTextDoc, u"asszony ");
+ sReplaced += u"𐳀𐳥𐳥𐳛𐳚 ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // disambiguate consonants: kosszarv -> kos|szarv
+ // (add explicit ZWSP temporarily for consonant disambiguation, because the requested
+ // hu_HU hyphenation dictionary isn't installed on all testing platform)
+ // pWrtShell->Insert(u"kosszarv");
+ emulateTyping(*pTextDoc, u"kos\u200Bszarv ");
+ sReplaced += u"𐳓𐳛𐳤𐳥𐳀𐳢𐳮 ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // transliterate numbers to Old Hungarian
+ emulateTyping(*pTextDoc, u"2020 ");
+ sReplaced += u"𐳺𐳺𐳿𐳼𐳼 ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // tdf#147546 transliterate punctuation marks
+
+ // question mark
+ emulateTyping(*pTextDoc, u"Kérdőjel?");
+ sReplaced += u"𐲓𐳋𐳢𐳇𐳟𐳒𐳉𐳖";
+ OUString sReplaced2(sReplaced + "?");
+ CPPUNIT_ASSERT_EQUAL(sReplaced2, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u" ");
+ sReplaced += u"⸮ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // comma
+ emulateTyping(*pTextDoc, u"Vessző,");
+ sReplaced += u"𐲮𐳉𐳥𐳥𐳟";
+ sReplaced2 = sReplaced + ",";
+ CPPUNIT_ASSERT_EQUAL(sReplaced2, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u" ");
+ sReplaced += u"⹁ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // semicolon
+ emulateTyping(*pTextDoc, u"pontosvessző;");
+ sReplaced += u"𐳠𐳛𐳙𐳦𐳛𐳤𐳮𐳉𐳥𐳥𐳟";
+ sReplaced2 = sReplaced + ";";
+ CPPUNIT_ASSERT_EQUAL(sReplaced2, getParagraph(1)->getString());
+ emulateTyping(*pTextDoc, u" ");
+ sReplaced += u"⁏ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+ // quotation marks
+ emulateTyping(*pTextDoc, u"„idézőjel” ");
+ sReplaced += u"⹂𐳐𐳇𐳋𐳯𐳟𐳒𐳉𐳖‟ ";
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // tdf#148672 transliterate word with closing bracket
+ emulateTyping(*pTextDoc, u"word] ");
+ sReplaced += u"𐳮𐳛𐳢𐳇] "; // This was "word]" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ // tdf#148672 transliterate words with parenthesis (libnumbertext 1.0.11)
+ emulateTyping(*pTextDoc, u"(word) ");
+ sReplaced += u"(𐳮𐳛𐳢𐳇) "; // This was "(word)" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"(word ");
+ sReplaced += u"(𐳮𐳛𐳢𐳇 "; // This was "(word" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"word) ");
+ sReplaced += u"𐳮𐳛𐳢𐳇) "; // This was "word)" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"{word} ");
+ sReplaced += u"{𐳮𐳛𐳢𐳇} "; // This was "(word)" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"{word ");
+ sReplaced += u"{𐳮𐳛𐳢𐳇 "; // This was "(word" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"word} ");
+ sReplaced += u"𐳮𐳛𐳢𐳇} "; // This was "word)" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"[word] ");
+ sReplaced += u"[𐳮𐳛𐳢𐳇] "; // This was "(word)" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+
+ emulateTyping(*pTextDoc, u"[word ");
+ sReplaced += u"[𐳮𐳛𐳢𐳇 "; // This was "(word" (no transliteration)
+ CPPUNIT_ASSERT_EQUAL(sReplaced, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testAutoCorr)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ //Normal AutoCorrect
+ emulateTyping(*pTextDoc, u"tset ");
+ CPPUNIT_ASSERT_EQUAL(OUString("Test "), getParagraph(1)->getString());
+
+ //AutoCorrect with change style to bolt
+ emulateTyping(*pTextDoc, u"Bolt ");
+ const uno::Reference<text::XTextRange> xRun = getRun(getParagraph(1), 2);
+ CPPUNIT_ASSERT_EQUAL(OUString("Bolt"), xRun->getString());
+ CPPUNIT_ASSERT_EQUAL(OUString("Arial"), getProperty<OUString>(xRun, "CharFontName"));
+
+ //AutoCorrect inserts Table with 2 rows and 3 columns
+ emulateTyping(*pTextDoc, u"4xx ");
+ const uno::Reference<text::XTextTable> xTable(getParagraphOrTable(2), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable->getRows()->getCount());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xTable->getColumns()->getCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf130274)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ CPPUNIT_ASSERT(!pWrtShell->GetLayout()->IsHideRedlines());
+ CPPUNIT_ASSERT(
+ !IDocumentRedlineAccess::IsRedlineOn(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ // "tset" may be replaced by the AutoCorrect in the test profile
+ emulateTyping(*pTextDoc, u"tset");
+ // select from left to right
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, /*bBasicCall=*/false);
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false);
+
+ pWrtShell->SetRedlineFlags(pWrtShell->GetRedlineFlags() | RedlineFlags::On);
+ // this would crash in AutoCorrect
+ emulateTyping(*pTextDoc, u".");
+
+ CPPUNIT_ASSERT(!pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf83260)
+{
+ createSwDoc("tdf83260-1.odt");
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // enabled but not shown
+ CPPUNIT_ASSERT(pWrtShell->GetLayout()->IsHideRedlines());
+#if 0
+ CPPUNIT_ASSERT(IDocumentRedlineAccess::IsHideChanges(
+ pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+#endif
+ CPPUNIT_ASSERT(
+ IDocumentRedlineAccess::IsRedlineOn(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+ CPPUNIT_ASSERT(!pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty());
+
+ // the document contains redlines that are combined with CompressRedlines()
+ // if that happens during AutoCorrect then indexes in Undo are off -> crash
+ emulateTyping(*pTextDoc, u"tset ");
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ auto const nActions(rUndoManager.GetUndoActionCount());
+ for (auto i = nActions; 0 < i; --i)
+ {
+ rUndoManager.Undo();
+ }
+ // check that every text node has a layout frame
+ for (SwNodeOffset i(0); i < pDoc->GetNodes().Count(); ++i)
+ {
+ if (SwTextNode const* const pNode = pDoc->GetNodes()[i]->GetTextNode())
+ {
+ CPPUNIT_ASSERT(pNode->getLayoutFrame(nullptr, nullptr, nullptr));
+ }
+ }
+ for (auto i = nActions; 0 < i; --i)
+ {
+ rUndoManager.Redo();
+ }
+ for (SwNodeOffset i(0); i < pDoc->GetNodes().Count(); ++i)
+ {
+ if (SwTextNode const* const pNode = pDoc->GetNodes()[i]->GetTextNode())
+ {
+ CPPUNIT_ASSERT(pNode->getLayoutFrame(nullptr, nullptr, nullptr));
+ }
+ }
+ for (auto i = nActions; 0 < i; --i)
+ {
+ rUndoManager.Undo();
+ }
+ for (SwNodeOffset i(0); i < pDoc->GetNodes().Count(); ++i)
+ {
+ if (SwTextNode const* const pNode = pDoc->GetNodes()[i]->GetTextNode())
+ {
+ CPPUNIT_ASSERT(pNode->getLayoutFrame(nullptr, nullptr, nullptr));
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf74363)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ //testing autocorrect of initial capitals on start of first paragraph
+ //Inserting one all-lowercase word into the first paragraph
+ emulateTyping(*pTextDoc, u"testing ");
+ //The word should be capitalized due to autocorrect
+ CPPUNIT_ASSERT_EQUAL(OUString("Testing "), getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf139922)
+{
+ createSwDoc();
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ emulateTyping(*pTextDoc, u"this is a SEntence. this is a SEntence.");
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: This is a Sentence. This is a Sentence.
+ // - Actual : this is a Sentence. This is a Sentence.
+ CPPUNIT_ASSERT_EQUAL(OUString("This is a Sentence. This is a Sentence."),
+ getParagraph(2)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf143176)
+{
+ // Hungarian test document with right-to-left paragraph setting
+ createSwDoc("tdf143176.fodt");
+
+ // transliterate the document to Old Hungarian (note: it only works
+ // with right-to-left text direction and Default Paragraph Style)
+ dispatchCommand(mxComponent, ".uno:AutoFormatApply", {});
+
+ // This was the original "Lorem ipsum..."
+ CPPUNIT_ASSERT_EQUAL(u"𐲖𐳛𐳢𐳉𐳘 𐳐𐳠𐳤𐳪𐳘 𐳇𐳛𐳖𐳛𐳢 "
+ u"𐳤𐳐𐳦 𐳀𐳘𐳉𐳦⹁"_ustr,
+ getParagraph(1)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"𐳄𐳛𐳙𐳤𐳉𐳄𐳦𐳉𐳦𐳪𐳢 "
+ u"𐳀𐳇𐳐𐳠𐳐𐳤𐳄𐳐𐳙𐳍 𐳉𐳖𐳐𐳦."_ustr,
+ getParagraph(2)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testInsertLongDateFormat)
+{
+ // only for Hungarian, yet
+ createSwDoc("tdf133524.fodt");
+ dispatchCommand(mxComponent, ".uno:InsertDateField", {});
+ // Make sure that the document starts with a field now, and its expanded string value contains space
+ const uno::Reference<text::XTextRange> xField = getRun(getParagraph(1), 1);
+ CPPUNIT_ASSERT_EQUAL(OUString("TextField"), getProperty<OUString>(xField, "TextPortionType"));
+ // the date format was "YYYY-MM-DD", but now "YYYY. MMM DD."
+ CPPUNIT_ASSERT(xField->getString().indexOf(" ") > -1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf129270)
+{
+ createSwDoc("tdf129270.odt");
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ // Go to document end
+ pWrtShell->SttEndDoc(/*bStt=*/false);
+
+ // Press enter
+ pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Numbering for previous outline should remain the same "2"
+ CPPUNIT_ASSERT_EQUAL(OUString("2"), getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // Numbering for newly created outline should be "2.1"
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(5), "ListLabelString"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testInsertPdf)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return;
+ }
+
+ createSwDoc();
+
+ // insert the PDF into the document
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ { { "FileName", uno::Any(createFileURL(u"hello-world.pdf")) } }));
+ dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
+
+ // Save and load cycle
+ saveAndReload("writer8");
+
+ uno::Reference<drawing::XShape> xShape = getShape(1);
+ // Assert that we have a replacement graphics
+ auto xReplacementGraphic
+ = getProperty<uno::Reference<graphic::XGraphic>>(xShape, "ReplacementGraphic");
+ CPPUNIT_ASSERT(xReplacementGraphic.is());
+
+ auto xGraphic = getProperty<uno::Reference<graphic::XGraphic>>(xShape, "Graphic");
+ CPPUNIT_ASSERT(xGraphic.is());
+ // Assert that the graphic is a PDF
+ CPPUNIT_ASSERT_EQUAL(OUString("application/pdf"), getProperty<OUString>(xGraphic, "MimeType"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf143760WrapContourToOff)
+{
+ // Actually, this is an ooxmlexport test. It is here because here is a ready environment
+ // to change a shape by dispatchCommand.
+ createSwDoc("tdf143760_ContourToWrapOff.docx");
+ SwDoc* pDoc = getSwDoc();
+ CPPUNIT_ASSERT(pDoc);
+ CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(getShape(1), "SurroundContour"));
+
+ // Mark the object
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ CPPUNIT_ASSERT(pObject);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pView->MarkObj(pObject, pView->GetSdrPageView());
+
+ // Set "wrap off"
+ dispatchCommand(mxComponent, ".uno:WrapOff", {});
+ CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(getShape(1), "SurroundContour"));
+
+ // Without fix this had failed, because the shape was written to file with contour.
+ saveAndReload("Office Open XML Text");
+ CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(getShape(1), "SurroundContour"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testHatchFill)
+{
+ createSwDoc();
+
+ // Add a rectangle shape to the document.
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(10000, 10000));
+ xShape->setPosition(awt::Point(1000, 1000));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ xShapeProps->setPropertyValue("FillStyle", uno::Any(drawing::FillStyle_HATCH));
+ xShapeProps->setPropertyValue("FillHatchName", uno::Any(OUString("Black 0 Degrees")));
+ xShapeProps->setPropertyValue("FillBackground", uno::Any(false));
+ xShapeProps->setPropertyValue("FillTransparence", uno::Any(sal_Int32(30)));
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ xDrawPage->add(xShape);
+
+ // Save it as DOCX and load it again.
+ saveAndReload("Office Open XML Text");
+ CPPUNIT_ASSERT_EQUAL(1, getShapes());
+
+ // tdf#127989 Without fix this had failed, because the background of the hatch was not set as 'no background'.
+ CPPUNIT_ASSERT(!getProperty<bool>(getShape(1), "FillBackground"));
+
+ // tdf#146822 Without fix this had failed, because the transparency value of the hatch was not exported.
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(30), getProperty<sal_Int32>(getShape(1), "FillTransparence"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testNestedGroupTextBoxCopyCrash)
+{
+ createSwDoc("tdf149550.docx");
+
+ dispatchCommand(mxComponent, ".uno:SelectAll", {});
+ dispatchCommand(mxComponent, ".uno:Copy", {});
+ // This crashed here before the fix.
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+ dispatchCommand(mxComponent, ".uno:Paste", {});
+
+ CPPUNIT_ASSERT_MESSAGE("Where is the doc, it crashed, isn't it?!", mxComponent);
+
+ auto pLayout = parseLayoutDump();
+ // There must be 2 textboxes!
+ assertXPath(pLayout, "/root/page/body/txt/anchored/fly[2]"_ostr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testCrashOnExit)
+{
+ // Load the bugdoc with a table and a textbox shape inside.
+ createSwDoc("tdf142715.odt");
+
+ // Get the textbox selected
+ CPPUNIT_ASSERT_EQUAL(1, getShapes());
+
+ selectShape(1);
+ auto xShape = getShape(1);
+ uno::Reference<beans::XPropertySet> xProperties(xShape, uno::UNO_QUERY);
+
+ // Check if the textbox is selected
+ CPPUNIT_ASSERT_EQUAL(true, xProperties->getPropertyValue("TextBox").get<bool>());
+
+ // Remove the textbox
+ dispatchCommand(mxComponent, ".uno:RemoveTextBox", {});
+
+ CPPUNIT_ASSERT_EQUAL(false, xProperties->getPropertyValue("TextBox").get<bool>());
+
+ // Readd the textbox (to run the textboxhelper::create() method)
+ dispatchCommand(mxComponent, ".uno:AddTextBox", {});
+
+ CPPUNIT_ASSERT_EQUAL(true, xProperties->getPropertyValue("TextBox").get<bool>());
+
+ // save and reload
+ saveAndReload("writer8");
+
+ // Before the fix this crashed here and could not reopen.
+ CPPUNIT_ASSERT_MESSAGE("Crash on exit, isn't it?", mxComponent);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testCaptionShape)
+{
+ createSwDoc();
+
+ // Add a caption shape to the document.
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.CaptionShape"), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(10000, 10000));
+ xShape->setPosition(awt::Point(1000, 1000));
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ xDrawPage->add(xShape);
+
+ // Save it as DOCX and load it again.
+ saveAndReload("Office Open XML Text");
+
+ // Without fix in place, the shape was lost on export.
+ CPPUNIT_ASSERT_EQUAL(1, getShapes());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf151828_Comment2)
+{
+ createSwDoc();
+
+ // Add a basic shape to the document.
+ uno::Sequence<beans::PropertyValue> aArgs(
+ comphelper::InitPropertySequence({ { "KeyModifier", uno::Any(KEY_MOD1) } }));
+ dispatchCommand(mxComponent, ".uno:BasicShapes", aArgs);
+
+ auto xBasicShape = getShape(1);
+ auto pObject = SdrObject::getSdrObjectFromXShape(xBasicShape);
+
+ CPPUNIT_ASSERT_EQUAL(1, getShapes());
+
+ // rename the shape name
+ pObject->SetName("Shape");
+
+ // cut and paste it
+ dispatchCommand(mxComponent, ".uno:Cut", {});
+
+ CPPUNIT_ASSERT_EQUAL(0, getShapes());
+
+ dispatchCommand(mxComponent, ".uno:Paste", {});
+
+ CPPUNIT_ASSERT_EQUAL(1, getShapes());
+
+ // it is required to get the shape object again after paste
+ xBasicShape = getShape(1);
+ pObject = SdrObject::getSdrObjectFromXShape(xBasicShape);
+
+ // Without fix in place, the shape name was 'Shape 1' after paste.
+ CPPUNIT_ASSERT_EQUAL(OUString("Shape"), pObject->GetName());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf151828)
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // insert a table
+ SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0);
+ pWrtShell->InsertTable(TableOpt, 1, 1);
+
+ // move cursor into the table
+ CPPUNIT_ASSERT(pWrtShell->MoveTable(GotoPrevTable, fnTableStart));
+
+ SwFrameFormat* pFormat = pWrtShell->GetTableFormat();
+ CPPUNIT_ASSERT(pFormat);
+
+ // set name of table to 'MyTableName'
+ pWrtShell->SetTableName(*pFormat, "MyTableName");
+
+ // cut and paste the table
+ dispatchCommand(mxComponent, ".uno:SelectTable", {});
+ dispatchCommand(mxComponent, ".uno:Cut", {});
+ dispatchCommand(mxComponent, ".uno:Paste", {});
+
+ // move cursor into the pasted table
+ CPPUNIT_ASSERT(pWrtShell->MoveTable(GotoPrevTable, fnTableStart));
+
+ pFormat = pWrtShell->GetTableFormat();
+ CPPUNIT_ASSERT(pFormat);
+
+ // Before the fix the pasted table name was 'Table1'.
+ CPPUNIT_ASSERT_EQUAL(OUString("MyTableName"), pFormat->GetName());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf146178)
+{
+ createSwDoc();
+
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwPaM* pCursor = pDoc->GetEditShell()->GetCursor();
+
+ // insert two fields
+ dispatchCommand(mxComponent, ".uno:InsertTimeField", {});
+ dispatchCommand(mxComponent, ".uno:InsertDateField", {});
+
+ // navigate by field
+ SwView::SetMoveType(NID_FIELD);
+
+ // set cursor to the start of the document
+ pWrtShell->SttEndDoc(false);
+ // navigate to the previous field
+ dispatchCommand(mxComponent, ".uno:ScrollToPrevious", {});
+ // Before the fix the position would be 0, navigation did not wrap to end of document
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pCursor->GetPoint()->GetContentIndex());
+
+ // set cursor to the end of the document
+ pWrtShell->SttEndDoc(false);
+ // navigate to the next field
+ dispatchCommand(mxComponent, ".uno:ScrollToNext", {});
+ // Before the fix the position would be 1, navigation did not wrap to start of document
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pCursor->GetPoint()->GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf106663HeaderTextFrameGoToNextPlacemarker)
+{
+ createSwDoc("testTdf106663.odt");
+
+ SwDoc* pDoc = getSwDoc();
+ SwPaM* pCursor = pDoc->GetEditShell()->GetCursor();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // Move the cursor into the fly frame of the document's header
+ pWrtShell->GotoFly("FrameInHeader", FLYCNTTYPE_FRM, false);
+
+ // Check that GoToNextPlacemarker highlights the first field instead of the second one
+ dispatchCommand(mxComponent, ".uno:GoToNextPlacemarker", {});
+ // Without the fix in place, this test would have failed with
+ // - Expected: Heading
+ // - Actual : Some other marker
+ // i.e. the GoToNextPlacemarker command skipped the first field
+ CPPUNIT_ASSERT(pCursor->GetPoint()->GetNode().GetTextNode()->GetText().startsWith("Heading"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf155407)
+{
+ createSwDoc();
+ SwXTextDocument& rTextDoc = dynamic_cast<SwXTextDocument&>(*mxComponent);
+
+ {
+ emulateTyping(rTextDoc, u"Foo - 11’--’22 ");
+ // Without the fix in place, this would fail with
+ // - Expected: Foo – 11’—’22
+ // - Actual : Foo – 11’--’22
+ CPPUNIT_ASSERT_EQUAL(u"Foo – 11’—’22 "_ustr, getParagraph(1)->getString());
+ }
+
+ dispatchCommand(mxComponent, ".uno:SelectAll", {}); // start again
+
+ {
+ emulateTyping(rTextDoc, u"Bar -- 111--222 ");
+ // Without the fix in place, this would fail with
+ // - Expected: Bar – 111–222
+ // - Actual : Bar – 111-–22
+ CPPUNIT_ASSERT_EQUAL(u"Bar – 111–222 "_ustr, getParagraph(1)->getString());
+ }
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */