summaryrefslogtreecommitdiff
path: root/sw/qa/extras/layout/layout3.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/qa/extras/layout/layout3.cxx')
-rw-r--r--sw/qa/extras/layout/layout3.cxx2657
1 files changed, 2657 insertions, 0 deletions
diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx
new file mode 100644
index 000000000000..e53d22c8fd1a
--- /dev/null
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -0,0 +1,2657 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <swmodeltestbase.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextSectionsSupplier.hpp>
+#include <vcl/event.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/scheduler.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/unolingu.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <anchoredobject.hxx>
+#include <fmtfsize.hxx>
+#include <wrtsh.hxx>
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <bodyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <ndtxt.hxx>
+#include <frmatr.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <unotxdoc.hxx>
+#include <rootfrm.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <svx/svdpage.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+namespace
+{
+/// Test to assert layout / rendering result of Writer.
+class SwLayoutWriter3 : public SwModelTestBase
+{
+public:
+ SwLayoutWriter3()
+ : SwModelTestBase(u"/sw/qa/extras/layout/data/"_ustr)
+ {
+ }
+};
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf134463)
+{
+ createSwDoc("tdf134463.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 621. The previous paragraph must have zero bottom border.
+ assertXPath(pXmlDoc, "/root/page/body/txt[3]/infos/prtBounds", "top", u"21");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf117188)
+{
+ createSwDoc("tdf117188.docx");
+ saveAndReload(u"writer8"_ustr);
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ OUString sWidth = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/bounds", "width");
+ OUString sHeight = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/bounds", "height");
+ // The text box must have zero border distances
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "left", u"0");
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "top", u"0");
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "width", sWidth);
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "height", sHeight);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf117187)
+{
+ createSwDoc("tdf117187.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // there should be no fly portions
+ assertXPath(
+ pXmlDoc,
+ "/root/page/body/txt/SwParaPortion/SwLineLayout/child::*[@nType='PortionType::Fly']", 0);
+}
+#if defined _WIN32 && defined _ARM64_
+// skip for windows arm64 build
+#else
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119875)
+{
+ createSwDoc("tdf119875.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ assertXPath(pXmlDoc, "//page[2]/body/section[1]", "formatName", u"S10");
+ assertXPath(pXmlDoc, "//page[2]/body/section[2]", "formatName", u"S11");
+ assertXPath(pXmlDoc, "//page[2]/body/section[3]", "formatName", u"S13");
+ assertXPath(pXmlDoc, "//page[2]/body/section[4]", "formatName", u"S14");
+ // Sections "S10" and "S13" are hidden -> their frames are zero-height
+ assertXPath(pXmlDoc, "//page[2]/body/section[1]/infos/bounds", "height", u"0");
+ assertXPath(pXmlDoc, "//page[2]/body/section[3]/infos/bounds", "height", u"0");
+
+ OUString S10Top = getXPath(pXmlDoc, "//page[2]/body/section[1]/infos/bounds", "top");
+ OUString S11Top = getXPath(pXmlDoc, "//page[2]/body/section[2]/infos/bounds", "top");
+ OUString S13Top = getXPath(pXmlDoc, "//page[2]/body/section[3]/infos/bounds", "top");
+ OUString S14Top = getXPath(pXmlDoc, "//page[2]/body/section[4]/infos/bounds", "top");
+
+ CPPUNIT_ASSERT_EQUAL(S10Top, S11Top);
+ CPPUNIT_ASSERT_EQUAL(S13Top, S14Top);
+
+ // Section "S11" had the same top value as section "S14", so they overlapped.
+ CPPUNIT_ASSERT_LESS(S14Top.toInt32(), S11Top.toInt32());
+}
+#endif
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf137523)
+{
+ createSwDoc("tdf137523-1-min.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // the problem was that in the footer, the text frames below the table
+ // had wrong height and were not visible
+ assertXPath(pXmlDoc, "/root/page/footer/txt[1]/infos/bounds", "height", u"304");
+ assertXPath(pXmlDoc, "/root/page/footer/txt[2]/infos/bounds", "height", u"191");
+ assertXPath(pXmlDoc, "/root/page/footer/txt[3]/infos/bounds", "height", u"219");
+ assertXPath(pXmlDoc, "/root/page/footer/tab/infos/bounds", "height", u"1378");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf120287)
+{
+ createSwDoc("tdf120287.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 2, TabOverMargin Word-specific compat flag did not imply
+ // default-in-Word printer-independent layout, resulting in an additional
+ // line break.
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 1);
+}
+
+auto getXPathIntAttributeValue(xmlXPathContextPtr pXmlXpathCtx, char const* const pXPath)
+ -> sal_Int32
+{
+ xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(pXPath), pXmlXpathCtx);
+ CPPUNIT_ASSERT(pXmlXpathObj->nodesetval);
+ CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlXpathObj->nodesetval));
+ auto ret
+ = sal_Int32(xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pXmlXpathObj->nodesetval, 0)));
+ xmlXPathFreeObject(pXmlXpathObj);
+ return ret;
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf128966)
+{
+ createSwDoc("tdf128966-2-min.odt");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ xmlXPathObjectPtr pXmlObj
+ = getXPathNode(pXmlDoc, "/root/page/body/tab/row/cell[@rowspan > 0][child::txt]");
+ xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
+ CPPUNIT_ASSERT(pXmlNodes);
+ CPPUNIT_ASSERT_GREATER(300, xmlXPathNodeSetGetLength(pXmlNodes)); // many...
+
+ xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc.get());
+ registerNamespaces(pXmlXpathCtx);
+
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); ++i)
+ {
+ xmlNodePtr pNode = xmlXPathNodeSetItem(pXmlNodes, i);
+ xmlXPathSetContextNode(pNode, pXmlXpathCtx);
+
+ OString msg("Cell nr.: " + OString::number(i)
+ + " id=" + OString::number(getXPathIntAttributeValue(pXmlXpathCtx, "@id")));
+
+ auto nCellTop = getXPathIntAttributeValue(pXmlXpathCtx, "infos/bounds/@top");
+ auto nCellHeight = getXPathIntAttributeValue(pXmlXpathCtx, "infos/bounds/@height");
+ auto nCellCenter = nCellTop + (nCellHeight / 2);
+
+ auto nContentTop
+ = getXPathIntAttributeValue(pXmlXpathCtx, "txt[position()=1]/infos/bounds/@top");
+ auto nContentBottom = getXPathIntAttributeValue(
+ pXmlXpathCtx, "txt[position()=last()]/infos/bounds/@bottom");
+
+ CPPUNIT_ASSERT_MESSAGE(msg.getStr(), nContentTop < nCellCenter);
+ CPPUNIT_ASSERT_MESSAGE(msg.getStr(), nContentBottom > nCellCenter);
+ }
+
+ xmlXPathFreeContext(pXmlXpathCtx);
+ xmlXPathFreeObject(pXmlObj);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf161718)
+{
+ createSwDoc("tdf161718.docx");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // everything on one page
+ assertXPath(pXmlDoc, "/root/page/header", 1);
+ assertXPath(pXmlDoc, "/root/page/header/txt/anchored", 1);
+ assertXPath(pXmlDoc, "/root/page/footer", 1);
+ assertXPath(pXmlDoc, "/root/page/ftncont/ftn", 1);
+ assertXPath(pXmlDoc, "/root/page/ftncont/ftn/txt", 1);
+ assertXPath(pXmlDoc, "/root/page/body/txt", 27);
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored", 1);
+ assertXPath(pXmlDoc, "/root/page", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908)
+{
+ createSwDoc("tdf130088.docx");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Keep real width of the exceeding line portions to calculate shrinking
+ sal_Int32 nPortionWidth
+ = getXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[2]/SwLinePortion[2]",
+ "width")
+ .toInt32();
+ // This was 5806 (not real portion width, but stripped to the line width)
+ CPPUNIT_ASSERT_GREATER(sal_Int32(5840), nPortionWidth);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158333)
+{
+ createSwDoc("tdf130088.docx");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // shrink line 2
+ assertXPath(
+ pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[2]", "portion",
+ u"viverra odio. Donec auctor molestie sem, sit amet tristique lectus hendrerit sed. ");
+
+ // shrink line 7
+ assertXPath(
+ pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[7]", "portion",
+ // This was "...diam ", not "...diam tempor "
+ u"laoreet vel leo nec, volutpat facilisis eros. Donec consequat arcu ut diam tempor ");
+
+ // shrink line 2 of paragraph 2
+ assertXPath(
+ pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[2]", "portion",
+ // This was "...Cras ", not "...Cras sodales "
+ u"Donec auctor molestie sem, sit amet tristique lectus hendrerit sed. Cras sodales ");
+
+ // shrink line 2 of paragraph 4
+ assertXPath(pXmlDoc, "/root/page/body/txt[4]/SwParaPortion/SwLineLayout[2]", "portion",
+ // This was "...et ", not "...et magnis "
+ u"consequat arcu ut diam tempor luctus. Cum sociis natoque penatibus et magnis ");
+
+ // tdf#158776 don't shrink line 11 of paragraph 4
+ assertXPath(pXmlDoc, "/root/page/body/txt[4]/SwParaPortion/SwLineLayout[11]", "portion",
+ // This was "...quis curcus ", not "...quis "
+ u"venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf159085)
+{
+ createSwDoc("tdf159085.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // This was "... cursus" instead of breaking the word at soft hyphen
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis cur­");
+
+ // This was "... cursus" instead of breaking the word at soft hyphen
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis cur­");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158419)
+{
+ createSwDoc("tdf130088.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwDocShell* pShell = getSwDocShell();
+
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // second paragraph.
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwWrtShell* pWrtShell = pShell->GetWrtShell();
+ SwPosition aPosition(*pWrtShell->GetCursor()->Start());
+ SwTwips nSecondParaLeft
+ = getXPath(pXmlDoc, "/root/page/body/txt[2]/infos/bounds", "left").toInt32();
+ SwTwips nSecondParaWidth
+ = getXPath(pXmlDoc, "/root/page/body/txt[2]/infos/bounds", "width").toInt32();
+ SwTwips nSecondParaTop
+ = getXPath(pXmlDoc, "/root/page/body/txt[2]/infos/bounds", "top").toInt32();
+ SwTwips nSecondParaHeight
+ = getXPath(pXmlDoc, "/root/page/body/txt[2]/infos/bounds", "height").toInt32();
+ Point aPoint;
+
+ // click at the end of the second line of the second paragraph
+ // (a line shrunk by the new justification)
+
+ aPoint.setX(nSecondParaLeft + nSecondParaWidth);
+ aPoint.setY(nSecondParaTop + (nSecondParaHeight / 6) * 1.5);
+ SwCursorMoveState aState(CursorMoveState::NONE);
+ pLayout->GetModelPositionForViewPoint(&aPosition, aPoint, &aState);
+ // Without the accompanying fix in place, this test would have failed: character position was 155,
+ // i.e. cursor was before the end of the paragraph.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(156), aPosition.GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf163042)
+{
+ createSwDoc("tdf163042.fodt");
+ SwDoc* pDoc = getSwDoc();
+ SwDocShell* pShell = getSwDocShell();
+
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 1-line paragraph
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwWrtShell* pWrtShell = pShell->GetWrtShell();
+ SwPosition aPosition(*pWrtShell->GetCursor()->Start());
+ SwTwips nParaLeft = getXPath(pXmlDoc, "/root/page/body/txt[1]/infos/bounds", "left").toInt32();
+ SwTwips nParaWidth
+ = getXPath(pXmlDoc, "/root/page/body/txt[1]/infos/bounds", "width").toInt32();
+ SwTwips nParaTop = getXPath(pXmlDoc, "/root/page/body/txt[1]/infos/bounds", "top").toInt32();
+ SwTwips nParaHeight
+ = getXPath(pXmlDoc, "/root/page/body/txt[1]/infos/bounds", "height").toInt32();
+ Point aPoint;
+
+ // click before the last but one character of the paragraph
+ // (in a line shrunk by the new space shrinking justification)
+
+ aPoint.setX(nParaLeft + nParaWidth - 2 * nParaWidth / 160);
+ aPoint.setY(nParaTop + nParaHeight * 0.5);
+ SwCursorMoveState aState(CursorMoveState::NONE);
+ pLayout->GetModelPositionForViewPoint(&aPosition, aPoint, &aState);
+ // Without the accompanying fix in place, this test would have failed: character position was 160,
+ // i.e. cursor was at the end of the paragraph instead of the last but one character
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(158), aPosition.GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf163060)
+{
+ createSwDoc("tdf163060.fodt");
+
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // There is only a single shrunk line 1, without breaking the last word
+ // before the last text portion "i"
+
+ // This ends in "dolorsi" (not "dolors", as before)
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"Quis pretium semper. Proin luctus orci a neque venenatis, quis commodo dolorsi");
+
+ // no second line (there was a second line with the text portion "i").
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf162109)
+{
+ createSwDoc("tdf162109.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // There was no SwGluePortion, because of missing justification of the last paragraph line,
+ // despite it is a full line with shrunk spaces
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf162220)
+{
+ createSwDoc("tdf162220.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // There was no SwGluePortion, because of missing justification of the last paragraph line,
+ // despite it is a full line with shrunk spaces
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf163720)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf163720.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Find the first text array action
+ for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+ {
+ auto pAction = xMetaFile->GetAction(nAction);
+ if (pAction->GetType() == MetaActionType::TEXTARRAY)
+ {
+ auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+ auto pDXArray = pTextArrayAction->GetDXArray();
+
+ // There should be 101 chars on the first line
+ CPPUNIT_ASSERT_EQUAL(size_t(101), pDXArray.size());
+
+ // Assert we are using the expected position for the last char
+ // This was 10093, now 10003, according to the less shrinking,
+ // than needed for the extra hyphen glyph at hyphenation
+ CPPUNIT_ASSERT_LESS(sal_Int32(10010), sal_Int32(pDXArray[100]));
+ break;
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf162725)
+{
+ createSwDoc("tdf162725.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // There was no SwGluePortion, because of missing justification of the last paragraph line,
+ // despite it is a full line with shrunk spaces
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf161810)
+{
+ createSwDoc("tdf161810.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Find the first text array action
+ for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+ {
+ auto pAction = xMetaFile->GetAction(nAction);
+ if (pAction->GetType() == MetaActionType::TEXTARRAY)
+ {
+ auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+ auto pDXArray = pTextArrayAction->GetDXArray();
+
+ // There should be 70 chars on the first line
+ // (tdf#164499 no space shrinking in lines with tabulation)
+ CPPUNIT_ASSERT_EQUAL(size_t(70), pDXArray.size());
+
+ break;
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf164905)
+{
+ createSwDoc("tdf164905.docx");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 9 (resulting broken ToC layout)
+ assertXPath(pXmlDoc, "//SwGluePortion", 3);
+ // For example, it was an unnecessary glue portion here
+ assertXPath(pXmlDoc,
+ "/root/page/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/SwGluePortion", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf163149)
+{
+ createSwDoc("tdf163149.docx");
+ // Ensure that all text portions are calculated before testing.
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Find the text array action for the second non-empty (shrunk) line
+ bool bFirst = true;
+ for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+ {
+ auto pAction = xMetaFile->GetAction(nAction);
+ if (pAction->GetType() == MetaActionType::TEXTARRAY)
+ {
+ auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+ auto pDXArray = pTextArrayAction->GetDXArray();
+
+ // skip empty paragraphs
+ if (pDXArray.size() <= 1)
+ continue;
+
+ // skip first non-empty line
+ if (bFirst)
+ {
+ bFirst = false;
+ continue;
+ }
+
+ // There should be 46 chars on the second line
+ CPPUNIT_ASSERT_EQUAL(size_t(46), pDXArray.size());
+
+ // Assert we are using the expected position for the last char
+ // This was 4673, now 4163, according to the fixed space shrinking
+ CPPUNIT_ASSERT_LESS(sal_Int32(4200), sal_Int32(pDXArray[45]));
+ break;
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf164499)
+{
+ createSwDoc("tdf164499.docx");
+
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // no shrinking in tabulated text lines
+
+ // This was 1 (no line break in heading 2.5.5)
+ assertXPath(pXmlDoc, "/root/page[1]/body/section/txt[18]/SwParaPortion/SwLineLayout", 2);
+ // line break in heading 2.5.5: the second line contains only the page number
+ assertXPath(pXmlDoc, "/root/page[1]/body/section/txt[18]/SwParaPortion/SwLineLayout[2]",
+ "portion", u"*1");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf132599_always)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf132599_always.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 2nd paragraph: hyphenated last full line
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[2]", "portion",
+ u"ent to any other celes");
+
+ // hyphenation-keep-type='always'
+ // 3rd paragraph: not hyphenated last full line of the hyphenated paragraph
+ assertXPath(pXmlDoc, "/root/page/body/txt[3]/SwParaPortion/SwLineLayout[2]", "portion",
+ u"ent to any other ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf132599_frames_on_same_page_no_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf132599_frames_on_same_page_no_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="column"
+ // 2nd frame: shifted hyphenated line (no hyphenation at the end of the first frame)
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly[2]/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"space, ex");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf132599_frames_on_same_page_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf132599_frames_on_same_page_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="page"
+ // 2nd frame: not shifted hyphenated line (hyphenation at the end of the first frame),
+ // This was "space, ex" (bad shifting)
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly[2]/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"cept that it ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf132599_frames_on_right_pages_no_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf132599_frames_on_right_pages_no_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="spread"
+ // 2nd frame: shifted hyphenated line
+ // This was "cept that it" (missing shifting)
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"space, ex");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf132599_frames_on_spread_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf132599_frames_on_spread_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="spread"
+ // 2nd frame on left page and 3rd frame on right page -> not shifted hyphenated line
+ // 2nd frame: not shifted hyphenated line (hyphenation at the end of the first frame),
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"cept that it ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_page)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_page.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-line="true"
+ // first: shifted hyphenated word
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[9]", "portion",
+ u"except that it has an ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_spread)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_spread.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-line="true"
+ // first: shifted hyphenated word at end of the spread (right page)
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[9]", "portion",
+ u"except that it has an ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_spread_left_page)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_spread-left-page.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-line="true"
+ // first: no shifted hyphenated word at the end of the first page of the spread (left page)
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[2]/SwParaPortion/SwLineLayout[9]", "portion",
+ u"except that it has an at");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_column)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_column.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="column" hyphenation-keep-line="true"
+ // 2nd frame: shifted hyphenated word (no hyphenation at the end of the first column)
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section/column[2]/body/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"iner");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_page_in_last_column)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_page_in_last_column.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="page" hyphenation-keep-line="true"
+ // the end line of the first page is a column boundary,
+ // but at the page boundary, too, so disable its hyphenation
+ // 2nd frame: shifted hyphenated word (no hyphenation at the end of the first column)
+ assertXPath(pXmlDoc,
+ "/root/page[2]/body/section/column[1]/body/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"iner");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_page_in_not_last_column)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_page_in_not_last_column.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="page" hyphenation-keep-line="true"
+ // 2nd frame: no shifted hyphenated word (hyphenation at the end of the first column)
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section/column[2]/body/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"tially. ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_page_in_table)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_page_in_table.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="page" hyphenation-keep-line="true"
+ // 2nd frame: no shifted hyphenated word (hyphenation at the end of the first column)
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab/row/cell/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"atmosphere. The Earth ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_frames_on_same_page_no_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_frames_on_same_page_no_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="column" hyphenation-keep-line="true"
+ // 2nd frame: shifted hyphenated word (no hyphenation at the end of the first frame)
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly[2]/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"except that ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_frames_on_same_page_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_frames_on_same_page_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="page" hyphenation-keep-line="true"
+ // 2nd frame: not shifted hyphenated word (hyphenation at the end of the first frame),
+ assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly[2]/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"cept that it ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_frames_on_spread_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_frames_on_spread_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-type="spread" hyphenation-keep-line="true"
+ // 2nd frame on left page and 3rd frame on right page -> not shifted hyphenated word
+ // 2nd frame: not shifted hyphenated word (hyphenation at the end of the first frame),
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[1]",
+ "portion", u"cept that it ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, tdf165354_frames_on_right_pages_no_hyphenation)
+{
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_frames_on_right_pages_no_hyphenation.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // loext:hyphenation-keep-line="true"
+ // It was "space, ex": missing layout update of the last line with disabled hyphenation
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[12]",
+ "portion", u"space, ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_long_paragraph)
+{
+ // disabled hyphenation on page 1 (no hyphenation at all on page 2,
+ // only in first line of page 3, which resulted broken layout)
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_long_paragraph.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 3-page paragraph, loext:hyphenation-keep-line="true"
+ // This started with "tially" (not disabled hyphenation, because of
+ // the first hyphenated line on the third page)
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[1]", "portion",
+ u"inertially. Even just one ");
+
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[12]", "portion",
+ u"of the Earth is space, ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_long_paragraph_2)
+{
+ // disabled hyphenation on page 1 and page 2
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_long_paragraph_2.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 3-page paragraph, loext:hyphenation-keep-line="true"
+ // This started with "tially" (not disabled hyphenation, because of
+ // the first hyphenated line on the third page)
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[1]", "portion",
+ u"inertially. Even just one ");
+
+ // disabled hyphenation by loext:hyphenation-keep-type="page"
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[12]", "portion",
+ u"of the Earth is space ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165354_long_paragraph_3)
+{
+ // disabled hyphenation on page 1, enabled on page 2
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165354_long_paragraph_3.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 3-page paragraph, loext:hyphenation-keep-line="true"
+ // This started with "tially" (not disabled hyphenation, because of
+ // the first hyphenated line on the third page)
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[1]", "portion",
+ u"inertially. Even just one ");
+
+ // not disabled hyphenation by loext:hyphenation-keep-type="spread"
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[12]", "portion",
+ u"of the Earth is space ex");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165984)
+{
+ // enabled hyphenation on page 1, disabled on page 2 by hyphenation-zone-page
+ // (no hyphenation, if the word part is completely inside the Page end zone:
+ // "iner-tially", but "except" and not "ex-cept")
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf165984.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // paragraph with loext:hyphenation-zone-paragraph="7.62cm"
+ // This was "tially. Even" (now disabled hyphenation in the last full paragraph line)
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout[6]", "portion",
+ u"inertially. Even");
+
+ // 3-page paragraph, loext:hyphenation-zone-page="5.08cm"
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[1]", "portion",
+ u"tially. Even just one ");
+
+ // disabled hyphenation by hyphenation-zone-page="5.08cm"
+ // This ended with "ex-"
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt/SwParaPortion/SwLineLayout[12]", "portion",
+ u"of the Earth is space ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154)
+{
+ // minimum, desired and maximum word spacing
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf126154.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // only desired word space: 100%, 100%, 100%
+ // 5 lines are hyphenated
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+
+ // also minimum word space: 80%, 100%, 100%
+ // only a single line was hyphenated from the previous ones
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ // This was "... bulum c" (more shrinking, than needed to remove hyphenation)
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[12]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+
+ // minimum, desired and maximum word spacing: 80%, 100%, 133%
+ // no hyphenation in the same text: hyphenation of all the short words were limited
+ // by the minimum and maximum word spacing settings
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ // This was "... bulum c" (more shrinking, than needed to remove hyphenation)
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[20]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[23]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf106234)
+{
+ createSwDoc("tdf106234.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // In justified paragraphs, there is justification between left tabulators and manual line breaks
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
+ "type", u"PortionType::Margin");
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
+ "width", u"0");
+ // but not after centered, right and decimal tabulators
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
+ "type", u"PortionType::Margin");
+ // This was a justified line, without width
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
+ "width", u"7882");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_minimum_shrinking)
+{
+ // minimum, desired and maximum word spacing
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf126154_minimum_shrinking.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // only desired word space: 100%, 100%, 100%
+ // 5 lines are hyphenated
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+
+ // also minimum word space: 80%, 100%, 100%
+ // only a single line was hyphenated from the previous ones
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[12]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+
+ // minimum, desired and maximum word spacing: 80%, 100%, 133%
+ // no hyphenation in the same text: hyphenation of all the short words were limited
+ // by the minimum and maximum word spacing settings
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[20]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bulum ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ u"Vesti bu");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[23]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+ assertXPath(
+ pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_portion)
+{
+ // text portions with word spacing, paragraph end zone and hyphenation zone
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
+ return;
+
+ createSwDoc("tdf126154_portion.fodt");
+ // Ensure that all text portions are calculated before testing.
+ SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // only maximum word spacing: 133%
+ // This was "... Vesti bu" (not disabled hyphenation because of text portion)
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,,,,,,,,,,,,,,, , , , , , , , , , , , , , , , Lorem ipsum dolor sit amet, "
+ u"consectetur adipiscing elit. Vesti ");
+
+ // minimum: 80%, desired: 100%, maximum word spacing: 133%
+ // prefer maximum word spacing, not minimum word spacing, if the weighted word space
+ // is nearer to the desired word space
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
+ u",,,,,,,,,,,,,,,,,,,,, , , , , , , , , , , , , , , , Lorem ipsum dolor sit amet, "
+ u"consectetur adipiscing elit. Vesti ");
+
+ // paragraph end zone
+ // This was "... other celes" (not disabled hyphenation because of text portion)
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[8]", "portion",
+ u"cally atmospherically atmospherically atmospherically. The Earth is no different "
+ u"to any other ");
+
+ // hyphenation
+ // This was "... other celes" (not disabled hyphenation because of text portion)
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[8]", "portion",
+ u"cally atmospherically atmospherically atmospherically. The Earth is no different "
+ u"to any other ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf155324)
+{
+ createSwDoc("tox-update-wrong-pages.odt");
+
+ dispatchCommand(mxComponent, u".uno:UpdateAllIndexes"_ustr, {});
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // the problem was that the first entry was on page 7, 2nd on page 9 etc.
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
+ "portion", u"Foo");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
+ "portion", u"5");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
+ "portion", u"bar");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
+ "portion", u"7");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[3]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
+ "portion", u"Three");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/section[2]/txt[3]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
+ "portion", u"7");
+
+ // check first content page has the footnotes
+ assertXPath(pXmlDoc, "/root/page[5]/body/txt[1]/SwParaPortion/SwLineLayout", "portion", u"Foo");
+ assertXPath(pXmlDoc, "/root/page[4]/ftncont", 0);
+ assertXPath(pXmlDoc, "/root/page[5]/ftncont/ftn", 5);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf120287b)
+{
+ createSwDoc("tdf120287b.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 1418, TabOverMargin did the right split of the paragraph to two
+ // lines, but then calculated a too large tab portion size on the first
+ // line.
+ assertXPath(
+ pXmlDoc,
+ "/root/page/body/txt[1]/SwParaPortion/SwLineLayout/child::*[@type='PortionType::TabRight']",
+ "width", u"1");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf120287c)
+{
+ createSwDoc("tdf120287c.fodt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was 3, the second line was broken into a 2nd and a 3rd one,
+ // not rendering text outside the paragraph frame like Word 2013 does.
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158658a)
+{
+ createSwDoc("tdf158658a.rtf");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Word 2013 puts all tabs into one line, the last 8 of them are off the page
+ assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout", 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabCenter']",
+ 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabRight']",
+ 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabLeft']",
+ 9);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158658b)
+{
+ createSwDoc("tdf158658b.rtf");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Word 2013 puts all tabs and the field following into one line
+ // and also puts the field off the page
+ assertXPath(pXmlDoc, "/root/page[1]/footer/txt[1]/SwParaPortion/SwLineLayout", 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/footer/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabCenter']",
+ 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/footer/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabRight']",
+ 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/footer/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabRight']",
+ "width", u"4446"); // was very small: 24
+ assertXPath(pXmlDoc,
+ "/root/page[1]/footer/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabLeft']",
+ 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158658c)
+{
+ createSwDoc("tdf158658c.rtf");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Word 2013 puts all tabs into one line, the last 17 of them are off the page
+ assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout", 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabCenter']",
+ 1);
+ // the right tab is exactly at the margin of the paragraph
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabRight']",
+ 1);
+ assertXPath(pXmlDoc,
+ "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::TabLeft']",
+ 20);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf155177)
+{
+ createSwDoc("tdf155177-1-min.odt");
+
+ uno::Reference<beans::XPropertySet> xStyle(
+ getStyles(u"ParagraphStyles"_ustr)->getByName(u"Text body"_ustr), uno::UNO_QUERY_THROW);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(210), getProperty<sal_Int32>(xStyle, u"ParaTopMargin"_ustr));
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 6);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[6]/SwParaPortion/SwLineLayout", 2);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[6]/SwParaPortion/SwLineLayout[2]", "portion",
+ u"long as two lines.");
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 3);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"This paragraph is even longer so that ");
+ }
+
+ // this should bring one line back
+ xStyle->setPropertyValue(u"ParaTopMargin"_ustr, uno::Any(sal_Int32(200)));
+
+ Scheduler::ProcessEventsToIdle();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 7);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"This paragraph is even longer so that ");
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 2);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"it is now three lines long though ");
+ }
+
+ // this should bring second line back
+ xStyle->setPropertyValue(u"ParaTopMargin"_ustr, uno::Any(sal_Int32(120)));
+
+ Scheduler::ProcessEventsToIdle();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 7);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout", 2);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"This paragraph is even longer so that ");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[2]", "portion",
+ u"it is now three lines long though ");
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 1);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
+ u"containing a single sentence.");
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf122878)
+{
+ createSwDoc("tdf122878.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ const sal_Int32 nTblTop
+ = getXPath(pXmlDoc, "/root/page[1]/footer/txt/anchored/fly/tab/infos/bounds", "top")
+ .toInt32();
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
+ CPPUNIT_ASSERT(pPage1);
+ SwFrame* pBody = pPage1->FindBodyCont();
+ for (SwFrame* pFrame = pBody->GetLower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ const sal_Int32 nTxtBottom = pFrame->getFrameArea().Bottom();
+ // No body paragraphs should overlap the table in the footer
+ CPPUNIT_ASSERT_MESSAGE(
+ OString("testing paragraph #" + OString::number(pFrame->GetFrameId())).getStr(),
+ nTxtBottom <= nTblTop);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf115094)
+{
+ createSwDoc("tdf115094.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ sal_Int32 nTopOfD1
+ = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/infos/bounds",
+ "top")
+ .toInt32();
+ sal_Int32 nTopOfD1Anchored = getXPath(pXmlDoc,
+ "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/"
+ "txt[2]/anchored/fly/infos/bounds",
+ "top")
+ .toInt32();
+ CPPUNIT_ASSERT_LESS(nTopOfD1Anchored, nTopOfD1);
+ sal_Int32 nTopOfB2
+ = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/infos/bounds",
+ "top")
+ .toInt32();
+ sal_Int32 nTopOfB2Anchored = getXPath(pXmlDoc,
+ "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/"
+ "txt[1]/anchored/fly/infos/bounds",
+ "top")
+ .toInt32();
+ CPPUNIT_ASSERT_LESS(nTopOfB2Anchored, nTopOfB2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf112290)
+{
+ createSwDoc("tdf112290.docx");
+ auto pXml = parseLayoutDump();
+ assertXPath(pXml, "/root/page/body/txt/SwParaPortion/SwLineLayout[2]", "portion", u"Xxxx Xxxx");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testKeepWithNextPlusFlyFollowTextFlow)
+{
+ createSwDoc("keep-with-next-fly.fodt");
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // 3 text frames on page 1
+ assertXPath(pXmlDoc, "/root/page[1]/body/infos/bounds", "bottom", u"7540");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly/infos/bounds", "top", u"1694");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page", 1);
+ }
+
+ // disable Field Names warning dialog
+ const bool bAsk = officecfg::Office::Common::Misc::QueryShowFieldName::get();
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges;
+ if (bAsk)
+ {
+ xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(false, xChanges);
+ xChanges->commit();
+ }
+
+ dispatchCommand(mxComponent, u".uno:Fieldnames"_ustr, {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // 1 text frame on page 1, and some empty space
+ assertXPath(pXmlDoc, "/root/page[1]/body/infos/bounds", "bottom", u"7540");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/infos/bounds", "height", u"5796");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/infos/bounds", "bottom", u"7213");
+ // 2 text frames on page 2
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly/infos/bounds", "top",
+ u"10093");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[2]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page", 2);
+ }
+
+ dispatchCommand(mxComponent, u".uno:Fieldnames"_ustr, {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // 3 text frames on page 1
+ assertXPath(pXmlDoc, "/root/page[1]/body/infos/bounds", "bottom", u"7540");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly/infos/bounds", "top", u"1694");
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/infos/bounds", "height", u"276");
+ assertXPath(pXmlDoc, "/root/page", 1);
+ }
+
+ if (bAsk)
+ {
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(true, xChanges);
+ xChanges->commit();
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf122607)
+{
+ createSwDoc("tdf122607.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc,
+ "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
+ "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
+ "height", u"253");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
+ "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
+ "width", u"427");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
+ "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
+ "portion", u"Fax:");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf122607_regression)
+{
+ // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
+ // does *not* forward initial VCL Window Resize and thereby triggers a
+ // layout which does not happen on soffice --convert-to pdf.
+ std::vector<beans::PropertyValue> aFilterOptions = {
+ { beans::PropertyValue(u"Hidden"_ustr, -1, uno::Any(true),
+ beans::PropertyState_DIRECT_VALUE) },
+ };
+
+ // inline the loading because currently properties can't be passed...
+ OUString const url(createFileURL(u"tdf122607_leerzeile.odt"));
+ loadWithParams(url, comphelper::containerToSequence(aFilterOptions));
+ save(u"writer_pdf_Export"_ustr);
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // somehow these 2 rows overlapped in the PDF unless CalcLayout() runs
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "mbFixSize",
+ u"false");
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "top", u"2977");
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "height", u"241");
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "mbFixSize",
+ u"true");
+ // this was 3034, causing the overlap
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "top", u"3218");
+ assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "height", u"164");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf150616)
+{
+ // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
+ // does *not* forward initial VCL Window Resize and thereby triggers a
+ // layout which does not happen on soffice --convert-to pdf.
+ std::vector<beans::PropertyValue> aFilterOptions = {
+ { beans::PropertyValue(u"Hidden"_ustr, -1, uno::Any(true),
+ beans::PropertyState_DIRECT_VALUE) },
+ };
+
+ // inline the loading because currently properties can't be passed...
+ OUString const url(createFileURL(u"in_056132_mod.odt"));
+ loadWithParams(url, comphelper::containerToSequence(aFilterOptions));
+ save(u"writer_pdf_Export"_ustr);
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // this one was 0 height
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/SwParaPortion/SwLineLayout",
+ "portion", u"Important information here!");
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/infos/bounds", "height",
+ u"253");
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/infos/bounds", "top",
+ u"7925");
+ assertXPath(pXmlDoc,
+ "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/SwParaPortion/SwLineLayout",
+ "portion", u"xxx 111 ");
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/infos/bounds", "height",
+ u"697");
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/infos/bounds", "top",
+ u"8178");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testBtlrCell)
+{
+ createSwDoc("btlr-cell.odt");
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Without the accompanying fix in place, this test would have failed, as
+ // the orientation was 0 (layout did not take btlr direction request from
+ // doc model).
+ assertXPath(pXmlDoc, "//font[1]", "orientation", u"900");
+
+#if !defined(MACOSX) && !defined(_WIN32) // macOS fails with x == 2662 for some reason.
+ // Without the accompanying fix in place, this test would have failed with 'Expected: 1915;
+ // Actual : 1756', i.e. the AAA1 text was too close to the left cell border due to an ascent vs
+ // descent mismatch when calculating the baseline offset of the text portion.
+ assertXPath(pXmlDoc, "//textarray[1]", "x", u"1915");
+ assertXPath(pXmlDoc, "//textarray[1]", "y", u"2707");
+
+ // Without the accompanying fix in place, this test would have failed with 'Expected: 1979;
+ // Actual : 2129', i.e. the gray background of the "AAA2." text was too close to the right edge
+ // of the text portion. Now it's exactly behind the text portion.
+ assertXPath(pXmlDoc, "(//rect)[2]", "left", u"1979");
+
+ // Without the accompanying fix in place, this test would have failed with 'Expected: 269;
+ // Actual : 0', i.e. the AAA2 frame was not visible due to 0 width.
+ pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width", u"269");
+
+ // Test the position of the cursor after doc load.
+ // We expect that it's inside the first text frame in the first cell.
+ // More precisely, this is a bottom to top vertical frame, so we expect it's at the start, which
+ // means it's at the lower half of the text frame rectangle (vertically).
+ SwWrtShell* pWrtShell = pShell->GetWrtShell();
+ CPPUNIT_ASSERT(pWrtShell);
+
+ const SwRect& rCharRect = pWrtShell->GetCharRect();
+ SwTwips nFirstParaTop
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "top").toInt32();
+ SwTwips nFirstParaHeight
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "height")
+ .toInt32();
+ SwTwips nFirstParaMiddle = nFirstParaTop + nFirstParaHeight / 2;
+ SwTwips nFirstParaBottom = nFirstParaTop + nFirstParaHeight;
+ // Without the accompanying fix in place, this test would have failed: the lower half (vertical)
+ // range was 2273 -> 2835, the good vertical position is 2730, the bad one was 1830.
+ CPPUNIT_ASSERT_GREATER(nFirstParaMiddle, rCharRect.Top());
+ CPPUNIT_ASSERT_LESS(nFirstParaBottom, rCharRect.Top());
+
+ // Save initial cursor position.
+ SwPosition aCellStart = *pWrtShell->GetCursor()->Start();
+
+ // Test that pressing "up" at the start of the cell goes to the next character position.
+ SwNodeOffset nNodeIndex = pWrtShell->GetCursor()->Start()->GetNodeIndex();
+ sal_Int32 nIndex = pWrtShell->GetCursor()->Start()->GetContentIndex();
+ KeyEvent aKeyEvent(0, KEY_UP);
+ SwEditWin& rEditWin = pShell->GetView()->GetEditWin();
+ rEditWin.KeyInput(aKeyEvent);
+ Scheduler::ProcessEventsToIdle();
+ // Without the accompanying fix in place, this test would have failed: "up" was interpreted as
+ // logical "left", which does nothing if you're at the start of the text anyway.
+ CPPUNIT_ASSERT_EQUAL(nIndex + 1, pWrtShell->GetCursor()->Start()->GetContentIndex());
+
+ // Test that pressing "right" goes to the next paragraph (logical "down").
+ sal_Int32 nContentIndex = pWrtShell->GetCursor()->Start()->GetContentIndex();
+ aKeyEvent = KeyEvent(0, KEY_RIGHT);
+ rEditWin.KeyInput(aKeyEvent);
+ Scheduler::ProcessEventsToIdle();
+ // Without the accompanying fix in place, this test would have failed: the cursor went to the
+ // paragraph after the table.
+ CPPUNIT_ASSERT_EQUAL(nNodeIndex + 1, pWrtShell->GetCursor()->Start()->GetNodeIndex());
+
+ // Test that we have the correct character index after traveling to the next paragraph.
+ // Without the accompanying fix in place, this test would have failed: char position was 5, i.e.
+ // the cursor jumped to the end of the paragraph for no reason.
+ CPPUNIT_ASSERT_EQUAL(nContentIndex, pWrtShell->GetCursor()->Start()->GetContentIndex());
+
+ // Test that clicking "below" the second paragraph positions the cursor at the start of the
+ // second paragraph.
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwPosition aPosition(aCellStart);
+ SwTwips nSecondParaLeft
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "left")
+ .toInt32();
+ SwTwips nSecondParaWidth
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width")
+ .toInt32();
+ SwTwips nSecondParaTop
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "top").toInt32();
+ SwTwips nSecondParaHeight
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "height")
+ .toInt32();
+ Point aPoint;
+ aPoint.setX(nSecondParaLeft + nSecondParaWidth / 2);
+ aPoint.setY(nSecondParaTop + nSecondParaHeight - 100);
+ SwCursorMoveState aState(CursorMoveState::NONE);
+ pLayout->GetModelPositionForViewPoint(&aPosition, aPoint, &aState);
+ CPPUNIT_ASSERT_EQUAL(aCellStart.GetNodeIndex() + 1, aPosition.GetNodeIndex());
+ // Without the accompanying fix in place, this test would have failed: character position was 5,
+ // i.e. cursor was at the end of the paragraph.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aPosition.GetContentIndex());
+
+ // Test that the selection rectangles are inside the cell frame if we select all the cell
+ // content.
+ SwTwips nCellLeft
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "left").toInt32();
+ SwTwips nCellWidth
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "width").toInt32();
+ SwTwips nCellTop
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "top").toInt32();
+ SwTwips nCellHeight
+ = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "height").toInt32();
+ SwRect aCellRect(Point(nCellLeft, nCellTop), Size(nCellWidth, nCellHeight));
+ pWrtShell->SelAll();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(/*bBlock=*/false);
+ CPPUNIT_ASSERT(!pShellCursor->empty());
+ // Without the accompanying fix in place, this test would have failed with:
+ // selection rectangle 269x2573@(1970,2172) is not inside cell rectangle 3207x1134@(1593,1701)
+ // i.e. the selection went past the bottom border of the cell frame.
+ for (const auto& rRect : *pShellCursor)
+ {
+ std::stringstream ss;
+ ss << "selection rectangle " << rRect << " is not inside cell rectangle " << aCellRect;
+ CPPUNIT_ASSERT_MESSAGE(ss.str(), aCellRect.Contains(rRect));
+ }
+
+ // Make sure that the correct rectangle gets repainted on scroll.
+ SwFrame* pPageFrame = pLayout->GetLower();
+ CPPUNIT_ASSERT(pPageFrame->IsPageFrame());
+
+ SwFrame* pBodyFrame = pPageFrame->GetLower();
+ CPPUNIT_ASSERT(pBodyFrame->IsBodyFrame());
+
+ SwFrame* pTabFrame = pBodyFrame->GetLower();
+ CPPUNIT_ASSERT(pTabFrame->IsTabFrame());
+
+ SwFrame* pRowFrame = pTabFrame->GetLower();
+ CPPUNIT_ASSERT(pRowFrame->IsRowFrame());
+
+ SwFrame* pCellFrame = pRowFrame->GetLower();
+ CPPUNIT_ASSERT(pCellFrame->IsCellFrame());
+
+ SwFrame* pFrame = pCellFrame->GetLower();
+ CPPUNIT_ASSERT(pFrame->IsTextFrame());
+
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pFrame);
+ pTextFrame->SwapWidthAndHeight();
+ // Mimic what normally SwTextFrame::PaintSwFrame() does:
+ SwRect aRect(4207, 2273, 269, 572);
+ pTextFrame->SwitchVerticalToHorizontal(aRect);
+ // Without the accompanying fix in place, this test would have failed with:
+ // Expected: 572x269@(1691,4217)
+ // Actual : 572x269@(2263,4217)
+ // i.e. the paint rectangle position was incorrect, text was not painted on scrolling up.
+ CPPUNIT_ASSERT_EQUAL(SwRect(1691, 4217, 572, 269), aRect);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf123898)
+{
+ createSwDoc("tdf123898.odt");
+
+ // Make sure spellchecker has done its job already
+ Scheduler::ProcessEventsToIdle();
+
+ uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
+ if (!xHyphenator->hasLocale(lang::Locale("de", "DE", OUString())))
+ return;
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Make sure that the arrow on the left is not there (the first portion's type is
+ // PortionType::Arrow if it's there)
+ assertXPath(pXmlDoc,
+ "/root/page/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[1]/child::*[1]",
+ "type", u"PortionType::Text");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf123651)
+{
+ createSwDoc("tdf123651.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Without the accompanying fix in place, this test would have failed with 'Expected: 7639;
+ // Actual: 12926'. The shape was below the second "Lorem ipsum" text, not above it.
+ const sal_Int32 nTopValue
+ = getXPath(pXmlDoc, "//anchored/SwAnchoredDrawObject/bounds", "top").toInt32();
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(7639, nTopValue, 10);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf116501)
+{
+ //just care it doesn't freeze
+ createSwDoc("tdf116501.odt");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf123163)
+{
+ //just care it doesn't assert
+ createSwDoc("tdf123163-1.docx");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testAbi11870)
+{
+ //just care it doesn't assert
+ createSwDoc("abi11870-2.odt");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testOfz64109)
+{
+ //just care it doesn't assert
+ createSwDoc("ofz64109-1.fodt");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf118719)
+{
+ // Insert a page break.
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+
+ // Enable hide whitespace mode.
+ SwViewOption aViewOptions(*pWrtShell->GetViewOptions());
+ aViewOptions.SetHideWhitespaceMode(true);
+ pWrtShell->ApplyViewOptions(aViewOptions);
+
+ pWrtShell->Insert(u"first"_ustr);
+ pWrtShell->InsertPageBreak();
+ pWrtShell->Insert(u"second"_ustr);
+
+ // Without the accompanying fix in place, this test would have failed, as the height of the
+ // first page was 15840 twips, instead of the much smaller 276.
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ sal_Int32 nOther = getXPath(pXmlDoc, "/root/page[1]/infos/bounds", "height").toInt32();
+ sal_Int32 nLast = getXPath(pXmlDoc, "/root/page[2]/infos/bounds", "height").toInt32();
+ CPPUNIT_ASSERT_GREATER(nOther, nLast);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTabOverMargin)
+{
+ createSwDoc("tab-over-margin.odt");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // 2nd paragraph has a tab over the right margin, and with the TabOverMargin compat option,
+ // there is enough space to have all content in a single line.
+ // Without the accompanying fix in place, this test would have failed, there were 2 lines.
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testImageComment)
+{
+ // Load a document that has "aaa" in it, then a commented image (4th char is the as-char image,
+ // 5th char is the comment anchor).
+ createSwDoc("image-comment.odt");
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+
+ // Look up a layout position which is on the right of the image.
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ CPPUNIT_ASSERT(pRoot->GetLower()->IsPageFrame());
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
+ CPPUNIT_ASSERT(pPage->GetLower()->IsBodyFrame());
+ SwBodyFrame* pBody = static_cast<SwBodyFrame*>(pPage->GetLower());
+ CPPUNIT_ASSERT(pBody->GetLower()->IsTextFrame());
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pBody->GetLower());
+ CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
+ SwSortedObjs& rDrawObjs = *pTextFrame->GetDrawObjs();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rDrawObjs.size());
+ SwAnchoredObject* pDrawObj = rDrawObjs[0];
+ const SwRect aDrawObjRect = pDrawObj->GetObjRect();
+ Point aPoint = aDrawObjRect.Center();
+ aPoint.setX(aPoint.getX() + aDrawObjRect.Width() / 2);
+
+ // Ask for the doc model pos of this layout point.
+ SwPosition aPosition(*pTextFrame->GetTextNodeForFirstText());
+ pTextFrame->GetModelPositionForViewPoint(&aPosition, aPoint);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 5
+ // - Actual : 4
+ // i.e. the cursor got positioned between the image and its comment, so typing extended the
+ // comment, instead of adding content after the commented image.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5), aPosition.GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testScriptField)
+{
+ // Test clicking script field inside table ( tdf#141079 )
+ createSwDoc("tdf141079.odt");
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+
+ // Look up layout position which is the first cell in the table
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ CPPUNIT_ASSERT(pRoot->GetLower()->IsPageFrame());
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
+ CPPUNIT_ASSERT(pPage->GetLower()->IsBodyFrame());
+ SwBodyFrame* pBody = static_cast<SwBodyFrame*>(pPage->GetLower());
+ CPPUNIT_ASSERT(pBody->GetLower()->IsTextFrame());
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pBody->GetLower());
+ CPPUNIT_ASSERT(pTextFrame->GetNext()->IsTabFrame());
+ SwFrame* pTable = pTextFrame->GetNext();
+ SwFrame* pRow1 = pTable->GetLower();
+ CPPUNIT_ASSERT(pRow1->GetLower()->IsCellFrame());
+ SwFrame* pCell1 = pRow1->GetLower();
+ CPPUNIT_ASSERT(pCell1->GetLower()->IsTextFrame());
+ SwTextFrame* pCellTextFrame = static_cast<SwTextFrame*>(pCell1->GetLower());
+ const SwRect& rCellRect = pCell1->getFrameArea();
+ Point aPoint = rCellRect.Center();
+ aPoint.setX(aPoint.getX() - rCellRect.Width() / 2);
+
+ // Ask for the doc model pos of this layout point.
+ SwPosition aPosition(*pCellTextFrame->GetTextNodeForFirstText());
+ pCellTextFrame->GetModelPositionForViewPoint(&aPosition, aPoint);
+
+ // Position was 1 without the fix from tdf#141079
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aPosition.GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testCommentCursorPosition)
+{
+ // Load a document that has "aaa" in it, followed by three comments.
+ createSwDoc("endOfLineComments.odt");
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ CPPUNIT_ASSERT(pRoot->GetLower()->IsPageFrame());
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
+ CPPUNIT_ASSERT(pPage->GetLower()->IsBodyFrame());
+ SwBodyFrame* pBody = static_cast<SwBodyFrame*>(pPage->GetLower());
+ CPPUNIT_ASSERT(pBody->GetLower()->IsTextFrame());
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pBody->GetLower());
+
+ // Set a point in the whitespace past the end of the first line.
+ Point aPoint = pWrtShell->getShellCursor(false)->GetSttPos();
+ aPoint.setX(aPoint.getX() + 10000);
+
+ // Ask for the doc model pos of this layout point.
+ SwPosition aPosition(*pTextFrame->GetTextNodeForFirstText());
+ pTextFrame->GetModelPositionForViewPoint(&aPosition, aPoint);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 6
+ // - Actual : 3 or 4
+ // i.e. the cursor got positioned before the comments,
+ // so typing extended the first comment instead of adding content after the comments.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6), aPosition.GetContentIndex());
+ // The second line is also important, but can't be auto-tested
+ // since the failing situation depends on GetViewWidth which is zero in the headless tests.
+ // bb<comment>| - the cursor should move behind the |, not before it.
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testCombiningCharacterCursorPosition)
+{
+ // Load a document that has "a" in it, followed by a combining acute in a separate rext span
+ createSwDoc("tdf138592-a-acute.fodt");
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ CPPUNIT_ASSERT(pRoot->GetLower()->IsPageFrame());
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
+ CPPUNIT_ASSERT(pPage->GetLower()->IsBodyFrame());
+ SwBodyFrame* pBody = static_cast<SwBodyFrame*>(pPage->GetLower());
+ CPPUNIT_ASSERT(pBody->GetLower()->IsTextFrame());
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pBody->GetLower());
+
+ // Set a point in the whitespace past the end of the first line.
+ Point aPoint = pWrtShell->getShellCursor(false)->GetSttPos();
+ aPoint.AdjustX(10000);
+
+ // Ask for the doc model pos of this layout point.
+ SwPosition aPosition(*pTextFrame->GetTextNodeForFirstText());
+ pTextFrame->GetModelPositionForViewPoint(&aPosition, aPoint);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 1
+ // i.e. the cursor got positioned before the acute, so typing shifted the acute (applying it
+ // to newly typed characters) instead of adding content after it.
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aPosition.GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf64222)
+{
+ createSwDoc("tdf64222.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc,
+ "/root/page/body/txt[2]/SwParaPortion/SwLineLayout/"
+ "child::*[@type='PortionType::Number']/SwFont",
+ "height", u"560");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf113014)
+{
+ createSwDoc("tdf113014.fodt");
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // This failed, if numbering of cell A1 is missing
+ // (A1: left indent: 3 cm, first line indent: -3 cm
+ // A2: left indent: 0 cm, first line indent: 0 cm)
+ assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", u"1.");
+ assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[3]/text", u"2.");
+ assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[5]/text", u"3.");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf130218)
+{
+ createSwDoc("tdf130218.fodt");
+ SwDocShell* pShell = getSwDocShell();
+
+ // Dump the rendering of the first page as an XML file.
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+ MetafileXmlDump dumper;
+
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // This failed, if hanging first line was hidden
+ assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", u"Text");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf127235)
+{
+ createSwDoc("tdf127235.odt");
+ // This resulted in a layout loop.
+ calcLayout();
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf138039)
+{
+ createSwDoc("tdf138039.odt");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // there are 3 pages
+ assertXPath(pXmlDoc, "/root/page", 3);
+ // table on first page
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 0);
+ // paragraph with large fly on second page
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", u"17915");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+ u"15819");
+ // paragraph on third page
+ assertXPath(pXmlDoc, "/root/page[3]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/anchored", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf134298)
+{
+ createSwDoc("tdf134298.ott");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // there are 2 pages
+ assertXPath(pXmlDoc, "/root/page", 2);
+ // table and first para on first page
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored", 0);
+ // paragraph with large fly on second page
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", u"17897");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+ u"15819");
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testShapeAllowOverlap)
+{
+// Need to find out why this fails on macOS and why this is unstable on Windows.
+#if !defined(MACOSX) && !defined(_WIN32)
+ // Create an empty document with two, intentionally overlapping shapes.
+ // Set their AllowOverlap property to false.
+ createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
+ awt::Point aPoint(1000, 1000);
+ awt::Size aSize(2000, 2000);
+ uno::Reference<drawing::XShape> xShape(
+ xDocument->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setPosition(aPoint);
+ xShape->setSize(aSize);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
+ xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(false));
+ xShapeProperties->setPropertyValue(u"AnchorType"_ustr,
+ uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ aPoint = awt::Point(2000, 2000);
+ xShape.set(xDocument->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr),
+ uno::UNO_QUERY);
+ xShape->setPosition(aPoint);
+ xShape->setSize(aSize);
+ xShapeProperties.set(xShape, uno::UNO_QUERY);
+ xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(false));
+ xShapeProperties->setPropertyValue(u"AnchorType"_ustr,
+ uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // Now verify that the rectangle of the anchored objects don't overlap.
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPageFrame = pLayout->GetLower();
+ SwFrame* pBodyFrame = pPageFrame->GetLower();
+ SwFrame* pTextFrame = pBodyFrame->GetLower();
+ CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
+ SwSortedObjs& rObjs = *pTextFrame->GetDrawObjs();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs.size());
+ SwAnchoredObject* pFirst = rObjs[0];
+ SwAnchoredObject* pSecond = rObjs[1];
+ // Without the accompanying fix in place, this test would have failed: the layout dump was
+ // <bounds left="1984" top="1984" width="1137" height="1137"/>
+ // <bounds left="2551" top="2551" width="1137" height="1137"/>
+ // so there was a clear vertical overlap. (Allow for 1px tolerance.)
+ OString aMessage = "Unexpected overlap: first shape's bottom is "
+ + OString::number(pFirst->GetObjRect().Bottom()) + ", second shape's top is "
+ + OString::number(pSecond->GetObjRect().Top());
+ CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(),
+ std::abs(pFirst->GetObjRect().Bottom() - pSecond->GetObjRect().Top())
+ < 15);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testShapeAllowOverlapWrap)
+{
+ // Create an empty document with two, intentionally overlapping shapes.
+ // Set their AllowOverlap property to false and their wrap to through.
+ createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
+ awt::Point aPoint(1000, 1000);
+ awt::Size aSize(2000, 2000);
+ uno::Reference<drawing::XShape> xShape(
+ xDocument->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setPosition(aPoint);
+ xShape->setSize(aSize);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
+ xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(false));
+ xShapeProperties->setPropertyValue(u"AnchorType"_ustr,
+ uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ xShapeProperties->setPropertyValue(u"Surround"_ustr, uno::Any(text::WrapTextMode_THROUGH));
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ aPoint = awt::Point(2000, 2000);
+ xShape.set(xDocument->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr),
+ uno::UNO_QUERY);
+ xShape->setPosition(aPoint);
+ xShape->setSize(aSize);
+ xShapeProperties.set(xShape, uno::UNO_QUERY);
+ xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(false));
+ xShapeProperties->setPropertyValue(u"AnchorType"_ustr,
+ uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ xShapeProperties->setPropertyValue(u"Surround"_ustr, uno::Any(text::WrapTextMode_THROUGH));
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // Now verify that the rectangle of the anchored objects do overlap.
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPageFrame = pLayout->GetLower();
+ SwFrame* pBodyFrame = pPageFrame->GetLower();
+ SwFrame* pTextFrame = pBodyFrame->GetLower();
+ CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
+ SwSortedObjs& rObjs = *pTextFrame->GetDrawObjs();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs.size());
+ SwAnchoredObject* pFirst = rObjs[0];
+ SwAnchoredObject* pSecond = rObjs[1];
+ // Without the accompanying fix in place, this test would have failed: AllowOverlap=no had
+ // priority over Surround=through (which is bad for Word compat).
+ CPPUNIT_ASSERT(pSecond->GetObjRect().Overlaps(pFirst->GetObjRect()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf124600)
+{
+ createSwDoc("tdf124600.docx");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // i.e. the last line in the body text had 2 lines, while it should have 1, as Word does (as the
+ // fly frame does not intersect with the print area of the paragraph.)
+ assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf124601)
+{
+ // This is a testcase for the ContinuousEndnotes compat flag.
+ // The document has 2 pages, the endnote anchor is on the first page.
+ // The endnote should be on the 2nd page together with the last page content.
+ createSwDoc("tdf124601.doc");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 3
+ // i.e. there was a separate endnote page, even when the ContinuousEndnotes compat option was
+ // on.
+ assertXPath(pXmlDoc, "/root/page", 2);
+ assertXPath(pXmlDoc, "/root/page[2]//ftncont", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf124601b)
+{
+ // Table has an image, which is anchored in the first row, but its vertical position is large
+ // enough to be rendered in the second row.
+ // The shape has layoutInCell=1, so should match what Word does here.
+ // Also the horizontal position should be in the last column, even if the anchor is in the
+ // last-but-one column.
+ createSwDoc("tdf124601b.doc");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ sal_Int32 nFlyTop = getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "top").toInt32();
+ sal_Int32 nFlyLeft = getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "left").toInt32();
+ sal_Int32 nFlyRight
+ = nFlyLeft + getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "width").toInt32();
+ sal_Int32 nSecondRowTop = getXPath(pXmlDoc, "//tab/row[2]/infos/bounds", "top").toInt32();
+ sal_Int32 nLastCellLeft
+ = getXPath(pXmlDoc, "//tab/row[1]/cell[5]/infos/bounds", "left").toInt32();
+ sal_Int32 nLastCellRight
+ = nLastCellLeft + getXPath(pXmlDoc, "//tab/row[1]/cell[5]/infos/bounds", "width").toInt32();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected greater than: 3736
+ // - Actual : 2852
+ // i.e. the image was still inside the first row.
+ CPPUNIT_ASSERT_GREATER(nSecondRowTop, nFlyTop);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected greater than: 9640
+ // - Actual : 9639
+ // i.e. the right edge of the image was not within the bounds of the last column, the right edge
+ // was in the last-but-one column.
+ CPPUNIT_ASSERT_GREATER(nLastCellLeft, nFlyRight);
+ CPPUNIT_ASSERT_LESS(nLastCellRight, nFlyRight);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf124770)
+{
+ // Enable content over margin.
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ pDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
+
+ // Set page width.
+ SwPageDesc& rPageDesc = pDoc->GetPageDesc(0);
+ SwFrameFormat& rPageFormat = rPageDesc.GetMaster();
+ const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
+ SwFormatFrameSize aPageSize = rPageSet.GetFrameSize();
+ aPageSize.SetWidth(3703);
+ rPageFormat.SetFormatAttr(aPageSize);
+
+ // Set left and right margin.
+ SvxLRSpaceItem aLRSpace = rPageSet.GetLRSpace();
+ aLRSpace.SetLeft(SvxIndentValue::twips(1418));
+ aLRSpace.SetRight(SvxIndentValue::twips(1418));
+ rPageFormat.SetFormatAttr(aLRSpace);
+ pDoc->ChgPageDesc(0, rPageDesc);
+
+ // Set font to italic 20pt Liberation Serif.
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ SfxItemSet aTextSet(pWrtShell->GetView().GetPool(),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>);
+ SvxFontItem aFont(RES_CHRATR_FONT);
+ aFont.SetFamilyName(u"Liberation Serif"_ustr);
+ aTextSet.Put(aFont);
+ SvxFontHeightItem aHeight(400, 100, RES_CHRATR_FONTSIZE);
+ aTextSet.Put(aHeight);
+ SvxPostureItem aItalic(ITALIC_NORMAL, RES_CHRATR_POSTURE);
+ aTextSet.Put(aItalic);
+ pWrtShell->SetAttrSet(aTextSet);
+
+ // Insert the text.
+ pWrtShell->Insert2(u"HHH"_ustr);
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // i.e. the italic string was broken into 2 lines, while Word kept it in a single line.
+ assertXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testContinuousEndnotesInsertPageAtStart)
+{
+ // Create a new document with CONTINUOUS_ENDNOTES enabled.
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ pDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
+
+ // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
+ // the 2nd page).
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->InsertPageBreak();
+ pWrtShell->InsertFootnote(u"endnote"_ustr, /*bEndNote=*/true, /*bEdit=*/false);
+
+ // Add a new page at the start of the document.
+ pWrtShell->SttEndDoc(/*bStart=*/true);
+ pWrtShell->InsertPageBreak();
+
+ // Make sure that the endnote is moved from the 2nd page to the 3rd one.
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page", 3);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the footnote container remained on page 2.
+ assertXPath(pXmlDoc, "/root/page[3]//ftncont", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testContinuousEndnotesDeletePageAtStart)
+{
+ // Create a new document with CONTINUOUS_ENDNOTES enabled.
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ pDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
+
+ // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
+ // the 2nd page).
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->InsertPageBreak();
+ pWrtShell->InsertFootnote(u"endnote"_ustr, /*bEndNote=*/true, /*bEdit=*/false);
+
+ // Remove the empty page at the start of the document.
+ pWrtShell->SttEndDoc(/*bStart=*/true);
+ pWrtShell->DelRight();
+
+ // Make sure that the endnote is moved from the 2nd page to the 1st one.
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // i.e. the endnote remained on an (otherwise) empty 2nd page.
+ assertXPath(pXmlDoc, "/root/page", 1);
+ assertXPath(pXmlDoc, "/root/page[1]//ftncont", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf128399)
+{
+ createSwDoc("tdf128399.docx");
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ SwFrame* pPage = pLayout->GetLower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pTable = pBody->GetLower();
+ SwFrame* pRow1 = pTable->GetLower();
+ SwFrame* pRow2 = pRow1->GetNext();
+ const SwRect& rRow2Rect = pRow2->getFrameArea();
+ Point aPoint = rRow2Rect.Center();
+
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ SwPosition aPosition = *pWrtShell->GetCursor()->Start();
+ SwPosition aFirstRow(aPosition);
+ SwCursorMoveState aState(CursorMoveState::NONE);
+ pLayout->GetModelPositionForViewPoint(&aPosition, aPoint, &aState);
+ // Second row is +3: end node, start node and the first text node in the 2nd row.
+ SwNodeOffset nExpected = aFirstRow.GetNodeIndex() + 3;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 14
+ // - Actual : 11
+ // i.e. clicking on the center of the 2nd row placed the cursor in the 1st row.
+ CPPUNIT_ASSERT_EQUAL(nExpected, aPosition.GetNodeIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf156724)
+{
+ // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
+ // does *not* forward initial VCL Window Resize and thereby triggers a
+ // layout which does not happen on soffice --convert-to pdf.
+ std::vector<beans::PropertyValue> aFilterOptions = {
+ { beans::PropertyValue(u"Hidden"_ustr, -1, uno::Any(true),
+ beans::PropertyState_DIRECT_VALUE) },
+ };
+
+ // inline the loading because currently properties can't be passed...
+ OUString const url(createFileURL(u"fdo56797-2-min.odt"));
+ loadWithParams(url, comphelper::containerToSequence(aFilterOptions));
+ save(u"writer_pdf_Export"_ustr);
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // both pages have a tab frame and one footnote
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/ftncont", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/ftncont", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/ftncont/ftn", 1);
+ assertXPath(pXmlDoc, "/root/page", 2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testHiddenParagraphFollowFrame)
+{
+ createSwDoc("hidden-para-follow-frame.fodt");
+
+ uno::Any aOldValue{ queryDispatchStatus(mxComponent, m_xContext, ".uno:ShowHiddenParagraphs") };
+
+ Resetter g([this, aOldValue] {
+ uno::Sequence<beans::PropertyValue> argsSH(
+ comphelper::InitPropertySequence({ { "ShowHiddenParagraphs", aOldValue } }));
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", argsSH);
+ });
+
+ // disable Field Names warning dialog
+ const bool bAsk = officecfg::Office::Common::Misc::QueryShowFieldName::get();
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges;
+ if (bAsk)
+ {
+ xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(false, xChanges);
+ xChanges->commit();
+ }
+ uno::Sequence<beans::PropertyValue> argsSH(
+ comphelper::InitPropertySequence({ { "ShowHiddenParagraphs", uno::Any(true) } }));
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", argsSH);
+ uno::Sequence<beans::PropertyValue> args(
+ comphelper::InitPropertySequence({ { "Fieldnames", uno::Any(false) } }));
+ dispatchCommand(mxComponent, ".uno:Fieldnames", args);
+ Scheduler::ProcessEventsToIdle();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page", 2);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 2);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 2);
+ }
+
+ if (bAsk)
+ {
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(true, xChanges);
+ xChanges->commit();
+ }
+
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // the problem was that the 3rd paragraph didn't move to page 1
+ assertXPath(pXmlDoc, "/root/page", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 3);
+ }
+
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page", 2);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 2);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 2);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testHiddenParagraphFlys)
+{
+ createSwDoc("hidden-para-as-char-fly.fodt");
+
+ uno::Any aOldValue{ queryDispatchStatus(mxComponent, m_xContext, ".uno:ShowHiddenParagraphs") };
+
+ Resetter g([this, aOldValue] {
+ uno::Sequence<beans::PropertyValue> argsSH(
+ comphelper::InitPropertySequence({ { "ShowHiddenParagraphs", aOldValue } }));
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", argsSH);
+ });
+
+ // disable Field Names warning dialog
+ const bool bAsk = officecfg::Office::Common::Misc::QueryShowFieldName::get();
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges;
+ if (bAsk)
+ {
+ xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(false, xChanges);
+ xChanges->commit();
+ }
+ uno::Sequence<beans::PropertyValue> argsSH(
+ comphelper::InitPropertySequence({ { "ShowHiddenParagraphs", uno::Any(true) } }));
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", argsSH);
+ uno::Sequence<beans::PropertyValue> args(
+ comphelper::InitPropertySequence({ { "Fieldnames", uno::Any(false) } }));
+ dispatchCommand(mxComponent, ".uno:Fieldnames", args);
+ Scheduler::ProcessEventsToIdle();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/txt[3]/anchored/fly/infos/bounds", "height", u"724");
+ }
+
+ if (bAsk)
+ {
+ officecfg::Office::Common::Misc::QueryShowFieldName::set(true, xChanges);
+ xChanges->commit();
+ }
+
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // the problem was that this did not shrink
+ assertXPath(pXmlDoc, "/root/page/body/txt[3]/anchored/fly/infos/bounds", "height", u"448");
+ }
+
+ dispatchCommand(mxComponent, ".uno:ShowHiddenParagraphs", {});
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/txt[3]/anchored/fly/infos/bounds", "height", u"724");
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testSectionUnhide)
+{
+ createSwDoc("hiddensection.fodt");
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/section/txt/infos/bounds[@height='0']", 0);
+ }
+
+ // Hide the section
+ auto xTextSectionsSupplier = mxComponent.queryThrow<css::text::XTextSectionsSupplier>();
+ auto xSections = xTextSectionsSupplier->getTextSections();
+ CPPUNIT_ASSERT(xSections);
+ auto xSection = xSections->getByName(u"Section1"_ustr).queryThrow<css::beans::XPropertySet>();
+ xSection->setPropertyValue(u"IsVisible"_ustr, css::uno::Any(false));
+ calcLayout();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/section/txt/infos/bounds[@height='0']", 4);
+ }
+
+ xSection->setPropertyValue(u"IsVisible"_ustr, css::uno::Any(true));
+ calcLayout();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // the problem was that 3 of the text frames had 0 height because Format was skipped
+ assertXPath(pXmlDoc, "/root/page/body/section/txt/infos/bounds[@height='0']", 0);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testHiddenSectionFlys)
+{
+ createSwDoc("U-min.fodt");
+
+ //NO! field update job masks if the visibility was created wrong when loading.
+ //Scheduler::ProcessEventsToIdle();
+
+ SwDoc* pDoc = getSwDoc();
+ IDocumentDrawModelAccess const& rIDMA{ pDoc->getIDocumentDrawModelAccess() };
+ SdrPage const* pDrawPage{ rIDMA.GetDrawModel()->GetPage(0) };
+ int invisibleHeaven{ rIDMA.GetInvisibleHeavenId().get() };
+ int visibleHeaven{ rIDMA.GetHeavenId().get() };
+
+ // these are hidden by moving to invisible layer, they're still in layout
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//anchored/fly", 6);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(6), pDrawPage->GetObjCount());
+ for (int i = 0; i < 6; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(invisibleHeaven, int(pDrawPage->GetObj(i)->GetLayer().get()));
+ }
+ }
+
+ // Show the section
+ auto xTextSectionsSupplier = mxComponent.queryThrow<css::text::XTextSectionsSupplier>();
+ auto xSections = xTextSectionsSupplier->getTextSections();
+ CPPUNIT_ASSERT(xSections);
+ auto xSection = xSections->getByName(u"Anlage"_ustr).queryThrow<css::beans::XPropertySet>();
+ xSection->setPropertyValue(u"IsVisible"_ustr, css::uno::Any(true));
+ calcLayout();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//anchored/fly", 6);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(6), pDrawPage->GetObjCount());
+ for (int i = 0; i < 6; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(visibleHeaven, int(pDrawPage->GetObj(i)->GetLayer().get()));
+ }
+ }
+
+ xSection->setPropertyValue(u"IsVisible"_ustr, css::uno::Any(false));
+ calcLayout();
+
+ {
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//anchored/fly", 6);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(6), pDrawPage->GetObjCount());
+ for (int i = 0; i < 6; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(invisibleHeaven, int(pDrawPage->GetObj(i)->GetLayer().get()));
+ }
+ }
+}
+
+} // end of anonymous namespace
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */