/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 GetLngSvcMgr_Impl() { uno::Reference xContext(comphelper::getProcessComponentContext()); uno::Reference 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 aArgs( comphelper::InitPropertySequence({ { "Columns", uno::Any(sal_Int32(2)) } })); dispatchCommand(mxComponent, ".uno:InsertSection", aArgs); uno::Reference xTextSectionsSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xSections(xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY); uno::Reference xTextSection(xSections->getByIndex(0), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); uno::Reference xTextColumns = getProperty>(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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); uno::Reference xTable(xTables->getByIndex(1), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(OUString("Table16"), xTable->getName()); uno::Reference xRowSupplier(xTable, uno::UNO_QUERY); uno::Reference xRows = xRowSupplier->getRows(); uno::Reference 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 xModel(mxComponent, uno::UNO_QUERY); uno::Reference xController(xModel->getCurrentController(), uno::UNO_QUERY); uno::Reference xViewCursor = xController->getViewCursor(); // This failed as there were no TextParagraph property. auto xParagraph = getProperty>(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(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(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(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(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(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(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(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 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 xPara = getParagraph(2); sal_uInt16 nPageNumber = getProperty(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 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 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 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( 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(SwCursorShell::GetFieldAtCursor(pCursor, true)); CPPUNIT_ASSERT(pField); OUString sColumn = static_cast(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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables->getCount()); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); CPPUNIT_ASSERT(xTableNames->hasByName("Table2")); uno::Reference xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY); uno::Reference 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 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 xTables2(xTablesSupplier->getTextTables(), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables2->getCount()); uno::Reference xTable1b(xTableNames->getByName("Table1"), uno::UNO_QUERY); uno::Reference 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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTables->getCount()); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); CPPUNIT_ASSERT(xTableNames->hasByName("Table2")); uno::Reference xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY); uno::Reference 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 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 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 xTable1b(xTableNames->getByName("Table1"), uno::UNO_QUERY); uno::Reference 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 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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); uno::Reference 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(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 xCellA1(xTable1->getCellByName("A1"), uno::UNO_QUERY); xCellA1->setString("A1"); uno::Reference xCellB1(xTable1->getCellByName("B1"), uno::UNO_QUERY); xCellB1->setString("B1"); uno::Reference 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 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 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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); uno::Reference 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 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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); uno::Reference 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(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 xCellA1(xTable1->getCellByName("A1"), uno::UNO_QUERY); xCellA1->setString("A1"); uno::Reference xCellA2(xTable1->getCellByName("A2"), uno::UNO_QUERY); xCellA2->setString("A2"); uno::Reference xCellA3(xTable1->getCellByName("A3"), uno::UNO_QUERY); xCellA3->setString("A3"); uno::Reference 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 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 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 xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTableNames = xTablesSupplier->getTextTables(); CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); uno::Reference 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 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 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 xDrawPagesSupplier(mxComponent, uno::UNO_QUERY); CPPUNIT_ASSERT_MESSAGE("XDrawPagesSupplier interface is unavailable", xDrawPagesSupplier.is()); uno::Reference 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 xDrawPageFromXDrawPages(aDrawPage, uno::UNO_QUERY); CPPUNIT_ASSERT(xDrawPageFromXDrawPages.is()); uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference 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 xParagraph = getParagraph(2, "1\t1"); auto aTabs = getProperty>(xParagraph, "ParaTabStops"); CPPUNIT_ASSERT_EQUAL(static_cast(1), aTabs.getLength()); CPPUNIT_ASSERT_EQUAL(static_cast(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>(xParagraph, "ParaTabStops"); CPPUNIT_ASSERT_EQUAL(static_cast(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(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 xBookmarksSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xText1; uno::Reference xText2; { uno::Reference xBookmark( xBookmarksSupplier->getBookmarks()->getByName("Bookmark 1"), uno::UNO_QUERY); xText1 = xBookmark->getAnchor()->getText(); } { uno::Reference 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 xTable(getParagraphOrTable(1), uno::UNO_QUERY); uno::Reference 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(pDoc->GetNodes()[nIndex])->GetText()); rUndoManager.Undo(); CPPUNIT_ASSERT_EQUAL(OUString("i"), static_cast(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 xFactory(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xProps( xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW); // Check font embedding state CPPUNIT_ASSERT_EQUAL(false, xProps->getPropertyValue("EmbedFonts").get()); CPPUNIT_ASSERT_EQUAL(false, xProps->getPropertyValue("EmbedOnlyUsedFonts").get()); // Font scripts should be enabled by default, however this has no effect unless "EmbedOnlyUsedFonts" is enabled CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedLatinScriptFonts").get()); CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedAsianScriptFonts").get()); CPPUNIT_ASSERT_EQUAL(true, xProps->getPropertyValue("EmbedComplexScriptFonts").get()); // 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 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 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 xLngSvcMgr(GetLngSvcMgr_Impl()); uno::Reference xSpell; xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY); LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString())); if (xSpell.is() && xSpell->hasLanguage(static_cast(eLang))) { // Type a correct word SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); emulateTyping(*pTextDoc, u"the "); SwCursorShell* pShell(pDoc->GetEditShell()); SwTextNode* pNode = pShell->GetCursor()->GetPointNode().GetTextNode(); // no bad word CPPUNIT_ASSERT_EQUAL(static_cast(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 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 xLngSvcMgr(GetLngSvcMgr_Impl()); uno::Reference xSpell; xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY); LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString())); if (xSpell.is() && xSpell->hasLanguage(static_cast(eLang))) { SwXTextDocument* pTextDoc = dynamic_cast(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 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 xLngSvcMgr(GetLngSvcMgr_Impl()); uno::Reference xSpell; xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY); LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString())); if (xSpell.is() && xSpell->hasLanguage(static_cast(eLang))) { uno::Reference xLinguProperties( LinguMgr::GetLinguPropertySet()); // Spell with digits is disabled by default CPPUNIT_ASSERT_EQUAL(sal_False, xLinguProperties->getIsSpellWithDigits()); SwXTextDocument* pTextDoc = dynamic_cast(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(mxComponent.get()); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); const SwViewOption* pOpt = pWrtShell->GetViewOptions(); uno::Sequence 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 xLngSvcMgr(GetLngSvcMgr_Impl()); uno::Reference xSpell; xSpell.set(xLngSvcMgr->getSpellChecker(), UNO_QUERY); LanguageType eLang = LanguageTag::convertToLanguageType(lang::Locale("en", "US", OUString())); if (xSpell.is() && xSpell->hasLanguage(static_cast(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("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(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(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(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(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(mxComponent.get()); emulateTyping(*pTextDoc, u"1. Item 1"); SwXTextDocument* pXTextDocument = dynamic_cast(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(getParagraph(1), "ListLabelString")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf106164) { createSwDoc(); SwDoc* pDoc = getSwDoc(); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); SwXTextDocument* pTextDoc = dynamic_cast(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(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(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(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(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(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(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(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(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(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 xRun = getRun(getParagraph(1), 2); CPPUNIT_ASSERT_EQUAL(OUString("Bolt"), xRun->getString()); CPPUNIT_ASSERT_EQUAL(OUString("Arial"), getProperty(xRun, "CharFontName")); //AutoCorrect inserts Table with 2 rows and 3 columns emulateTyping(*pTextDoc, u"4xx "); const uno::Reference 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(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(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(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(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 xField = getRun(getParagraph(1), 1); CPPUNIT_ASSERT_EQUAL(OUString("TextField"), getProperty(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(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(getParagraph(4), "ListLabelString")); // Numbering for newly created outline should be "2.1" CPPUNIT_ASSERT_EQUAL(OUString("2.1"), getProperty(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 aArgs(comphelper::InitPropertySequence( { { "FileName", uno::Any(createFileURL(u"hello-world.pdf")) } })); dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs); // Save and load cycle saveAndReload("writer8"); uno::Reference xShape = getShape(1); // Assert that we have a replacement graphics auto xReplacementGraphic = getProperty>(xShape, "ReplacementGraphic"); CPPUNIT_ASSERT(xReplacementGraphic.is()); auto xGraphic = getProperty>(xShape, "Graphic"); CPPUNIT_ASSERT(xGraphic.is()); // Assert that the graphic is a PDF CPPUNIT_ASSERT_EQUAL(OUString("application/pdf"), getProperty(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(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(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(getShape(1), "SurroundContour")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testHatchFill) { createSwDoc(); // Add a rectangle shape to the document. uno::Reference xFactory(mxComponent, uno::UNO_QUERY); uno::Reference 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 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 xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference 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(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(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(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 xProperties(xShape, uno::UNO_QUERY); // Check if the textbox is selected CPPUNIT_ASSERT_EQUAL(true, xProperties->getPropertyValue("TextBox").get()); // Remove the textbox dispatchCommand(mxComponent, ".uno:RemoveTextBox", {}); CPPUNIT_ASSERT_EQUAL(false, xProperties->getPropertyValue("TextBox").get()); // Readd the textbox (to run the textboxhelper::create() method) dispatchCommand(mxComponent, ".uno:AddTextBox", {}); CPPUNIT_ASSERT_EQUAL(true, xProperties->getPropertyValue("TextBox").get()); // 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 xFactory(mxComponent, uno::UNO_QUERY); uno::Reference 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 xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference 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 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(*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: */