Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / qa / core / layout / layout.cxx
blob4399c01a0074a03b359943a3275cdfa4f5c30af4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <swmodeltestbase.hxx>
12 #include <com/sun/star/style/PageStyleLayout.hpp>
14 #include <vcl/gdimtf.hxx>
15 #include <svx/svdpage.hxx>
16 #include <o3tl/string_view.hxx>
17 #include <sfx2/viewfrm.hxx>
18 #include <sfx2/dispatch.hxx>
19 #include <editeng/frmdiritem.hxx>
21 #include <wrtsh.hxx>
22 #include <docsh.hxx>
23 #include <unotxdoc.hxx>
24 #include <drawdoc.hxx>
25 #include <IDocumentDrawModelAccess.hxx>
26 #include <IDocumentState.hxx>
27 #include <IDocumentLayoutAccess.hxx>
28 #include <rootfrm.hxx>
29 #include <itabenum.hxx>
30 #include <fmtanchr.hxx>
31 #include <fmtsrnd.hxx>
32 #include <IDocumentSettingAccess.hxx>
33 #include <fmtfsize.hxx>
34 #include <fmtfollowtextflow.hxx>
35 #include <view.hxx>
36 #include <cmdid.h>
37 #include <node.hxx>
38 #include <ndtxt.hxx>
39 #include <tabfrm.hxx>
40 #include <cntfrm.hxx>
42 /// Covers sw/source/core/layout/ fixes.
43 class SwCoreLayoutTest : public SwModelTestBase
45 public:
46 SwCoreLayoutTest()
47 : SwModelTestBase("/sw/qa/core/layout/data/")
52 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlap)
54 // Load a document that has an image anchored in the header.
55 // It also has a table which has the wrap around the image.
56 createSwDoc("table-fly-overlap.docx");
57 SwTwips nFlyTop = parseDump("//header/txt/anchored/fly/infos/bounds", "top").toInt32();
58 SwTwips nFlyHeight = parseDump("//header/txt/anchored/fly/infos/bounds", "height").toInt32();
59 SwTwips nFlyBottom = nFlyTop + nFlyHeight;
60 SwTwips nTableFrameTop = parseDump("//tab/infos/bounds", "top").toInt32();
61 SwTwips nTablePrintTop = parseDump("//tab/infos/prtBounds", "top").toInt32();
62 SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
63 // Without the accompanying fix in place, this test would have failed with:
64 // - Expected greater or equal than: 3579
65 // - Actual : 2210
66 // i.e. the table's top border overlapped with the image, even if the image's wrap mode was set
67 // to parallel.
68 CPPUNIT_ASSERT_GREATEREQUAL(nFlyBottom, nTableTop);
71 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf128195)
73 // Load a document that has two paragraphs in the header.
74 // The second paragraph should have its bottom spacing applied.
75 createSwDoc("tdf128195.docx");
76 sal_Int32 nTxtHeight = parseDump("//header/txt[2]/infos/bounds", "height").toInt32();
77 sal_Int32 nTxtBottom = parseDump("//header/txt[2]/infos/bounds", "bottom").toInt32();
78 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2269), nTxtHeight);
79 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3529), nTxtBottom);
82 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBIRT)
84 // this looped
85 createSwDoc("birt_min.odt");
88 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBorderCollapseCompat)
90 // Load a document with a border conflict: top cell has a dotted bottom border, bottom cell has
91 // a solid upper border.
92 createSwDoc("border-collapse-compat.docx");
93 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
94 SwDocShell* pShell = pTextDoc->GetDocShell();
95 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
96 MetafileXmlDump aDumper;
97 xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
99 // Make sure the solid border has priority.
100 // Without the accompanying fix in place, this test would have failed with:
101 // - Expected: 1
102 // - Actual : 48
103 // i.e. there was no single cell border with width=20, rather there were 48 border parts
104 // (forming a dotted border), all with width=40.
105 assertXPath(pXmlDoc, "//polyline[@style='solid']", "width", "20");
108 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrTableRowSpan)
110 // Load a document which has a table. The A1 cell has btlr text direction, and the A1..A3 cells
111 // are merged.
112 createSwDoc("btlr-table-row-span.odt");
113 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
114 SwDocShell* pShell = pTextDoc->GetDocShell();
115 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
116 MetafileXmlDump aDumper;
117 xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
119 // Without the accompanying fix in place, this test would have failed with:
120 // - Expected: USA
121 // - Actual : West
122 // i.e. the "USA" text completely disappeared.
123 assertXPathContent(pXmlDoc, "//textarray[1]/text", "USA");
126 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlapSpacing)
128 // Load a document that has an image on the right of a table. The table wraps around the image.
129 createSwDoc("table-fly-overlap-spacing.docx");
130 SwTwips nFlyTop = parseDump("//body/txt/anchored/fly/infos/bounds", "top").toInt32();
131 SwTwips nFlyHeight = parseDump("//body/txt/anchored/fly/infos/bounds", "height").toInt32();
132 SwTwips nFlyBottom = nFlyTop + nFlyHeight;
133 SwTwips nTableFrameTop = parseDump("//tab/infos/bounds", "top").toInt32();
134 SwTwips nTablePrintTop = parseDump("//tab/infos/prtBounds", "top").toInt32();
135 SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
136 // Without the accompanying fix in place, this test would have failed with:
137 // - Expected greater or equal than: 3993
138 // - Actual : 3993
139 // i.e. the table was below the image, not on the left of the image.
140 CPPUNIT_ASSERT_LESS(nFlyBottom, nTableTop);
143 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTablesMoveBackwards)
145 // Load a document with 1 pages: empty content on first page, then 21 tables on the second page.
146 createSwDoc("tables-move-backwards.odt");
147 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
148 SwDocShell* pDocShell = pTextDoc->GetDocShell();
149 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
151 // Delete the content on the first page.
152 pWrtShell->SttEndDoc(/*bStart=*/true);
153 pWrtShell->EndPg(/*bSelect=*/true);
154 pWrtShell->DelLeft();
156 // Calc the layout and check the number of pages.
157 pWrtShell->CalcLayout();
158 xmlDocUniquePtr pLayout = parseLayoutDump();
159 // Without the accompanying fix in place, this test would have failed with:
160 // - Expected: 1
161 // - Actual : 2
162 // i.e. there was an unexpected 2nd page, as only 20 out of 21 tables were moved to the first
163 // page.
164 assertXPath(pLayout, "//page", 1);
167 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testContinuousEndnotesMoveBackwards)
169 // Load a document with the ContinuousEndnotes flag turned on.
170 createSwDoc("continuous-endnotes-move-backwards.doc");
171 xmlDocUniquePtr pLayout = parseLayoutDump();
172 // We have 2 pages.
173 assertXPath(pLayout, "/root/page", 2);
174 // No endnote container on page 1.
175 // Without the accompanying fix in place, this test would have failed with:
176 // - Expected: 0
177 // - Actual : 1
178 // i.e. there were unexpected endnotes on page 1.
179 assertXPath(pLayout, "/root/page[1]/ftncont", 0);
180 // All endnotes are in a container on page 2.
181 assertXPath(pLayout, "/root/page[2]/ftncont", 1);
184 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testAnchorPositionBasedOnParagraph)
186 // tdf#134783 check whether position of shape is good if it is anchored to paragraph and
187 // the "Don't add space between paragraphs of the same style" option is set
188 createSwDoc("tdf134783_testAnchorPositionBasedOnParagraph.fodt");
189 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
190 CPPUNIT_ASSERT(pXmlDoc);
191 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "top", "1671");
192 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "bottom", "1732");
193 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "top", "1947");
194 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "bottom", "2008");
195 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "top", "3783");
196 assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "bottom", "3844");
199 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxStaysInsideShape)
201 // tdf#135198: check whether text box stays inside shape after moving it upwards
202 createSwDoc("shape-textbox.odt");
203 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
204 CPPUNIT_ASSERT(pXmlDoc);
206 // Without the fix in place, this test would have failed with
207 // - Expected: 1932
208 // - Actual : 7476
209 assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "top", "1932");
210 assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "bottom", "7184");
213 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxNotModifiedOnOpen)
215 // tdf#138050: a freshly opened document containing a shape with a text box
216 // should not appear to be modified
217 createSwDoc("textbox-phantom-change.docx");
218 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
219 SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
221 // Without the fix in place this test would have shown that the document
222 // was modified due to a fix to tdf#135198
223 CPPUNIT_ASSERT(!pDoc->getIDocumentState().IsModified());
226 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxAutoGrowVertical)
228 createSwDoc("textbox-autogrow-vertical.docx");
229 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
230 SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
231 SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
232 SdrObject* pShape = pPage->GetObj(0);
233 tools::Rectangle aShapeRect = pShape->GetCurrentBoundRect();
235 discardDumpedLayout();
236 xmlDocUniquePtr pLayout = parseLayoutDump();
237 CPPUNIT_ASSERT(pLayout);
238 sal_Int32 nFlyLeft = getXPath(pLayout, "//anchored/fly/infos/bounds", "left").toInt32();
239 sal_Int32 nFlyTop = getXPath(pLayout, "//anchored/fly/infos/bounds", "top").toInt32();
240 sal_Int32 nFlyRight = getXPath(pLayout, "//anchored/fly/infos/bounds", "right").toInt32();
241 sal_Int32 nFlyBottom = getXPath(pLayout, "//anchored/fly/infos/bounds", "bottom").toInt32();
242 tools::Rectangle aFlyRect(nFlyLeft, nFlyTop, nFlyRight, nFlyBottom);
243 // Without the accompanying fix in place, this test would have failed, as aFlyRect was too wide,
244 // so it was not inside aShapeRect anymore.
245 CPPUNIT_ASSERT(aShapeRect.Contains(aFlyRect));
248 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextboxModification)
250 // Load a document with a textbox in it: the layout will have to position the shape part.
251 createSwDoc("textbox-modification.docx");
252 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
253 SwDocShell* pDocShell = pTextDoc->GetDocShell();
255 // Without the accompanying fix in place, this test would have failed, as the document was
256 // marked as modified right after the import.
257 CPPUNIT_ASSERT(!pDocShell->IsModified());
260 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxInHeaderIsPositioned)
262 // Load a document with a floating text box in the header
263 createSwDoc("header-textbox.docx");
264 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
265 CPPUNIT_ASSERT(pXmlDoc);
267 // Without the fix in place, this test would have failed with
268 // - Expected: 8051
269 // - Actual : 1418
270 // Comparison with 7000 chosen due to variability between devices
271 CPPUNIT_ASSERT_GREATEREQUAL(
272 double(7000), getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "left").toDouble());
275 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrNestedCell)
277 // Load a document with a nested table, the inner A1 cell has a btlr text direction.
278 createSwDoc("btlr-nested-cell.odt");
279 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
280 SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
281 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
282 SwFrame* pPage = pLayout->GetLower();
283 SwFrame* pBody = pPage->GetLower();
284 SwFrame* pOuterTable = pBody->GetLower()->GetNext();
285 SwFrame* pInnerTable = pOuterTable->GetLower()->GetLower()->GetLower();
287 // Check the paint area of the only text frame in the cell.
288 SwFrame* pTextFrame = pInnerTable->GetLower()->GetLower()->GetLower();
289 tools::Long nFrameBottom = pTextFrame->getFrameArea().Bottom();
290 SwRect aPaintArea = pTextFrame->GetPaintArea();
292 // Without the accompanying fix in place, this test would have failed with:
293 // - Expected greater or equal than: 2829
294 // - Actual : 2080
295 // i.e. part of the text frame area was not painted, hiding the actual text.
296 CPPUNIT_ASSERT_GREATEREQUAL(nFrameBottom, aPaintArea.Bottom());
299 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testKeepwithnextFullheight)
301 // The document has a heading (keep with next) and a full-page image in the next paragraph, i.e.
302 // conflicting requirements.
303 // Without the accompanying fix in place, this test would have failed with a layout loop in
304 // SwEditShell::CalcLayout().
305 createSwDoc("keepwithnext-fullheight.fodt");
307 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
308 CPPUNIT_ASSERT(pXmlDoc);
309 // Make sure the document has 2 pages.
310 assertXPath(pXmlDoc, "//page", 2);
311 // Heading stays on page 1 to avoid a layout loop.
312 assertXPathContent(pXmlDoc, "//page[1]/body/txt[2]", "Heading");
313 // Image stays on page 2.
314 assertXPath(pXmlDoc, "//page[2]/body/txt/anchored/fly", 1);
317 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMargin)
319 // Create a document, remember the old left edge of the page print area (the rectangle that is
320 // inside margins).
321 createSwDoc();
322 SwDoc* pDoc = getSwDoc();
323 uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
324 uno::UNO_QUERY);
325 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
326 SwFrame* pPage = pLayout->GetLower();
327 tools::Long nOldLeft = pPage->getFramePrintArea().Left();
329 // Set the gutter margin to 2cm.
330 sal_Int32 nGutterMm100 = 2000;
331 xStandard->setPropertyValue("GutterMargin", uno::Any(nGutterMm100));
333 // Verify that the new left edge is larger.
334 tools::Long nNewLeft = pPage->getFramePrintArea().Left();
335 tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
336 // Without the accompanying fix in place, this test would have failed with:
337 // - Expected: 1134
338 // - Actual : 0
339 // i.e. the gutter was not added to the left margin.
340 CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
343 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterTopMargin)
345 // Create a document, remember the old top edge of the page print area (the rectangle that is
346 // inside margins).
347 createSwDoc();
348 SwDoc* pDoc = getSwDoc();
349 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
350 uno::Reference<beans::XPropertySet> xSettings(
351 xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
352 xSettings->setPropertyValue("GutterAtTop", uno::Any(true));
353 uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
354 uno::UNO_QUERY);
355 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
356 SwFrame* pPage = pLayout->GetLower();
357 tools::Long nOldTop = pPage->getFramePrintArea().Top();
359 // Set the gutter margin to 2cm.
360 sal_Int32 nGutterMm100 = 2000;
361 xStandard->setPropertyValue("GutterMargin", uno::Any(nGutterMm100));
363 // Verify that the new top edge is larger.
364 tools::Long nNewTop = pPage->getFramePrintArea().Top();
365 tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
366 // Without the accompanying fix in place, this test would have failed with:
367 // - Expected: 1134
368 // - Actual : 0
369 // i.e. the gutter was not added to the left margin.
370 CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewTop - nOldTop);
373 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMirrorMargin)
375 createSwDoc();
376 SwDoc* pDoc = getSwDoc();
377 SwDocShell* pDocShell = pDoc->GetDocShell();
378 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
379 pWrtShell->InsertPageBreak();
380 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
381 SwFrame* pPage = pLayout->GetLower();
382 tools::Long nOldLeft = pPage->getFramePrintArea().Left();
383 SwFrame* pPage2 = pPage->GetNext();
384 tools::Long nOldRight = pPage2->getFramePrintArea().Right();
386 uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
387 uno::UNO_QUERY);
388 xStandard->setPropertyValue("PageStyleLayout", uno::Any(style::PageStyleLayout_MIRRORED));
389 sal_Int32 nGutterMm100 = 2000;
390 xStandard->setPropertyValue("GutterMargin", uno::Any(nGutterMm100));
392 tools::Long nNewLeft = pPage->getFramePrintArea().Left();
393 tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
394 CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
395 tools::Long nNewRight = pPage2->getFramePrintArea().Right();
396 // Without the accompanying fix in place, this test would have failed with:
397 // - Expected: 1134
398 // - Actual : 0
399 // i.e. the gutter was missing on the second, mirrored page.
400 CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
403 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testRtlGutterMargin)
405 // Given a document with a right margin:
406 createSwDoc();
407 SwDoc* pDoc = getSwDoc();
408 uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
409 uno::UNO_QUERY);
410 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
411 SwFrame* pPage = pLayout->GetLower();
412 tools::Long nOldRight = pPage->getFramePrintArea().Right();
414 // When setting enable RTL gutter mode and setting a gutter margin:
415 xStandard->setPropertyValue("RtlGutter", uno::Any(true));
416 sal_Int32 nGutterMm100 = 2000;
417 xStandard->setPropertyValue("GutterMargin", uno::Any(nGutterMm100));
419 // Then make sure the new right edge of the print area is decreased:
420 tools::Long nNewRight = pPage->getFramePrintArea().Right();
421 tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
422 // Without the accompanying fix in place, this test would have failed with:
423 // - Expected: 1134
424 // - Actual : 0
425 // i.e. the gutter was missing on the right side.
426 CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
429 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMarginPageBorder)
431 // FIXME this is 3369 on macOS -- calculate this number dynamically?
432 // FIXME this is random on Windows at the moment (in two subsequent tests without any scaling,
433 // the actual values were 6346, 10066) - something broke metafile generation on Windows?
434 #if !defined(MACOSX) && !defined(_WIN32)
435 // Given a document with a non-0 gutter margin.
436 createSwDoc();
437 SwDoc* pDoc = getSwDoc();
438 uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
439 uno::UNO_QUERY);
440 sal_Int32 nGutterMm100 = 2000;
441 xStandard->setPropertyValue("GutterMargin", uno::Any(nGutterMm100));
443 // When setting a left border.
444 table::BorderLine2 aBorder;
445 aBorder.LineWidth = 2;
446 aBorder.OuterLineWidth = 2;
447 xStandard->setPropertyValue("LeftBorder", uno::Any(aBorder));
449 // Then make sure border is at the left edge of the text area.
450 SwDocShell* pShell = pDoc->GetDocShell();
451 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
452 MetafileXmlDump dumper;
453 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
454 // Without the accompanying fix in place, this test would have failed with:
455 // - Expected: 2565
456 // - Actual : 1425
457 // Where 2565 is close to the left edge of the text area (2553).
458 assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "x", "2565");
459 #endif
462 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf45908_invoice)
464 // without the fix, this was hanging (and slowly consuming memory) on fileopen.
465 createSwDoc("tdf45908_invoice.odt");
468 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testVerticallyMergedCellBorder)
470 // Given a document with a table: 2 columns, 5 rows. B2 -> B5 is merged:
471 createSwDoc("vmerge-cell-border.docx");
472 SwDoc* pDoc = getSwDoc();
473 SwDocShell* pShell = pDoc->GetDocShell();
475 // When rendering the table:
476 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
478 // Make sure that B4->B5 has no borders.
479 MetafileXmlDump dumper;
480 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
481 // Collect vertical positions of all border points.
482 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
483 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
484 std::vector<sal_Int32> aBorderPositions;
485 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); ++i)
487 xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
488 xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("y"));
489 sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
490 aBorderPositions.push_back(nValue);
492 xmlXPathFreeObject(pXmlObj);
493 // Collect top and bottom of the B1->B3 rows.
494 xmlDocUniquePtr pLayout = parseLayoutDump();
495 pXmlObj = getXPathNode(pLayout, "//tab/row/infos/bounds");
496 pXmlNodes = pXmlObj->nodesetval;
497 std::vector<sal_Int32> aLayoutPositions;
498 for (int i = 0; i < 3; ++i)
500 xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
501 if (i == 0)
503 xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("top"));
504 sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
505 aLayoutPositions.push_back(nValue);
507 xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("bottom"));
508 sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
509 aLayoutPositions.push_back(nValue);
511 xmlXPathFreeObject(pXmlObj);
512 // Check if any border is outside the B1->B3 range.
513 for (const auto nBorderPosition : aBorderPositions)
515 bool bFound = false;
516 for (const auto nLayoutPosition : aLayoutPositions)
518 if (std::abs(nBorderPosition - nLayoutPosition) <= 30)
520 bFound = true;
521 break;
524 std::stringstream ss;
525 ss << "Bad vertical position for border point: " << nBorderPosition;
526 ss << " Expected positions: ";
527 for (size_t i = 0; i < aLayoutPositions.size(); ++i)
529 if (i > 0)
531 ss << ", ";
533 ss << aLayoutPositions[i];
536 // Without the accompanying fix in place, this test would have failed with:
537 // - Bad vertical position for border point: 5624 Expected positions: 3022, 3540, 4059, 4578
538 // i.e. the middle vertical border end was the bottom of B5, not bottom of B3.
539 CPPUNIT_ASSERT_MESSAGE(ss.str(), bFound);
543 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testCrashRemoveFromLayout)
545 createSwDoc("tdf122894-4.doc");
548 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testLinkedBullet)
550 // Given a document with a graphic bullet, where the image is a linked one:
551 createSwDoc("linked-bullet.odt");
552 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
553 SwDocShell* pShell = pTextDoc->GetDocShell();
555 // When rendering that document:
556 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
558 // Then make sure the render result contains exactly one bitmap:
559 MetafileXmlDump aDumper;
560 xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
561 // Without the accompanying fix in place, this test would have failed with:
562 // - Expected: 1
563 // - Actual : 0
564 // i.e. the bullet's bitmap was lost.
565 assertXPath(pXmlDoc, "//bmpexscale", 1);
568 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testInnerCellBorderIntersect)
570 // Given a table with both outer and inner borders:
571 createSwDoc("inner-border.docx");
572 SwDoc* pDoc = getSwDoc();
573 SwDocShell* pShell = pDoc->GetDocShell();
575 // When rendering table borders:
576 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
578 // Then make sure that inner and outer borders don't overlap in Word compatibility mode,
579 // and inner borders are reduced to prevent an overlap:
580 MetafileXmlDump dumper;
581 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
582 // Collect start/end (vertical) positions of horizontal borders.
583 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
584 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
585 std::vector<std::pair<sal_Int32, sal_Int32>> aBorderStartEnds;
586 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
588 xmlNodePtr pStart = pXmlNodes->nodeTab[i];
589 xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
590 xmlChar* pStartY = xmlGetProp(pStart, BAD_CAST("y"));
591 xmlChar* pEndY = xmlGetProp(pEnd, BAD_CAST("y"));
592 sal_Int32 nStartY = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
593 sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
594 if (nStartY != nEndY)
596 // Vertical border.
597 continue;
599 xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
600 xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
601 sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
602 sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
603 aBorderStartEnds.emplace_back(nStartX, nEndX);
605 xmlXPathFreeObject(pXmlObj);
606 // We have 3 lines: top, middle and bottom. The top and the bottom one is a full line, since
607 // it's an outer border. The middle one has increased start and decreased end to avoid an
608 // overlap.
609 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aBorderStartEnds.size());
610 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[2].first);
611 // Without the accompanying fix in place, this test would have failed with:
612 // - Expected greater than: 1724
613 // - Actual : 1724
614 // i.e. the middle line's start was the same as the top line start, while what we want is a
615 // larger X position, so the start of the middle line doesn't overlap with the thick vertical
616 // outer border on the left of the table.
617 CPPUNIT_ASSERT_GREATER(aBorderStartEnds[0].first, aBorderStartEnds[1].first);
618 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[2].second);
619 CPPUNIT_ASSERT_LESS(aBorderStartEnds[0].second, aBorderStartEnds[1].second);
622 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testInnerCellBorderNocompatIntersect)
624 // Given a table with both outer and inner borders:
625 createSwDoc();
626 SwDocShell* pShell = getSwDocShell();
627 SwDoc* pDoc = pShell->GetDoc();
628 // Set the default page style's writing direction to vertical:
629 SwPageDesc aStandard(pDoc->GetPageDesc(0));
630 SvxFrameDirectionItem aDirection(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR);
631 aStandard.GetMaster().SetFormatAttr(aDirection);
632 pDoc->ChgPageDesc(0, aStandard);
633 // Insert a 4x4 table:
634 SwWrtShell* pWrtShell = pShell->GetWrtShell();
635 SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
636 pWrtShell->InsertTable(aTableOptions, 4, 4);
638 // When rendering table borders:
639 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
641 // Then make sure that all horizontal lines have the same length:
642 MetafileXmlDump dumper;
643 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
644 // Collect start/end (vertical) positions of horizontal borders.
645 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
646 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
647 std::vector<std::pair<sal_Int32, sal_Int32>> aBorderStartEnds;
648 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
650 xmlNodePtr pStart = pXmlNodes->nodeTab[i];
651 xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
652 xmlChar* pStartY = xmlGetProp(pStart, BAD_CAST("y"));
653 xmlChar* pEndY = xmlGetProp(pEnd, BAD_CAST("y"));
654 sal_Int32 nStartY = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
655 sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
656 if (nStartY != nEndY)
658 // Vertical border.
659 continue;
661 xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
662 xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
663 sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
664 sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
665 aBorderStartEnds.emplace_back(nStartX, nEndX);
667 xmlXPathFreeObject(pXmlObj);
668 // We have 5 lines: top of 4 cells + bottom.
669 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aBorderStartEnds.size());
670 // Without the accompanying fix in place, this test would have failed with:
671 // - Expected: 9719
672 // - Actual : 10064
673 // i.e. the 2nd line started later than the first one, which is incorrect.
674 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[1].first);
675 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[2].first);
676 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[3].first);
677 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[4].first);
678 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[1].second);
679 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[2].second);
680 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[3].second);
681 CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[4].second);
684 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoubleBorderVertical)
686 // Given a table with a left and right double border, outer is thick, inner is thin:
687 createSwDoc("double-border-vertical.docx");
688 SwDoc* pDoc = getSwDoc();
689 SwDocShell* pShell = pDoc->GetDocShell();
691 // When rendering that document:
692 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
694 // Then make sure the left border is thick+thin and the right border is thin+thick (from left to
695 // right):
696 MetafileXmlDump dumper;
697 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
698 // Collect widths of vertical lines.
699 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
700 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
701 // Horizontal position -> width.
702 std::map<sal_Int32, sal_Int32> aBorderWidths;
703 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
705 xmlNodePtr pStart = pXmlNodes->nodeTab[i];
706 xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
707 xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
708 xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
709 sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
710 sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
711 if (nStartX != nEndX)
713 // Horizontal border.
714 continue;
716 xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
717 sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
718 aBorderWidths[nStartX] = nWidth;
720 xmlXPathFreeObject(pXmlObj);
721 std::vector<sal_Int32> aBorderWidthVec;
722 std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
723 [](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
724 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
725 // Without the accompanying fix in place, this test would have failed with:
726 // - Expected greater than: 120
727 // - Actual : 60
728 // i.e. the left border was thin+thick, not thick+thin.
729 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[1], aBorderWidthVec[0]);
730 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[2], aBorderWidthVec[3]);
733 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoubleBorderHorizontal)
735 // Given a table with a top and bottom double border, outer is thin, inner is thick:
736 createSwDoc("double-border-horizontal.docx");
737 SwDoc* pDoc = getSwDoc();
738 SwDocShell* pShell = pDoc->GetDocShell();
740 // When rendering table borders:
741 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
743 // Then make sure the top border is thin+thick and the bottom border is thick+thin (from top to
744 // bottom):
745 MetafileXmlDump dumper;
746 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
747 // Collect widths of horizontal lines.
748 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
749 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
750 // Vertical position -> width.
751 std::map<sal_Int32, sal_Int32> aBorderWidths;
752 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
754 xmlNodePtr pStart = pXmlNodes->nodeTab[i];
755 xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
756 xmlChar* pStartY = xmlGetProp(pStart, BAD_CAST("y"));
757 xmlChar* pEndY = xmlGetProp(pEnd, BAD_CAST("y"));
758 sal_Int32 nStartY = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
759 sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
760 if (nStartY != nEndY)
762 // Vertical border.
763 continue;
765 xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
766 sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
767 aBorderWidths[nStartY] = nWidth;
769 xmlXPathFreeObject(pXmlObj);
770 std::vector<sal_Int32> aBorderWidthVec;
771 std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
772 [](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
773 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
774 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[0], aBorderWidthVec[1]);
775 // Without the accompanying fix in place, this test would have failed with:
776 // - Expected greater than: 120
777 // - Actual : 60
778 // i.e. the bottom border was thin+thick, not thick+thin.
779 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[3], aBorderWidthVec[2]);
782 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testParaBorderInCellClip)
784 // Given a document which has outside-cell borders defined, which should not be visible:
785 createSwDoc("para-border-in-cell-clip.docx");
786 SwDoc* pDoc = getSwDoc();
787 SwDocShell* pShell = pDoc->GetDocShell();
789 // When rendering those borders:
790 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
792 // Then make sure that we have clipping setup for both paragraphs inside the table cell:
793 MetafileXmlDump dumper;
794 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
795 // Without the accompanying fix in place, this test would have failed with:
796 // - Expected: 2
797 // - Actual : 0
798 // - XPath '//clipregion/polygon' number of nodes is incorrect
799 // i.e. there was no clipping applied, leading to unexpected left/right borders.
800 assertXPath(pXmlDoc, "//clipregion/polygon", 2);
803 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoublePageBorder)
805 // Given a page with a top and bottom double border, outer is thick, inner is thin:
806 createSwDoc("double-page-border.docx");
807 SwDoc* pDoc = getSwDoc();
808 SwDocShell* pShell = pDoc->GetDocShell();
810 // When rendering that document:
811 std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
813 // Then make sure the top border is thick+thing and the bottom border is thin+thick (from top to
814 // bottom):
815 MetafileXmlDump dumper;
816 xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
817 // Collect widths of horizontal lines.
818 xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
819 xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
820 // Vertical position -> width.
821 std::map<sal_Int32, sal_Int32> aBorderWidths;
822 for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
824 xmlNodePtr pStart = pXmlNodes->nodeTab[i];
825 xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
826 xmlChar* pStartY = xmlGetProp(pStart, BAD_CAST("y"));
827 xmlChar* pEndY = xmlGetProp(pEnd, BAD_CAST("y"));
828 sal_Int32 nStartY = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
829 sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
830 if (nStartY != nEndY)
832 // Vertical border.
833 continue;
835 xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
836 sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
837 aBorderWidths[nStartY] = nWidth;
839 xmlXPathFreeObject(pXmlObj);
840 std::vector<sal_Int32> aBorderWidthVec;
841 std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
842 [](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
843 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
844 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[1], aBorderWidthVec[0]);
845 // Without the accompanying fix in place, this test would have failed with:
846 // - Expected greater than: 60
847 // - Actual : 15
848 // i.e. the bottom border was thick+thin, not thin+thick.
849 CPPUNIT_ASSERT_GREATER(aBorderWidthVec[2], aBorderWidthVec[3]);
852 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorder)
854 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
855 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
856 if (!IsDefaultDPI())
857 return;
859 // Given a document with a top margin and a negative border distance:
860 createSwDoc();
861 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
862 SwDocShell* pDocShell = pTextDoc->GetDocShell();
863 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
864 pWrtShell->Insert("test");
865 uno::Reference<beans::XPropertySet> xPageStyle(getStyles("PageStyles")->getByName("Standard"),
866 uno::UNO_QUERY);
867 xPageStyle->setPropertyValue("TopMargin", uno::Any(static_cast<sal_Int32>(501))); // 284 twips
868 table::BorderLine2 aBorder;
869 aBorder.LineWidth = 159; // 90 twips
870 aBorder.OuterLineWidth = 159;
871 xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder));
872 sal_Int32 nTopBorderDistance = -646; // -366 twips
873 xPageStyle->setPropertyValue("TopBorderDistance", uno::Any(nTopBorderDistance));
874 nTopBorderDistance = 0;
875 xPageStyle->getPropertyValue("TopBorderDistance") >>= nTopBorderDistance;
876 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
878 // When rendering that border:
879 std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
881 // Then make sure that the negative distance pushes the horizontal borderline down:
882 MetafileXmlDump aDumper;
883 xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
884 // Without the accompanying fix in place, this test would have failed with:
885 // - Expected: 899
886 // - Actual : 524
887 // i.e. the negative border distance was rounded up to 0 in lcl_CalcBorderRect().
888 // Ideally this would be 284 (top of first page) + 284 (top margin) + 366 (border distance) =
889 // 934.
890 assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", "899");
891 assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", "899");
894 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorderNoMargin)
896 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
897 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
898 if (!IsDefaultDPI())
899 return;
901 // Given a document with no top margin and a negative border distance:
902 createSwDoc();
903 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
904 SwDocShell* pDocShell = pTextDoc->GetDocShell();
905 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
906 pWrtShell->Insert("test");
907 uno::Reference<beans::XPropertySet> xPageStyle(getStyles("PageStyles")->getByName("Standard"),
908 uno::UNO_QUERY);
909 xPageStyle->setPropertyValue("TopMargin", uno::Any(static_cast<sal_Int32>(0))); // 0 twips
910 table::BorderLine2 aBorder;
911 aBorder.LineWidth = 159; // 90 twips
912 aBorder.OuterLineWidth = 159;
913 xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder));
914 sal_Int32 nTopBorderDistance = -1147; // -650 twips
915 xPageStyle->setPropertyValue("TopBorderDistance", uno::Any(nTopBorderDistance));
917 // When rendering that border:
918 std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
920 // Then make sure that the negative distance pushes the horizontal borderline down:
921 MetafileXmlDump aDumper;
922 xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
923 // Without the accompanying fix in place, this test would have failed with:
924 // - Expected: 899
925 // - Actual : 329
926 // i.e. this failed differently: the lack of top margin caused a second problem.
927 // Ideally this would be 284 (top of first page) + 650 (border distance) =
928 // 934.
929 assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", "899");
930 assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", "899");
933 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testFollowTextFlowWrapInBackground)
935 // Given a document with a table, and a graphic inside that table -- anchored, wrap set to
936 // through and follow-text-flow set to true:
937 createSwDoc();
938 SwDoc* pDoc = getSwDoc();
939 pDoc->getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, true);
940 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
941 SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
942 pWrtShell->InsertTable(aTableOptions, 1, 1);
943 pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
944 SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
945 SwFormatAnchor aAnchor(RndStdIds::FLY_AT_CHAR);
946 aFrameSet.Put(aAnchor);
947 SwFormatSurround aSurround(text::WrapTextMode_THROUGH);
948 aFrameSet.Put(aSurround);
949 SwFormatFrameSize aSize(SwFrameSize::Fixed, 1000, 1000);
950 aFrameSet.Put(aSize);
951 SwFormatFollowTextFlow aFlow(true);
952 aFrameSet.Put(aFlow);
953 Graphic aGrf;
955 // When inserting that image:
956 pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet);
958 // Then make sure that the cell height grows to have space for the graphic, given that
959 // background=true is not specified.
960 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
961 sal_Int32 nCellHeight = getXPath(pXmlDoc, "//cell[1]/infos/bounds", "height").toInt32();
962 // Without the accompanying fix in place, this test would have failed with:
963 // - Expected greater than: 1000
964 // - Actual : 396
965 // i.e. the image was larger than the containing cell.
966 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(1000), nCellHeight);
969 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testPageRemoveFlyTable)
971 // Given a document with a ToC and several tables, one table marked with a bookmark:
972 createSwDoc("page-remove-fly-table.odt");
973 SwDoc* pDoc = getSwDoc();
975 // When updating the ToC and incrementally formatting the document:
976 SwView* pView = pDoc->GetDocShell()->GetView();
977 SfxDispatcher& rDispatcher = *pView->GetViewFrame().GetDispatcher();
978 rDispatcher.Execute(FN_UPDATE_TOX);
979 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
980 pWrtShell->Reformat();
982 // Then make sure that the 2nd table below the bookmark has no unwanted top margin:
983 pWrtShell->GotoMark("test");
984 pWrtShell->Down(/*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
985 pWrtShell->Down(/*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
986 SwCursor* pCursor = pWrtShell->GetCursor();
987 SwTextNode* pTextNode = pCursor->GetPoint()->GetNode().GetTextNode();
988 SwFrame* pTextFrame = pTextNode->getLayoutFrame(nullptr);
989 SwTabFrame* pInnerTable = pTextFrame->FindTabFrame();
990 SwTabFrame* pOuterTable = pInnerTable->GetUpper()->FindTabFrame();
991 tools::Long nActual = pOuterTable->getFramePrintArea().Top();
992 // Without the accompanying fix in place, this test would have failed with:
993 // - Expected: 0
994 // - Actual : 5879
995 // i.e. the problematic table had a large, unwanted/leftover top margin.
996 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(0), nActual);
999 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testPageRemoveFlyNoTable)
1001 createSwDoc("page-remove-fly-no-table.fodt");
1002 // This never returned.
1003 calcLayout();
1006 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNewFollowTextFlowWrapInBackground)
1008 // Given a document with a table, and a graphic inside that table -- anchored, wrap set to
1009 // through and follow-text-flow set to true, legacy USE_FORMER_TEXT_WRAPPING is not set:
1010 createSwDoc();
1011 SwDoc* pDoc = getSwDoc();
1012 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
1013 SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
1014 pWrtShell->InsertTable(aTableOptions, 1, 1);
1015 pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
1016 SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
1017 SwFormatAnchor aAnchor(RndStdIds::FLY_AT_CHAR);
1018 aFrameSet.Put(aAnchor);
1019 SwFormatSurround aSurround(text::WrapTextMode_THROUGH);
1020 aFrameSet.Put(aSurround);
1021 SwFormatFrameSize aSize(SwFrameSize::Fixed, 1000, 1000);
1022 aFrameSet.Put(aSize);
1023 SwFormatFollowTextFlow aFlow(true);
1024 aFrameSet.Put(aFlow);
1025 Graphic aGrf;
1027 // When inserting that image:
1028 pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet);
1030 // Then make sure that the cell height grows to have space for the graphic, given that
1031 // background=true is not specified.
1032 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
1033 sal_Int32 nCellHeight = getXPath(pXmlDoc, "//cell[1]/infos/bounds", "height").toInt32();
1034 // Without the accompanying fix in place, this test would have failed with:
1035 // - Expected less than: 1000
1036 // - Actual : 1120
1037 // i.e. the cell height was too large, the image influenced it, which is not expected.
1038 CPPUNIT_ASSERT_LESS(static_cast<sal_Int32>(1000), nCellHeight);
1041 CPPUNIT_PLUGIN_IMPLEMENT();
1043 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */