1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <swmodeltestbase.hxx>
11 #include <unotest/bootstrapfixturebase.hxx>
12 #include <comphelper/propertysequence.hxx>
13 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
14 #include <com/sun/star/frame/DispatchHelper.hpp>
15 #include <com/sun/star/text/WrapTextMode.hpp>
16 #include <comphelper/scopeguard.hxx>
17 #include <unotools/syslocaleoptions.hxx>
18 #include <i18nlangtag/languagetag.hxx>
19 #include <vcl/event.hxx>
20 #include <vcl/scheduler.hxx>
21 #include <editeng/lrspitem.hxx>
22 #include <editeng/fontitem.hxx>
23 #include <editeng/fhgtitem.hxx>
24 #include <editeng/postitem.hxx>
25 #include <editeng/unolingu.hxx>
26 #include <fmtanchr.hxx>
27 #include <fmtfsize.hxx>
28 #include <fmtcntnt.hxx>
29 #include <wrtsh.hxx>
30 #include <edtwin.hxx>
31 #include <view.hxx>
32 #include <txtfrm.hxx>
33 #include <pagefrm.hxx>
34 #include <bodyfrm.hxx>
35 #include <sortedobjs.hxx>
36 #include <anchoredobject.hxx>
37 #include <ndtxt.hxx>
38 #include <frmatr.hxx>
39 #include <IDocumentSettingAccess.hxx>
40 
41 static char const DATA_DIRECTORY[] = "/sw/qa/extras/layout/data/";
42 
43 /// Test to assert layout / rendering result of Writer.
44 class SwLayoutWriter : public SwModelTestBase
45 {
46 protected:
47     void CheckRedlineFootnotesHidden();
48     void CheckRedlineSectionsHidden();
49     void CheckRedlineCharAttributesHidden();
50 
51     SwDoc* createDoc(const char* pName = nullptr);
52 };
53 
createDoc(const char * pName)54 SwDoc* SwLayoutWriter::createDoc(const char* pName)
55 {
56     if (!pName)
57         loadURL("private:factory/swriter", nullptr);
58     else
59         load(DATA_DIRECTORY, pName);
60 
61     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
62     CPPUNIT_ASSERT(pTextDoc);
63     return pTextDoc->GetDocShell()->GetDoc();
64 }
65 
lcl_dispatchCommand(const uno::Reference<lang::XComponent> & xComponent,const OUString & rCommand,const uno::Sequence<beans::PropertyValue> & rPropertyValues)66 static void lcl_dispatchCommand(const uno::Reference<lang::XComponent>& xComponent,
67                                 const OUString& rCommand,
68                                 const uno::Sequence<beans::PropertyValue>& rPropertyValues)
69 {
70     uno::Reference<frame::XController> xController
71         = uno::Reference<frame::XModel>(xComponent, uno::UNO_QUERY_THROW)->getCurrentController();
72     CPPUNIT_ASSERT(xController.is());
73     uno::Reference<frame::XDispatchProvider> xFrame(xController->getFrame(), uno::UNO_QUERY);
74     CPPUNIT_ASSERT(xFrame.is());
75 
76     uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
77     uno::Reference<frame::XDispatchHelper> xDispatchHelper(frame::DispatchHelper::create(xContext));
78     CPPUNIT_ASSERT(xDispatchHelper.is());
79 
80     xDispatchHelper->executeDispatch(xFrame, rCommand, OUString(), 0, rPropertyValues);
81 }
82 
83 // this is a member because our test classes have protected members :(
CheckRedlineFootnotesHidden()84 void SwLayoutWriter::CheckRedlineFootnotesHidden()
85 {
86     discardDumpedLayout();
87     xmlDocPtr pXmlDoc = parseLayoutDump();
88     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "24");
89     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType", "PortionType::Footnote");
90     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
91     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
92     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
93     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "nType", "PortionType::Footnote");
94     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "rText", "2");
95     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/merged", "paraPropsNodeIndex", "13");
96     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
97                 "PortionType::FootnoteNum");
98     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
99     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[1]", "nType",
100                 "PortionType::Text");
101     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[1]", "Portion", "ac");
102     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/merged", "paraPropsNodeIndex", "16");
103     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "nType",
104                 "PortionType::FootnoteNum");
105     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "rText", "2");
106     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "nType",
107                 "PortionType::Text");
108     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "Portion", "mo");
109 }
110 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFootnotes)111 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFootnotes)
112 {
113     createDoc("redline_footnotes.odt");
114     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
115     CPPUNIT_ASSERT(pTextDoc);
116     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
117     SwRootFrame* pLayout(pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
118     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
119 
120     // verify after load
121     CheckRedlineFootnotesHidden();
122 
123     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
124     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
125     discardDumpedLayout();
126     xmlDocPtr pXmlDoc = parseLayoutDump();
127 
128     // show: nothing is merged
129     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
130     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
131     CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
132     xmlXPathFreeObject(pXmlObj);
133     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType", "PortionType::Footnote");
134     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
135     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
136     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
137     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
138     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
139     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "nType", "PortionType::Footnote");
140     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "rText", "2");
141 
142     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
143                 "PortionType::FootnoteNum");
144     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
145     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[1]", "nType",
146                 "PortionType::Text");
147     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[1]", "Portion", "a");
148     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[2]", "nType",
149                 "PortionType::Text");
150     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[2]", "Portion", "b");
151     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[3]", "nType",
152                 "PortionType::Text");
153     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Text[3]", "Portion", "c");
154     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "nType",
155                 "PortionType::FootnoteNum");
156     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "rText", "2");
157     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "nType",
158                 "PortionType::Text");
159     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "Portion", "def");
160     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Text");
161     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "b");
162     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Special[1]", "nType", "PortionType::Footnote");
163     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Special[1]", "rText", "3");
164     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "nType", "PortionType::Text");
165     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "Portion", "ar");
166     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[3]/txt[1]/Special[1]", "nType",
167                 "PortionType::FootnoteNum");
168     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[3]/txt[1]/Special[1]", "rText", "3");
169     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[3]/txt[1]/Text[1]", "nType",
170                 "PortionType::Text");
171     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[3]/txt[1]/Text[1]", "Portion", "ghi");
172     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Special[1]", "nType", "PortionType::Footnote");
173     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Special[1]", "rText", "4");
174     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
175     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
176     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
177     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
178     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Special[2]", "nType", "PortionType::Footnote");
179     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Special[2]", "rText", "5");
180 
181     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[4]/txt[1]/Special[1]", "nType",
182                 "PortionType::FootnoteNum");
183     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[4]/txt[1]/Special[1]", "rText", "4");
184     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[4]/txt[1]/Text[1]", "nType",
185                 "PortionType::Text");
186     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[4]/txt[1]/Text[1]", "Portion", "jkl");
187     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Special[1]", "nType",
188                 "PortionType::FootnoteNum");
189     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Special[1]", "rText", "5");
190     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[1]", "nType",
191                 "PortionType::Text");
192     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[1]", "Portion", "m");
193     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[2]", "nType",
194                 "PortionType::Text");
195     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[2]", "Portion", "n");
196     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[3]", "nType",
197                 "PortionType::Text");
198     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[5]/txt[1]/Text[3]", "Portion", "o");
199 
200     // verify after hide
201     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
202     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
203     discardDumpedLayout();
204     CheckRedlineFootnotesHidden();
205 }
206 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFlysInBody)207 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFlysInBody)
208 {
209     loadURL("private:factory/swriter", nullptr);
210     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
211     CPPUNIT_ASSERT(pTextDoc);
212     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
213     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
214     SwRootFrame* pLayout(pWrtShell->GetLayout());
215     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
216     pWrtShell->Insert("foo");
217     pWrtShell->SplitNode(false);
218     pWrtShell->Insert("bar");
219     pWrtShell->SplitNode(false);
220     pWrtShell->Insert("baz");
221     SfxItemSet flySet(pDoc->GetAttrPool(),
222                       svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
223     SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
224     pWrtShell->StartOfSection(false);
225     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
226     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
227     flySet.Put(anchor);
228     SwFormatFrameSize size(ATT_MIN_SIZE, 1000, 1000);
229     flySet.Put(size); // set a size, else we get 1 char per line...
230     SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
231     CPPUNIT_ASSERT(pFly != nullptr);
232     // move inside fly
233     pWrtShell->GotoFly(pFly->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
234     pWrtShell->Insert("abc");
235     pWrtShell->SplitNode(false);
236     pWrtShell->Insert("def");
237     pWrtShell->SplitNode(false);
238     pWrtShell->Insert("ghi");
239 
240     lcl_dispatchCommand(mxComponent, ".uno:TrackChanges", {});
241     // delete redline inside fly
242     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
243     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false);
244     pWrtShell->Delete();
245 
246     pWrtShell->SttEndDoc(true); // note: SttDoc actually moves to start of fly?
247     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
248     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
249     pWrtShell->Delete();
250 
251     for (int i = 0; i < 2; ++i)
252     {
253         if (i == 1) // secondly, try with different anchor type
254         {
255             anchor.SetType(RndStdIds::FLY_AT_PARA);
256             SwPosition pos(*anchor.GetContentAnchor());
257             pos.nContent.Assign(nullptr, 0);
258             anchor.SetAnchor(&pos);
259             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
260         }
261 
262         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
263         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
264         discardDumpedLayout();
265         xmlDocPtr pXmlDoc = parseLayoutDump();
266         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "14");
267         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
268         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
269         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/merged",
270                     "paraPropsNodeIndex", "6");
271         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
272                     "PortionType::Para");
273         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
274                     "ahi");
275 
276         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
277         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
278         discardDumpedLayout();
279         pXmlDoc = parseLayoutDump();
280 
281         { // show: nothing is merged
282             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
283             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
284             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
285             xmlXPathFreeObject(pXmlObj);
286         }
287 
288         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
289         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
290         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
291         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
292         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
293                     "PortionType::Text");
294         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
295                     "a");
296         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "nType",
297                     "PortionType::Text");
298         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
299                     "bc");
300         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "nType",
301                     "PortionType::Para");
302         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
303                     "def");
304         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "nType",
305                     "PortionType::Text");
306         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
307                     "g");
308         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "nType",
309                     "PortionType::Text");
310         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
311                     "hi");
312         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
313         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
314         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
315         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
316         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
317         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
318     }
319 
320     // anchor to 2nd (deleted) paragraph
321     pWrtShell->StartOfSection();
322     pWrtShell->Down(false, 1);
323     anchor.SetType(RndStdIds::FLY_AT_CHAR);
324     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
325     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
326 
327     for (int i = 0; i < 2; ++i)
328     {
329         if (i == 1) // secondly, try with different anchor type
330         {
331             anchor.SetType(RndStdIds::FLY_AT_PARA);
332             SwPosition pos(*anchor.GetContentAnchor());
333             pos.nContent.Assign(nullptr, 0);
334             anchor.SetAnchor(&pos);
335             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
336         }
337 
338         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
339         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
340         discardDumpedLayout();
341         xmlDocPtr pXmlDoc = parseLayoutDump();
342         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "14");
343         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
344         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
345 
346         { // hide: no anchored object shown
347             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//anchored");
348             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
349             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
350             xmlXPathFreeObject(pXmlObj);
351         }
352 
353         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
354         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
355         discardDumpedLayout();
356         pXmlDoc = parseLayoutDump();
357 
358         { // show: nothing is merged
359             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
360             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
361             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
362             xmlXPathFreeObject(pXmlObj);
363         }
364 
365         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
366         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
367         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
368         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
369         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
370         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
371         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "nType",
372                     "PortionType::Text");
373         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
374                     "a");
375         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "nType",
376                     "PortionType::Text");
377         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
378                     "bc");
379         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "nType",
380                     "PortionType::Para");
381         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
382                     "def");
383         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "nType",
384                     "PortionType::Text");
385         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
386                     "g");
387         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "nType",
388                     "PortionType::Text");
389         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
390                     "hi");
391         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
392         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
393         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
394         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
395     }
396 
397     // anchor to 3rd paragraph
398     pWrtShell->EndOfSection();
399     anchor.SetType(RndStdIds::FLY_AT_CHAR);
400     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
401     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
402 
403     for (int i = 0; i < 2; ++i)
404     {
405         if (i == 1) // secondly, try with different anchor type
406         {
407             anchor.SetType(RndStdIds::FLY_AT_PARA);
408             SwPosition pos(*anchor.GetContentAnchor());
409             pos.nContent.Assign(nullptr, 0);
410             anchor.SetAnchor(&pos);
411             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
412         }
413 
414         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
415         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
416         discardDumpedLayout();
417         xmlDocPtr pXmlDoc = parseLayoutDump();
418         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "14");
419         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
420         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
421         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/merged",
422                     "paraPropsNodeIndex", "6");
423         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
424                     "PortionType::Para");
425         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
426                     "ahi");
427 
428         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
429         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
430         discardDumpedLayout();
431         pXmlDoc = parseLayoutDump();
432 
433         { // show: nothing is merged
434             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
435             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
436             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
437             xmlXPathFreeObject(pXmlObj);
438         }
439 
440         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
441         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
442         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
443         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
444         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
445         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
446         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
447         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
448         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
449         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
450         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[1]", "nType",
451                     "PortionType::Text");
452         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
453                     "a");
454         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[2]", "nType",
455                     "PortionType::Text");
456         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
457                     "bc");
458         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[2]/Text[1]", "nType",
459                     "PortionType::Para");
460         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
461                     "def");
462         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[1]", "nType",
463                     "PortionType::Text");
464         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
465                     "g");
466         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[2]", "nType",
467                     "PortionType::Text");
468         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
469                     "hi");
470     }
471 }
472 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,TestTdf134272)473 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, TestTdf134272)
474 {
475     SwDoc* pDoc = createDoc("tdf134472.odt");
476     CPPUNIT_ASSERT(pDoc);
477     xmlDocPtr pXmlDoc = parseLayoutDump();
478     assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/infos/bounds", "height", "843");
479 }
480 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFlysInHeader)481 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFlysInHeader)
482 {
483     loadURL("private:factory/swriter", nullptr);
484     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
485     CPPUNIT_ASSERT(pTextDoc);
486     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
487     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
488     SwRootFrame* pLayout(pWrtShell->GetLayout());
489     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
490     pWrtShell->ChangeHeaderOrFooter("Default Style", /*bHeader*/ true, /*bOn*/ true, false);
491     CPPUNIT_ASSERT(
492         pWrtShell
493             ->IsInHeaderFooter()); // assume this is supposed to put cursor in the new header...
494     pWrtShell->Insert("foo");
495     pWrtShell->SplitNode(false);
496     pWrtShell->Insert("bar");
497     pWrtShell->SplitNode(false);
498     pWrtShell->Insert("baz");
499     SfxItemSet flySet(pDoc->GetAttrPool(),
500                       svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
501     SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
502     pWrtShell->StartOfSection(false);
503     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
504     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
505     flySet.Put(anchor);
506     SwFormatFrameSize size(ATT_MIN_SIZE, 1000, 1000);
507     flySet.Put(size); // set a size, else we get 1 char per line...
508     SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
509     CPPUNIT_ASSERT(pFly != nullptr);
510     // move inside fly
511     pWrtShell->GotoFly(pFly->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
512     pWrtShell->Insert("abc");
513     pWrtShell->SplitNode(false);
514     pWrtShell->Insert("def");
515     pWrtShell->SplitNode(false);
516     pWrtShell->Insert("ghi");
517 
518     lcl_dispatchCommand(mxComponent, ".uno:TrackChanges", {});
519     // delete redline inside fly
520     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
521     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false);
522     pWrtShell->Delete();
523 
524     pWrtShell->GotoHeaderText();
525     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
526     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
527     pWrtShell->Delete();
528 
529     for (int i = 0; i < 2; ++i)
530     {
531         if (i == 1) // secondly, try with different anchor type
532         {
533             anchor.SetType(RndStdIds::FLY_AT_PARA);
534             SwPosition pos(*anchor.GetContentAnchor());
535             pos.nContent.Assign(nullptr, 0);
536             anchor.SetAnchor(&pos);
537             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
538         }
539 
540         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
541         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
542         discardDumpedLayout();
543         xmlDocPtr pXmlDoc = parseLayoutDump();
544         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
545         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
546         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/merged", "paraPropsNodeIndex", "6");
547         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Para");
548         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "foaz");
549         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/merged",
550                     "paraPropsNodeIndex", "11");
551         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
552                     "PortionType::Para");
553         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
554                     "Portion", "ahi");
555 
556         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
557         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
558         discardDumpedLayout();
559         pXmlDoc = parseLayoutDump();
560 
561         { // show: nothing is merged
562             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
563             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
564             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
565             xmlXPathFreeObject(pXmlObj);
566         }
567 
568         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
569         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
570         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Text");
571         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "fo");
572         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "nType", "PortionType::Text");
573         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "Portion", "o");
574         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
575                     "PortionType::Text");
576         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
577                     "Portion", "a");
578         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "nType",
579                     "PortionType::Text");
580         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[2]",
581                     "Portion", "bc");
582         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "nType",
583                     "PortionType::Para");
584         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[2]/Text[1]",
585                     "Portion", "def");
586         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "nType",
587                     "PortionType::Text");
588         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[3]/Text[1]",
589                     "Portion", "g");
590         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "nType",
591                     "PortionType::Text");
592         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[3]/Text[2]",
593                     "Portion", "hi");
594         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "nType", "PortionType::Para");
595         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "Portion", "bar");
596         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "nType", "PortionType::Text");
597         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "Portion", "b");
598         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "nType", "PortionType::Text");
599         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "Portion", "az");
600     }
601 
602     // anchor to 2nd (deleted) paragraph
603     pWrtShell->StartOfSection();
604     pWrtShell->Down(false, 1);
605     anchor.SetType(RndStdIds::FLY_AT_CHAR);
606     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
607     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
608 
609     for (int i = 0; i < 2; ++i)
610     {
611         if (i == 1) // secondly, try with different anchor type
612         {
613             anchor.SetType(RndStdIds::FLY_AT_PARA);
614             SwPosition pos(*anchor.GetContentAnchor());
615             pos.nContent.Assign(nullptr, 0);
616             anchor.SetAnchor(&pos);
617             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
618         }
619 
620         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
621         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
622         discardDumpedLayout();
623         xmlDocPtr pXmlDoc = parseLayoutDump();
624         // now the frame has no Text portion? not sure why it's a 0-length one first and now none?
625         //        assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
626         //        assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
627         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/merged", "paraPropsNodeIndex", "6");
628         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Para");
629         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "foaz");
630 
631         { // hide: no anchored object shown
632             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//anchored");
633             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
634             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
635             xmlXPathFreeObject(pXmlObj);
636         }
637 
638         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
639         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
640         discardDumpedLayout();
641         pXmlDoc = parseLayoutDump();
642 
643         { // show: nothing is merged
644             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
645             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
646             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
647             xmlXPathFreeObject(pXmlObj);
648         }
649 
650         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
651         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
652         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Text");
653         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "fo");
654         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "nType", "PortionType::Text");
655         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "Portion", "o");
656         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "nType", "PortionType::Para");
657         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "Portion", "bar");
658         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "nType",
659                     "PortionType::Text");
660         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[1]/Text[1]",
661                     "Portion", "a");
662         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "nType",
663                     "PortionType::Text");
664         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[1]/Text[2]",
665                     "Portion", "bc");
666         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "nType",
667                     "PortionType::Para");
668         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[2]/Text[1]",
669                     "Portion", "def");
670         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "nType",
671                     "PortionType::Text");
672         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[3]/Text[1]",
673                     "Portion", "g");
674         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "nType",
675                     "PortionType::Text");
676         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/anchored/fly[1]/txt[3]/Text[2]",
677                     "Portion", "hi");
678         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "nType", "PortionType::Text");
679         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "Portion", "b");
680         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "nType", "PortionType::Text");
681         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "Portion", "az");
682     }
683 
684     // anchor to 3rd paragraph
685     pWrtShell->EndOfSection();
686     anchor.SetType(RndStdIds::FLY_AT_CHAR);
687     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
688     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
689 
690     for (int i = 0; i < 2; ++i)
691     {
692         if (i == 1) // secondly, try with different anchor type
693         {
694             anchor.SetType(RndStdIds::FLY_AT_PARA);
695             SwPosition pos(*anchor.GetContentAnchor());
696             pos.nContent.Assign(nullptr, 0);
697             anchor.SetAnchor(&pos);
698             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
699         }
700 
701         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
702         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
703         discardDumpedLayout();
704         xmlDocPtr pXmlDoc = parseLayoutDump();
705         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
706         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
707         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/merged", "paraPropsNodeIndex", "6");
708         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Para");
709         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "foaz");
710         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/merged",
711                     "paraPropsNodeIndex", "11");
712         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
713                     "PortionType::Para");
714         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
715                     "Portion", "ahi");
716 
717         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
718         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
719         discardDumpedLayout();
720         pXmlDoc = parseLayoutDump();
721 
722         { // show: nothing is merged
723             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
724             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
725             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
726             xmlXPathFreeObject(pXmlObj);
727         }
728 
729         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
730         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nLength", "0");
731         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "nType", "PortionType::Text");
732         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[1]", "Portion", "fo");
733         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "nType", "PortionType::Text");
734         assertXPath(pXmlDoc, "/root/page[1]/header/txt[1]/Text[2]", "Portion", "o");
735         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "nType", "PortionType::Para");
736         assertXPath(pXmlDoc, "/root/page[1]/header/txt[2]/Text[1]", "Portion", "bar");
737         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "nType", "PortionType::Text");
738         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[1]", "Portion", "b");
739         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "nType", "PortionType::Text");
740         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/Text[2]", "Portion", "az");
741         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[1]/Text[1]", "nType",
742                     "PortionType::Text");
743         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[1]/Text[1]",
744                     "Portion", "a");
745         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[1]/Text[2]", "nType",
746                     "PortionType::Text");
747         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[1]/Text[2]",
748                     "Portion", "bc");
749         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[2]/Text[1]", "nType",
750                     "PortionType::Para");
751         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[2]/Text[1]",
752                     "Portion", "def");
753         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[3]/Text[1]", "nType",
754                     "PortionType::Text");
755         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[3]/Text[1]",
756                     "Portion", "g");
757         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[3]/Text[2]", "nType",
758                     "PortionType::Text");
759         assertXPath(pXmlDoc, "/root/page[1]/header/txt[3]/anchored/fly[1]/txt[3]/Text[2]",
760                     "Portion", "hi");
761     }
762 }
763 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFlysInFootnote)764 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFlysInFootnote)
765 {
766     loadURL("private:factory/swriter", nullptr);
767     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
768     CPPUNIT_ASSERT(pTextDoc);
769     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
770     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
771     SwRootFrame* pLayout(pWrtShell->GetLayout());
772     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
773     pWrtShell->InsertFootnote("");
774     CPPUNIT_ASSERT(pWrtShell->IsCursorInFootnote());
775 
776     SfxItemSet flySet(pDoc->GetAttrPool(),
777                       svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
778     SwFormatFrameSize size(ATT_MIN_SIZE, 1000, 1000);
779     flySet.Put(size); // set a size, else we get 1 char per line...
780     SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
781     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
782     flySet.Put(anchor);
783     // first fly is in first footnote that will be deleted
784     /*  SwFrameFormat const* pFly1 =*/pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
785     pWrtShell->Insert("quux");
786 
787     pWrtShell->SttEndDoc(false);
788 
789     pWrtShell->InsertFootnote("");
790     CPPUNIT_ASSERT(pWrtShell->IsCursorInFootnote());
791     pWrtShell->Insert("foo");
792     pWrtShell->SplitNode(false);
793     pWrtShell->Insert("bar");
794     pWrtShell->SplitNode(false);
795     pWrtShell->Insert("baz");
796 
797     pWrtShell->StartOfSection(false);
798     CPPUNIT_ASSERT(pWrtShell->IsCursorInFootnote());
799     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
800     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
801     flySet.Put(anchor);
802     // second fly is in second footnote that is not deleted
803     SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
804     CPPUNIT_ASSERT(pFly != nullptr);
805     // move inside fly
806     pWrtShell->GotoFly(pFly->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
807     pWrtShell->Insert("abc");
808     pWrtShell->SplitNode(false);
809     pWrtShell->Insert("def");
810     pWrtShell->SplitNode(false);
811     pWrtShell->Insert("ghi");
812 
813     lcl_dispatchCommand(mxComponent, ".uno:TrackChanges", {});
814     // delete redline inside fly
815     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
816     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false);
817     pWrtShell->Delete();
818 
819     //    pWrtShell->GotoFlyAnchor(); // sigh... why, now we're in the body...
820     pWrtShell->SttEndDoc(false);
821     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
822     pWrtShell->GotoFootnoteText();
823     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
824     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
825     pWrtShell->Delete();
826     pWrtShell->EndSelect(); // ?
827     // delete first footnote
828     pWrtShell->SttEndDoc(true);
829     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
830     pWrtShell->Delete();
831 
832     for (int i = 0; i < 2; ++i)
833     {
834         if (i == 1) // secondly, try with different anchor type
835         {
836             anchor.SetType(RndStdIds::FLY_AT_PARA);
837             SwPosition pos(*anchor.GetContentAnchor());
838             pos.nContent.Assign(nullptr, 0);
839             anchor.SetAnchor(&pos);
840             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
841         }
842 
843         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
844         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
845         discardDumpedLayout();
846         xmlDocPtr pXmlDoc = parseLayoutDump();
847         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "25");
848         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
849                     "PortionType::Footnote");
850         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
851         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/merged", "paraPropsNodeIndex",
852                     "7");
853         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/merged",
854                     "paraPropsNodeIndex", "17");
855         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
856                     "nType", "PortionType::Para");
857         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
858                     "Portion", "ahi");
859         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
860                     "PortionType::FootnoteNum");
861         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
862 
863         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
864         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
865         discardDumpedLayout();
866         pXmlDoc = parseLayoutDump();
867 
868         { // show: nothing is merged
869             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
870             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
871             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
872             xmlXPathFreeObject(pXmlObj);
873         }
874 
875         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
876                     "PortionType::Footnote");
877         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
878         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "nType",
879                     "PortionType::Footnote");
880         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "rText", "2");
881         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
882                     "nType", "PortionType::Para");
883         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
884                     "Portion", "quux");
885         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
886                     "PortionType::FootnoteNum");
887         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
888         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
889                     "nType", "PortionType::Text");
890         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
891                     "Portion", "a");
892         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[1]/Text[2]",
893                     "nType", "PortionType::Text");
894         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[1]/Text[2]",
895                     "Portion", "bc");
896         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[2]/Text[1]",
897                     "nType", "PortionType::Para");
898         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[2]/Text[1]",
899                     "Portion", "def");
900         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[3]/Text[1]",
901                     "nType", "PortionType::Text");
902         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[3]/Text[1]",
903                     "Portion", "g");
904         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[3]/Text[2]",
905                     "nType", "PortionType::Text");
906         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/anchored/fly[1]/txt[3]/Text[2]",
907                     "Portion", "hi");
908         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "nType",
909                     "PortionType::FootnoteNum");
910         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "rText", "2");
911         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "nType",
912                     "PortionType::Text");
913         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "Portion", "fo");
914         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "nType",
915                     "PortionType::Text");
916         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "Portion", "o");
917         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "nType",
918                     "PortionType::Para");
919         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "Portion", "bar");
920         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "nType",
921                     "PortionType::Text");
922         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "Portion", "b");
923         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "nType",
924                     "PortionType::Text");
925         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "Portion", "az");
926     }
927 
928     // anchor to 2nd (deleted) paragraph
929     pWrtShell->SttEndDoc(false);
930     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
931     pWrtShell->GotoFootnoteText();
932     pWrtShell->Down(false, 1);
933     anchor.SetType(RndStdIds::FLY_AT_CHAR);
934     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
935     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
936 
937     for (int i = 0; i < 2; ++i)
938     {
939         if (i == 1) // secondly, try with different anchor type
940         {
941             anchor.SetType(RndStdIds::FLY_AT_PARA);
942             SwPosition pos(*anchor.GetContentAnchor());
943             pos.nContent.Assign(nullptr, 0);
944             anchor.SetAnchor(&pos);
945             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
946         }
947 
948         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
949         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
950         discardDumpedLayout();
951         xmlDocPtr pXmlDoc = parseLayoutDump();
952 
953         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "25");
954         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
955                     "PortionType::Footnote");
956         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
957         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/merged", "paraPropsNodeIndex",
958                     "7");
959         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
960                     "PortionType::FootnoteNum");
961         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
962 
963         { // hide: no anchored object shown
964             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//anchored");
965             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
966             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
967             xmlXPathFreeObject(pXmlObj);
968         }
969 
970         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
971         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
972         discardDumpedLayout();
973         pXmlDoc = parseLayoutDump();
974 
975         { // show: nothing is merged
976             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
977             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
978             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
979             xmlXPathFreeObject(pXmlObj);
980         }
981 
982         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
983                     "PortionType::Footnote");
984         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
985         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "nType",
986                     "PortionType::Footnote");
987         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "rText", "2");
988         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
989                     "nType", "PortionType::Para");
990         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
991                     "Portion", "quux");
992         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
993                     "PortionType::FootnoteNum");
994         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
995         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "nType",
996                     "PortionType::FootnoteNum");
997         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "rText", "2");
998         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "nType",
999                     "PortionType::Text");
1000         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "Portion", "fo");
1001         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "nType",
1002                     "PortionType::Text");
1003         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "Portion", "o");
1004         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[1]/Text[1]",
1005                     "nType", "PortionType::Text");
1006         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[1]/Text[1]",
1007                     "Portion", "a");
1008         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[1]/Text[2]",
1009                     "nType", "PortionType::Text");
1010         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[1]/Text[2]",
1011                     "Portion", "bc");
1012         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[2]/Text[1]",
1013                     "nType", "PortionType::Para");
1014         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[2]/Text[1]",
1015                     "Portion", "def");
1016         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[3]/Text[1]",
1017                     "nType", "PortionType::Text");
1018         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[3]/Text[1]",
1019                     "Portion", "g");
1020         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[3]/Text[2]",
1021                     "nType", "PortionType::Text");
1022         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/anchored/fly[1]/txt[3]/Text[2]",
1023                     "Portion", "hi");
1024         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "nType",
1025                     "PortionType::Para");
1026         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "Portion", "bar");
1027         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "nType",
1028                     "PortionType::Text");
1029         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "Portion", "b");
1030         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "nType",
1031                     "PortionType::Text");
1032         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "Portion", "az");
1033     }
1034 
1035     // anchor to 3rd paragraph
1036     pWrtShell->EndOfSection();
1037     pWrtShell->SttEndDoc(false);
1038     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1039     pWrtShell->GotoFootnoteText();
1040     pWrtShell->EndOfSection();
1041     anchor.SetType(RndStdIds::FLY_AT_CHAR);
1042     anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1043     pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
1044 
1045     for (int i = 0; i < 2; ++i)
1046     {
1047         if (i == 1) // secondly, try with different anchor type
1048         {
1049             anchor.SetType(RndStdIds::FLY_AT_PARA);
1050             SwPosition pos(*anchor.GetContentAnchor());
1051             pos.nContent.Assign(nullptr, 0);
1052             anchor.SetAnchor(&pos);
1053             pDoc->SetAttr(anchor, *const_cast<SwFrameFormat*>(pFly));
1054         }
1055 
1056         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1057         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1058         discardDumpedLayout();
1059         xmlDocPtr pXmlDoc = parseLayoutDump();
1060         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "25");
1061         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
1062                     "PortionType::Footnote");
1063         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
1064         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/merged", "paraPropsNodeIndex",
1065                     "7");
1066         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/merged",
1067                     "paraPropsNodeIndex", "17");
1068         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1069                     "nType", "PortionType::Para");
1070         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1071                     "Portion", "ahi");
1072         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
1073                     "PortionType::FootnoteNum");
1074         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
1075 
1076         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1077         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1078         discardDumpedLayout();
1079         pXmlDoc = parseLayoutDump();
1080 
1081         { // show: nothing is merged
1082             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1083             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1084             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1085             xmlXPathFreeObject(pXmlObj);
1086         }
1087 
1088         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "nType",
1089                     "PortionType::Footnote");
1090         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[1]", "rText", "1");
1091         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "nType",
1092                     "PortionType::Footnote");
1093         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Special[2]", "rText", "2");
1094         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1095                     "nType", "PortionType::Para");
1096         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1097                     "Portion", "quux");
1098         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "nType",
1099                     "PortionType::FootnoteNum");
1100         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[1]/txt[1]/Special[1]", "rText", "1");
1101         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "nType",
1102                     "PortionType::FootnoteNum");
1103         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Special[1]", "rText", "2");
1104         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "nType",
1105                     "PortionType::Text");
1106         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[1]", "Portion", "fo");
1107         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "nType",
1108                     "PortionType::Text");
1109         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[1]/Text[2]", "Portion", "o");
1110         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "nType",
1111                     "PortionType::Para");
1112         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[2]/Text[1]", "Portion", "bar");
1113         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[1]/Text[1]",
1114                     "nType", "PortionType::Text");
1115         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[1]/Text[1]",
1116                     "Portion", "a");
1117         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[1]/Text[2]",
1118                     "nType", "PortionType::Text");
1119         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[1]/Text[2]",
1120                     "Portion", "bc");
1121         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[2]/Text[1]",
1122                     "nType", "PortionType::Para");
1123         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[2]/Text[1]",
1124                     "Portion", "def");
1125         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[3]/Text[1]",
1126                     "nType", "PortionType::Text");
1127         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[3]/Text[1]",
1128                     "Portion", "g");
1129         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[3]/Text[2]",
1130                     "nType", "PortionType::Text");
1131         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/anchored/fly[1]/txt[3]/Text[2]",
1132                     "Portion", "hi");
1133         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "nType",
1134                     "PortionType::Text");
1135         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[1]", "Portion", "b");
1136         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "nType",
1137                     "PortionType::Text");
1138         assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn[2]/txt[3]/Text[2]", "Portion", "az");
1139     }
1140 }
1141 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf116486)1142 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf116486)
1143 {
1144     SwDoc* pDoc = createDoc("tdf116486.docx");
1145     CPPUNIT_ASSERT(pDoc);
1146     OUString aTop = parseDump("/root/page/body/txt/Special", "nHeight");
1147     CPPUNIT_ASSERT_EQUAL(OUString("4006"), aTop);
1148 }
1149 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf128198)1150 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf128198)
1151 {
1152     SwDoc* pDoc = createDoc("tdf128198-1.docx");
1153     CPPUNIT_ASSERT(pDoc);
1154     xmlDocPtr pLayout = parseLayoutDump();
1155     // the problem was that line 5 was truncated at "this  "
1156     // due to the fly anchored in previous paragraph
1157     assertXPath(pLayout, "/root/page/body/txt[2]/LineBreak[5]", "Line",
1158                 "to access any service, any time, anywhere. From this  perspective, satellite "
1159                 "boasts some ");
1160     assertXPath(pLayout, "/root/page/body/txt[2]/LineBreak[6]", "Line", "significant advantages. ");
1161 }
1162 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testNoLineBreakAtSlash)1163 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testNoLineBreakAtSlash)
1164 {
1165     load(DATA_DIRECTORY, "no-line-break-at-slash.fodt");
1166     xmlDocPtr pLayout = parseLayoutDump();
1167 
1168     // the line break was between  "Foostrasse 13/c/" and "2"
1169     xmlXPathObjectPtr pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[2]");
1170     CPPUNIT_ASSERT_EQUAL(std::string("Text"), std::string(reinterpret_cast<char const*>(
1171                                                   pXmlObj->nodesetval->nodeTab[0]->name)));
1172     xmlXPathFreeObject(pXmlObj);
1173     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[3]");
1174     CPPUNIT_ASSERT_EQUAL(std::string("LineBreak"), std::string(reinterpret_cast<char const*>(
1175                                                        pXmlObj->nodesetval->nodeTab[0]->name)));
1176     xmlXPathFreeObject(pXmlObj);
1177     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[4]");
1178     CPPUNIT_ASSERT_EQUAL(std::string("Text"), std::string(reinterpret_cast<char const*>(
1179                                                   pXmlObj->nodesetval->nodeTab[0]->name)));
1180     xmlXPathFreeObject(pXmlObj);
1181     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[5]");
1182     CPPUNIT_ASSERT_EQUAL(std::string("Special"), std::string(reinterpret_cast<char const*>(
1183                                                      pXmlObj->nodesetval->nodeTab[0]->name)));
1184     xmlXPathFreeObject(pXmlObj);
1185     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[6]");
1186     CPPUNIT_ASSERT_EQUAL(std::string("Text"), std::string(reinterpret_cast<char const*>(
1187                                                   pXmlObj->nodesetval->nodeTab[0]->name)));
1188     xmlXPathFreeObject(pXmlObj);
1189     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[7]");
1190     CPPUNIT_ASSERT_EQUAL(std::string("LineBreak"), std::string(reinterpret_cast<char const*>(
1191                                                        pXmlObj->nodesetval->nodeTab[0]->name)));
1192     xmlXPathFreeObject(pXmlObj);
1193     pXmlObj = getXPathNode(pLayout, "/root/page[1]/body/txt[1]/child::*[8]");
1194     CPPUNIT_ASSERT_EQUAL(std::string("Finish"), std::string(reinterpret_cast<char const*>(
1195                                                     pXmlObj->nodesetval->nodeTab[0]->name)));
1196     xmlXPathFreeObject(pXmlObj);
1197 
1198     assertXPath(pLayout, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "Blah blah bla bla bla ");
1199     assertXPath(pLayout, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "Foostrasse");
1200     assertXPath(pLayout, "/root/page[1]/body/txt[1]/Text[3]", "Portion", "13/c/2, etc.");
1201 }
1202 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFlysInFlys)1203 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFlysInFlys)
1204 {
1205     loadURL("private:factory/swriter", nullptr);
1206     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
1207     CPPUNIT_ASSERT(pTextDoc);
1208     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
1209     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
1210     SwRootFrame* pLayout(pWrtShell->GetLayout());
1211     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1212     pWrtShell->Insert("foo");
1213     pWrtShell->SplitNode(false);
1214     pWrtShell->Insert("bar");
1215     pWrtShell->SplitNode(false);
1216     pWrtShell->Insert("baz");
1217     SfxItemSet flySet(pDoc->GetAttrPool(),
1218                       svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
1219     SwFormatFrameSize size(ATT_MIN_SIZE, 1000, 1000);
1220     flySet.Put(size); // set a size, else we get 1 char per line...
1221     pWrtShell->StartOfSection(false);
1222     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1223     SwFormatAnchor anchor1(RndStdIds::FLY_AT_CHAR);
1224     anchor1.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1225     flySet.Put(anchor1);
1226     SwFrameFormat const* pFly1 = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
1227     CPPUNIT_ASSERT(pFly1 != nullptr);
1228     // move inside fly1
1229     pWrtShell->GotoFly(pFly1->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1230     pWrtShell->Insert("abc");
1231     pWrtShell->SplitNode(false);
1232     pWrtShell->Insert("def");
1233     pWrtShell->SplitNode(false);
1234     pWrtShell->Insert("ghi");
1235 
1236     SwFormatAnchor anchor2(RndStdIds::FLY_AT_CHAR);
1237     pWrtShell->StartOfSection(false); // start of fly...
1238     anchor2.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1239     flySet.Put(anchor2);
1240     SwFrameFormat const* pFly2 = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
1241     CPPUNIT_ASSERT(pFly2 != nullptr);
1242     // move inside fly2
1243     pWrtShell->GotoFly(pFly2->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1244     pWrtShell->Insert("jkl");
1245     pWrtShell->SplitNode(false);
1246     pWrtShell->Insert("mno");
1247     pWrtShell->SplitNode(false);
1248     pWrtShell->Insert("pqr");
1249 
1250     lcl_dispatchCommand(mxComponent, ".uno:TrackChanges", {});
1251     // delete redline inside fly2
1252     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1253     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false);
1254     pWrtShell->Delete();
1255 
1256     // delete redline inside fly1
1257     pWrtShell->GotoFly(pFly1->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1258     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1259     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
1260     pWrtShell->Delete();
1261 
1262     pWrtShell->ClearMark(); // otherwise it refuses to leave the fly...
1263     pWrtShell->SttEndDoc(true); // note: SttDoc actually moves to start of fly?
1264     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1265     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
1266     pWrtShell->Delete();
1267 
1268     for (int i = 0; i < 2; ++i)
1269     {
1270         if (i == 1) // secondly, try with different anchor type
1271         {
1272             anchor1.SetType(RndStdIds::FLY_AT_PARA);
1273             SwPosition pos(*anchor1.GetContentAnchor());
1274             pos.nContent.Assign(nullptr, 0);
1275             anchor1.SetAnchor(&pos);
1276             pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1277             anchor2.SetType(RndStdIds::FLY_AT_PARA);
1278             pos.nNode = anchor2.GetContentAnchor()->nNode;
1279             anchor2.SetAnchor(&pos);
1280             pDoc->SetAttr(anchor2, *const_cast<SwFrameFormat*>(pFly2));
1281         }
1282 
1283         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1284         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1285         discardDumpedLayout();
1286         xmlDocPtr pXmlDoc = parseLayoutDump();
1287         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "19");
1288         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/merged",
1289                     "paraPropsNodeIndex", "6");
1290         assertXPath(
1291             pXmlDoc,
1292             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/merged",
1293             "paraPropsNodeIndex", "11");
1294         assertXPath(
1295             pXmlDoc,
1296             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1297             "nType", "PortionType::Para");
1298         assertXPath(
1299             pXmlDoc,
1300             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1301             "Portion", "jqr");
1302         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1303                     "PortionType::Fly"); // remove???
1304         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1305                     "PortionType::Lay");
1306         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1307                     "abhi");
1308         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1309         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
1310 
1311         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1312         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1313         discardDumpedLayout();
1314         pXmlDoc = parseLayoutDump();
1315 
1316         { // show: nothing is merged
1317             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1318             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1319             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1320             xmlXPathFreeObject(pXmlObj);
1321         }
1322 
1323         assertXPath(
1324             pXmlDoc,
1325             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1326             "nType", "PortionType::Text");
1327         assertXPath(
1328             pXmlDoc,
1329             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1330             "Portion", "j");
1331         assertXPath(
1332             pXmlDoc,
1333             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1334             "nType", "PortionType::Text");
1335         assertXPath(
1336             pXmlDoc,
1337             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1338             "Portion", "kl");
1339         assertXPath(
1340             pXmlDoc,
1341             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1342             "nType", "PortionType::Para");
1343         assertXPath(
1344             pXmlDoc,
1345             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1346             "Portion", "mno");
1347         assertXPath(
1348             pXmlDoc,
1349             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1350             "nType", "PortionType::Text");
1351         assertXPath(
1352             pXmlDoc,
1353             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1354             "Portion", "p");
1355         assertXPath(
1356             pXmlDoc,
1357             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1358             "nType", "PortionType::Text");
1359         assertXPath(
1360             pXmlDoc,
1361             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1362             "Portion", "qr");
1363         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1364                     "PortionType::Fly"); // remove???
1365         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1366                     "PortionType::Text");
1367         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1368                     "ab");
1369         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "nType",
1370                     "PortionType::Text");
1371         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
1372                     "c");
1373         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "nType",
1374                     "PortionType::Para");
1375         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
1376                     "def");
1377         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "nType",
1378                     "PortionType::Text");
1379         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
1380                     "g");
1381         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "nType",
1382                     "PortionType::Text");
1383         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
1384                     "hi");
1385         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1386         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1387         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1388         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1389         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
1390         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
1391         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
1392         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
1393         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
1394         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
1395     }
1396 
1397     // anchor to 2nd (deleted) paragraph
1398     // also, switch the in-fly anchoring to the other fly, for additional fun!
1399     pWrtShell->StartOfSection();
1400     pWrtShell->Down(false, 1);
1401     anchor2.SetType(RndStdIds::FLY_AT_CHAR);
1402     anchor2.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1403     pDoc->SetAttr(anchor2, *const_cast<SwFrameFormat*>(pFly2));
1404     pWrtShell->GotoFly(pFly2->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1405     pWrtShell->Down(false, 1);
1406     anchor1.SetType(RndStdIds::FLY_AT_CHAR);
1407     anchor1.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1408     pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1409 
1410     for (int i = 0; i < 2; ++i)
1411     {
1412         if (i == 1) // secondly, try with different anchor type
1413         {
1414             anchor1.SetType(RndStdIds::FLY_AT_PARA);
1415             SwPosition pos(*anchor1.GetContentAnchor());
1416             pos.nContent.Assign(nullptr, 0);
1417             anchor1.SetAnchor(&pos);
1418             pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1419             anchor2.SetType(RndStdIds::FLY_AT_PARA);
1420             pos.nNode = anchor2.GetContentAnchor()->nNode;
1421             anchor2.SetAnchor(&pos);
1422             pDoc->SetAttr(anchor2, *const_cast<SwFrameFormat*>(pFly2));
1423         }
1424 
1425         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1426         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1427         discardDumpedLayout();
1428         xmlDocPtr pXmlDoc = parseLayoutDump();
1429         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "19");
1430         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1431         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
1432 
1433         { // hide: no anchored object shown
1434             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//anchored");
1435             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1436             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1437             xmlXPathFreeObject(pXmlObj);
1438         }
1439 
1440         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1441         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1442         discardDumpedLayout();
1443         pXmlDoc = parseLayoutDump();
1444 
1445         { // show: nothing is merged
1446             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1447             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1448             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1449             xmlXPathFreeObject(pXmlObj);
1450         }
1451 
1452         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1453         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1454         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1455         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1456         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1457                     "PortionType::Text");
1458         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1459                     "j");
1460         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "nType",
1461                     "PortionType::Text");
1462         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
1463                     "kl");
1464         assertXPath(
1465             pXmlDoc,
1466             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[1]/Text[1]",
1467             "nType", "PortionType::Text");
1468         assertXPath(
1469             pXmlDoc,
1470             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[1]/Text[1]",
1471             "Portion", "ab");
1472         assertXPath(
1473             pXmlDoc,
1474             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[1]/Text[2]",
1475             "nType", "PortionType::Text");
1476         assertXPath(
1477             pXmlDoc,
1478             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[1]/Text[2]",
1479             "Portion", "c");
1480         assertXPath(
1481             pXmlDoc,
1482             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[2]/Text[1]",
1483             "nType", "PortionType::Para");
1484         assertXPath(
1485             pXmlDoc,
1486             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[2]/Text[1]",
1487             "Portion", "def");
1488         assertXPath(
1489             pXmlDoc,
1490             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[3]/Text[1]",
1491             "nType", "PortionType::Text");
1492         assertXPath(
1493             pXmlDoc,
1494             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[3]/Text[1]",
1495             "Portion", "g");
1496         assertXPath(
1497             pXmlDoc,
1498             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[3]/Text[2]",
1499             "nType", "PortionType::Text");
1500         assertXPath(
1501             pXmlDoc,
1502             "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/anchored[1]/fly[1]/txt[3]/Text[2]",
1503             "Portion", "hi");
1504         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Special[1]", "nType",
1505                     "PortionType::Fly"); // remove???
1506         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "nType",
1507                     "PortionType::Lay");
1508         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
1509                     "mno");
1510         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "nType",
1511                     "PortionType::Text");
1512         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
1513                     "p");
1514         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "nType",
1515                     "PortionType::Text");
1516         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
1517                     "qr");
1518         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
1519         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
1520         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
1521         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
1522         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
1523         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
1524     }
1525 
1526     // anchor to 3rd paragraph
1527     pWrtShell->SttEndDoc(false);
1528     anchor1.SetType(RndStdIds::FLY_AT_CHAR);
1529     anchor1.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1530     pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1531     pWrtShell->GotoFly(pFly1->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1532     pWrtShell->EndOfSection();
1533     anchor2.SetType(RndStdIds::FLY_AT_CHAR);
1534     anchor2.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1535     pDoc->SetAttr(anchor2, *const_cast<SwFrameFormat*>(pFly2));
1536 
1537     for (int i = 0; i < 2; ++i)
1538     {
1539         if (i == 1) // secondly, try with different anchor type
1540         {
1541             anchor1.SetType(RndStdIds::FLY_AT_PARA);
1542             SwPosition pos(*anchor1.GetContentAnchor());
1543             pos.nContent.Assign(nullptr, 0);
1544             anchor1.SetAnchor(&pos);
1545             pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1546             anchor2.SetType(RndStdIds::FLY_AT_PARA);
1547             pos.nNode = anchor2.GetContentAnchor()->nNode;
1548             anchor2.SetAnchor(&pos);
1549             pDoc->SetAttr(anchor2, *const_cast<SwFrameFormat*>(pFly2));
1550         }
1551 
1552         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1553         CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1554         discardDumpedLayout();
1555         xmlDocPtr pXmlDoc = parseLayoutDump();
1556         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "19");
1557         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/merged",
1558                     "paraPropsNodeIndex", "6");
1559         assertXPath(
1560             pXmlDoc,
1561             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/merged",
1562             "paraPropsNodeIndex", "11");
1563         assertXPath(
1564             pXmlDoc,
1565             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1566             "nType", "PortionType::Para");
1567         assertXPath(
1568             pXmlDoc,
1569             "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1570             "Portion", "jqr");
1571         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1572                     "PortionType::Fly"); // remove???
1573         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1574                     "PortionType::Lay");
1575         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1576                     "abhi");
1577         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1578         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
1579 
1580         lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1581         CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1582         discardDumpedLayout();
1583         pXmlDoc = parseLayoutDump();
1584 
1585         { // show: nothing is merged
1586             xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1587             xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1588             CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1589             xmlXPathFreeObject(pXmlObj);
1590         }
1591 
1592         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1593         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1594         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1595         assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1596         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
1597         assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
1598         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1599                     "PortionType::Text");
1600         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1601                     "ab");
1602         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[2]", "nType",
1603                     "PortionType::Text");
1604         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
1605                     "c");
1606         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[2]/Text[1]", "nType",
1607                     "PortionType::Para");
1608         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
1609                     "def");
1610         assertXPath(
1611             pXmlDoc,
1612             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[1]/Text[1]",
1613             "nType", "PortionType::Text");
1614         assertXPath(
1615             pXmlDoc,
1616             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[1]/Text[1]",
1617             "Portion", "j");
1618         assertXPath(
1619             pXmlDoc,
1620             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[1]/Text[2]",
1621             "nType", "PortionType::Text");
1622         assertXPath(
1623             pXmlDoc,
1624             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[1]/Text[2]",
1625             "Portion", "kl");
1626         assertXPath(
1627             pXmlDoc,
1628             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[2]/Text[1]",
1629             "nType", "PortionType::Para");
1630         assertXPath(
1631             pXmlDoc,
1632             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[2]/Text[1]",
1633             "Portion", "mno");
1634         assertXPath(
1635             pXmlDoc,
1636             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[3]/Text[1]",
1637             "nType", "PortionType::Text");
1638         assertXPath(
1639             pXmlDoc,
1640             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[3]/Text[1]",
1641             "Portion", "p");
1642         assertXPath(
1643             pXmlDoc,
1644             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[3]/Text[2]",
1645             "nType", "PortionType::Text");
1646         assertXPath(
1647             pXmlDoc,
1648             "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/anchored[1]/fly[1]/txt[3]/Text[2]",
1649             "Portion", "qr");
1650         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[1]", "nType",
1651                     "PortionType::Text");
1652         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Special[1]", "nType",
1653                     "PortionType::Fly"); // remove???
1654         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
1655                     "g");
1656         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[2]", "nType",
1657                     "PortionType::Text");
1658         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
1659                     "hi");
1660         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
1661         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
1662         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
1663         assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
1664     }
1665 }
1666 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineFlysAtFlys)1667 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineFlysAtFlys)
1668 {
1669     loadURL("private:factory/swriter", nullptr);
1670     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
1671     CPPUNIT_ASSERT(pTextDoc);
1672     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
1673     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
1674     SwRootFrame* pLayout(pWrtShell->GetLayout());
1675     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1676     pWrtShell->Insert("foo");
1677     pWrtShell->SplitNode(false);
1678     pWrtShell->Insert("bar");
1679     pWrtShell->SplitNode(false);
1680     pWrtShell->Insert("baz");
1681     SfxItemSet flySet(pDoc->GetAttrPool(),
1682                       svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
1683     SwFormatFrameSize size(ATT_MIN_SIZE, 1000, 1000);
1684     flySet.Put(size); // set a size, else we get 1 char per line...
1685     pWrtShell->StartOfSection(false);
1686     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1687     SwFormatAnchor anchor1(RndStdIds::FLY_AT_CHAR);
1688     anchor1.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1689     flySet.Put(anchor1);
1690     SwFrameFormat const* pFly1 = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
1691     CPPUNIT_ASSERT(pFly1 != nullptr);
1692     // move inside fly1
1693     pWrtShell->GotoFly(pFly1->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1694     pWrtShell->Insert("abc");
1695     pWrtShell->SplitNode(false);
1696     pWrtShell->Insert("def");
1697     pWrtShell->SplitNode(false);
1698     pWrtShell->Insert("ghi");
1699 
1700     SwFormatAnchor anchor2(RndStdIds::FLY_AT_FLY);
1701     SwPosition pos(*pFly1->GetContent().GetContentIdx());
1702     anchor2.SetAnchor(&pos);
1703     flySet.Put(anchor2);
1704     SwFrameFormat const* pFly2 = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true);
1705     CPPUNIT_ASSERT(pFly2 != nullptr);
1706     // move inside fly2
1707     pWrtShell->GotoFly(pFly2->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1708     pWrtShell->Insert("jkl");
1709     pWrtShell->SplitNode(false);
1710     pWrtShell->Insert("mno");
1711     pWrtShell->SplitNode(false);
1712     pWrtShell->Insert("pqr");
1713 
1714     lcl_dispatchCommand(mxComponent, ".uno:TrackChanges", {});
1715     // delete redline inside fly2
1716     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1717     pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false);
1718     pWrtShell->Delete();
1719 
1720     // delete redline inside fly1
1721     pWrtShell->GotoFly(pFly1->GetName(), FLYCNTTYPE_FRM, /*bSelFrame=*/false);
1722     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1723     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
1724     pWrtShell->Delete();
1725 
1726     pWrtShell->ClearMark(); // otherwise it refuses to leave the fly...
1727     pWrtShell->SttEndDoc(true); // note: SttDoc actually moves to start of fly?
1728     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
1729     pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false);
1730     pWrtShell->Delete();
1731 
1732     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1733     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1734     discardDumpedLayout();
1735     xmlDocPtr pXmlDoc = parseLayoutDump();
1736     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "19");
1737     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/merged",
1738                 "paraPropsNodeIndex", "6");
1739     assertXPath(pXmlDoc,
1740                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/merged",
1741                 "paraPropsNodeIndex", "11");
1742     assertXPath(pXmlDoc,
1743                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1744                 "nType", "PortionType::Para");
1745     assertXPath(pXmlDoc,
1746                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1747                 "Portion", "jqr");
1748     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1749                 "PortionType::Fly"); // remove???
1750     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1751                 "PortionType::Lay");
1752     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1753                 "abhi");
1754     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1755     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
1756 
1757     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1758     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1759     discardDumpedLayout();
1760     pXmlDoc = parseLayoutDump();
1761 
1762     { // show: nothing is merged
1763         xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1764         xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1765         CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1766         xmlXPathFreeObject(pXmlObj);
1767     }
1768 
1769     assertXPath(pXmlDoc,
1770                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1771                 "nType", "PortionType::Text");
1772     assertXPath(pXmlDoc,
1773                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1774                 "Portion", "j");
1775     assertXPath(pXmlDoc,
1776                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1777                 "nType", "PortionType::Text");
1778     assertXPath(pXmlDoc,
1779                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1780                 "Portion", "kl");
1781     assertXPath(pXmlDoc,
1782                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1783                 "nType", "PortionType::Para");
1784     assertXPath(pXmlDoc,
1785                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1786                 "Portion", "mno");
1787     assertXPath(pXmlDoc,
1788                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1789                 "nType", "PortionType::Text");
1790     assertXPath(pXmlDoc,
1791                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1792                 "Portion", "p");
1793     assertXPath(pXmlDoc,
1794                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1795                 "nType", "PortionType::Text");
1796     assertXPath(pXmlDoc,
1797                 "/root/page[1]/body/txt[1]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1798                 "Portion", "qr");
1799     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1800                 "PortionType::Fly"); // remove???
1801     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1802                 "PortionType::Text");
1803     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1804                 "ab");
1805     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "nType",
1806                 "PortionType::Text");
1807     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
1808                 "c");
1809     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "nType",
1810                 "PortionType::Para");
1811     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
1812                 "def");
1813     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "nType",
1814                 "PortionType::Text");
1815     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
1816                 "g");
1817     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "nType",
1818                 "PortionType::Text");
1819     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
1820                 "hi");
1821     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1822     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1823     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1824     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1825     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
1826     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
1827     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
1828     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
1829     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
1830     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
1831 
1832     // anchor to 2nd (deleted) paragraph
1833     pWrtShell->StartOfSection();
1834     pWrtShell->Down(false, 1);
1835     anchor1.SetType(RndStdIds::FLY_AT_CHAR);
1836     anchor1.SetAnchor(pWrtShell->GetCursor()->GetPoint());
1837     pDoc->SetAttr(anchor1, *const_cast<SwFrameFormat*>(pFly1));
1838 
1839     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1840     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1841     discardDumpedLayout();
1842     pXmlDoc = parseLayoutDump();
1843     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "19");
1844     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1845     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foaz");
1846 
1847     { // hide: no anchored object shown
1848         xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//anchored");
1849         xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1850         CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1851         xmlXPathFreeObject(pXmlObj);
1852     }
1853 
1854     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1855     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1856     discardDumpedLayout();
1857     pXmlDoc = parseLayoutDump();
1858 
1859     { // show: nothing is merged
1860         xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1861         xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1862         CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1863         xmlXPathFreeObject(pXmlObj);
1864     }
1865 
1866     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1867     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1868     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1869     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1870     assertXPath(pXmlDoc,
1871                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1872                 "nType", "PortionType::Text");
1873     assertXPath(pXmlDoc,
1874                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[1]",
1875                 "Portion", "j");
1876     assertXPath(pXmlDoc,
1877                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1878                 "nType", "PortionType::Text");
1879     assertXPath(pXmlDoc,
1880                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[1]/Text[2]",
1881                 "Portion", "kl");
1882     assertXPath(pXmlDoc,
1883                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1884                 "nType", "PortionType::Para");
1885     assertXPath(pXmlDoc,
1886                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[2]/Text[1]",
1887                 "Portion", "mno");
1888     assertXPath(pXmlDoc,
1889                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1890                 "nType", "PortionType::Text");
1891     assertXPath(pXmlDoc,
1892                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[1]",
1893                 "Portion", "p");
1894     assertXPath(pXmlDoc,
1895                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1896                 "nType", "PortionType::Text");
1897     assertXPath(pXmlDoc,
1898                 "/root/page[1]/body/txt[2]/anchored/fly[1]/anchored[1]/fly[1]/txt[3]/Text[2]",
1899                 "Portion", "qr");
1900     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "nType",
1901                 "PortionType::Text");
1902     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Special[1]", "nType",
1903                 "PortionType::Fly"); // remove???
1904     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[1]", "Portion",
1905                 "ab");
1906     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "nType",
1907                 "PortionType::Text");
1908     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[1]/Text[2]", "Portion",
1909                 "c");
1910     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "nType",
1911                 "PortionType::Para");
1912     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[2]/Text[1]", "Portion",
1913                 "def");
1914     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "nType",
1915                 "PortionType::Text");
1916     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[1]", "Portion",
1917                 "g");
1918     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "nType",
1919                 "PortionType::Text");
1920     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly[1]/txt[3]/Text[2]", "Portion",
1921                 "hi");
1922     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
1923     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "bar");
1924     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
1925     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "b");
1926     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
1927     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "az");
1928 }
1929 
CheckRedlineSectionsHidden()1930 void SwLayoutWriter::CheckRedlineSectionsHidden()
1931 {
1932     discardDumpedLayout();
1933     xmlDocPtr pXmlDoc = parseLayoutDump();
1934     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "12");
1935     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
1936     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "folah");
1937     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/merged", "paraPropsNodeIndex", "20");
1938     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/Text[1]", "nType",
1939                 "PortionType::Para");
1940     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/Text[1]", "Portion", "folah");
1941 }
1942 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineSections)1943 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineSections)
1944 {
1945     createDoc("redline_sections.fodt");
1946     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
1947     CPPUNIT_ASSERT(pTextDoc);
1948     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
1949     SwRootFrame* pLayout(pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
1950     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
1951 
1952     // verify after load
1953     CheckRedlineSectionsHidden();
1954 
1955     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
1956     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
1957     // why is this needed explicitly?
1958     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
1959     discardDumpedLayout();
1960     xmlDocPtr pXmlDoc = parseLayoutDump();
1961 
1962     // show: nothing is merged
1963     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
1964     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
1965     CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
1966     xmlXPathFreeObject(pXmlObj);
1967     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
1968     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
1969     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
1970     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
1971 
1972     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1973                 "nType", "PortionType::Para");
1974     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
1975                 "Portion", "FRAME");
1976     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/Text[1]", "nType",
1977                 "PortionType::Para");
1978     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[1]/Text[1]", "Portion", "bar");
1979     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[2]/Text[1]", "nType",
1980                 "PortionType::Para");
1981     assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt[2]/Text[1]", "Portion", "baz");
1982     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Text");
1983     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "b");
1984     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "nType", "PortionType::Text");
1985     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "Portion", "lah");
1986     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[1]/Text[1]", "nType",
1987                 "PortionType::Text");
1988     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[1]/Text[1]", "Portion", "fo");
1989     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[1]/Text[2]", "nType",
1990                 "PortionType::Text");
1991     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[1]/Text[2]", "Portion", "o");
1992     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[2]/Text[1]", "nType",
1993                 "PortionType::Para");
1994     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[2]/Text[1]", "Portion", "bar");
1995     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[3]/Text[1]", "nType",
1996                 "PortionType::Text");
1997     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[3]/Text[1]", "Portion", "b");
1998     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[3]/Text[2]", "nType",
1999                 "PortionType::Text");
2000     assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[3]/Text[2]", "Portion", "lah");
2001 
2002     // verify after hide
2003     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2004     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
2005     // why is this needed explicitly?
2006     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2007     discardDumpedLayout();
2008     CheckRedlineSectionsHidden();
2009 }
2010 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineTables)2011 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineTables)
2012 {
2013     createDoc("redline_table.fodt");
2014     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
2015     CPPUNIT_ASSERT(pTextDoc);
2016     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
2017     SwRootFrame* pLayout(pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
2018     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
2019 
2020     // verify after load
2021     discardDumpedLayout();
2022     xmlDocPtr pXmlDoc = parseLayoutDump();
2023     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "12");
2024     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
2025     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foar");
2026 
2027     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2028     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
2029     // why is this needed explicitly?
2030     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2031     discardDumpedLayout();
2032     pXmlDoc = parseLayoutDump();
2033 
2034     // show: nothing is merged
2035     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
2036     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
2037     CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
2038     xmlXPathFreeObject(pXmlObj);
2039     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
2040     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "fo");
2041     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
2042     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "o");
2043 
2044     assertXPath(pXmlDoc,
2045                 "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
2046                 "nType", "PortionType::Para");
2047     assertXPath(pXmlDoc,
2048                 "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/anchored/fly[1]/txt[1]/Text[1]",
2049                 "Portion", "FRAME");
2050     assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/Text[1]", "nType",
2051                 "PortionType::Para");
2052     assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/Text[1]", "Portion",
2053                 "aaa");
2054     assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row[2]/cell[2]/txt[1]/Text[1]", "nType",
2055                 "PortionType::Para");
2056     assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row[2]/cell[2]/txt[1]/Text[1]", "Portion",
2057                 "ddd");
2058     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Text");
2059     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "b");
2060     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "nType", "PortionType::Text");
2061     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "Portion", "ar");
2062 
2063     // verify after hide
2064     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2065     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
2066     // why is this needed explicitly?
2067     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2068     discardDumpedLayout();
2069     pXmlDoc = parseLayoutDump();
2070     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "12");
2071     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
2072     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foar");
2073 }
2074 
2075 // this mainly tests that splitting portions across redlines in SwAttrIter works
CheckRedlineCharAttributesHidden()2076 void SwLayoutWriter::CheckRedlineCharAttributesHidden()
2077 {
2078     discardDumpedLayout();
2079     xmlDocPtr pXmlDoc = parseLayoutDump();
2080     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/merged", "paraPropsNodeIndex", "9");
2081     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Para");
2082     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foobaz");
2083     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/merged", "paraPropsNodeIndex", "10");
2084     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Para");
2085     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "foobaz");
2086     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/merged", "paraPropsNodeIndex", "11");
2087     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
2088     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "foo");
2089     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
2090     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "baz");
2091     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/merged", "paraPropsNodeIndex", "12");
2092     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[1]", "nType", "PortionType::Text");
2093     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[1]", "Portion", "foo");
2094     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[2]", "nType", "PortionType::Text");
2095     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[2]", "Portion", "baz");
2096     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/merged", "paraPropsNodeIndex", "13");
2097     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[1]", "nType", "PortionType::Text");
2098     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[1]", "Portion", "foo");
2099     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[2]", "nType", "PortionType::Text");
2100     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[2]", "Portion", "baz");
2101     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/merged", "paraPropsNodeIndex", "14");
2102     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[1]", "nType", "PortionType::Text");
2103     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[1]", "Portion", "foo");
2104     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[2]", "nType", "PortionType::Text");
2105     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[2]", "Portion", "baz");
2106     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/merged", "paraPropsNodeIndex", "15");
2107     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[1]", "nType", "PortionType::Text");
2108     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[1]", "Portion", "foo");
2109     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[2]", "nType", "PortionType::Text");
2110     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[2]", "Portion", "baz");
2111     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/merged", "paraPropsNodeIndex", "16");
2112     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[1]", "nType", "PortionType::Text");
2113     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[1]", "Portion", "foo");
2114     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[2]", "nType", "PortionType::Text");
2115     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[2]", "Portion", "baz");
2116     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/merged", "paraPropsNodeIndex", "17");
2117     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[1]", "nType", "PortionType::Para");
2118     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[1]", "Portion", "foobaz");
2119     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/merged", "paraPropsNodeIndex", "18");
2120     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[1]", "nType", "PortionType::Text");
2121     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[1]", "Portion", "fo");
2122     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[2]", "nType", "PortionType::Text");
2123     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[2]", "Portion", "ob");
2124     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[3]", "nType", "PortionType::Text");
2125     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[3]", "Portion", "az");
2126     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/merged", "paraPropsNodeIndex", "19");
2127     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[1]", "nType", "PortionType::Para");
2128     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[1]", "Portion", "foobaz");
2129 }
2130 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineCharAttributes)2131 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineCharAttributes)
2132 {
2133     createDoc("redline_charatr.fodt");
2134     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
2135     CPPUNIT_ASSERT(pTextDoc);
2136     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
2137     SwRootFrame* pLayout(pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
2138     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
2139 
2140     // verify after load
2141     CheckRedlineCharAttributesHidden();
2142 
2143     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2144     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
2145     // why is this needed explicitly?
2146     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2147     discardDumpedLayout();
2148     xmlDocPtr pXmlDoc = parseLayoutDump();
2149 
2150     // show: nothing is merged
2151     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//merged");
2152     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
2153     CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
2154     xmlXPathFreeObject(pXmlObj);
2155     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "nType", "PortionType::Text");
2156     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[1]", "Portion", "foo");
2157     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "nType", "PortionType::Text");
2158     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[2]", "Portion", "bar");
2159     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[3]", "nType", "PortionType::Text");
2160     assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/Text[3]", "Portion", "baz");
2161     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "nType", "PortionType::Text");
2162     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[1]", "Portion", "foo");
2163     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "nType", "PortionType::Text");
2164     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[2]", "Portion", "bar");
2165     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[3]", "nType", "PortionType::Text");
2166     assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/Text[3]", "Portion", "baz");
2167     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "nType", "PortionType::Text");
2168     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[1]", "Portion", "foo");
2169     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "nType", "PortionType::Text");
2170     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[2]", "Portion", "bar");
2171     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[3]", "nType", "PortionType::Text");
2172     assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/Text[3]", "Portion", "baz");
2173     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[1]", "nType", "PortionType::Text");
2174     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[1]", "Portion", "foo");
2175     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[2]", "nType", "PortionType::Text");
2176     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[2]", "Portion", "bar");
2177     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[3]", "nType", "PortionType::Text");
2178     assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/Text[3]", "Portion", "baz");
2179     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[1]", "nType", "PortionType::Text");
2180     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[1]", "Portion", "foo");
2181     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[2]", "nType", "PortionType::Text");
2182     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[2]", "Portion", "bar");
2183     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[3]", "nType", "PortionType::Text");
2184     assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/Text[3]", "Portion", "baz");
2185     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[1]", "nType", "PortionType::Text");
2186     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[1]", "Portion", "foo");
2187     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[2]", "nType", "PortionType::Text");
2188     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[2]", "Portion", "bar");
2189     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[3]", "nType", "PortionType::Text");
2190     assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/Text[3]", "Portion", "baz");
2191     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[1]", "nType", "PortionType::Text");
2192     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[1]", "Portion", "foo");
2193     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[2]", "nType", "PortionType::Text");
2194     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[2]", "Portion", "bar");
2195     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[3]", "nType", "PortionType::Text");
2196     assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/Text[3]", "Portion", "baz");
2197     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[1]", "nType", "PortionType::Text");
2198     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[1]", "Portion", "foo");
2199     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[2]", "nType", "PortionType::Text");
2200     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[2]", "Portion", "ba");
2201     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[3]", "nType", "PortionType::Text");
2202     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[3]", "Portion", "r");
2203     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[4]", "nType", "PortionType::Text");
2204     assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/Text[4]", "Portion", "baz");
2205     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[1]", "nType", "PortionType::Text");
2206     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[1]", "Portion", "foo");
2207     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[2]", "nType", "PortionType::Text");
2208     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[2]", "Portion", "bar");
2209     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[3]", "nType", "PortionType::Text");
2210     assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/Text[3]", "Portion", "baz");
2211     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[1]", "nType", "PortionType::Text");
2212     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[1]", "Portion", "fo");
2213     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[2]", "nType", "PortionType::Text");
2214     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[2]", "Portion", "o");
2215     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[3]", "nType", "PortionType::Text");
2216     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[3]", "Portion", "bar");
2217     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[4]", "nType", "PortionType::Text");
2218     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[4]", "Portion", "b");
2219     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[5]", "nType", "PortionType::Text");
2220     assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/Text[5]", "Portion", "az");
2221     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[1]", "nType", "PortionType::Text");
2222     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[1]", "Portion", "foo");
2223     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[2]", "nType", "PortionType::Text");
2224     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[2]", "Portion", "b");
2225     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[3]", "nType", "PortionType::Text");
2226     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[3]", "Portion", "a");
2227     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[4]", "nType", "PortionType::Text");
2228     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[4]", "Portion", "r");
2229     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[5]", "nType", "PortionType::Text");
2230     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[5]", "Portion", "baz");
2231 
2232     // verify after hide
2233     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2234     CPPUNIT_ASSERT(pLayout->IsHideRedlines());
2235     // why is this needed explicitly?
2236     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2237     CheckRedlineCharAttributesHidden();
2238 }
2239 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineShowHideFootnotePagination)2240 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineShowHideFootnotePagination)
2241 {
2242     createDoc("redline_footnote_pagination.fodt");
2243     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
2244     CPPUNIT_ASSERT(pTextDoc);
2245     SwDoc* pDoc(pTextDoc->GetDocShell()->GetDoc());
2246     SwRootFrame* pLayout(pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
2247     CPPUNIT_ASSERT(!pLayout->IsHideRedlines());
2248 
2249     xmlDocPtr pXmlDoc = parseLayoutDump();
2250 
2251     // check footnotes
2252     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn", 6);
2253     assertXPath(pXmlDoc, "/root/page[2]/ftncont/ftn", 3);
2254     // check that first page ends with the y line and second page starts with z
2255     assertXPath(pXmlDoc, "/root/page[1]/body/txt[last()]/LineBreak[last()]", "Line",
2256                 "yyyyyyyyy yyy yyyyyyyyyyyyyyyy yyyyyyy yyy yyyyy yyyyyyyyy yyy yyyyyyyyy ");
2257     assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/LineBreak[1]", "Line",
2258                 "zzz. zzz zzzz zzzz7 zzz zzz zzzzzzz zzz zzzz zzzzzzzzzzzzzz zzzzzzzzzzzz ");
2259 
2260     // hide redlines - all still visible footnotes move to page 1
2261     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2262 
2263     discardDumpedLayout();
2264     pXmlDoc = parseLayoutDump();
2265 
2266     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn", 2);
2267     assertXPath(pXmlDoc, "/root/page[2]/ftncont/ftn", 0);
2268 
2269     // show again - should now get the same result as on loading
2270     lcl_dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
2271 
2272     discardDumpedLayout();
2273     pXmlDoc = parseLayoutDump();
2274 
2275     // check footnotes
2276     assertXPath(pXmlDoc, "/root/page[1]/ftncont/ftn", 6);
2277     assertXPath(pXmlDoc, "/root/page[2]/ftncont/ftn", 3);
2278     // check that first page ends with the y line and second page starts with z
2279     assertXPath(pXmlDoc, "/root/page[1]/body/txt[last()]/LineBreak[last()]", "Line",
2280                 "yyyyyyyyy yyy yyyyyyyyyyyyyyyy yyyyyyy yyy yyyyy yyyyyyyyy yyy yyyyyyyyy ");
2281     assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/LineBreak[1]", "Line",
2282                 "zzz. zzz zzzz zzzz7 zzz zzz zzzzzzz zzz zzzz zzzzzzzzzzzzzz zzzzzzzzzzzz ");
2283 }
2284 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testRedlineNumberInNumbering)2285 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testRedlineNumberInNumbering)
2286 {
2287     SwDoc* pDoc = createDoc("tdf42748.fodt");
2288     SwDocShell* pShell = pDoc->GetDocShell();
2289 
2290     // Dump the rendering of the first page as an XML file.
2291     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2292     MetafileXmlDump dumper;
2293 
2294     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2295     CPPUNIT_ASSERT(pXmlDoc);
2296 
2297     // Assert the tracked deletion of the number of joined list item and
2298     // the tracked insertion of the number after a split list item as not black elements
2299     assertXPath(pXmlDoc, "/metafile/push/push/push/textcolor[not(@color='#000000')]", 6);
2300 }
2301 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf125300)2302 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf125300)
2303 {
2304     SwDoc* pDoc = createDoc("tdf125300.docx");
2305     SwDocShell* pShell = pDoc->GetDocShell();
2306 
2307     // Dump the rendering of the first page as an XML file.
2308     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2309     MetafileXmlDump dumper;
2310 
2311     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2312     CPPUNIT_ASSERT(pXmlDoc);
2313 
2314     // Keep line spacing before bottom cell border (it was 1892)
2315     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[5]/polyline/point[@y='2092']", 2);
2316 }
2317 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf116830)2318 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf116830)
2319 {
2320     SwDoc* pDoc = createDoc("tdf116830.odt");
2321     SwDocShell* pShell = pDoc->GetDocShell();
2322 
2323     // Dump the rendering of the first page as an XML file.
2324     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2325     MetafileXmlDump dumper;
2326     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2327     CPPUNIT_ASSERT(pXmlDoc);
2328 
2329     // Assert that the yellow rectangle (cell background) is painted after the
2330     // polypolygon (background shape).
2331     // Background shape: 1.1.1.2
2332     // Cell background: 1.1.1.3
2333     assertXPath(
2334         pXmlDoc,
2335         "/metafile/push[1]/push[1]/push[1]/push[2]/push[1]/push[1]/fillcolor[@color='#729fcf']", 1);
2336     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[2]/push[1]/push[1]/polypolygon",
2337                 1);
2338 
2339     // This failed: cell background was painted before the background shape.
2340     assertXPath(pXmlDoc,
2341                 "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/fillcolor[@color='#ffff00']", 1);
2342     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/rect", 1);
2343 }
2344 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf114163)2345 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf114163)
2346 {
2347     SwDoc* pDoc = createDoc("tdf114163.odt");
2348     SwDocShell* pShell = pDoc->GetDocShell();
2349 
2350     // Dump the rendering of the first page as an XML file.
2351     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2352     MetafileXmlDump dumper;
2353     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2354     CPPUNIT_ASSERT(pXmlDoc);
2355 
2356     assertXPathContent(
2357         pXmlDoc,
2358         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[12]/text",
2359         "Data3");
2360     // This failed, if the legend first label is not "Data3". The legend position is right.
2361 }
2362 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf125335)2363 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf125335)
2364 {
2365     SwDoc* pDoc = createDoc("tdf125335.odt");
2366     SwDocShell* pShell = pDoc->GetDocShell();
2367 
2368     // Dump the rendering of the first page as an XML file.
2369     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2370     MetafileXmlDump dumper;
2371     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2372     CPPUNIT_ASSERT(pXmlDoc);
2373 
2374     assertXPathContent(
2375         pXmlDoc,
2376         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[12]/text",
2377         "Data3");
2378     // This failed, if the legend first label is not "Data3". The legend position is bottom.
2379 }
2380 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf75659)2381 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf75659)
2382 {
2383     SwDoc* pDoc = createDoc("tdf75659.docx");
2384     SwDocShell* pShell = pDoc->GetDocShell();
2385 
2386     // Dump the rendering of the first page as an XML file.
2387     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2388     MetafileXmlDump dumper;
2389     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2390     CPPUNIT_ASSERT(pXmlDoc);
2391 
2392     assertXPathContent(
2393         pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray[17]/text", "Series1");
2394 
2395     assertXPathContent(
2396         pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray[18]/text", "Series2");
2397 
2398     assertXPathContent(
2399         pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray[19]/text", "Series3");
2400     // These failed, if the legend names are empty strings.
2401 }
2402 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf123268)2403 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf123268)
2404 {
2405     SwDoc* pDoc = createDoc("tdf123268.odt");
2406     SwDocShell* pShell = pDoc->GetDocShell();
2407 
2408     // Dump the rendering of the first page as an XML file.
2409     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2410     MetafileXmlDump dumper;
2411     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2412     CPPUNIT_ASSERT(pXmlDoc);
2413     // Without the accompanying fix in place, this test would have failed with:
2414     // - Expected: 41
2415     // - Actual  : 0
2416     // i.e. the chart lost.
2417     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/push",
2418                 41);
2419 }
2420 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf115630)2421 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf115630)
2422 {
2423     SwDoc* pDoc = createDoc("tdf115630.docx");
2424     SwDocShell* pShell = pDoc->GetDocShell();
2425 
2426     // Dump the rendering of the first page as an XML file.
2427     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2428     MetafileXmlDump dumper;
2429     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2430     CPPUNIT_ASSERT(pXmlDoc);
2431 
2432     // Test wide of inner chart area.
2433     sal_Int32 nXRight
2434         = getXPath(pXmlDoc,
2435                    "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[3]/polyline[1]/point[1]",
2436                    "x")
2437               .toInt32();
2438     sal_Int32 nXLeft
2439         = getXPath(pXmlDoc,
2440                    "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[3]/polyline[1]/point[2]",
2441                    "x")
2442               .toInt32();
2443     CPPUNIT_ASSERT_DOUBLES_EQUAL(2895, nXRight - nXLeft, 50);
2444 }
2445 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf108021)2446 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf108021)
2447 {
2448     SwDoc* pDoc = createDoc("tdf108021.odt");
2449     SwDocShell* pShell = pDoc->GetDocShell();
2450 
2451     // Dump the rendering of the first page as an XML file.
2452     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2453     MetafileXmlDump dumper;
2454     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2455     CPPUNIT_ASSERT(pXmlDoc);
2456 
2457     assertXPath(
2458         pXmlDoc,
2459         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[@length='22']",
2460         8);
2461     // This failed, if the textarray length of the first axis label not 22.
2462 }
2463 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf125334)2464 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf125334)
2465 {
2466     SwDoc* pDoc = createDoc("tdf125334.odt");
2467     SwDocShell* pShell = pDoc->GetDocShell();
2468 
2469     // Dump the rendering of the first page as an XML file.
2470     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2471     MetafileXmlDump dumper;
2472     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2473     CPPUNIT_ASSERT(pXmlDoc);
2474 
2475     assertXPath(
2476         pXmlDoc,
2477         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[@length='17']",
2478         4);
2479     // This failed, if the textarray length of the category axis label not 17.
2480 }
2481 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf122800)2482 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf122800)
2483 {
2484     SwDoc* pDoc = createDoc("tdf122800.odt");
2485     SwDocShell* pShell = pDoc->GetDocShell();
2486 
2487     // Dump the rendering of the first page as an XML file.
2488     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2489     MetafileXmlDump dumper;
2490     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2491     CPPUNIT_ASSERT(pXmlDoc);
2492 
2493     assertXPath(
2494         pXmlDoc,
2495         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[@length='22']",
2496         9);
2497     // This failed, if the textarray length of the first axis label not 22.
2498 }
2499 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTruncatedAxisLabel)2500 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTruncatedAxisLabel)
2501 {
2502     SwDoc* pDoc = createDoc("testTruncatedAxisLabel.odt");
2503     SwDocShell* pShell = pDoc->GetDocShell();
2504 
2505     // Dump the rendering of the first page as an XML file.
2506     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2507     MetafileXmlDump dumper;
2508     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2509     CPPUNIT_ASSERT(pXmlDoc);
2510 
2511     // test the X axis label visibility
2512     assertXPathContent(
2513         pXmlDoc,
2514         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[1]/text",
2515         "Long axis label truncated 1");
2516 
2517     // test the Y axis label visibility
2518     assertXPathContent(
2519         pXmlDoc,
2520         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[3]/text",
2521         "-5.00");
2522 }
2523 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf128996)2524 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf128996)
2525 {
2526     SwDoc* pDoc = createDoc("tdf128996.docx");
2527     SwDocShell* pShell = pDoc->GetDocShell();
2528 
2529     // Dump the rendering of the first page as an XML file.
2530     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2531     MetafileXmlDump dumper;
2532     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2533     CPPUNIT_ASSERT(pXmlDoc);
2534 
2535     assertXPathContent(pXmlDoc,
2536                        "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray[1]/text",
2537                        "A very long category name 1");
2538 }
2539 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf126244)2540 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf126244)
2541 {
2542     SwDoc* pDoc = createDoc("tdf126244.docx");
2543     SwDocShell* pShell = pDoc->GetDocShell();
2544 
2545     // Dump the rendering of the first page as an XML file.
2546     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2547     MetafileXmlDump dumper;
2548     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2549     CPPUNIT_ASSERT(pXmlDoc);
2550     // Test the first level of vertical category axis labels orientation. The first level orientation should be horizontal.
2551     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/font[1]", "orientation",
2552                 "0");
2553     // Test the second level of vertical category axis labels orientation. The second level orientation should be vertical.
2554     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/font[5]", "orientation",
2555                 "900");
2556     // Test the third level of vertical category axis labels orientation. The third level orientation should be vertical.
2557     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/font[7]", "orientation",
2558                 "900");
2559 }
2560 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf127304)2561 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf127304)
2562 {
2563     SwDoc* pDoc = createDoc("tdf127304.odt");
2564     SwDocShell* pShell = pDoc->GetDocShell();
2565 
2566     // Dump the rendering of the first page as an XML file.
2567     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2568     MetafileXmlDump dumper;
2569     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2570     CPPUNIT_ASSERT(pXmlDoc);
2571     // Test the first level of horizontal category axis labels orientation. The first level orientation should be vertical.
2572     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/font[1]", "orientation",
2573                 "900");
2574     // Test the second level of horizontal category axis labels orientation. The second level orientation should be horizontal.
2575     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/font[5]", "orientation",
2576                 "0");
2577     // Test the third level of horizontal category axis labels orientation. The third level orientation should be horizontal.
2578     assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/font[7]", "orientation",
2579                 "0");
2580 }
2581 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testHorizontal_multilevel)2582 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testHorizontal_multilevel)
2583 {
2584     SwDoc* pDoc = createDoc("horizontal_multilevel.odt");
2585     SwDocShell* pShell = pDoc->GetDocShell();
2586 
2587     // Dump the rendering of the first page as an XML file.
2588     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2589     MetafileXmlDump dumper;
2590     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2591     CPPUNIT_ASSERT(pXmlDoc);
2592     // Test the Y position of horizontal category axis label.
2593     sal_Int32 nYposition
2594         = getXPath(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/textarray[7]", "y")
2595               .toInt32();
2596     CPPUNIT_ASSERT(nYposition > 7943 && nYposition < 7947);
2597 }
2598 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf124796)2599 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124796)
2600 {
2601     SwDoc* pDoc = createDoc("tdf124796.odt");
2602     SwDocShell* pShell = pDoc->GetDocShell();
2603 
2604     // Dump the rendering of the first page as an XML file.
2605     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2606     MetafileXmlDump dumper;
2607     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2608     CPPUNIT_ASSERT(pXmlDoc);
2609 
2610     // This failed, if the minimum value of Y axis is not -10.
2611     assertXPathContent(
2612         pXmlDoc,
2613         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[5]/text",
2614         "-10");
2615 
2616     // This failed, if the maximum value of Y axis is not 15.
2617     assertXPathContent(
2618         pXmlDoc,
2619         "/metafile/push[1]/push[1]/push[1]/push[3]/push[1]/push[1]/push[1]/textarray[10]/text",
2620         "15");
2621 }
2622 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf129054)2623 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf129054)
2624 {
2625     SwDoc* pDoc = createDoc("tdf129054.docx");
2626     SwDocShell* pShell = pDoc->GetDocShell();
2627 
2628     // Dump the rendering of the first page as an XML file.
2629     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2630     MetafileXmlDump dumper;
2631     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2632     CPPUNIT_ASSERT(pXmlDoc);
2633 
2634     // Test the size of diameter of Pie chart.
2635     sal_Int32 nYTop
2636         = getXPath(pXmlDoc,
2637                    "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[4]/polyline[1]/point[1]",
2638                    "y")
2639               .toInt32();
2640     sal_Int32 nYBottom
2641         = getXPath(
2642               pXmlDoc,
2643               "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[4]/polyline[1]/point[31]",
2644               "y")
2645               .toInt32();
2646     CPPUNIT_ASSERT_EQUAL(sal_Int32(4810), nYTop - nYBottom);
2647 }
2648 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf129173)2649 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf129173)
2650 {
2651     SwDoc* pDoc = createDoc("testAreaChartNumberFormat.docx");
2652     SwDocShell* pShell = pDoc->GetDocShell();
2653 
2654     // Dump the rendering of the first page as an XML file.
2655     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2656     MetafileXmlDump dumper;
2657     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2658     CPPUNIT_ASSERT(pXmlDoc);
2659 
2660     // Check the first data label of area chart.
2661     assertXPathContent(
2662         pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray[22]/text", "56");
2663 }
2664 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf129095)2665 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf129095)
2666 {
2667     SwDoc* pDoc = createDoc("tdf129095.docx");
2668     SwDocShell* pShell = pDoc->GetDocShell();
2669 
2670     // Dump the rendering of the first page as an XML file.
2671     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2672     MetafileXmlDump dumper;
2673     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2674     CPPUNIT_ASSERT(pXmlDoc);
2675 
2676     // check the inner chart area (relative size) visibility with testing the X axis label
2677     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray/text",
2678                        "Category 1");
2679 }
2680 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf132956)2681 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf132956)
2682 {
2683     SwDoc* pDoc = createDoc("tdf132956.docx");
2684     SwDocShell* pShell = pDoc->GetDocShell();
2685 
2686     // Dump the rendering of the first page as an XML file.
2687     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2688     MetafileXmlDump dumper;
2689     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2690     CPPUNIT_ASSERT(pXmlDoc);
2691 
2692     // check the inner chart area (default size) visibility with testing the X axis label
2693     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/textarray/text",
2694                        "Category 1");
2695 }
2696 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf116925)2697 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf116925)
2698 {
2699     SwDoc* pDoc = createDoc("tdf116925.docx");
2700     SwDocShell* pShell = pDoc->GetDocShell();
2701 
2702     // Dump the rendering of the first page as an XML file.
2703     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2704     MetafileXmlDump dumper;
2705     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2706     CPPUNIT_ASSERT(pXmlDoc);
2707 
2708     assertXPathContent(pXmlDoc,
2709                        "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[3]/textarray/text",
2710                        "hello");
2711     // This failed, text color was #000000.
2712     assertXPath(
2713         pXmlDoc,
2714         "/metafile/push[1]/push[1]/push[1]/push[4]/push[1]/push[3]/textcolor[@color='#ffffff']", 1);
2715 }
2716 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117028)2717 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117028)
2718 {
2719     SwDoc* pDoc = createDoc("tdf117028.docx");
2720     SwDocShell* pShell = pDoc->GetDocShell();
2721 
2722     // Dump the rendering of the first page as an XML file.
2723     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2724     MetafileXmlDump dumper;
2725     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2726     CPPUNIT_ASSERT(pXmlDoc);
2727 
2728     // The only polypolygon in the rendering result was the white background we
2729     // want to avoid.
2730     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polypolygon");
2731     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
2732     CPPUNIT_ASSERT_EQUAL(0, xmlXPathNodeSetGetLength(pXmlNodes));
2733     xmlXPathFreeObject(pXmlObj);
2734 
2735     // Make sure the text is still rendered.
2736     assertXPathContent(pXmlDoc, "//textarray/text", "Hello");
2737 }
2738 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf106390)2739 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf106390)
2740 {
2741     SwDoc* pDoc = createDoc("tdf106390.odt");
2742     SwDocShell* pShell = pDoc->GetDocShell();
2743 
2744     // Dump the rendering of the first page as an XML file.
2745     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2746     MetafileXmlDump dumper;
2747     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2748     CPPUNIT_ASSERT(pXmlDoc);
2749     sal_Int32 nBottom = getXPath(pXmlDoc, "//sectrectclipregion", "bottom").toInt32();
2750 
2751     // No end point of line segments shall go below the bottom of the clipping area.
2752     const OString sXPath = "//polyline/point[@y>" + OString::number(nBottom) + "]";
2753 
2754     assertXPath(pXmlDoc, sXPath, 0);
2755 }
2756 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTableExtrusion1)2757 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTableExtrusion1)
2758 {
2759     SwDoc* pDoc = createDoc("table-extrusion1.odt");
2760     SwDocShell* pShell = pDoc->GetDocShell();
2761 
2762     // Dump the rendering of the first page as an XML file.
2763     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2764     MetafileXmlDump dumper;
2765     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2766     CPPUNIT_ASSERT(pXmlDoc);
2767     sal_Int32 nRight = getXPath(pXmlDoc, "//sectrectclipregion", "right").toInt32();
2768     sal_Int32 nLeft = static_cast<sal_Int32>(nRight * 0.95);
2769 
2770     // Expect table borders in right page margin.
2771     const OString sXPath = "//polyline/point[@x>" + OString::number(nLeft) + " and @x<"
2772                            + OString::number(nRight) + "]";
2773 
2774     assertXPath(pXmlDoc, sXPath, 4);
2775 }
2776 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTableExtrusion2)2777 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTableExtrusion2)
2778 {
2779     SwDoc* pDoc = createDoc("table-extrusion2.odt");
2780     SwDocShell* pShell = pDoc->GetDocShell();
2781 
2782     // Dump the rendering of the first page as an XML file.
2783     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
2784     MetafileXmlDump dumper;
2785     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
2786     CPPUNIT_ASSERT(pXmlDoc);
2787     // End point position of the outer table.
2788     sal_Int32 nX = getXPath(pXmlDoc, "(//polyline[1]/point)[2]", "x").toInt32();
2789 
2790     // Do not allow inner table extrude outer table.
2791     const OString sXPath = "//polyline/point[@x>" + OString::number(nX) + "]";
2792 
2793     assertXPath(pXmlDoc, sXPath, 0);
2794 }
2795 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf116848)2796 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf116848)
2797 {
2798     SwDoc* pDoc = createDoc("tdf116848.odt");
2799     // This resulted in a layout loop.
2800     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2801 }
2802 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117245)2803 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117245)
2804 {
2805     createDoc("tdf117245.odt");
2806     xmlDocPtr pXmlDoc = parseLayoutDump();
2807     // This was 2, TabOverMargin did not use a single line when there was
2808     // enough space for the text.
2809     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak", 1);
2810 
2811     // This was 2, same problem elsewhere due to code duplication.
2812     assertXPath(pXmlDoc, "/root/page/body/txt[2]/LineBreak", 1);
2813 }
2814 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf118672)2815 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf118672)
2816 {
2817     createDoc("tdf118672.odt");
2818     xmlDocPtr pXmlDoc = parseLayoutDump();
2819 
2820     // Check if we have hyphenation support, otherwise can't test SwHyphPortion.
2821     uno::Reference<linguistic2::XLinguServiceManager2> xLinguServiceManager
2822         = linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext());
2823     uno::Sequence<lang::Locale> aLocales
2824         = xLinguServiceManager->getAvailableLocales("com.sun.star.linguistic2.Hyphenator");
2825     if (std::none_of(aLocales.begin(), aLocales.end(), [](const lang::Locale& rLocale) {
2826             return rLocale.Language == "en" && rLocale.Country == "US";
2827         }))
2828         return;
2829 
2830     const OUString aLine1(
2831         "He heard quiet steps behind him. That didn't bode well. Who could be fol*1 2 "
2832         "3 4 5 6 7 8 9 10con-");
2833     // This ended as "fol*1 2 3 4 5 6 7 8 9", i.e. "10con-" was moved to the next line.
2834     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak[1]", "Line", aLine1);
2835     const OUString aLine2("setetur");
2836     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak[2]", "Line", aLine2);
2837 }
2838 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117923)2839 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117923)
2840 {
2841     createDoc("tdf117923.doc");
2842     // Ensure that all text portions are calculated before testing.
2843     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
2844     CPPUNIT_ASSERT(pTextDoc);
2845     SwViewShell* pViewShell
2846         = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
2847     CPPUNIT_ASSERT(pViewShell);
2848     pViewShell->Reformat();
2849 
2850     xmlDocPtr pXmlDoc = parseLayoutDump();
2851 
2852     // Check that we actually test the line we need
2853     assertXPathContent(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]", "GHI GHI GHI GHI");
2854     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nType",
2855                 "PortionType::Number");
2856     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "rText", "2.");
2857     // The numbering height was 960.
2858     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "220");
2859 }
2860 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf127606)2861 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf127606)
2862 {
2863     createDoc("tdf117923.docx");
2864     // Ensure that all text portions are calculated before testing.
2865     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
2866     CPPUNIT_ASSERT(pTextDoc);
2867     SwViewShell* pViewShell
2868         = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
2869     CPPUNIT_ASSERT(pViewShell);
2870     pViewShell->Reformat();
2871 
2872     xmlDocPtr pXmlDoc = parseLayoutDump();
2873 
2874     // Check that we actually test the line we need
2875     assertXPathContent(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]", "GHI GHI GHI GHI");
2876     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nType",
2877                 "PortionType::Number");
2878     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "rText", "2.");
2879     // The numbering height was 960 in DOC format.
2880     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "220");
2881 
2882     // tdf#127606: now it's possible to change formatting of numbering
2883     // increase font size (220 -> 260)
2884     lcl_dispatchCommand(mxComponent, ".uno:SelectAll", {});
2885     lcl_dispatchCommand(mxComponent, ".uno:Grow", {});
2886     pViewShell->Reformat();
2887     discardDumpedLayout();
2888     pXmlDoc = parseLayoutDump();
2889     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "260");
2890 }
2891 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf109077)2892 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf109077)
2893 {
2894     createDoc("tdf109077.docx");
2895     xmlDocPtr pXmlDoc = parseLayoutDump();
2896     sal_Int32 nShapeTop
2897         = getXPath(pXmlDoc, "//anchored/SwAnchoredDrawObject/bounds", "top").toInt32();
2898     sal_Int32 nTextBoxTop = getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "top").toInt32();
2899     // This was 281: the top of the shape and its textbox should match, though
2900     // tolerate differences <= 1px (about 15 twips).
2901     CPPUNIT_ASSERT_LESS(static_cast<sal_Int32>(15), nTextBoxTop - nShapeTop);
2902 }
2903 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testUserFieldTypeLanguage)2904 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testUserFieldTypeLanguage)
2905 {
2906     // Set the system locale to German, the document will be English.
2907     SvtSysLocaleOptions aOptions;
2908     OUString sLocaleConfigString = aOptions.GetLanguageTag().getBcp47();
2909     aOptions.SetLocaleConfigString("de-DE");
2910     aOptions.Commit();
2911     comphelper::ScopeGuard g([&aOptions, &sLocaleConfigString] {
2912         aOptions.SetLocaleConfigString(sLocaleConfigString);
2913         aOptions.Commit();
2914     });
2915 
2916     SwDoc* pDoc = createDoc("user-field-type-language.fodt");
2917     SwViewShell* pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
2918     pViewShell->UpdateFields();
2919     xmlDocPtr pXmlDoc = parseLayoutDump();
2920     // This was "123,456.00", via a buggy 1234.56 -> 1234,56 -> 123456 ->
2921     // 123,456.00 transform chain.
2922     assertXPath(pXmlDoc, "/root/page/body/txt/Special[@nType='PortionType::Field']", "rText",
2923                 "1,234.56");
2924 }
2925 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf109137)2926 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf109137)
2927 {
2928     createDoc("tdf109137.docx");
2929     uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
2930     utl::TempFile aTempFile;
2931     aTempFile.EnableKillingFile();
2932     uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
2933         { "FilterName", uno::Any(OUString("writer8")) },
2934     }));
2935     xStorable->storeToURL(aTempFile.GetURL(), aDescriptor);
2936     loadURL(aTempFile.GetURL(), "tdf109137.odt");
2937     xmlDocPtr pXmlDoc = parseLayoutDump();
2938     // This was 0, the blue rectangle moved from the 1st to the 2nd page.
2939     assertXPath(pXmlDoc, "/root/page[1]/body/txt/anchored/fly/notxt",
2940                 /*nNumberOfNodes=*/1);
2941 }
2942 
2943 //just care it doesn't crash/assert
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testForcepoint72)2944 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepoint72) { createDoc("forcepoint72-1.rtf"); }
2945 
2946 //just care it doesn't crash/assert
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testForcepoint75)2947 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepoint75) { createDoc("forcepoint75-1.rtf"); }
2948 
2949 //just care it doesn't crash/assert
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testForcepointFootnoteFrame)2950 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepointFootnoteFrame)
2951 {
2952     createDoc("forcepoint-swfootnoteframe-1.rtf");
2953 }
2954 
2955 //FIXME: disabled after failing again with fixed layout
2956 //CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepoint76) { createDoc("forcepoint76-1.rtf"); }
2957 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf118058)2958 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf118058)
2959 {
2960     SwDoc* pDoc = createDoc("tdf118058.fodt");
2961     // This resulted in a layout loop.
2962     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
2963 }
2964 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf128611)2965 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf128611)
2966 {
2967     createDoc("tdf128611.fodt");
2968     xmlDocPtr pXmlDoc = parseLayoutDump();
2969     CPPUNIT_ASSERT(pXmlDoc);
2970     // Without the accompanying fix in place, this test would have failed with:
2971     // - Expected: 1
2972     // - Actual  : 14
2973     // i.e. there were multiple portions in the first paragraph of the A1 cell, which means that the
2974     // rotated text was broken into multiple lines without a good reason.
2975     assertXPath(pXmlDoc, "//tab/row/cell[1]/txt/Text", "Portion", "Abcd efghijkl");
2976 }
2977 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117188)2978 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117188)
2979 {
2980     createDoc("tdf117188.docx");
2981     uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
2982     utl::TempFile aTempFile;
2983     aTempFile.EnableKillingFile();
2984     uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
2985         { "FilterName", uno::Any(OUString("writer8")) },
2986     }));
2987     xStorable->storeToURL(aTempFile.GetURL(), aDescriptor);
2988     loadURL(aTempFile.GetURL(), "tdf117188.odt");
2989     xmlDocPtr pXmlDoc = parseLayoutDump();
2990     OUString sWidth = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/bounds", "width");
2991     OUString sHeight = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/bounds", "height");
2992     // The text box must have zero border distances
2993     assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "left", "0");
2994     assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "top", "0");
2995     assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "width", sWidth);
2996     assertXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "height", sHeight);
2997 }
2998 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117187)2999 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117187)
3000 {
3001     createDoc("tdf117187.odt");
3002     xmlDocPtr pXmlDoc = parseLayoutDump();
3003 
3004     // there should be no fly portions
3005     assertXPath(pXmlDoc, "/root/page/body/txt/Special[@nType='PortionType::Fly']", 0);
3006 }
3007 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf119875)3008 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf119875)
3009 {
3010     createDoc("tdf119875.odt");
3011     xmlDocPtr pXmlDoc = parseLayoutDump();
3012     sal_Int32 nFirstTop
3013         = getXPath(pXmlDoc, "/root/page[2]/body/section[1]/infos/bounds", "top").toInt32();
3014     sal_Int32 nSecondTop
3015         = getXPath(pXmlDoc, "/root/page[2]/body/section[2]/infos/bounds", "top").toInt32();
3016     // The first section had the same top value as the second one, so they
3017     // overlapped.
3018     CPPUNIT_ASSERT_LESS(nSecondTop, nFirstTop);
3019 }
3020 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf120287)3021 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287)
3022 {
3023     createDoc("tdf120287.fodt");
3024     xmlDocPtr pXmlDoc = parseLayoutDump();
3025     // This was 2, TabOverMargin Word-specific compat flag did not imply
3026     // default-in-Word printer-independent layout, resulting in an additional
3027     // line break.
3028     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak", 1);
3029 }
3030 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf120287b)3031 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287b)
3032 {
3033     createDoc("tdf120287b.fodt");
3034     xmlDocPtr pXmlDoc = parseLayoutDump();
3035     // This was 1418, TabOverMargin did the right split of the paragraph to two
3036     // lines, but then calculated a too large tab portion size on the first
3037     // line.
3038     assertXPath(pXmlDoc, "/root/page/body/txt[1]/Text[@nType='PortionType::TabRight']", "nWidth",
3039                 "17");
3040 }
3041 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf120287c)3042 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287c)
3043 {
3044     createDoc("tdf120287c.fodt");
3045     xmlDocPtr pXmlDoc = parseLayoutDump();
3046     // This was 2, the second line was not broken into a 2nd and a 3rd one,
3047     // rendering text outside the paragraph frame.
3048     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak", 3);
3049 }
3050 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf122878)3051 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf122878)
3052 {
3053     createDoc("tdf122878.docx");
3054     xmlDocPtr pXmlDoc = parseLayoutDump();
3055     // FIXME: the XPath should be adjusted when the proper floating table would be imported
3056     const sal_Int32 nTblTop
3057         = getXPath(pXmlDoc, "/root/page[1]/footer/txt/anchored/fly/tab/infos/bounds", "top")
3058               .toInt32();
3059     const sal_Int32 nFirstPageParaCount
3060         = getXPathContent(pXmlDoc, "count(/root/page[1]/body/txt)").toInt32();
3061     CPPUNIT_ASSERT_EQUAL(sal_Int32(30), nFirstPageParaCount);
3062     for (sal_Int32 i = 1; i <= nFirstPageParaCount; ++i)
3063     {
3064         const OString xPath = "/root/page[1]/body/txt[" + OString::number(i) + "]/infos/bounds";
3065         const sal_Int32 nTxtBottom = getXPath(pXmlDoc, xPath.getStr(), "top").toInt32()
3066                                      + getXPath(pXmlDoc, xPath.getStr(), "height").toInt32();
3067         // No body paragraphs should overlap the table in the footer
3068         CPPUNIT_ASSERT_MESSAGE(OString("testing paragraph #" + OString::number(i)).getStr(),
3069                                nTxtBottom <= nTblTop);
3070     }
3071 }
3072 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf115094)3073 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf115094)
3074 {
3075     createDoc("tdf115094.docx");
3076     xmlDocPtr pXmlDoc = parseLayoutDump();
3077 
3078     sal_Int32 nTopOfD1
3079         = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/infos/bounds",
3080                    "top")
3081               .toInt32();
3082     sal_Int32 nTopOfD1Anchored = getXPath(pXmlDoc,
3083                                           "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/"
3084                                           "txt[2]/anchored/fly/infos/bounds",
3085                                           "top")
3086                                      .toInt32();
3087     CPPUNIT_ASSERT_LESS(nTopOfD1Anchored, nTopOfD1);
3088     sal_Int32 nTopOfB2
3089         = getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/infos/bounds",
3090                    "top")
3091               .toInt32();
3092     sal_Int32 nTopOfB2Anchored = getXPath(pXmlDoc,
3093                                           "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/"
3094                                           "txt[1]/anchored/fly/infos/bounds",
3095                                           "top")
3096                                      .toInt32();
3097     CPPUNIT_ASSERT_LESS(nTopOfB2Anchored, nTopOfB2);
3098 }
3099 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf122607)3100 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf122607)
3101 {
3102     createDoc("tdf122607.odt");
3103     xmlDocPtr pXmlDoc = parseLayoutDump();
3104     assertXPath(pXmlDoc,
3105                 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
3106                 "fly/txt/Text[1]",
3107                 "nHeight", "253");
3108     assertXPath(pXmlDoc,
3109                 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
3110                 "fly/txt/Text[1]",
3111                 "nWidth", "428");
3112     assertXPath(pXmlDoc,
3113                 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
3114                 "fly/txt/Text[1]",
3115                 "Portion", "Fax:");
3116 }
3117 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf122607_regression)3118 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf122607_regression)
3119 {
3120     discardDumpedLayout();
3121     if (mxComponent.is())
3122         mxComponent->dispose();
3123 
3124     OUString const pName("tdf122607_leerzeile.odt");
3125 
3126     OUString const url(m_directories.getURLFromSrc(DATA_DIRECTORY) + pName);
3127 
3128     // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
3129     // does *not* forward initial VCL Window Resize and thereby triggers a
3130     // layout which does not happen on soffice --convert-to pdf.
3131     std::vector<beans::PropertyValue> aFilterOptions = {
3132         { beans::PropertyValue("Hidden", -1, uno::Any(true), beans::PropertyState_DIRECT_VALUE) },
3133     };
3134 
3135     std::cout << pName << ":\n";
3136 
3137     // inline the loading because currently properties can't be passed...
3138     mxComponent = loadFromDesktop(url, "com.sun.star.text.TextDocument",
3139                                   comphelper::containerToSequence(aFilterOptions));
3140 
3141     CPPUNIT_ASSERT(mxComponent.is());
3142 
3143     uno::Sequence<beans::PropertyValue> props(comphelper::InitPropertySequence({
3144         { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
3145     }));
3146     utl::TempFile aTempFile;
3147     uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
3148     xStorable->storeToURL(aTempFile.GetURL(), props);
3149 
3150     xmlDocPtr pXmlDoc = parseLayoutDump();
3151     // somehow these 2 rows overlapped in the PDF unless CalcLayout() runs
3152     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "mbFixSize",
3153                 "false");
3154     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "top", "2977");
3155     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "height", "241");
3156     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "mbFixSize",
3157                 "true");
3158     // this was 3034, causing the overlap
3159     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "top", "3218");
3160     assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "height", "164");
3161 
3162     aTempFile.EnableKillingFile();
3163 }
3164 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testBtlrCell)3165 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testBtlrCell)
3166 {
3167     SwDoc* pDoc = createDoc("btlr-cell.odt");
3168     SwDocShell* pShell = pDoc->GetDocShell();
3169 
3170     // Dump the rendering of the first page as an XML file.
3171     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
3172     MetafileXmlDump dumper;
3173     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
3174     CPPUNIT_ASSERT(pXmlDoc);
3175 
3176     // Without the accompanying fix in place, this test would have failed, as
3177     // the orientation was 0 (layout did not take btlr direction request from
3178     // doc model).
3179     assertXPath(pXmlDoc, "//font[1]", "orientation", "900");
3180 
3181 #if !defined(MACOSX) && !defined(_WIN32) // macOS fails with x == 2662 for some reason.
3182     // Without the accompanying fix in place, this test would have failed with 'Expected: 1915;
3183     // Actual  : 1756', i.e. the AAA1 text was too close to the left cell border due to an ascent vs
3184     // descent mismatch when calculating the baseline offset of the text portion.
3185     assertXPath(pXmlDoc, "//textarray[1]", "x", "1915");
3186     assertXPath(pXmlDoc, "//textarray[1]", "y", "2707");
3187 
3188     // Without the accompanying fix in place, this test would have failed with 'Expected: 1979;
3189     // Actual  : 2129', i.e. the gray background of the "AAA2." text was too close to the right edge
3190     // of the text portion. Now it's exactly behind the text portion.
3191     assertXPath(pXmlDoc, "//rect[@top='2159']", "left", "1979");
3192 
3193     // Without the accompanying fix in place, this test would have failed with 'Expected: 269;
3194     // Actual  : 0', i.e. the AAA2 frame was not visible due to 0 width.
3195     pXmlDoc = parseLayoutDump();
3196     assertXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width", "269");
3197 
3198     // Test the position of the cursor after doc load.
3199     // We expect that it's inside the first text frame in the first cell.
3200     // More precisely, this is a bottom to top vertical frame, so we expect it's at the start, which
3201     // means it's at the lower half of the text frame rectangle (vertically).
3202     SwWrtShell* pWrtShell = pShell->GetWrtShell();
3203     CPPUNIT_ASSERT(pWrtShell);
3204 
3205     const SwRect& rCharRect = pWrtShell->GetCharRect();
3206     SwTwips nFirstParaTop
3207         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "top").toInt32();
3208     SwTwips nFirstParaHeight
3209         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "height")
3210               .toInt32();
3211     SwTwips nFirstParaMiddle = nFirstParaTop + nFirstParaHeight / 2;
3212     SwTwips nFirstParaBottom = nFirstParaTop + nFirstParaHeight;
3213     // Without the accompanying fix in place, this test would have failed: the lower half (vertical)
3214     // range was 2273 -> 2835, the good vertical position is 2730, the bad one was 1830.
3215     CPPUNIT_ASSERT_GREATER(nFirstParaMiddle, rCharRect.Top());
3216     CPPUNIT_ASSERT_LESS(nFirstParaBottom, rCharRect.Top());
3217 
3218     // Save initial cursor position.
3219     SwPosition aCellStart = *pWrtShell->GetCursor()->Start();
3220 
3221     // Test that pressing "up" at the start of the cell goes to the next character position.
3222     sal_uLong nNodeIndex = pWrtShell->GetCursor()->Start()->nNode.GetIndex();
3223     sal_Int32 nIndex = pWrtShell->GetCursor()->Start()->nContent.GetIndex();
3224     KeyEvent aKeyEvent(0, KEY_UP);
3225     SwEditWin& rEditWin = pShell->GetView()->GetEditWin();
3226     rEditWin.KeyInput(aKeyEvent);
3227     Scheduler::ProcessEventsToIdle();
3228     // Without the accompanying fix in place, this test would have failed: "up" was interpreted as
3229     // logical "left", which does nothing if you're at the start of the text anyway.
3230     CPPUNIT_ASSERT_EQUAL(nIndex + 1, pWrtShell->GetCursor()->Start()->nContent.GetIndex());
3231 
3232     // Test that pressing "right" goes to the next paragraph (logical "down").
3233     sal_Int32 nContentIndex = pWrtShell->GetCursor()->Start()->nContent.GetIndex();
3234     aKeyEvent = KeyEvent(0, KEY_RIGHT);
3235     rEditWin.KeyInput(aKeyEvent);
3236     Scheduler::ProcessEventsToIdle();
3237     // Without the accompanying fix in place, this test would have failed: the cursor went to the
3238     // paragraph after the table.
3239     CPPUNIT_ASSERT_EQUAL(nNodeIndex + 1, pWrtShell->GetCursor()->Start()->nNode.GetIndex());
3240 
3241     // Test that we have the correct character index after traveling to the next paragraph.
3242     // Without the accompanying fix in place, this test would have failed: char position was 5, i.e.
3243     // the cursor jumped to the end of the paragraph for no reason.
3244     CPPUNIT_ASSERT_EQUAL(nContentIndex, pWrtShell->GetCursor()->Start()->nContent.GetIndex());
3245 
3246     // Test that clicking "below" the second paragraph positions the cursor at the start of the
3247     // second paragraph.
3248     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
3249     SwPosition aPosition(aCellStart);
3250     SwTwips nSecondParaLeft
3251         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "left")
3252               .toInt32();
3253     SwTwips nSecondParaWidth
3254         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width")
3255               .toInt32();
3256     SwTwips nSecondParaTop
3257         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "top").toInt32();
3258     SwTwips nSecondParaHeight
3259         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "height")
3260               .toInt32();
3261     Point aPoint;
3262     aPoint.setX(nSecondParaLeft + nSecondParaWidth / 2);
3263     aPoint.setY(nSecondParaTop + nSecondParaHeight - 100);
3264     SwCursorMoveState aState(MV_NONE);
3265     pLayout->GetCursorOfst(&aPosition, aPoint, &aState);
3266     CPPUNIT_ASSERT_EQUAL(aCellStart.nNode.GetIndex() + 1, aPosition.nNode.GetIndex());
3267     // Without the accompanying fix in place, this test would have failed: character position was 5,
3268     // i.e. cursor was at the end of the paragraph.
3269     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aPosition.nContent.GetIndex());
3270 
3271     // Test that the selection rectangles are inside the cell frame if we select all the cell
3272     // content.
3273     SwTwips nCellLeft
3274         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "left").toInt32();
3275     SwTwips nCellWidth
3276         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "width").toInt32();
3277     SwTwips nCellTop
3278         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "top").toInt32();
3279     SwTwips nCellHeight
3280         = getXPath(pXmlDoc, "/root/page/body/tab/row/cell[1]/infos/bounds", "height").toInt32();
3281     SwRect aCellRect(Point(nCellLeft, nCellTop), Size(nCellWidth, nCellHeight));
3282     pWrtShell->SelAll();
3283     SwShellCursor* pShellCursor = pWrtShell->getShellCursor(/*bBlock=*/false);
3284     CPPUNIT_ASSERT(!pShellCursor->empty());
3285     // Without the accompanying fix in place, this test would have failed with:
3286     // selection rectangle 269x2573@(1970,2172) is not inside cell rectangle 3207x1134@(1593,1701)
3287     // i.e. the selection went past the bottom border of the cell frame.
3288     for (const auto& rRect : *pShellCursor)
3289     {
3290         std::stringstream ss;
3291         ss << "selection rectangle " << rRect << " is not inside cell rectangle " << aCellRect;
3292         CPPUNIT_ASSERT_MESSAGE(ss.str(), aCellRect.IsInside(rRect));
3293     }
3294 
3295     // Make sure that the correct rectangle gets repainted on scroll.
3296     SwFrame* pPageFrame = pLayout->GetLower();
3297     CPPUNIT_ASSERT(pPageFrame->IsPageFrame());
3298 
3299     SwFrame* pBodyFrame = pPageFrame->GetLower();
3300     CPPUNIT_ASSERT(pBodyFrame->IsBodyFrame());
3301 
3302     SwFrame* pTabFrame = pBodyFrame->GetLower();
3303     CPPUNIT_ASSERT(pTabFrame->IsTabFrame());
3304 
3305     SwFrame* pRowFrame = pTabFrame->GetLower();
3306     CPPUNIT_ASSERT(pRowFrame->IsRowFrame());
3307 
3308     SwFrame* pCellFrame = pRowFrame->GetLower();
3309     CPPUNIT_ASSERT(pCellFrame->IsCellFrame());
3310 
3311     SwFrame* pFrame = pCellFrame->GetLower();
3312     CPPUNIT_ASSERT(pFrame->IsTextFrame());
3313 
3314     SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pFrame);
3315     pTextFrame->SwapWidthAndHeight();
3316     // Mimic what normally SwTextFrame::PaintSwFrame() does:
3317     SwRect aRect(4207, 2273, 269, 572);
3318     pTextFrame->SwitchVerticalToHorizontal(aRect);
3319     // Without the accompanying fix in place, this test would have failed with:
3320     // Expected: 572x269@(1691,4217)
3321     // Actual  : 572x269@(2263,4217)
3322     // i.e. the paint rectangle position was incorrect, text was not painted on scrolling up.
3323     CPPUNIT_ASSERT_EQUAL(SwRect(1691, 4217, 572, 269), aRect);
3324 #endif
3325 }
3326 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf123898)3327 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf123898)
3328 {
3329     createDoc("tdf123898.odt");
3330 
3331     // Make sure spellchecker has done its job already
3332     Scheduler::ProcessEventsToIdle();
3333 
3334     xmlDocPtr pXmlDoc = parseLayoutDump();
3335     // Make sure that the arrow on the left is not there (there are 43 children if it's there)
3336     assertXPathChildren(pXmlDoc, "/root/page/body/txt/anchored/fly/txt", 42);
3337 }
3338 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf123651)3339 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf123651)
3340 {
3341     createDoc("tdf123651.docx");
3342     xmlDocPtr pXmlDoc = parseLayoutDump();
3343     // Without the accompanying fix in place, this test would have failed with 'Expected: 7639;
3344     // Actual: 12926'. The shape was below the second "Lorem ipsum" text, not above it.
3345     assertXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds", "top", "7639");
3346 }
3347 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf116501)3348 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf116501)
3349 {
3350     //just care it doesn't freeze
3351     createDoc("tdf116501.odt");
3352 }
3353 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf118719)3354 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf118719)
3355 {
3356     // Insert a page break.
3357     SwDoc* pDoc = createDoc();
3358     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3359 
3360     // Enable hide whitespace mode.
3361     SwViewOption aViewOptions(*pWrtShell->GetViewOptions());
3362     aViewOptions.SetHideWhitespaceMode(true);
3363     pWrtShell->ApplyViewOptions(aViewOptions);
3364 
3365     pWrtShell->Insert("first");
3366     pWrtShell->InsertPageBreak();
3367     pWrtShell->Insert("second");
3368 
3369     // Without the accompanying fix in place, this test would have failed, as the height of the
3370     // first page was 15840 twips, instead of the much smaller 276.
3371     sal_Int32 nOther = parseDump("/root/page[1]/infos/bounds", "height").toInt32();
3372     sal_Int32 nLast = parseDump("/root/page[2]/infos/bounds", "height").toInt32();
3373     CPPUNIT_ASSERT_GREATER(nOther, nLast);
3374 }
3375 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTabOverMargin)3376 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTabOverMargin)
3377 {
3378     createDoc("tab-over-margin.odt");
3379     xmlDocPtr pXmlDoc = parseLayoutDump();
3380 
3381     // 2nd paragraph has a tab over the right margin, and with the TabOverMargin compat option,
3382     // there is enough space to have all content in a single line.
3383     // Without the accompanying fix in place, this test would have failed, there were 2 lines.
3384     assertXPath(pXmlDoc, "/root/page/body/txt[2]/LineBreak", 1);
3385 }
3386 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testImageComment)3387 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testImageComment)
3388 {
3389     // Load a document that has "aaa" in it, then a commented image (4th char is the as-char image,
3390     // 5th char is the comment anchor).
3391     SwDoc* pDoc = createDoc("image-comment.odt");
3392     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3393 
3394     // Look up a layout position which is on the right of the image.
3395     SwRootFrame* pRoot = pWrtShell->GetLayout();
3396     CPPUNIT_ASSERT(pRoot->GetLower()->IsPageFrame());
3397     SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
3398     CPPUNIT_ASSERT(pPage->GetLower()->IsBodyFrame());
3399     SwBodyFrame* pBody = static_cast<SwBodyFrame*>(pPage->GetLower());
3400     CPPUNIT_ASSERT(pBody->GetLower()->IsTextFrame());
3401     SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pBody->GetLower());
3402     CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
3403     SwSortedObjs& rDrawObjs = *pTextFrame->GetDrawObjs();
3404     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rDrawObjs.size());
3405     SwAnchoredObject* pDrawObj = rDrawObjs[0];
3406     const SwRect& rDrawObjRect = pDrawObj->GetObjRect();
3407     Point aPoint = rDrawObjRect.Center();
3408     aPoint.setX(aPoint.getX() + rDrawObjRect.Width() / 2);
3409 
3410     // Ask for the doc model pos of this layout point.
3411     SwPosition aPosition(*pTextFrame->GetTextNodeForFirstText());
3412     pTextFrame->GetCursorOfst(&aPosition, aPoint);
3413 
3414     // Without the accompanying fix in place, this test would have failed with:
3415     // - Expected: 5
3416     // - Actual  : 4
3417     // i.e. the cursor got positioned between the image and its comment, so typing extended the
3418     // comment, instead of adding content after the commented image.
3419     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5), aPosition.nContent.GetIndex());
3420 }
3421 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf64222)3422 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf64222)
3423 {
3424     createDoc("tdf64222.docx");
3425     xmlDocPtr pXmlDoc = parseLayoutDump();
3426     assertXPath(pXmlDoc, "/root/page/body/txt[2]/Special", "nHeight", "560");
3427 }
3428 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf113014)3429 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf113014)
3430 {
3431     SwDoc* pDoc = createDoc("tdf113014.fodt");
3432     SwDocShell* pShell = pDoc->GetDocShell();
3433 
3434     // Dump the rendering of the first page as an XML file.
3435     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
3436     MetafileXmlDump dumper;
3437     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
3438     CPPUNIT_ASSERT(pXmlDoc);
3439 
3440     // This failed, if numbering of cell A1 is missing
3441     // (A1: left indent: 3 cm, first line indent: -3 cm
3442     // A2: left indent: 0 cm, first line indent: 0 cm)
3443     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", "1.");
3444     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[3]/text", "2.");
3445     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[5]/text", "3.");
3446 }
3447 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf127235)3448 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf127235)
3449 {
3450     SwDoc* pDoc = createDoc("tdf127235.odt");
3451     // This resulted in a layout loop.
3452     pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
3453 }
3454 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testShapeAllowOverlap)3455 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testShapeAllowOverlap)
3456 {
3457 // Need to find out why this fails on macOS.
3458 #ifndef MACOSX
3459     // Create an empty document with two, intentionally overlapping shapes.
3460     // Set their AllowOverlap property to false.
3461     loadURL("private:factory/swriter", nullptr);
3462     uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
3463     awt::Point aPoint(1000, 1000);
3464     awt::Size aSize(2000, 2000);
3465     uno::Reference<drawing::XShape> xShape(
3466         xDocument->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
3467     xShape->setPosition(aPoint);
3468     xShape->setSize(aSize);
3469     uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
3470     uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
3471     xShapeProperties->setPropertyValue("AllowOverlap", uno::makeAny(false));
3472     xShapeProperties->setPropertyValue("AnchorType",
3473                                        uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
3474     xDrawPageSupplier->getDrawPage()->add(xShape);
3475 
3476     aPoint = awt::Point(2000, 2000);
3477     xShape.set(xDocument->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
3478     xShape->setPosition(aPoint);
3479     xShape->setSize(aSize);
3480     xShapeProperties.set(xShape, uno::UNO_QUERY);
3481     xShapeProperties->setPropertyValue("AllowOverlap", uno::makeAny(false));
3482     xShapeProperties->setPropertyValue("AnchorType",
3483                                        uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
3484     xDrawPageSupplier->getDrawPage()->add(xShape);
3485 
3486     // Now verify that the rectangle of the anchored objects don't overlap.
3487     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
3488     CPPUNIT_ASSERT(pTextDoc);
3489     SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
3490     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
3491     SwFrame* pPageFrame = pLayout->GetLower();
3492     SwFrame* pBodyFrame = pPageFrame->GetLower();
3493     SwFrame* pTextFrame = pBodyFrame->GetLower();
3494     CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
3495     SwSortedObjs& rObjs = *pTextFrame->GetDrawObjs();
3496     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs.size());
3497     SwAnchoredObject* pFirst = rObjs[0];
3498     SwAnchoredObject* pSecond = rObjs[1];
3499     // Without the accompanying fix in place, this test would have failed: the layout dump was
3500     // <bounds left="1984" top="1984" width="1137" height="1137"/>
3501     // <bounds left="2551" top="2551" width="1137" height="1137"/>
3502     // so there was a clear vertical overlap. (Allow for 1px tolerance.)
3503     OString aMessage = "Unexpected overlap: first shape's bottom is "
3504                        + OString::number(pFirst->GetObjRect().Bottom()) + ", second shape's top is "
3505                        + OString::number(pSecond->GetObjRect().Top());
3506     CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(),
3507                            std::abs(pFirst->GetObjRect().Bottom() - pSecond->GetObjRect().Top())
3508                                < 15);
3509 #endif
3510 }
3511 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testShapeAllowOverlapWrap)3512 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testShapeAllowOverlapWrap)
3513 {
3514     // Create an empty document with two, intentionally overlapping shapes.
3515     // Set their AllowOverlap property to false and their wrap to through.
3516     loadURL("private:factory/swriter", nullptr);
3517     uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
3518     awt::Point aPoint(1000, 1000);
3519     awt::Size aSize(2000, 2000);
3520     uno::Reference<drawing::XShape> xShape(
3521         xDocument->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
3522     xShape->setPosition(aPoint);
3523     xShape->setSize(aSize);
3524     uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
3525     uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
3526     xShapeProperties->setPropertyValue("AllowOverlap", uno::makeAny(false));
3527     xShapeProperties->setPropertyValue("AnchorType",
3528                                        uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
3529     xShapeProperties->setPropertyValue("Surround", uno::makeAny(text::WrapTextMode_THROUGH));
3530     xDrawPageSupplier->getDrawPage()->add(xShape);
3531 
3532     aPoint = awt::Point(2000, 2000);
3533     xShape.set(xDocument->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
3534     xShape->setPosition(aPoint);
3535     xShape->setSize(aSize);
3536     xShapeProperties.set(xShape, uno::UNO_QUERY);
3537     xShapeProperties->setPropertyValue("AllowOverlap", uno::makeAny(false));
3538     xShapeProperties->setPropertyValue("AnchorType",
3539                                        uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
3540     xShapeProperties->setPropertyValue("Surround", uno::makeAny(text::WrapTextMode_THROUGH));
3541     xDrawPageSupplier->getDrawPage()->add(xShape);
3542 
3543     // Now verify that the rectangle of the anchored objects do overlap.
3544     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
3545     CPPUNIT_ASSERT(pTextDoc);
3546     SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
3547     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
3548     SwFrame* pPageFrame = pLayout->GetLower();
3549     SwFrame* pBodyFrame = pPageFrame->GetLower();
3550     SwFrame* pTextFrame = pBodyFrame->GetLower();
3551     CPPUNIT_ASSERT(pTextFrame->GetDrawObjs());
3552     SwSortedObjs& rObjs = *pTextFrame->GetDrawObjs();
3553     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs.size());
3554     SwAnchoredObject* pFirst = rObjs[0];
3555     SwAnchoredObject* pSecond = rObjs[1];
3556     // Without the accompanying fix in place, this test would have failed: AllowOverlap=no had
3557     // priority over Surround=through (which is bad for Word compat).
3558     CPPUNIT_ASSERT(pSecond->GetObjRect().IsOver(pFirst->GetObjRect()));
3559 }
3560 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf124600)3561 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124600)
3562 {
3563     createDoc("tdf124600.docx");
3564     xmlDocPtr pXmlDoc = parseLayoutDump();
3565 
3566     // Without the accompanying fix in place, this test would have failed with:
3567     // - Expected: 1
3568     // - Actual  : 2
3569     // i.e. the last line in the body text had 2 lines, while it should have 1, as Word does (as the
3570     // fly frame does not intersect with the print area of the paragraph.)
3571     assertXPath(pXmlDoc, "/root/page/body/txt[2]/LineBreak", 1);
3572 }
3573 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf124601)3574 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124601)
3575 {
3576     // This is a testcase for the ContinuousEndnotes compat flag.
3577     // The document has 2 pages, the endnote anchor is on the first page.
3578     // The endnote should be on the 2nd page together with the last page content.
3579     createDoc("tdf124601.doc");
3580     xmlDocPtr pXmlDoc = parseLayoutDump();
3581 
3582     // Without the accompanying fix in place, this test would have failed with:
3583     // - Expected: 2
3584     // - Actual  : 3
3585     // i.e. there was a separate endnote page, even when the ContinuousEndnotes compat option was
3586     // on.
3587     assertXPath(pXmlDoc, "/root/page", 2);
3588     assertXPath(pXmlDoc, "/root/page[2]/ftncont", 1);
3589 }
3590 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf124601b)3591 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124601b)
3592 {
3593     // Table has an image, which is anchored in the first row, but its vertical position is large
3594     // enough to be rendered in the second row.
3595     // The shape has layoutInCell=1, so should match what Word does here.
3596     // Also the horizontal position should be in the last column, even if the anchor is in the
3597     // last-but-one column.
3598     createDoc("tdf124601b.doc");
3599     xmlDocPtr pXmlDoc = parseLayoutDump();
3600 
3601     sal_Int32 nFlyTop = getXPath(pXmlDoc, "//fly/infos/bounds", "top").toInt32();
3602     sal_Int32 nFlyLeft = getXPath(pXmlDoc, "//fly/infos/bounds", "left").toInt32();
3603     sal_Int32 nFlyRight = nFlyLeft + getXPath(pXmlDoc, "//fly/infos/bounds", "width").toInt32();
3604     sal_Int32 nSecondRowTop = getXPath(pXmlDoc, "//tab/row[2]/infos/bounds", "top").toInt32();
3605     sal_Int32 nLastCellLeft
3606         = getXPath(pXmlDoc, "//tab/row[1]/cell[5]/infos/bounds", "left").toInt32();
3607     sal_Int32 nLastCellRight
3608         = nLastCellLeft + getXPath(pXmlDoc, "//tab/row[1]/cell[5]/infos/bounds", "width").toInt32();
3609     // Without the accompanying fix in place, this test would have failed with:
3610     // - Expected greater than: 3736
3611     // - Actual  : 2852
3612     // i.e. the image was still inside the first row.
3613     CPPUNIT_ASSERT_GREATER(nSecondRowTop, nFlyTop);
3614 
3615     // Without the accompanying fix in place, this test would have failed with:
3616     // - Expected greater than: 9640
3617     // - Actual  : 9639
3618     // i.e. the right edge of the image was not within the bounds of the last column, the right edge
3619     // was in the last-but-one column.
3620     CPPUNIT_ASSERT_GREATER(nLastCellLeft, nFlyRight);
3621     CPPUNIT_ASSERT_LESS(nLastCellRight, nFlyRight);
3622 }
3623 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf124770)3624 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124770)
3625 {
3626     // Enable content over margin.
3627     SwDoc* pDoc = createDoc();
3628     pDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
3629 
3630     // Set page width.
3631     SwPageDesc& rPageDesc = pDoc->GetPageDesc(0);
3632     SwFrameFormat& rPageFormat = rPageDesc.GetMaster();
3633     const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
3634     SwFormatFrameSize aPageSize = rPageSet.GetFrameSize();
3635     aPageSize.SetWidth(3703);
3636     rPageFormat.SetFormatAttr(aPageSize);
3637 
3638     // Set left and right margin.
3639     SvxLRSpaceItem aLRSpace = rPageSet.GetLRSpace();
3640     aLRSpace.SetLeft(1418);
3641     aLRSpace.SetRight(1418);
3642     rPageFormat.SetFormatAttr(aLRSpace);
3643     pDoc->ChgPageDesc(0, rPageDesc);
3644 
3645     // Set font to italic 20pt Liberation Serif.
3646     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3647     SfxItemSet aTextSet(pWrtShell->GetView().GetPool(),
3648                         svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>{});
3649     SvxFontItem aFont(RES_CHRATR_FONT);
3650     aFont.SetFamilyName("Liberation Serif");
3651     aTextSet.Put(aFont);
3652     SvxFontHeightItem aHeight(400, 100, RES_CHRATR_FONTSIZE);
3653     aTextSet.Put(aHeight);
3654     SvxPostureItem aItalic(ITALIC_NORMAL, RES_CHRATR_POSTURE);
3655     aTextSet.Put(aItalic);
3656     pWrtShell->SetAttrSet(aTextSet);
3657 
3658     // Insert the text.
3659     pWrtShell->Insert2("HHH");
3660 
3661     xmlDocPtr pXmlDoc = parseLayoutDump();
3662     // Without the accompanying fix in place, this test would have failed with:
3663     // - Expected: 1
3664     // - Actual  : 2
3665     // i.e. the italic string was broken into 2 lines, while Word kept it in a single line.
3666     assertXPath(pXmlDoc, "/root/page/body/txt[1]/LineBreak", 1);
3667 }
3668 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testContinuousEndnotesInsertPageAtStart)3669 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testContinuousEndnotesInsertPageAtStart)
3670 {
3671     // Create a new document with CONTINUOUS_ENDNOTES enabled.
3672     SwDoc* pDoc = createDoc();
3673     pDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
3674 
3675     // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
3676     // the 2nd page).
3677     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3678     pWrtShell->InsertPageBreak();
3679     pWrtShell->InsertFootnote("endnote", /*bEndNote=*/true, /*bEdit=*/false);
3680 
3681     // Add a new page at the start of the document.
3682     pWrtShell->SttEndDoc(/*bStart=*/true);
3683     pWrtShell->InsertPageBreak();
3684 
3685     // Make sure that the endnote is moved from the 2nd page to the 3rd one.
3686     xmlDocPtr pXmlDoc = parseLayoutDump();
3687     assertXPath(pXmlDoc, "/root/page", 3);
3688     // Without the accompanying fix in place, this test would have failed with:
3689     // - Expected: 1
3690     // - Actual  : 0
3691     // i.e. the footnote container remained on page 2.
3692     assertXPath(pXmlDoc, "/root/page[3]/ftncont", 1);
3693 }
3694 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testContinuousEndnotesDeletePageAtStart)3695 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testContinuousEndnotesDeletePageAtStart)
3696 {
3697     // Create a new document with CONTINUOUS_ENDNOTES enabled.
3698     SwDoc* pDoc = createDoc();
3699     pDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
3700 
3701     // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
3702     // the 2nd page).
3703     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3704     pWrtShell->InsertPageBreak();
3705     pWrtShell->InsertFootnote("endnote", /*bEndNote=*/true, /*bEdit=*/false);
3706 
3707     // Remove the empty page at the start of the document.
3708     pWrtShell->SttEndDoc(/*bStart=*/true);
3709     pWrtShell->DelRight();
3710 
3711     // Make sure that the endnote is moved from the 2nd page to the 1st one.
3712     xmlDocPtr pXmlDoc = parseLayoutDump();
3713     // Without the accompanying fix in place, this test would have failed with:
3714     // - Expected: 1
3715     // - Actual  : 2
3716     // i.e. the endnote remained on an (otherwise) empty 2nd page.
3717     assertXPath(pXmlDoc, "/root/page", 1);
3718     assertXPath(pXmlDoc, "/root/page[1]/ftncont", 1);
3719 }
3720 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf128399)3721 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf128399)
3722 {
3723     SwDoc* pDoc = createDoc("tdf128399.docx");
3724     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
3725     SwFrame* pPage = pLayout->GetLower();
3726     SwFrame* pBody = pPage->GetLower();
3727     SwFrame* pTable = pBody->GetLower();
3728     SwFrame* pRow1 = pTable->GetLower();
3729     SwFrame* pRow2 = pRow1->GetNext();
3730     const SwRect& rRow2Rect = pRow2->getFrameArea();
3731     Point aPoint = rRow2Rect.Center();
3732 
3733     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3734     SwPosition aPosition = *pWrtShell->GetCursor()->Start();
3735     SwPosition aFirstRow(aPosition);
3736     SwCursorMoveState aState(MV_NONE);
3737     pLayout->GetCursorOfst(&aPosition, aPoint, &aState);
3738     // Second row is +3: end node, start node and the first text node in the 2nd row.
3739     sal_uLong nExpected = aFirstRow.nNode.GetIndex() + 3;
3740 
3741     // Without the accompanying fix in place, this test would have failed with:
3742     // - Expected: 14
3743     // - Actual  : 11
3744     // i.e. clicking on the center of the 2nd row placed the cursor in the 1st row.
3745     CPPUNIT_ASSERT_EQUAL(nExpected, aPosition.nNode.GetIndex());
3746 }
3747 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf105481)3748 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf105481)
3749 {
3750     createDoc("tdf105481.odt");
3751     xmlDocPtr pXmlDoc = parseLayoutDump();
3752     CPPUNIT_ASSERT(pXmlDoc);
3753 
3754     // Without the accompanying fix in place, this test would have failed
3755     // because the vertical position of the as-char shape object and the
3756     // as-char math object will be wrong (below/beyond the text frame's bottom).
3757 
3758     SwTwips nTxtTop = getXPath(pXmlDoc,
3759                                "/root/page/anchored/fly/txt[2]"
3760                                "/infos/bounds",
3761                                "top")
3762                           .toInt32();
3763     SwTwips nTxtBottom = nTxtTop
3764                          + getXPath(pXmlDoc,
3765                                     "/root/page/anchored/fly/txt[2]"
3766                                     "/infos/bounds",
3767                                     "height")
3768                                .toInt32();
3769 
3770     SwTwips nFormula1Top = getXPath(pXmlDoc,
3771                                     "/root/page/anchored/fly/txt[2]"
3772                                     "/anchored/fly[1]/infos/bounds",
3773                                     "top")
3774                                .toInt32();
3775     SwTwips nFormula1Bottom = nFormula1Top
3776                               + getXPath(pXmlDoc,
3777                                          "/root/page/anchored/fly/txt[2]"
3778                                          "/anchored/fly[1]/infos/bounds",
3779                                          "height")
3780                                     .toInt32();
3781 
3782     SwTwips nFormula2Top = getXPath(pXmlDoc,
3783                                     "/root/page/anchored/fly/txt[2]"
3784                                     "/anchored/fly[2]/infos/bounds",
3785                                     "top")
3786                                .toInt32();
3787     SwTwips nFormula2Bottom = nFormula2Top
3788                               + getXPath(pXmlDoc,
3789                                          "/root/page/anchored/fly/txt[2]"
3790                                          "/anchored/fly[2]/infos/bounds",
3791                                          "height")
3792                                     .toInt32();
3793 
3794     // Ensure that the two formula positions are at least between top and bottom of the text frame.
3795     // The below two are satisfied even without the fix.
3796     CPPUNIT_ASSERT_GREATEREQUAL(nTxtTop, nFormula1Top);
3797     CPPUNIT_ASSERT_GREATEREQUAL(nTxtTop, nFormula2Top);
3798 
3799     // Without the accompanying fix in place, this test would have failed with:
3800     // - Expected less than or equal to : 14423
3801     // - Actual  : 14828
3802     // that is, the formula is below the text-frame's y bound.
3803     CPPUNIT_ASSERT_LESSEQUAL(nTxtBottom, nFormula1Bottom);
3804     // Similarly for formula # 2 :
3805     // - Expected less than or equal to : 14423
3806     // - Actual  : 15035
3807     // that is, the formula is below the text-frame's y bound.
3808     CPPUNIT_ASSERT_LESSEQUAL(nTxtBottom, nFormula2Bottom);
3809 }
3810 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf121658)3811 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf121658)
3812 {
3813     uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
3814     if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString())))
3815         return;
3816 
3817     createDoc("tdf121658.odt");
3818     xmlDocPtr pXmlDoc = parseLayoutDump();
3819 
3820     // Only 2 hyphenated words should appear in the document (in the lowercase words).
3821     // Uppercase words should not be hyphenated.
3822     assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 2);
3823 }
3824 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf117982)3825 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf117982)
3826 {
3827     SwDoc* pDocument = createDoc("tdf117982.docx");
3828     SwDocShell* pShell = pDocument->GetDocShell();
3829     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
3830     MetafileXmlDump dumper;
3831     xmlDocPtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
3832     assertXPathContent(pXmlDoc, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", "FOO AAA");
3833     //The first cell must be "FOO AAA". If not, this means the first cell content not visible in
3834     //the source document.
3835 }
3836 
lcl_getVisibleFlyObjRect(SwWrtShell * pWrtShell)3837 static SwRect lcl_getVisibleFlyObjRect(SwWrtShell* pWrtShell)
3838 {
3839     SwRootFrame* pRoot = pWrtShell->GetLayout();
3840     SwPageFrame* pPage = static_cast<SwPageFrame*>(pRoot->GetLower());
3841     pPage = static_cast<SwPageFrame*>(pPage->GetNext());
3842     pPage = static_cast<SwPageFrame*>(pPage->GetNext());
3843     SwSortedObjs* pDrawObjs = pPage->GetDrawObjs();
3844     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDrawObjs->size());
3845     SwAnchoredObject* pDrawObj = (*pDrawObjs)[0];
3846     CPPUNIT_ASSERT_EQUAL(OUString("Rahmen8"), pDrawObj->GetFrameFormat().GetName());
3847     pPage = static_cast<SwPageFrame*>(pPage->GetNext());
3848     pDrawObjs = pPage->GetDrawObjs();
3849     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDrawObjs->size());
3850     pDrawObj = (*pDrawObjs)[0];
3851     CPPUNIT_ASSERT_EQUAL(OUString("Rahmen123"), pDrawObj->GetFrameFormat().GetName());
3852     SwRect aFlyRect = pDrawObj->GetObjRect();
3853     CPPUNIT_ASSERT(pPage->getFrameArea().IsInside(aFlyRect));
3854     return aFlyRect;
3855 }
3856 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testStableAtPageAnchoredFlyPosition)3857 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testStableAtPageAnchoredFlyPosition)
3858 {
3859     // this doc has two page-anchored frames: one tiny on page 3 and one large on page 4.
3860     // it also has a style:master-page named "StandardEntwurf", which contains some fields.
3861     // if you add a break to page 2, or append some text to page 4 (or just toggle display field names),
3862     // the page anchored frame on page 4 vanishes, as it is incorrectly moved out of the page bounds.
3863     SwDoc* pDoc = createDoc("stable-at-page-anchored-fly-position.odt");
3864     SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
3865 
3866     // look up the layout position of the page-bound frame on page four
3867     SwRect aOrigRect = lcl_getVisibleFlyObjRect(pWrtShell);
3868 
3869     // append some text to the document to trigger bug / relayout
3870     pWrtShell->SttEndDoc(false);
3871     pWrtShell->Insert("foo");
3872 
3873     // get the current position of the frame on page four
3874     SwRect aRelayoutRect = lcl_getVisibleFlyObjRect(pWrtShell);
3875 
3876     // the anchored frame should not have moved
3877     CPPUNIT_ASSERT_EQUAL(aOrigRect, aRelayoutRect);
3878 }
3879 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testBtlrTableRowSpan)3880 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testBtlrTableRowSpan)
3881 {
3882     // Load a document which has a table. The A1 cell has btlr text direction, and the A1..A3 cells
3883     // are merged.
3884     load(DATA_DIRECTORY, "btlr-table-row-span.odt");
3885     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
3886     SwDocShell* pShell = pTextDoc->GetDocShell();
3887     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
3888     MetafileXmlDump aDumper;
3889     xmlDocPtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
3890 
3891     // Without the accompanying fix in place, this test would have failed with:
3892     // - Expected: USA
3893     // - Actual  : West
3894     // i.e. the "USA" text completely disappeared.
3895     assertXPathContent(pXmlDoc, "//textarray[1]/text", "USA");
3896 }
3897 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testContinuousEndnotesMoveBackwards)3898 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testContinuousEndnotesMoveBackwards)
3899 {
3900     // Load a document with the ContinuousEndnotes flag turned on.
3901     load(DATA_DIRECTORY, "continuous-endnotes-move-backwards.doc");
3902     xmlDocPtr pLayout = parseLayoutDump();
3903     // We have 2 pages.
3904     assertXPath(pLayout, "/root/page", 2);
3905     // No endnote container on page 1.
3906     // Without the accompanying fix in place, this test would have failed with:
3907     // - Expected: 0
3908     // - Actual  : 1
3909     // i.e. there were unexpected endnotes on page 1.
3910     assertXPath(pLayout, "/root/page[1]/ftncont", 0);
3911     // All endnotes are in a container on page 2.
3912     assertXPath(pLayout, "/root/page[2]/ftncont", 1);
3913 }
3914 
CPPUNIT_TEST_FIXTURE(SwLayoutWriter,testTdf134548)3915 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134548)
3916 {
3917     createDoc("tdf134548.odt");
3918 
3919     // Second paragraph has two non zero width tabs in beginning of line
3920     {
3921         OUString sNodeType = parseDump("/root/page/body/txt[2]/Text[1]", "nType");
3922         CPPUNIT_ASSERT_EQUAL(OUString("PortionType::TabLeft"), sNodeType);
3923         sal_Int32 nWidth = parseDump("/root/page/body/txt[2]/Text[1]", "nWidth").toInt32();
3924         CPPUNIT_ASSERT_GREATER(sal_Int32(0), nWidth);
3925     }
3926     {
3927         OUString sNodeType = parseDump("/root/page/body/txt[2]/Text[2]", "nType");
3928         CPPUNIT_ASSERT_EQUAL(OUString("PortionType::TabLeft"), sNodeType);
3929         sal_Int32 nWidth = parseDump("/root/page/body/txt[2]/Text[2]", "nWidth").toInt32();
3930         CPPUNIT_ASSERT_GREATER(sal_Int32(0), nWidth);
3931     }
3932 }
3933 
3934 CPPUNIT_PLUGIN_IMPLEMENT();
3935 
3936 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3937