1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <swmodeltestbase.hxx>
11 #include <comphelper/propertysequence.hxx>
12 #include <com/sun/star/linguistic2/XHyphenator.hpp>
13 #include <com/sun/star/text/WrapTextMode.hpp>
14 #include <vcl/event.hxx>
15 #include <vcl/scheduler.hxx>
16 #include <editeng/fontitem.hxx>
17 #include <editeng/fhgtitem.hxx>
18 #include <editeng/postitem.hxx>
19 #include <editeng/unolingu.hxx>
20 #include <editeng/editobj.hxx>
21 #include <comphelper/sequence.hxx>
23 #include <fmtfsize.hxx>
28 #include <pagefrm.hxx>
29 #include <bodyfrm.hxx>
30 #include <sortedobjs.hxx>
33 #include <IDocumentSettingAccess.hxx>
34 #include <unotxdoc.hxx>
35 #include <rootfrm.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <IDocumentDrawModelAccess.hxx>
38 #include <unoframe.hxx>
39 #include <drawdoc.hxx>
40 #include <svx/svdpage.hxx>
41 #include <svx/svdotext.hxx>
42 #include <dcontact.hxx>
43 #include <frameformats.hxx>
45 /// Test to assert layout / rendering result of Writer.
46 class SwLayoutWriter3
: public SwModelTestBase
50 : SwModelTestBase("/sw/qa/extras/layout/data/")
55 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf134463
)
57 createSwDoc("tdf134463.docx");
58 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
59 // This was 621. The previous paragraph must have zero bottom border.
60 assertXPath(pXmlDoc
, "/root/page/body/txt[3]/infos/prtBounds", "top", "21");
63 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf117188
)
65 createSwDoc("tdf117188.docx");
66 saveAndReload("writer8");
67 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
68 OUString sWidth
= getXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/bounds", "width");
69 OUString sHeight
= getXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/bounds", "height");
70 // The text box must have zero border distances
71 assertXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/prtBounds", "left", "0");
72 assertXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/prtBounds", "top", "0");
73 assertXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/prtBounds", "width", sWidth
);
74 assertXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/infos/prtBounds", "height", sHeight
);
77 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf117187
)
79 createSwDoc("tdf117187.odt");
80 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
82 // there should be no fly portions
85 "/root/page/body/txt/SwParaPortion/SwLineLayout/child::*[@nType='PortionType::Fly']", 0);
88 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf119875
)
90 createSwDoc("tdf119875.odt");
91 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
93 = getXPath(pXmlDoc
, "/root/page[2]/body/section[1]/infos/bounds", "top").toInt32();
95 = getXPath(pXmlDoc
, "/root/page[2]/body/section[2]/infos/bounds", "top").toInt32();
96 // The first section had the same top value as the second one, so they
98 CPPUNIT_ASSERT_LESS(nSecondTop
, nFirstTop
);
101 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf137523
)
103 createSwDoc("tdf137523-1-min.fodt");
104 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
105 // the problem was that in the footer, the text frames below the table
106 // had wrong height and were not visible
107 assertXPath(pXmlDoc
, "/root/page/footer/txt[1]/infos/bounds", "height", "304");
108 assertXPath(pXmlDoc
, "/root/page/footer/txt[2]/infos/bounds", "height", "191");
109 assertXPath(pXmlDoc
, "/root/page/footer/txt[3]/infos/bounds", "height", "219");
110 assertXPath(pXmlDoc
, "/root/page/footer/tab/infos/bounds", "height", "1378");
113 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf120287
)
115 createSwDoc("tdf120287.fodt");
116 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
117 // This was 2, TabOverMargin Word-specific compat flag did not imply
118 // default-in-Word printer-independent layout, resulting in an additional
120 assertXPath(pXmlDoc
, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 1);
123 static auto getXPathIntAttributeValue(xmlXPathContextPtr pXmlXpathCtx
, char const* const pXPath
)
126 xmlXPathObjectPtr pXmlXpathObj
= xmlXPathEvalExpression(BAD_CAST(pXPath
), pXmlXpathCtx
);
127 CPPUNIT_ASSERT(pXmlXpathObj
->nodesetval
);
128 CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlXpathObj
->nodesetval
));
130 = sal_Int32(xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pXmlXpathObj
->nodesetval
, 0)));
131 xmlXPathFreeObject(pXmlXpathObj
);
135 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf128966
)
137 createSwDoc("tdf128966-2-min.odt");
139 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
141 xmlXPathObjectPtr pXmlObj
142 = getXPathNode(pXmlDoc
, "/root/page/body/tab/row/cell[@rowspan > 0][child::txt]");
143 xmlNodeSetPtr pXmlNodes
= pXmlObj
->nodesetval
;
144 CPPUNIT_ASSERT(pXmlNodes
);
145 CPPUNIT_ASSERT_GREATER(300, xmlXPathNodeSetGetLength(pXmlNodes
)); // many...
147 xmlXPathContextPtr pXmlXpathCtx
= xmlXPathNewContext(pXmlDoc
.get());
148 registerNamespaces(pXmlXpathCtx
);
150 for (int i
= 0; i
< xmlXPathNodeSetGetLength(pXmlNodes
); ++i
)
152 xmlNodePtr pNode
= xmlXPathNodeSetItem(pXmlNodes
, i
);
153 xmlXPathSetContextNode(pNode
, pXmlXpathCtx
);
155 OString
msg("Cell nr.: " + OString::number(i
)
156 + " id=" + OString::number(getXPathIntAttributeValue(pXmlXpathCtx
, "@id")));
158 auto nCellTop
= getXPathIntAttributeValue(pXmlXpathCtx
, "infos/bounds/@top");
159 auto nCellHeight
= getXPathIntAttributeValue(pXmlXpathCtx
, "infos/bounds/@height");
160 auto nCellCenter
= nCellTop
+ (nCellHeight
/ 2);
163 = getXPathIntAttributeValue(pXmlXpathCtx
, "txt[position()=1]/infos/bounds/@top");
164 auto nContentBottom
= getXPathIntAttributeValue(
165 pXmlXpathCtx
, "txt[position()=last()]/infos/bounds/@bottom");
167 CPPUNIT_ASSERT_MESSAGE(msg
.getStr(), nContentTop
< nCellCenter
);
168 CPPUNIT_ASSERT_MESSAGE(msg
.getStr(), nContentBottom
> nCellCenter
);
171 xmlXPathFreeContext(pXmlXpathCtx
);
172 xmlXPathFreeObject(pXmlObj
);
175 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf106234
)
177 createSwDoc("tdf106234.fodt");
178 // Ensure that all text portions are calculated before testing.
179 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
180 CPPUNIT_ASSERT(pTextDoc
);
181 SwViewShell
* pViewShell
182 = pTextDoc
->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
183 CPPUNIT_ASSERT(pViewShell
);
184 pViewShell
->Reformat();
186 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
187 // In justified paragraphs, there is justification between left tabulators and manual line breaks
188 assertXPath(pXmlDoc
, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
189 "type", "PortionType::Margin");
190 assertXPath(pXmlDoc
, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
192 // but not after centered, right and decimal tabulators
193 assertXPath(pXmlDoc
, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
194 "type", "PortionType::Margin");
195 // This was a justified line, without width
196 assertXPath(pXmlDoc
, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwGluePortion",
200 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf155324
)
202 createSwDoc("tox-update-wrong-pages.odt");
204 dispatchCommand(mxComponent
, ".uno:UpdateAllIndexes", {});
206 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
208 // the problem was that the first entry was on page 7, 2nd on page 9 etc.
210 "/root/page[1]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
213 "/root/page[1]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
216 "/root/page[1]/body/section[2]/txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
219 "/root/page[1]/body/section[2]/txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
222 "/root/page[1]/body/section[2]/txt[3]/SwParaPortion/SwLineLayout/SwLinePortion[1]",
225 "/root/page[1]/body/section[2]/txt[3]/SwParaPortion/SwLineLayout/SwLinePortion[2]",
228 // check first content page has the footnotes
229 assertXPath(pXmlDoc
, "/root/page[5]/body/txt[1]/SwParaPortion/SwLineLayout", "portion", "Foo");
230 assertXPath(pXmlDoc
, "/root/page[4]/ftncont", 0);
231 assertXPath(pXmlDoc
, "/root/page[5]/ftncont/ftn", 5);
234 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf120287b
)
236 createSwDoc("tdf120287b.fodt");
237 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
238 // This was 1418, TabOverMargin did the right split of the paragraph to two
239 // lines, but then calculated a too large tab portion size on the first
243 "/root/page/body/txt[1]/SwParaPortion/SwLineLayout/child::*[@type='PortionType::TabRight']",
247 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf120287c
)
249 createSwDoc("tdf120287c.fodt");
250 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
251 // This was 2, the second line was not broken into a 2nd and a 3rd one,
252 // rendering text outside the paragraph frame.
253 assertXPath(pXmlDoc
, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 3);
256 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf155177
)
258 createSwDoc("tdf155177-1-min.odt");
260 uno::Reference
<beans::XPropertySet
> xStyle(getStyles("ParagraphStyles")->getByName("Body Text"),
261 uno::UNO_QUERY_THROW
);
262 CPPUNIT_ASSERT_EQUAL(sal_Int32(210), getProperty
<sal_Int32
>(xStyle
, "ParaTopMargin"));
265 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
266 assertXPath(pXmlDoc
, "/root/page[2]/body/txt", 6);
267 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[6]/SwParaPortion/SwLineLayout", 2);
268 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[6]/SwParaPortion/SwLineLayout[2]", "portion",
269 "long as two lines.");
270 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 3);
271 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
272 "This paragraph is even longer so that ");
273 discardDumpedLayout();
276 // this should bring one line back
277 xStyle
->setPropertyValue("ParaTopMargin", uno::Any(sal_Int32(200)));
279 Scheduler::ProcessEventsToIdle();
282 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
283 assertXPath(pXmlDoc
, "/root/page[2]/body/txt", 7);
284 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout", 1);
285 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
286 "This paragraph is even longer so that ");
287 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 2);
288 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
289 "it is now three lines long though ");
290 discardDumpedLayout();
293 // this should bring second line back
294 xStyle
->setPropertyValue("ParaTopMargin", uno::Any(sal_Int32(120)));
296 Scheduler::ProcessEventsToIdle();
299 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
300 assertXPath(pXmlDoc
, "/root/page[2]/body/txt", 7);
301 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout", 2);
302 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
303 "This paragraph is even longer so that ");
304 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[7]/SwParaPortion/SwLineLayout[2]", "portion",
305 "it is now three lines long though ");
306 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout", 1);
307 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
308 "containing a single sentence.");
309 discardDumpedLayout();
313 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf122878
)
315 createSwDoc("tdf122878.docx");
316 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
317 const sal_Int32 nTblTop
318 = getXPath(pXmlDoc
, "/root/page[1]/footer/txt/anchored/fly/tab/infos/bounds", "top")
320 SwDoc
* pDoc
= getSwDoc();
321 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
322 auto pPage1
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
323 CPPUNIT_ASSERT(pPage1
);
324 SwFrame
* pBody
= pPage1
->FindBodyCont();
325 for (SwFrame
* pFrame
= pBody
->GetLower(); pFrame
; pFrame
= pFrame
->GetNext())
327 const sal_Int32 nTxtBottom
= pFrame
->getFrameArea().Bottom();
328 // No body paragraphs should overlap the table in the footer
329 CPPUNIT_ASSERT_MESSAGE(
330 OString("testing paragraph #" + OString::number(pFrame
->GetFrameId())).getStr(),
331 nTxtBottom
<= nTblTop
);
335 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf115094
)
337 createSwDoc("tdf115094.docx");
338 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
341 = getXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/infos/bounds",
344 sal_Int32 nTopOfD1Anchored
= getXPath(pXmlDoc
,
345 "/root/page/body/txt/anchored/fly/tab/row[1]/cell[4]/"
346 "txt[2]/anchored/fly/infos/bounds",
349 CPPUNIT_ASSERT_LESS(nTopOfD1Anchored
, nTopOfD1
);
351 = getXPath(pXmlDoc
, "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/infos/bounds",
354 sal_Int32 nTopOfB2Anchored
= getXPath(pXmlDoc
,
355 "/root/page/body/txt/anchored/fly/tab/row[2]/cell[2]/"
356 "txt[1]/anchored/fly/infos/bounds",
359 CPPUNIT_ASSERT_LESS(nTopOfB2Anchored
, nTopOfB2
);
362 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf112290
)
364 createSwDoc("tdf112290.docx");
365 SwDoc
* pDoc
= getSwDoc();
366 CPPUNIT_ASSERT(pDoc
);
367 auto pXml
= parseLayoutDump();
368 assertXPath(pXml
, "/root/page/body/txt/SwParaPortion/SwLineLayout[2]", "portion", "Xxxx Xxxx");
371 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testKeepWithNextPlusFlyFollowTextFlow
)
373 createSwDoc("keep-with-next-fly.fodt");
376 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
377 // 3 text frames on page 1
378 assertXPath(pXmlDoc
, "/root/page[1]/body/infos/bounds", "bottom", "7540");
379 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[1]/infos/bounds", "height", "276");
380 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/infos/bounds", "height", "276");
381 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/anchored/fly", 1);
382 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/anchored/fly/infos/bounds", "top", "1694");
383 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[3]/infos/bounds", "height", "276");
384 assertXPath(pXmlDoc
, "/root/page", 1);
385 discardDumpedLayout();
388 dispatchCommand(mxComponent
, ".uno:Fieldnames", {});
391 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
392 // 1 text frame on page 1, and some empty space
393 assertXPath(pXmlDoc
, "/root/page[1]/body/infos/bounds", "bottom", "7540");
394 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[1]/infos/bounds", "height", "5796");
395 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[1]/infos/bounds", "bottom", "7213");
396 // 2 text frames on page 2
397 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/infos/bounds", "height", "276");
398 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly", 1);
399 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly/infos/bounds", "top", "10093");
400 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[2]/infos/bounds", "height", "276");
401 assertXPath(pXmlDoc
, "/root/page", 2);
402 discardDumpedLayout();
405 dispatchCommand(mxComponent
, ".uno:Fieldnames", {});
408 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
409 // 3 text frames on page 1
410 assertXPath(pXmlDoc
, "/root/page[1]/body/infos/bounds", "bottom", "7540");
411 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[1]/infos/bounds", "height", "276");
412 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/infos/bounds", "height", "276");
413 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/anchored/fly", 1);
414 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[2]/anchored/fly/infos/bounds", "top", "1694");
415 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[3]/infos/bounds", "height", "276");
416 assertXPath(pXmlDoc
, "/root/page", 1);
417 discardDumpedLayout();
421 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf122607
)
423 createSwDoc("tdf122607.odt");
424 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
426 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
427 "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
430 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
431 "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
434 "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/"
435 "fly/txt/SwParaPortion/SwLineLayout/child::*[1]",
439 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf122607_regression
)
441 discardDumpedLayout();
442 if (mxComponent
.is())
443 mxComponent
->dispose();
445 OUString
const url(createFileURL(u
"tdf122607_leerzeile.odt"));
447 // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
448 // does *not* forward initial VCL Window Resize and thereby triggers a
449 // layout which does not happen on soffice --convert-to pdf.
450 std::vector
<beans::PropertyValue
> aFilterOptions
= {
451 { beans::PropertyValue("Hidden", -1, uno::Any(true), beans::PropertyState_DIRECT_VALUE
) },
454 // inline the loading because currently properties can't be passed...
455 mxComponent
= loadFromDesktop(url
, "com.sun.star.text.TextDocument",
456 comphelper::containerToSequence(aFilterOptions
));
457 uno::Sequence
<beans::PropertyValue
> props(comphelper::InitPropertySequence({
458 { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
460 utl::TempFileNamed aTempFile
;
461 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
462 xStorable
->storeToURL(aTempFile
.GetURL(), props
);
464 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
465 // somehow these 2 rows overlapped in the PDF unless CalcLayout() runs
466 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "mbFixSize",
468 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "top", "2977");
469 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[1]/infos/bounds", "height", "241");
470 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "mbFixSize",
472 // this was 3034, causing the overlap
473 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "top", "3218");
474 assertXPath(pXmlDoc
, "/root/page[1]/anchored/fly/tab[1]/row[2]/infos/bounds", "height", "164");
476 aTempFile
.EnableKillingFile();
479 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, TestTdf150616
)
481 discardDumpedLayout();
482 if (mxComponent
.is())
483 mxComponent
->dispose();
485 OUString
const url(createFileURL(u
"in_056132_mod.odt"));
487 // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
488 // does *not* forward initial VCL Window Resize and thereby triggers a
489 // layout which does not happen on soffice --convert-to pdf.
490 std::vector
<beans::PropertyValue
> aFilterOptions
= {
491 { beans::PropertyValue("Hidden", -1, uno::Any(true), beans::PropertyState_DIRECT_VALUE
) },
494 // inline the loading because currently properties can't be passed...
495 mxComponent
= loadFromDesktop(url
, "com.sun.star.text.TextDocument",
496 comphelper::containerToSequence(aFilterOptions
));
497 uno::Sequence
<beans::PropertyValue
> props(comphelper::InitPropertySequence({
498 { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
500 utl::TempFileNamed aTempFile
;
501 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
502 xStorable
->storeToURL(aTempFile
.GetURL(), props
);
504 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
505 CPPUNIT_ASSERT(pXmlDoc
);
507 // this one was 0 height
509 "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/SwParaPortion/SwLineLayout",
510 "portion", "Important information here!");
511 assertXPath(pXmlDoc
, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/infos/bounds", "height",
513 assertXPath(pXmlDoc
, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[2]/infos/bounds", "top",
516 "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/SwParaPortion/SwLineLayout",
517 "portion", "xxx 111 ");
518 assertXPath(pXmlDoc
, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/infos/bounds", "height",
520 assertXPath(pXmlDoc
, "/root/page[1]/body/tab[3]/row[2]/cell[2]/txt[3]/infos/bounds", "top",
523 aTempFile
.EnableKillingFile();
526 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testBtlrCell
)
528 createSwDoc("btlr-cell.odt");
529 SwDoc
* pDoc
= getSwDoc();
530 SwDocShell
* pShell
= pDoc
->GetDocShell();
532 // Dump the rendering of the first page as an XML file.
533 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
534 MetafileXmlDump dumper
;
535 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
536 CPPUNIT_ASSERT(pXmlDoc
);
538 // Without the accompanying fix in place, this test would have failed, as
539 // the orientation was 0 (layout did not take btlr direction request from
541 assertXPath(pXmlDoc
, "//font[1]", "orientation", "900");
543 #if !defined(MACOSX) && !defined(_WIN32) // macOS fails with x == 2662 for some reason.
544 // Without the accompanying fix in place, this test would have failed with 'Expected: 1915;
545 // Actual : 1756', i.e. the AAA1 text was too close to the left cell border due to an ascent vs
546 // descent mismatch when calculating the baseline offset of the text portion.
547 assertXPath(pXmlDoc
, "//textarray[1]", "x", "1915");
548 assertXPath(pXmlDoc
, "//textarray[1]", "y", "2707");
550 // Without the accompanying fix in place, this test would have failed with 'Expected: 1979;
551 // Actual : 2129', i.e. the gray background of the "AAA2." text was too close to the right edge
552 // of the text portion. Now it's exactly behind the text portion.
553 assertXPath(pXmlDoc
, "(//rect)[2]", "left", "1979");
555 // Without the accompanying fix in place, this test would have failed with 'Expected: 269;
556 // Actual : 0', i.e. the AAA2 frame was not visible due to 0 width.
557 pXmlDoc
= parseLayoutDump();
558 assertXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width", "269");
560 // Test the position of the cursor after doc load.
561 // We expect that it's inside the first text frame in the first cell.
562 // More precisely, this is a bottom to top vertical frame, so we expect it's at the start, which
563 // means it's at the lower half of the text frame rectangle (vertically).
564 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
565 CPPUNIT_ASSERT(pWrtShell
);
567 const SwRect
& rCharRect
= pWrtShell
->GetCharRect();
568 SwTwips nFirstParaTop
569 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "top").toInt32();
570 SwTwips nFirstParaHeight
571 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[1]/infos/bounds", "height")
573 SwTwips nFirstParaMiddle
= nFirstParaTop
+ nFirstParaHeight
/ 2;
574 SwTwips nFirstParaBottom
= nFirstParaTop
+ nFirstParaHeight
;
575 // Without the accompanying fix in place, this test would have failed: the lower half (vertical)
576 // range was 2273 -> 2835, the good vertical position is 2730, the bad one was 1830.
577 CPPUNIT_ASSERT_GREATER(nFirstParaMiddle
, rCharRect
.Top());
578 CPPUNIT_ASSERT_LESS(nFirstParaBottom
, rCharRect
.Top());
580 // Save initial cursor position.
581 SwPosition aCellStart
= *pWrtShell
->GetCursor()->Start();
583 // Test that pressing "up" at the start of the cell goes to the next character position.
584 SwNodeOffset nNodeIndex
= pWrtShell
->GetCursor()->Start()->GetNodeIndex();
585 sal_Int32 nIndex
= pWrtShell
->GetCursor()->Start()->GetContentIndex();
586 KeyEvent
aKeyEvent(0, KEY_UP
);
587 SwEditWin
& rEditWin
= pShell
->GetView()->GetEditWin();
588 rEditWin
.KeyInput(aKeyEvent
);
589 Scheduler::ProcessEventsToIdle();
590 // Without the accompanying fix in place, this test would have failed: "up" was interpreted as
591 // logical "left", which does nothing if you're at the start of the text anyway.
592 CPPUNIT_ASSERT_EQUAL(nIndex
+ 1, pWrtShell
->GetCursor()->Start()->GetContentIndex());
594 // Test that pressing "right" goes to the next paragraph (logical "down").
595 sal_Int32 nContentIndex
= pWrtShell
->GetCursor()->Start()->GetContentIndex();
596 aKeyEvent
= KeyEvent(0, KEY_RIGHT
);
597 rEditWin
.KeyInput(aKeyEvent
);
598 Scheduler::ProcessEventsToIdle();
599 // Without the accompanying fix in place, this test would have failed: the cursor went to the
600 // paragraph after the table.
601 CPPUNIT_ASSERT_EQUAL(nNodeIndex
+ 1, pWrtShell
->GetCursor()->Start()->GetNodeIndex());
603 // Test that we have the correct character index after traveling to the next paragraph.
604 // Without the accompanying fix in place, this test would have failed: char position was 5, i.e.
605 // the cursor jumped to the end of the paragraph for no reason.
606 CPPUNIT_ASSERT_EQUAL(nContentIndex
, pWrtShell
->GetCursor()->Start()->GetContentIndex());
608 // Test that clicking "below" the second paragraph positions the cursor at the start of the
610 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
611 SwPosition
aPosition(aCellStart
);
612 SwTwips nSecondParaLeft
613 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "left")
615 SwTwips nSecondParaWidth
616 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "width")
618 SwTwips nSecondParaTop
619 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "top").toInt32();
620 SwTwips nSecondParaHeight
621 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/txt[2]/infos/bounds", "height")
624 aPoint
.setX(nSecondParaLeft
+ nSecondParaWidth
/ 2);
625 aPoint
.setY(nSecondParaTop
+ nSecondParaHeight
- 100);
626 SwCursorMoveState
aState(CursorMoveState::NONE
);
627 pLayout
->GetModelPositionForViewPoint(&aPosition
, aPoint
, &aState
);
628 CPPUNIT_ASSERT_EQUAL(aCellStart
.GetNodeIndex() + 1, aPosition
.GetNodeIndex());
629 // Without the accompanying fix in place, this test would have failed: character position was 5,
630 // i.e. cursor was at the end of the paragraph.
631 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), aPosition
.GetContentIndex());
633 // Test that the selection rectangles are inside the cell frame if we select all the cell
636 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/infos/bounds", "left").toInt32();
638 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/infos/bounds", "width").toInt32();
640 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/infos/bounds", "top").toInt32();
642 = getXPath(pXmlDoc
, "/root/page/body/tab/row/cell[1]/infos/bounds", "height").toInt32();
643 SwRect
aCellRect(Point(nCellLeft
, nCellTop
), Size(nCellWidth
, nCellHeight
));
645 SwShellCursor
* pShellCursor
= pWrtShell
->getShellCursor(/*bBlock=*/false);
646 CPPUNIT_ASSERT(!pShellCursor
->empty());
647 // Without the accompanying fix in place, this test would have failed with:
648 // selection rectangle 269x2573@(1970,2172) is not inside cell rectangle 3207x1134@(1593,1701)
649 // i.e. the selection went past the bottom border of the cell frame.
650 for (const auto& rRect
: *pShellCursor
)
652 std::stringstream ss
;
653 ss
<< "selection rectangle " << rRect
<< " is not inside cell rectangle " << aCellRect
;
654 CPPUNIT_ASSERT_MESSAGE(ss
.str(), aCellRect
.Contains(rRect
));
657 // Make sure that the correct rectangle gets repainted on scroll.
658 SwFrame
* pPageFrame
= pLayout
->GetLower();
659 CPPUNIT_ASSERT(pPageFrame
->IsPageFrame());
661 SwFrame
* pBodyFrame
= pPageFrame
->GetLower();
662 CPPUNIT_ASSERT(pBodyFrame
->IsBodyFrame());
664 SwFrame
* pTabFrame
= pBodyFrame
->GetLower();
665 CPPUNIT_ASSERT(pTabFrame
->IsTabFrame());
667 SwFrame
* pRowFrame
= pTabFrame
->GetLower();
668 CPPUNIT_ASSERT(pRowFrame
->IsRowFrame());
670 SwFrame
* pCellFrame
= pRowFrame
->GetLower();
671 CPPUNIT_ASSERT(pCellFrame
->IsCellFrame());
673 SwFrame
* pFrame
= pCellFrame
->GetLower();
674 CPPUNIT_ASSERT(pFrame
->IsTextFrame());
676 SwTextFrame
* pTextFrame
= static_cast<SwTextFrame
*>(pFrame
);
677 pTextFrame
->SwapWidthAndHeight();
678 // Mimic what normally SwTextFrame::PaintSwFrame() does:
679 SwRect
aRect(4207, 2273, 269, 572);
680 pTextFrame
->SwitchVerticalToHorizontal(aRect
);
681 // Without the accompanying fix in place, this test would have failed with:
682 // Expected: 572x269@(1691,4217)
683 // Actual : 572x269@(2263,4217)
684 // i.e. the paint rectangle position was incorrect, text was not painted on scrolling up.
685 CPPUNIT_ASSERT_EQUAL(SwRect(1691, 4217, 572, 269), aRect
);
689 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf123898
)
691 createSwDoc("tdf123898.odt");
693 // Make sure spellchecker has done its job already
694 Scheduler::ProcessEventsToIdle();
696 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
697 // Make sure that the arrow on the left is not there (the first portion's type is
698 // PortionType::Arrow if it's there)
700 "/root/page/body/txt/anchored/fly/txt/SwParaPortion/SwLineLayout[1]/child::*[1]",
701 "type", "PortionType::Para");
704 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf123651
)
706 createSwDoc("tdf123651.docx");
707 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
708 // Without the accompanying fix in place, this test would have failed with 'Expected: 7639;
709 // Actual: 12926'. The shape was below the second "Lorem ipsum" text, not above it.
710 const sal_Int32 nTopValue
711 = getXPath(pXmlDoc
, "//anchored/SwAnchoredDrawObject/bounds", "top").toInt32();
712 CPPUNIT_ASSERT_DOUBLES_EQUAL(7639, nTopValue
, 10);
715 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf116501
)
717 //just care it doesn't freeze
718 createSwDoc("tdf116501.odt");
721 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf123163
)
723 //just care it doesn't assert
724 createSwDoc("tdf123163-1.docx");
727 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testAbi11870
)
729 //just care it doesn't assert
730 createSwDoc("abi11870-2.odt");
733 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf118719
)
735 // Insert a page break.
737 SwDoc
* pDoc
= getSwDoc();
738 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
740 // Enable hide whitespace mode.
741 SwViewOption
aViewOptions(*pWrtShell
->GetViewOptions());
742 aViewOptions
.SetHideWhitespaceMode(true);
743 pWrtShell
->ApplyViewOptions(aViewOptions
);
745 pWrtShell
->Insert("first");
746 pWrtShell
->InsertPageBreak();
747 pWrtShell
->Insert("second");
749 // Without the accompanying fix in place, this test would have failed, as the height of the
750 // first page was 15840 twips, instead of the much smaller 276.
751 sal_Int32 nOther
= parseDump("/root/page[1]/infos/bounds", "height").toInt32();
752 sal_Int32 nLast
= parseDump("/root/page[2]/infos/bounds", "height").toInt32();
753 CPPUNIT_ASSERT_GREATER(nOther
, nLast
);
756 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTabOverMargin
)
758 createSwDoc("tab-over-margin.odt");
759 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
761 // 2nd paragraph has a tab over the right margin, and with the TabOverMargin compat option,
762 // there is enough space to have all content in a single line.
763 // Without the accompanying fix in place, this test would have failed, there were 2 lines.
764 assertXPath(pXmlDoc
, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout", 1);
767 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testImageComment
)
769 // Load a document that has "aaa" in it, then a commented image (4th char is the as-char image,
770 // 5th char is the comment anchor).
771 createSwDoc("image-comment.odt");
772 SwDoc
* pDoc
= getSwDoc();
773 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
775 // Look up a layout position which is on the right of the image.
776 SwRootFrame
* pRoot
= pWrtShell
->GetLayout();
777 CPPUNIT_ASSERT(pRoot
->GetLower()->IsPageFrame());
778 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pRoot
->GetLower());
779 CPPUNIT_ASSERT(pPage
->GetLower()->IsBodyFrame());
780 SwBodyFrame
* pBody
= static_cast<SwBodyFrame
*>(pPage
->GetLower());
781 CPPUNIT_ASSERT(pBody
->GetLower()->IsTextFrame());
782 SwTextFrame
* pTextFrame
= static_cast<SwTextFrame
*>(pBody
->GetLower());
783 CPPUNIT_ASSERT(pTextFrame
->GetDrawObjs());
784 SwSortedObjs
& rDrawObjs
= *pTextFrame
->GetDrawObjs();
785 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rDrawObjs
.size());
786 SwAnchoredObject
* pDrawObj
= rDrawObjs
[0];
787 const SwRect
& rDrawObjRect
= pDrawObj
->GetObjRect();
788 Point aPoint
= rDrawObjRect
.Center();
789 aPoint
.setX(aPoint
.getX() + rDrawObjRect
.Width() / 2);
791 // Ask for the doc model pos of this layout point.
792 SwPosition
aPosition(*pTextFrame
->GetTextNodeForFirstText());
793 pTextFrame
->GetModelPositionForViewPoint(&aPosition
, aPoint
);
795 // Without the accompanying fix in place, this test would have failed with:
798 // i.e. the cursor got positioned between the image and its comment, so typing extended the
799 // comment, instead of adding content after the commented image.
800 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(5), aPosition
.GetContentIndex());
803 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testScriptField
)
805 // Test clicking script field inside table ( tdf#141079 )
806 createSwDoc("tdf141079.odt");
807 SwDoc
* pDoc
= getSwDoc();
808 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
810 // Look up layout position which is the first cell in the table
811 SwRootFrame
* pRoot
= pWrtShell
->GetLayout();
812 CPPUNIT_ASSERT(pRoot
->GetLower()->IsPageFrame());
813 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pRoot
->GetLower());
814 CPPUNIT_ASSERT(pPage
->GetLower()->IsBodyFrame());
815 SwBodyFrame
* pBody
= static_cast<SwBodyFrame
*>(pPage
->GetLower());
816 CPPUNIT_ASSERT(pBody
->GetLower()->IsTextFrame());
817 SwTextFrame
* pTextFrame
= static_cast<SwTextFrame
*>(pBody
->GetLower());
818 CPPUNIT_ASSERT(pTextFrame
->GetNext()->IsTabFrame());
819 SwFrame
* pTable
= pTextFrame
->GetNext();
820 SwFrame
* pRow1
= pTable
->GetLower();
821 CPPUNIT_ASSERT(pRow1
->GetLower()->IsCellFrame());
822 SwFrame
* pCell1
= pRow1
->GetLower();
823 CPPUNIT_ASSERT(pCell1
->GetLower()->IsTextFrame());
824 SwTextFrame
* pCellTextFrame
= static_cast<SwTextFrame
*>(pCell1
->GetLower());
825 const SwRect
& rCellRect
= pCell1
->getFrameArea();
826 Point aPoint
= rCellRect
.Center();
827 aPoint
.setX(aPoint
.getX() - rCellRect
.Width() / 2);
829 // Ask for the doc model pos of this layout point.
830 SwPosition
aPosition(*pCellTextFrame
->GetTextNodeForFirstText());
831 pCellTextFrame
->GetModelPositionForViewPoint(&aPosition
, aPoint
);
833 // Position was 1 without the fix from tdf#141079
834 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), aPosition
.GetContentIndex());
837 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testCommentCursorPosition
)
839 // Load a document that has "aaa" in it, followed by three comments.
840 createSwDoc("endOfLineComments.odt");
841 SwDoc
* pDoc
= getSwDoc();
842 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
844 SwRootFrame
* pRoot
= pWrtShell
->GetLayout();
845 CPPUNIT_ASSERT(pRoot
->GetLower()->IsPageFrame());
846 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pRoot
->GetLower());
847 CPPUNIT_ASSERT(pPage
->GetLower()->IsBodyFrame());
848 SwBodyFrame
* pBody
= static_cast<SwBodyFrame
*>(pPage
->GetLower());
849 CPPUNIT_ASSERT(pBody
->GetLower()->IsTextFrame());
850 SwTextFrame
* pTextFrame
= static_cast<SwTextFrame
*>(pBody
->GetLower());
852 // Set a point in the whitespace past the end of the first line.
853 Point aPoint
= pWrtShell
->getShellCursor(false)->GetSttPos();
854 aPoint
.setX(aPoint
.getX() + 10000);
856 // Ask for the doc model pos of this layout point.
857 SwPosition
aPosition(*pTextFrame
->GetTextNodeForFirstText());
858 pTextFrame
->GetModelPositionForViewPoint(&aPosition
, aPoint
);
860 // Without the accompanying fix in place, this test would have failed with:
863 // i.e. the cursor got positioned before the comments,
864 // so typing extended the first comment instead of adding content after the comments.
865 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(6), aPosition
.GetContentIndex());
866 // The second line is also important, but can't be auto-tested
867 // since the failing situation depends on GetViewWidth which is zero in the headless tests.
868 // bb<comment>| - the cursor should move behind the |, not before it.
871 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testCombiningCharacterCursorPosition
)
873 // Load a document that has "a" in it, followed by a combining acute in a separate rext span
874 createSwDoc("tdf138592-a-acute.fodt");
875 SwDoc
* pDoc
= getSwDoc();
876 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
878 SwRootFrame
* pRoot
= pWrtShell
->GetLayout();
879 CPPUNIT_ASSERT(pRoot
->GetLower()->IsPageFrame());
880 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pRoot
->GetLower());
881 CPPUNIT_ASSERT(pPage
->GetLower()->IsBodyFrame());
882 SwBodyFrame
* pBody
= static_cast<SwBodyFrame
*>(pPage
->GetLower());
883 CPPUNIT_ASSERT(pBody
->GetLower()->IsTextFrame());
884 SwTextFrame
* pTextFrame
= static_cast<SwTextFrame
*>(pBody
->GetLower());
886 // Set a point in the whitespace past the end of the first line.
887 Point aPoint
= pWrtShell
->getShellCursor(false)->GetSttPos();
888 aPoint
.AdjustX(10000);
890 // Ask for the doc model pos of this layout point.
891 SwPosition
aPosition(*pTextFrame
->GetTextNodeForFirstText());
892 pTextFrame
->GetModelPositionForViewPoint(&aPosition
, aPoint
);
894 // Without the accompanying fix in place, this test would have failed with:
897 // i.e. the cursor got positioned before the acute, so typing shifted the acute (applying it
898 // to newly typed characters) instead of adding content after it.
899 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aPosition
.GetContentIndex());
902 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf64222
)
904 createSwDoc("tdf64222.docx");
905 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
907 "/root/page/body/txt[2]/SwParaPortion/SwLineLayout/"
908 "child::*[@type='PortionType::Number']/SwFont",
912 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf113014
)
914 createSwDoc("tdf113014.fodt");
915 SwDoc
* pDoc
= getSwDoc();
916 SwDocShell
* pShell
= pDoc
->GetDocShell();
918 // Dump the rendering of the first page as an XML file.
919 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
920 MetafileXmlDump dumper
;
921 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
922 CPPUNIT_ASSERT(pXmlDoc
);
924 // This failed, if numbering of cell A1 is missing
925 // (A1: left indent: 3 cm, first line indent: -3 cm
926 // A2: left indent: 0 cm, first line indent: 0 cm)
927 assertXPathContent(pXmlDoc
, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", "1.");
928 assertXPathContent(pXmlDoc
, "/metafile/push[1]/push[1]/push[1]/textarray[3]/text", "2.");
929 assertXPathContent(pXmlDoc
, "/metafile/push[1]/push[1]/push[1]/textarray[5]/text", "3.");
932 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf130218
)
934 createSwDoc("tdf130218.fodt");
935 SwDoc
* pDoc
= getSwDoc();
936 SwDocShell
* pShell
= pDoc
->GetDocShell();
938 // Dump the rendering of the first page as an XML file.
939 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
940 MetafileXmlDump dumper
;
942 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
943 CPPUNIT_ASSERT(pXmlDoc
);
945 // This failed, if hanging first line was hidden
946 assertXPathContent(pXmlDoc
, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", "Text");
949 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf127235
)
951 createSwDoc("tdf127235.odt");
952 SwDoc
* pDoc
= getSwDoc();
953 // This resulted in a layout loop.
954 pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
957 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf138039
)
959 createSwDoc("tdf138039.odt");
961 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
964 assertXPath(pXmlDoc
, "/root/page", 3);
965 // table on first page
966 assertXPath(pXmlDoc
, "/root/page[1]/body/tab", 1);
967 assertXPath(pXmlDoc
, "/root/page[1]/body/txt", 0);
968 // paragraph with large fly on second page
969 assertXPath(pXmlDoc
, "/root/page[2]/body/tab", 0);
970 assertXPath(pXmlDoc
, "/root/page[2]/body/txt", 1);
971 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly", 1);
972 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17915");
973 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
975 // paragraph on third page
976 assertXPath(pXmlDoc
, "/root/page[3]/body/tab", 0);
977 assertXPath(pXmlDoc
, "/root/page[3]/body/txt", 1);
978 assertXPath(pXmlDoc
, "/root/page[3]/body/txt[1]/anchored", 0);
981 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf134298
)
983 createSwDoc("tdf134298.ott");
985 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
988 assertXPath(pXmlDoc
, "/root/page", 2);
989 // table and first para on first page
990 assertXPath(pXmlDoc
, "/root/page[1]/body/tab", 1);
991 assertXPath(pXmlDoc
, "/root/page[1]/body/txt", 1);
992 assertXPath(pXmlDoc
, "/root/page[1]/body/txt[1]/anchored", 0);
993 // paragraph with large fly on second page
994 assertXPath(pXmlDoc
, "/root/page[2]/body/tab", 0);
995 assertXPath(pXmlDoc
, "/root/page[2]/body/txt", 1);
996 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly", 1);
997 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17897");
998 assertXPath(pXmlDoc
, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
1002 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testShapeAllowOverlap
)
1004 // Need to find out why this fails on macOS and why this is unstable on Windows.
1005 #if !defined(MACOSX) && !defined(_WIN32)
1006 // Create an empty document with two, intentionally overlapping shapes.
1007 // Set their AllowOverlap property to false.
1009 uno::Reference
<lang::XMultiServiceFactory
> xDocument(mxComponent
, uno::UNO_QUERY
);
1010 awt::Point
aPoint(1000, 1000);
1011 awt::Size
aSize(2000, 2000);
1012 uno::Reference
<drawing::XShape
> xShape(
1013 xDocument
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1014 xShape
->setPosition(aPoint
);
1015 xShape
->setSize(aSize
);
1016 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xDocument
, uno::UNO_QUERY
);
1017 uno::Reference
<beans::XPropertySet
> xShapeProperties(xShape
, uno::UNO_QUERY
);
1018 xShapeProperties
->setPropertyValue("AllowOverlap", uno::Any(false));
1019 xShapeProperties
->setPropertyValue("AnchorType",
1020 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1021 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1023 aPoint
= awt::Point(2000, 2000);
1024 xShape
.set(xDocument
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1025 xShape
->setPosition(aPoint
);
1026 xShape
->setSize(aSize
);
1027 xShapeProperties
.set(xShape
, uno::UNO_QUERY
);
1028 xShapeProperties
->setPropertyValue("AllowOverlap", uno::Any(false));
1029 xShapeProperties
->setPropertyValue("AnchorType",
1030 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1031 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1033 // Now verify that the rectangle of the anchored objects don't overlap.
1034 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1035 CPPUNIT_ASSERT(pTextDoc
);
1036 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1037 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
1038 SwFrame
* pPageFrame
= pLayout
->GetLower();
1039 SwFrame
* pBodyFrame
= pPageFrame
->GetLower();
1040 SwFrame
* pTextFrame
= pBodyFrame
->GetLower();
1041 CPPUNIT_ASSERT(pTextFrame
->GetDrawObjs());
1042 SwSortedObjs
& rObjs
= *pTextFrame
->GetDrawObjs();
1043 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs
.size());
1044 SwAnchoredObject
* pFirst
= rObjs
[0];
1045 SwAnchoredObject
* pSecond
= rObjs
[1];
1046 // Without the accompanying fix in place, this test would have failed: the layout dump was
1047 // <bounds left="1984" top="1984" width="1137" height="1137"/>
1048 // <bounds left="2551" top="2551" width="1137" height="1137"/>
1049 // so there was a clear vertical overlap. (Allow for 1px tolerance.)
1050 OString aMessage
= "Unexpected overlap: first shape's bottom is "
1051 + OString::number(pFirst
->GetObjRect().Bottom()) + ", second shape's top is "
1052 + OString::number(pSecond
->GetObjRect().Top());
1053 CPPUNIT_ASSERT_MESSAGE(aMessage
.getStr(),
1054 std::abs(pFirst
->GetObjRect().Bottom() - pSecond
->GetObjRect().Top())
1059 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testShapeAllowOverlapWrap
)
1061 // Create an empty document with two, intentionally overlapping shapes.
1062 // Set their AllowOverlap property to false and their wrap to through.
1064 uno::Reference
<lang::XMultiServiceFactory
> xDocument(mxComponent
, uno::UNO_QUERY
);
1065 awt::Point
aPoint(1000, 1000);
1066 awt::Size
aSize(2000, 2000);
1067 uno::Reference
<drawing::XShape
> xShape(
1068 xDocument
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1069 xShape
->setPosition(aPoint
);
1070 xShape
->setSize(aSize
);
1071 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xDocument
, uno::UNO_QUERY
);
1072 uno::Reference
<beans::XPropertySet
> xShapeProperties(xShape
, uno::UNO_QUERY
);
1073 xShapeProperties
->setPropertyValue("AllowOverlap", uno::Any(false));
1074 xShapeProperties
->setPropertyValue("AnchorType",
1075 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1076 xShapeProperties
->setPropertyValue("Surround", uno::Any(text::WrapTextMode_THROUGH
));
1077 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1079 aPoint
= awt::Point(2000, 2000);
1080 xShape
.set(xDocument
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1081 xShape
->setPosition(aPoint
);
1082 xShape
->setSize(aSize
);
1083 xShapeProperties
.set(xShape
, uno::UNO_QUERY
);
1084 xShapeProperties
->setPropertyValue("AllowOverlap", uno::Any(false));
1085 xShapeProperties
->setPropertyValue("AnchorType",
1086 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1087 xShapeProperties
->setPropertyValue("Surround", uno::Any(text::WrapTextMode_THROUGH
));
1088 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1090 // Now verify that the rectangle of the anchored objects do overlap.
1091 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1092 CPPUNIT_ASSERT(pTextDoc
);
1093 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1094 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
1095 SwFrame
* pPageFrame
= pLayout
->GetLower();
1096 SwFrame
* pBodyFrame
= pPageFrame
->GetLower();
1097 SwFrame
* pTextFrame
= pBodyFrame
->GetLower();
1098 CPPUNIT_ASSERT(pTextFrame
->GetDrawObjs());
1099 SwSortedObjs
& rObjs
= *pTextFrame
->GetDrawObjs();
1100 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rObjs
.size());
1101 SwAnchoredObject
* pFirst
= rObjs
[0];
1102 SwAnchoredObject
* pSecond
= rObjs
[1];
1103 // Without the accompanying fix in place, this test would have failed: AllowOverlap=no had
1104 // priority over Surround=through (which is bad for Word compat).
1105 CPPUNIT_ASSERT(pSecond
->GetObjRect().Overlaps(pFirst
->GetObjRect()));
1108 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf124600
)
1110 createSwDoc("tdf124600.docx");
1111 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1113 // Without the accompanying fix in place, this test would have failed with:
1116 // i.e. the last line in the body text had 2 lines, while it should have 1, as Word does (as the
1117 // fly frame does not intersect with the print area of the paragraph.)
1118 assertXPath(pXmlDoc
, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout", 1);
1121 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf124601
)
1123 // This is a testcase for the ContinuousEndnotes compat flag.
1124 // The document has 2 pages, the endnote anchor is on the first page.
1125 // The endnote should be on the 2nd page together with the last page content.
1126 createSwDoc("tdf124601.doc");
1127 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1129 // Without the accompanying fix in place, this test would have failed with:
1132 // i.e. there was a separate endnote page, even when the ContinuousEndnotes compat option was
1134 assertXPath(pXmlDoc
, "/root/page", 2);
1135 assertXPath(pXmlDoc
, "/root/page[2]/ftncont", 1);
1138 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf124601b
)
1140 // Table has an image, which is anchored in the first row, but its vertical position is large
1141 // enough to be rendered in the second row.
1142 // The shape has layoutInCell=1, so should match what Word does here.
1143 // Also the horizontal position should be in the last column, even if the anchor is in the
1144 // last-but-one column.
1145 createSwDoc("tdf124601b.doc");
1146 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1148 sal_Int32 nFlyTop
= getXPath(pXmlDoc
, "//anchored/fly/infos/bounds", "top").toInt32();
1149 sal_Int32 nFlyLeft
= getXPath(pXmlDoc
, "//anchored/fly/infos/bounds", "left").toInt32();
1151 = nFlyLeft
+ getXPath(pXmlDoc
, "//anchored/fly/infos/bounds", "width").toInt32();
1152 sal_Int32 nSecondRowTop
= getXPath(pXmlDoc
, "//tab/row[2]/infos/bounds", "top").toInt32();
1153 sal_Int32 nLastCellLeft
1154 = getXPath(pXmlDoc
, "//tab/row[1]/cell[5]/infos/bounds", "left").toInt32();
1155 sal_Int32 nLastCellRight
1156 = nLastCellLeft
+ getXPath(pXmlDoc
, "//tab/row[1]/cell[5]/infos/bounds", "width").toInt32();
1157 // Without the accompanying fix in place, this test would have failed with:
1158 // - Expected greater than: 3736
1160 // i.e. the image was still inside the first row.
1161 CPPUNIT_ASSERT_GREATER(nSecondRowTop
, nFlyTop
);
1163 // Without the accompanying fix in place, this test would have failed with:
1164 // - Expected greater than: 9640
1166 // i.e. the right edge of the image was not within the bounds of the last column, the right edge
1167 // was in the last-but-one column.
1168 CPPUNIT_ASSERT_GREATER(nLastCellLeft
, nFlyRight
);
1169 CPPUNIT_ASSERT_LESS(nLastCellRight
, nFlyRight
);
1172 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf124770
)
1174 // Enable content over margin.
1176 SwDoc
* pDoc
= getSwDoc();
1177 pDoc
->getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN
, true);
1180 SwPageDesc
& rPageDesc
= pDoc
->GetPageDesc(0);
1181 SwFrameFormat
& rPageFormat
= rPageDesc
.GetMaster();
1182 const SwAttrSet
& rPageSet
= rPageFormat
.GetAttrSet();
1183 SwFormatFrameSize aPageSize
= rPageSet
.GetFrameSize();
1184 aPageSize
.SetWidth(3703);
1185 rPageFormat
.SetFormatAttr(aPageSize
);
1187 // Set left and right margin.
1188 SvxLRSpaceItem aLRSpace
= rPageSet
.GetLRSpace();
1189 aLRSpace
.SetLeft(1418);
1190 aLRSpace
.SetRight(1418);
1191 rPageFormat
.SetFormatAttr(aLRSpace
);
1192 pDoc
->ChgPageDesc(0, rPageDesc
);
1194 // Set font to italic 20pt Liberation Serif.
1195 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1196 SfxItemSet
aTextSet(pWrtShell
->GetView().GetPool(),
1197 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1>);
1198 SvxFontItem
aFont(RES_CHRATR_FONT
);
1199 aFont
.SetFamilyName("Liberation Serif");
1200 aTextSet
.Put(aFont
);
1201 SvxFontHeightItem
aHeight(400, 100, RES_CHRATR_FONTSIZE
);
1202 aTextSet
.Put(aHeight
);
1203 SvxPostureItem
aItalic(ITALIC_NORMAL
, RES_CHRATR_POSTURE
);
1204 aTextSet
.Put(aItalic
);
1205 pWrtShell
->SetAttrSet(aTextSet
);
1208 pWrtShell
->Insert2("HHH");
1210 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1211 // Without the accompanying fix in place, this test would have failed with:
1214 // i.e. the italic string was broken into 2 lines, while Word kept it in a single line.
1215 assertXPath(pXmlDoc
, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout", 1);
1218 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testContinuousEndnotesInsertPageAtStart
)
1220 // Create a new document with CONTINUOUS_ENDNOTES enabled.
1222 SwDoc
* pDoc
= getSwDoc();
1223 pDoc
->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES
, true);
1225 // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
1227 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1228 pWrtShell
->InsertPageBreak();
1229 pWrtShell
->InsertFootnote("endnote", /*bEndNote=*/true, /*bEdit=*/false);
1231 // Add a new page at the start of the document.
1232 pWrtShell
->SttEndDoc(/*bStart=*/true);
1233 pWrtShell
->InsertPageBreak();
1235 // Make sure that the endnote is moved from the 2nd page to the 3rd one.
1236 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1237 assertXPath(pXmlDoc
, "/root/page", 3);
1238 // Without the accompanying fix in place, this test would have failed with:
1241 // i.e. the footnote container remained on page 2.
1242 assertXPath(pXmlDoc
, "/root/page[3]/ftncont", 1);
1245 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testContinuousEndnotesDeletePageAtStart
)
1247 // Create a new document with CONTINUOUS_ENDNOTES enabled.
1249 SwDoc
* pDoc
= getSwDoc();
1250 pDoc
->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES
, true);
1252 // Insert a second page, and an endnote on the 2nd page (both the anchor and the endnote is on
1254 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1255 pWrtShell
->InsertPageBreak();
1256 pWrtShell
->InsertFootnote("endnote", /*bEndNote=*/true, /*bEdit=*/false);
1258 // Remove the empty page at the start of the document.
1259 pWrtShell
->SttEndDoc(/*bStart=*/true);
1260 pWrtShell
->DelRight();
1262 // Make sure that the endnote is moved from the 2nd page to the 1st one.
1263 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1264 // Without the accompanying fix in place, this test would have failed with:
1267 // i.e. the endnote remained on an (otherwise) empty 2nd page.
1268 assertXPath(pXmlDoc
, "/root/page", 1);
1269 assertXPath(pXmlDoc
, "/root/page[1]/ftncont", 1);
1272 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf128399
)
1274 createSwDoc("tdf128399.docx");
1275 SwDoc
* pDoc
= getSwDoc();
1276 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
1277 SwFrame
* pPage
= pLayout
->GetLower();
1278 SwFrame
* pBody
= pPage
->GetLower();
1279 SwFrame
* pTable
= pBody
->GetLower();
1280 SwFrame
* pRow1
= pTable
->GetLower();
1281 SwFrame
* pRow2
= pRow1
->GetNext();
1282 const SwRect
& rRow2Rect
= pRow2
->getFrameArea();
1283 Point aPoint
= rRow2Rect
.Center();
1285 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1286 SwPosition aPosition
= *pWrtShell
->GetCursor()->Start();
1287 SwPosition
aFirstRow(aPosition
);
1288 SwCursorMoveState
aState(CursorMoveState::NONE
);
1289 pLayout
->GetModelPositionForViewPoint(&aPosition
, aPoint
, &aState
);
1290 // Second row is +3: end node, start node and the first text node in the 2nd row.
1291 SwNodeOffset nExpected
= aFirstRow
.GetNodeIndex() + 3;
1293 // Without the accompanying fix in place, this test would have failed with:
1296 // i.e. clicking on the center of the 2nd row placed the cursor in the 1st row.
1297 CPPUNIT_ASSERT_EQUAL(nExpected
, aPosition
.GetNodeIndex());
1300 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf156724
)
1302 discardDumpedLayout();
1303 if (mxComponent
.is())
1304 mxComponent
->dispose();
1306 OUString
const url(createFileURL(u
"fdo56797-2-min.odt"));
1308 // note: must set Hidden property, so that SfxFrameViewWindow_Impl::Resize()
1309 // does *not* forward initial VCL Window Resize and thereby triggers a
1310 // layout which does not happen on soffice --convert-to pdf.
1311 std::vector
<beans::PropertyValue
> aFilterOptions
= {
1312 { beans::PropertyValue("Hidden", -1, uno::Any(true), beans::PropertyState_DIRECT_VALUE
) },
1315 // inline the loading because currently properties can't be passed...
1316 mxComponent
= loadFromDesktop(url
, "com.sun.star.text.TextDocument",
1317 comphelper::containerToSequence(aFilterOptions
));
1318 save("writer_pdf_Export");
1320 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1321 // both pages have a tab frame and one footnote
1322 assertXPath(pXmlDoc
, "/root/page[1]/body/tab", 1);
1323 assertXPath(pXmlDoc
, "/root/page[1]/ftncont", 1);
1324 assertXPath(pXmlDoc
, "/root/page[1]/ftncont/ftn", 1);
1325 assertXPath(pXmlDoc
, "/root/page[2]/body/tab", 1);
1326 assertXPath(pXmlDoc
, "/root/page[2]/ftncont", 1);
1327 assertXPath(pXmlDoc
, "/root/page[2]/ftncont/ftn", 1);
1328 assertXPath(pXmlDoc
, "/root/page", 2);
1331 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf156725
)
1333 createSwDoc("tdf156725.fodt");
1335 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1336 assertXPath(pXmlDoc
, "/root/page", 2);
1337 // the fly has 2 columns, the section in it has 2 columns, and is split
1338 // across the fly columns => 4 columns with 1 text frame each
1339 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly/column", 2);
1340 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly/column[1]/body/section/column", 2);
1341 assertXPath(pXmlDoc
,
1342 "/root/page[2]/body/txt/anchored/fly/column[1]/body/section/column[1]/body/txt", 1);
1343 assertXPath(pXmlDoc
,
1344 "/root/page[2]/body/txt/anchored/fly/column[1]/body/section/column[2]/body/txt", 1);
1345 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly/column[2]/body/section/column", 2);
1346 assertXPath(pXmlDoc
,
1347 "/root/page[2]/body/txt/anchored/fly/column[2]/body/section/column[1]/body/txt", 1);
1348 assertXPath(pXmlDoc
,
1349 "/root/page[2]/body/txt/anchored/fly/column[2]/body/section/column[2]/body/txt", 1);
1352 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf156419
)
1354 createSwDoc("linked_frames_section_bug.odt");
1356 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1357 assertXPath(pXmlDoc
, "/root/page", 2);
1358 // there are 2 flys on page 1, and 1 on page 2, all linked
1359 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[1]/section/column", 2);
1360 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[1]/section/column[1]/body/txt", 11);
1361 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[1]/section/column[2]/body/txt", 11);
1362 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[2]/section/column", 2);
1363 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[2]/section/column[1]/body/txt", 12);
1364 assertXPath(pXmlDoc
, "/root/page[1]/body/txt/anchored/fly[2]/section/column[2]/body/txt", 12);
1365 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly[1]/section/column", 2);
1366 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly[1]/section/column[1]/body/txt", 2);
1367 assertXPath(pXmlDoc
, "/root/page[2]/body/txt/anchored/fly[1]/section/column[2]/body/txt", 1);
1370 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf145826
)
1372 createSwDoc("tdf145826.odt");
1373 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1374 CPPUNIT_ASSERT(pXmlDoc
);
1376 assertXPath(pXmlDoc
, "/root/page/body/section/column", 2);
1378 // Without the fix in place, this test would have failed with
1381 assertXPath(pXmlDoc
, "/root/page/body/section/column[1]/ftncont", 1);
1382 assertXPath(pXmlDoc
, "/root/page/body/section/column[2]/ftncont", 1);
1383 assertXPath(pXmlDoc
, "/root/page/body/section/column[1]/ftncont/ftn", 3);
1384 assertXPath(pXmlDoc
, "/root/page/body/section/column[2]/ftncont/ftn", 3);
1387 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf105481
)
1389 createSwDoc("tdf105481.odt");
1390 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1391 CPPUNIT_ASSERT(pXmlDoc
);
1393 // Without the accompanying fix in place, this test would have failed
1394 // because the vertical position of the as-char shape object and the
1395 // as-char math object will be wrong (below/beyond the text frame's bottom).
1397 SwTwips nTxtTop
= getXPath(pXmlDoc
,
1398 "/root/page/anchored/fly/txt[2]"
1402 SwTwips nTxtBottom
= nTxtTop
1404 "/root/page/anchored/fly/txt[2]"
1409 SwTwips nFormula1Top
= getXPath(pXmlDoc
,
1410 "/root/page/anchored/fly/txt[2]"
1411 "/anchored/fly[1]/infos/bounds",
1414 SwTwips nFormula1Bottom
= nFormula1Top
1416 "/root/page/anchored/fly/txt[2]"
1417 "/anchored/fly[1]/infos/bounds",
1421 SwTwips nFormula2Top
= getXPath(pXmlDoc
,
1422 "/root/page/anchored/fly/txt[2]"
1423 "/anchored/fly[2]/infos/bounds",
1426 SwTwips nFormula2Bottom
= nFormula2Top
1428 "/root/page/anchored/fly/txt[2]"
1429 "/anchored/fly[2]/infos/bounds",
1433 // Ensure that the two formula positions are at least between top and bottom of the text frame.
1434 // The below two are satisfied even without the fix.
1435 CPPUNIT_ASSERT_GREATEREQUAL(nTxtTop
, nFormula1Top
);
1436 CPPUNIT_ASSERT_GREATEREQUAL(nTxtTop
, nFormula2Top
);
1438 // Without the accompanying fix in place, this test would have failed with:
1439 // - Expected less than or equal to : 14423
1441 // that is, the formula is below the text-frame's y bound.
1442 CPPUNIT_ASSERT_LESSEQUAL(nTxtBottom
, nFormula1Bottom
);
1443 // Similarly for formula # 2 :
1444 // - Expected less than or equal to : 14423
1446 // that is, the formula is below the text-frame's y bound.
1447 CPPUNIT_ASSERT_LESSEQUAL(nTxtBottom
, nFormula2Bottom
);
1450 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf117982
)
1452 createSwDoc("tdf117982.docx");
1453 SwDoc
* pDocument
= getSwDoc();
1454 SwDocShell
* pShell
= pDocument
->GetDocShell();
1455 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
1456 MetafileXmlDump dumper
;
1457 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
1458 assertXPathContent(pXmlDoc
, "/metafile/push[1]/push[1]/push[1]/textarray[1]/text", "FOO AAA");
1459 //The first cell must be "FOO AAA". If not, this means the first cell content not visible in
1460 //the source document.
1463 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf128959
)
1465 // no orphan/widow control in table cells
1466 createSwDoc("tdf128959.docx");
1467 SwDoc
* pDocument
= getSwDoc();
1468 CPPUNIT_ASSERT(pDocument
);
1469 discardDumpedLayout();
1470 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1472 // first two lines of the paragraph in the split table cell on the first page
1473 // (these lines were completely lost)
1475 pXmlDoc
, "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/SwParaPortion/SwLineLayout[1]",
1477 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue ");
1479 pXmlDoc
, "/root/page[1]/body/tab[1]/row[1]/cell[1]/txt[1]/SwParaPortion/SwLineLayout[2]",
1481 "massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit ");
1482 // last line of the paragraph in the split table cell on the second page
1483 assertXPath(pXmlDoc
,
1484 "/root/page[2]/body/tab[1]/row[1]/cell[1]/txt[1]/SwParaPortion/SwLineLayout[1]",
1485 "portion", "amet commodo magna eros quis urna.");
1487 // Also check that the widow control for the paragraph is not turned off:
1488 sw::TableFrameFormats
& rTableFormats
= *pDocument
->GetTableFrameFormats();
1489 SwFrameFormat
* pTableFormat
= rTableFormats
[0];
1490 SwTable
* pTable
= SwTable::FindTable(pTableFormat
);
1491 const SwTableBox
* pCell
= pTable
->GetTableBox("A1");
1492 const SwStartNode
* pStartNode
= pCell
->GetSttNd();
1493 SwNodeIndex
aNodeIndex(*pStartNode
);
1495 const SwTextNode
* pTextNode
= aNodeIndex
.GetNode().GetTextNode();
1496 // Without the accompanying fix in place, this test would have failed with:
1499 // i.e. the original fix only worked as the entire widow / orphan control was switched off.
1500 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(pTextNode
->GetSwAttrSet().GetWidows().GetValue()));
1503 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf121658
)
1505 uno::Reference
<linguistic2::XHyphenator
> xHyphenator
= LinguMgr::GetHyphenator();
1506 if (!xHyphenator
->hasLocale(lang::Locale("en", "US", OUString())))
1509 createSwDoc("tdf121658.odt");
1510 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1512 // Only 2 hyphenated words should appear in the document (in the lowercase words).
1513 // Uppercase words should not be hyphenated.
1514 assertXPath(pXmlDoc
, "//SwLineLayout/child::*[@type='PortionType::Hyphen']", 2);
1517 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf149420
)
1519 uno::Reference
<linguistic2::XHyphenator
> xHyphenator
= LinguMgr::GetHyphenator();
1520 if (!xHyphenator
->hasLocale(lang::Locale("en", "US", OUString())))
1523 createSwDoc("tdf149420.odt");
1524 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1526 // Only 3 hyphenated words should appear in the document (last paragraph
1527 // has got a 1 cm hyphenation zone, removing two hyphenations, which visible
1528 // in the second paragraph).
1529 assertXPath(pXmlDoc
, "//SwLineLayout/child::*[@type='PortionType::Hyphen']", 8);
1532 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf149324
)
1534 uno::Reference
<linguistic2::XHyphenator
> xHyphenator
= LinguMgr::GetHyphenator();
1535 if (!xHyphenator
->hasLocale(lang::Locale("en", "US", OUString())))
1538 createSwDoc("tdf149324.odt");
1539 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1541 // Only 3 hyphenated words should appear in the document (last paragraph
1542 // has got a 7-character word limit for hyphenation, removing the
1543 // hyphenation "ex-cept".
1544 assertXPath(pXmlDoc
, "//SwLineLayout/child::*[@type='PortionType::Hyphen']", 3);
1547 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf149248
)
1549 uno::Reference
<linguistic2::XHyphenator
> xHyphenator
= LinguMgr::GetHyphenator();
1550 if (!xHyphenator
->hasLocale(lang::Locale("en", "US", OUString())))
1553 createSwDoc("tdf149248.odt");
1554 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1556 // Only 1 hyphenated word should appear in the document (last word of the second
1557 // paragraph). Last word should not be hyphenated for the fourth paragraph
1558 // (the same paragraph, but with forbidden hyphenation of the last word).
1559 assertXPath(pXmlDoc
, "//SwLineLayout/child::*[@type='PortionType::Hyphen']", 1);
1562 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testWriterImageNoCapture
)
1564 createSwDoc("writer-image-no-capture.docx");
1565 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1566 CPPUNIT_ASSERT(pXmlDoc
);
1567 sal_Int32 nPageLeft
= getXPath(pXmlDoc
, "//page/infos/bounds", "left").toInt32();
1568 sal_Int32 nImageLeft
= getXPath(pXmlDoc
, "//anchored/fly/infos/bounds", "left").toInt32();
1569 // Without the accompanying fix in place, this test would have failed with:
1570 // - Expected less than: 284
1572 // i.e. the image position was modified to be inside the page frame ("captured"), even if Word
1573 // does not do that.
1574 CPPUNIT_ASSERT_LESS(nPageLeft
, nImageLeft
);
1577 static SwRect
lcl_getVisibleFlyObjRect(SwWrtShell
* pWrtShell
)
1579 SwRootFrame
* pRoot
= pWrtShell
->GetLayout();
1580 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pRoot
->GetLower());
1581 pPage
= static_cast<SwPageFrame
*>(pPage
->GetNext());
1582 pPage
= static_cast<SwPageFrame
*>(pPage
->GetNext());
1583 SwSortedObjs
* pDrawObjs
= pPage
->GetDrawObjs();
1584 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDrawObjs
->size());
1585 SwAnchoredObject
* pDrawObj
= (*pDrawObjs
)[0];
1586 CPPUNIT_ASSERT_EQUAL(OUString("Rahmen8"), pDrawObj
->GetFrameFormat().GetName());
1587 pPage
= static_cast<SwPageFrame
*>(pPage
->GetNext());
1588 pDrawObjs
= pPage
->GetDrawObjs();
1589 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDrawObjs
->size());
1590 pDrawObj
= (*pDrawObjs
)[0];
1591 CPPUNIT_ASSERT_EQUAL(OUString("Rahmen123"), pDrawObj
->GetFrameFormat().GetName());
1592 SwRect aFlyRect
= pDrawObj
->GetObjRect();
1593 CPPUNIT_ASSERT(pPage
->getFrameArea().Contains(aFlyRect
));
1597 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testStableAtPageAnchoredFlyPosition
)
1599 // this doc has two page-anchored frames: one tiny on page 3 and one large on page 4.
1600 // it also has a style:master-page named "StandardEntwurf", which contains some fields.
1601 // if you add a break to page 2, or append some text to page 4 (or just toggle display field names),
1602 // the page anchored frame on page 4 vanishes, as it is incorrectly moved out of the page bounds.
1603 createSwDoc("stable-at-page-anchored-fly-position.odt");
1604 SwDoc
* pDoc
= getSwDoc();
1605 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1607 // look up the layout position of the page-bound frame on page four
1608 SwRect aOrigRect
= lcl_getVisibleFlyObjRect(pWrtShell
);
1610 // append some text to the document to trigger bug / relayout
1611 pWrtShell
->SttEndDoc(false);
1612 pWrtShell
->Insert("foo");
1614 // get the current position of the frame on page four
1615 SwRect aRelayoutRect
= lcl_getVisibleFlyObjRect(pWrtShell
);
1617 // the anchored frame should not have moved
1618 CPPUNIT_ASSERT_EQUAL(aOrigRect
, aRelayoutRect
);
1621 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf134548
)
1623 createSwDoc("tdf134548.odt");
1624 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1626 // Second paragraph has two non zero width tabs in beginning of line
1628 OUString sNodeType
= parseDump(
1629 "/root/page/body/txt[2]/SwParaPortion/SwLineLayout/SwFixPortion[1]", "type");
1630 CPPUNIT_ASSERT_EQUAL(OUString("PortionType::TabLeft"), sNodeType
);
1632 = parseDump("/root/page/body/txt[2]/SwParaPortion/SwLineLayout/SwFixPortion[1]",
1635 CPPUNIT_ASSERT_GREATER(sal_Int32(0), nWidth
);
1638 OUString sNodeType
= parseDump(
1639 "/root/page/body/txt[2]/SwParaPortion/SwLineLayout/SwFixPortion[2]", "type");
1640 CPPUNIT_ASSERT_EQUAL(OUString("PortionType::TabLeft"), sNodeType
);
1642 = parseDump("/root/page/body/txt[2]/SwParaPortion/SwLineLayout/SwFixPortion[2]",
1645 CPPUNIT_ASSERT_GREATER(sal_Int32(0), nWidth
);
1649 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf124423
)
1651 createSwDoc("tdf124423.docx");
1652 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1653 sal_Int32 nFly1Width
1654 = getXPath(pXmlDoc
, "(//anchored/fly)[1]/infos/prtBounds", "width").toInt32();
1655 sal_Int32 nFly2Width
1656 = getXPath(pXmlDoc
, "(//anchored/fly)[2]/infos/prtBounds", "width").toInt32();
1657 sal_Int32 nPageWidth
= getXPath(pXmlDoc
, "//page/infos/prtBounds", "width").toInt32();
1658 CPPUNIT_ASSERT_EQUAL(nPageWidth
, nFly2Width
);
1659 CPPUNIT_ASSERT_LESS(nPageWidth
/ 2, nFly1Width
);
1661 createSwDoc("tdf124423.odt");
1662 pXmlDoc
= parseLayoutDump();
1663 nFly1Width
= getXPath(pXmlDoc
, "(//anchored/fly)[1]/infos/prtBounds", "width").toInt32();
1664 nFly2Width
= getXPath(pXmlDoc
, "(//anchored/fly)[2]/infos/prtBounds", "width").toInt32();
1665 nPageWidth
= getXPath(pXmlDoc
, "//page/infos/prtBounds", "width").toInt32();
1666 CPPUNIT_ASSERT_LESS(nPageWidth
/ 2, nFly2Width
);
1667 CPPUNIT_ASSERT_LESS(nPageWidth
/ 2, nFly1Width
);
1670 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf137185
)
1672 // First load the sample bugdoc
1673 createSwDoc("tdf137185.odt");
1674 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1675 CPPUNIT_ASSERT(pTextDoc
);
1676 // Get the doc shell
1677 SwDoc
* pDoc(pTextDoc
->GetDocShell()->GetDoc());
1679 // Get the DrawObject from page
1680 auto pModel
= pDoc
->getIDocumentDrawModelAccess().GetDrawModel();
1681 CPPUNIT_ASSERT(pModel
);
1682 auto pPage
= pModel
->GetPage(0);
1683 CPPUNIT_ASSERT(pPage
);
1684 auto pObj
= pPage
->GetObj(0);
1685 CPPUNIT_ASSERT(pObj
);
1687 // Get the format of the draw object
1688 auto pShape
= FindFrameFormat(pObj
);
1689 CPPUNIT_ASSERT(pShape
);
1691 // Check the text of the shape
1692 uno::Reference
<text::XText
> xTxt(getShape(1), uno::UNO_QUERY
);
1693 CPPUNIT_ASSERT_EQUAL(OUString("Align me!"), xTxt
->getText()->getString());
1695 // Add a textbox to the shape
1696 SwTextBoxHelper::create(pShape
, pShape
->FindRealSdrObject(), true);
1698 // Check if the text moved from the shape to the frame
1699 auto pFormat
= SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
1700 auto xTextFrame
= SwXTextFrame::CreateXTextFrame(*pFormat
->GetDoc(), pFormat
);
1702 CPPUNIT_ASSERT_EQUAL(OUString("Align me!"), xTextFrame
->getText()->getString());
1703 SdrTextObj
* pTextObj
= DynCastSdrTextObj(pObj
);
1704 CPPUNIT_ASSERT(pTextObj
);
1705 const auto& aOutStr
= pTextObj
->GetOutlinerParaObject()->GetTextObject();
1707 CPPUNIT_ASSERT(aOutStr
.GetText(0).isEmpty());
1708 // Before the patch it failed, because the text appeared 2 times on each other.
1711 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf138782
)
1713 createSwDoc("tdf138782.docx");
1714 auto pXml
= parseLayoutDump();
1715 CPPUNIT_ASSERT(pXml
);
1717 // Without the fix it failed because the 3rd shape was outside the page:
1718 // - Expected less than: 13327
1721 CPPUNIT_ASSERT_LESS(
1722 getXPath(pXml
, "/root/page/infos/bounds", "right").toInt32(),
1723 getXPath(pXml
, "/root/page/body/txt[8]/anchored/SwAnchoredDrawObject/bounds", "right")
1727 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf135035
)
1729 createSwDoc("tdf135035.docx");
1730 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1731 sal_Int32 nFly1Width
1732 = getXPath(pXmlDoc
, "(//anchored/fly)[1]/infos/prtBounds", "width").toInt32();
1733 sal_Int32 nFly2Width
1734 = getXPath(pXmlDoc
, "(//anchored/fly)[2]/infos/prtBounds", "width").toInt32();
1735 sal_Int32 nFly3Width
1736 = getXPath(pXmlDoc
, "(//anchored/fly)[3]/infos/prtBounds", "width").toInt32();
1737 sal_Int32 nParentWidth
= getXPath(pXmlDoc
, "(//txt)[1]/infos/prtBounds", "width").toInt32();
1738 CPPUNIT_ASSERT_EQUAL(nParentWidth
, nFly2Width
);
1739 CPPUNIT_ASSERT_EQUAL(nParentWidth
, nFly3Width
);
1740 CPPUNIT_ASSERT_LESS(nParentWidth
/ 2, nFly1Width
);
1742 createSwDoc("tdf135035.odt");
1743 pXmlDoc
= parseLayoutDump();
1744 nFly1Width
= getXPath(pXmlDoc
, "(//anchored/fly)[1]/infos/prtBounds", "width").toInt32();
1745 nFly2Width
= getXPath(pXmlDoc
, "(//anchored/fly)[2]/infos/prtBounds", "width").toInt32();
1746 nFly3Width
= getXPath(pXmlDoc
, "(//anchored/fly)[3]/infos/prtBounds", "width").toInt32();
1747 nParentWidth
= getXPath(pXmlDoc
, "(//txt)[1]/infos/prtBounds", "width").toInt32();
1748 CPPUNIT_ASSERT_LESS(nParentWidth
/ 2, nFly2Width
);
1749 CPPUNIT_ASSERT_LESS(nParentWidth
/ 2, nFly1Width
);
1750 CPPUNIT_ASSERT_GREATER(nParentWidth
, nFly3Width
);
1753 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf146704_EndnoteInSection
)
1755 createSwDoc("tdf146704_EndnoteInSection.odt");
1756 SwDoc
* pDoc
= getSwDoc();
1757 CPPUNIT_ASSERT(pDoc
);
1758 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1759 // Without the fix, the endnote placed to 2. page
1760 assertXPath(pXmlDoc
, "/root/page", 1);
1763 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf139336_ColumnsWithFootnoteDoNotOccupyEntirePage
)
1765 createSwDoc("tdf139336_ColumnsWithFootnoteDoNotOccupyEntirePage.docx");
1766 SwDoc
* pDoc
= getSwDoc();
1767 CPPUNIT_ASSERT(pDoc
);
1768 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1769 // Without the fix, it would be 5 pages, but with the fix the whole document
1770 // would fit into 1 page, but it will be 2 pages right now, because
1771 // when writer import (from docx) the last section with columns, then it does not set
1772 // the evenly distributed settings, and this settings is required for the fix now, to
1773 // avoid some regression.
1774 assertXPath(pXmlDoc
, "/root/page", 2);
1777 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf54465_ColumnsWithFootnoteDoNotOccupyEntirePage
)
1779 // Old odt files should keep their original layout, as it was before Tdf139336 fix.
1780 // The new odt file is only 1 page long, while the old odt file (with the same content)
1781 // was more than 1 page long.
1782 // Note: Somewhy this test miscalculates the layout of the old odt file.
1783 // It will be 4 pages long, while opened in Writer it is 5 pages long.
1784 createSwDoc("tdf54465_ColumnsWithFootnoteDoNotOccupyEntirePage_Old.odt");
1785 SwDoc
* pDoc
= getSwDoc();
1786 CPPUNIT_ASSERT(pDoc
);
1787 Scheduler::ProcessEventsToIdle();
1788 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1789 xmlXPathObjectPtr pXmlObj
= getXPathNode(pXmlDoc
, "/root/page");
1790 xmlNodeSetPtr pXmlNodes
= pXmlObj
->nodesetval
;
1791 CPPUNIT_ASSERT_GREATER(1, xmlXPathNodeSetGetLength(pXmlNodes
));
1792 xmlXPathFreeObject(pXmlObj
);
1794 discardDumpedLayout();
1795 createSwDoc("tdf54465_ColumnsWithFootnoteDoNotOccupyEntirePage_New.odt");
1797 CPPUNIT_ASSERT(pDoc
);
1798 pXmlDoc
= parseLayoutDump();
1799 assertXPath(pXmlDoc
, "/root/page", 1);
1802 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf138124
)
1804 // When the only portion after the footnote number is a FlyCnt, and it doesn't fit into
1805 // the page width, it should be moved to the next line without the footnote number, and
1806 // not loop, nor OOM, nor fail assertions.
1808 createSwDoc("wideBoxInFootnote.fodt");
1809 Scheduler::ProcessEventsToIdle();
1811 // Without the fix in place, the layout would loop, creating new FootnoteNum portions
1812 // indefinitely, until OOM.
1813 // If the footnote paragraph had no orphan control, then the loop would finally end,
1814 // but an assertion in SwTextPainter::DrawTextLine would fail during paint.
1816 xmlDocUniquePtr pXml
= parseLayoutDump();
1817 assertXPath(pXml
, "/root/page", 1);
1818 assertXPath(pXml
, "/root/page/ftncont/ftn/txt/anchored", 1);
1820 // And finally, if there were no assertion in SwTextPainter::DrawTextLine, it would have
1821 // produced multiple lines with FootnoteNum portions, failing the following check like
1826 "/root/page/ftncont/ftn/txt//SwFieldPortion[@type='PortionType::FootnoteNum']", 1);
1827 assertXPath(pXml
, "/root/page/ftncont/ftn/txt//SwLinePortion[@type='PortionType::FlyCnt']", 1);
1830 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf154113
)
1832 createSwDoc("three_sections.fodt");
1833 Scheduler::ProcessEventsToIdle();
1835 dispatchCommand(mxComponent
, ".uno:GoToStartOfDoc", {});
1836 dispatchCommand(mxComponent
, ".uno:GoToNextPara", {});
1837 dispatchCommand(mxComponent
, ".uno:EndOfDocumentSel", {}); // to the end of current section!
1838 dispatchCommand(mxComponent
, ".uno:EndOfDocumentSel", {}); // to the end of the document.
1840 auto xModel
= mxComponent
.queryThrow
<frame::XModel
>();
1841 auto xSelected
= xModel
->getCurrentSelection().queryThrow
<container::XIndexAccess
>();
1842 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelected
->getCount());
1843 auto xRange
= xSelected
->getByIndex(0).queryThrow
<text::XTextRange
>();
1844 CPPUNIT_ASSERT_EQUAL(OUString("<-- Start selection here. Section1" SAL_NEWLINE_STRING
1845 "Section2" SAL_NEWLINE_STRING
"Section3. End selection here -->"),
1846 xRange
->getString());
1848 dispatchCommand(mxComponent
, ".uno:Cut", {});
1850 xSelected
= xModel
->getCurrentSelection().queryThrow
<container::XIndexAccess
>();
1851 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelected
->getCount());
1852 xRange
= xSelected
->getByIndex(0).queryThrow
<text::XTextRange
>();
1853 CPPUNIT_ASSERT_EQUAL(OUString(), xRange
->getString());
1855 dispatchCommand(mxComponent
, ".uno:Paste", {});
1857 xmlDocUniquePtr pXml
= parseLayoutDump();
1859 // Without the fix in place, this would fail with
1862 assertXPath(pXml
, "/root/page/body/section", 3);
1863 assertXPath(pXml
, "/root/page/body/section[1]/txt/SwParaPortion/SwLineLayout", "portion",
1864 "<-- Start selection here. Section1");
1865 assertXPath(pXml
, "/root/page/body/section[2]/txt/SwParaPortion/SwLineLayout", "portion",
1867 assertXPath(pXml
, "/root/page/body/section[3]/txt/SwParaPortion/SwLineLayout", "portion",
1868 "Section3. End selection here -->");
1871 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3
, testTdf155611
)
1873 createSwDoc("tdf155611_table_and_nested_section.fodt");
1874 Scheduler::ProcessEventsToIdle();
1876 xmlDocUniquePtr pXml
= parseLayoutDump();
1877 CPPUNIT_ASSERT(pXml
);
1879 // Check the layout: single page, two section frames (no section frames after the one for Inner
1880 // section), correct table structure and content in the first section frame, including nested
1881 // table in the last cell, and the last section text.
1882 assertXPath(pXml
, "/root/page");
1883 // Without the fix in place, this would fail with
1886 assertXPath(pXml
, "/root/page/body/section", 2);
1887 assertXPath(pXml
, "/root/page/body/section[1]/tab");
1888 assertXPath(pXml
, "/root/page/body/section[1]/tab/row");
1889 assertXPath(pXml
, "/root/page/body/section[1]/tab/row/cell", 2);
1890 assertXPath(pXml
, "/root/page/body/section[1]/tab/row/cell[1]/txt/SwParaPortion/SwLineLayout/"
1891 "SwParaPortion[@portion='foo']");
1892 assertXPath(pXml
, "/root/page/body/section[1]/tab/row/cell[2]/txt/SwParaPortion/SwLineLayout/"
1893 "SwParaPortion[@portion='bar']");
1894 assertXPath(pXml
, "/root/page/body/section[1]/tab/row/cell[2]/tab/row/cell/txt/SwParaPortion/"
1895 "SwLineLayout/SwParaPortion[@portion='baz']");
1896 assertXPath(pXml
, "/root/page/body/section[2]/txt[1]/SwParaPortion/SwLineLayout/"
1897 "SwParaPortion[@portion='abc']");
1899 // Also must not crash on close because of a frame that accidentally fell off of the layout
1902 CPPUNIT_PLUGIN_IMPLEMENT();
1904 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */