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>
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>
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>
42 /// Covers sw/source/core/layout/ fixes.
43 class SwCoreLayoutTest
: public SwModelTestBase
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
66 // i.e. the table's top border overlapped with the image, even if the image's wrap mode was set
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
)
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:
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
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:
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
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:
162 // i.e. there was an unexpected 2nd page, as only 20 out of 21 tables were moved to the first
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();
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:
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
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
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
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
322 SwDoc
* pDoc
= getSwDoc();
323 uno::Reference
<beans::XPropertySet
> xStandard(getStyles("PageStyles")->getByName("Standard"),
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:
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
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"),
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:
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
)
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"),
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:
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:
407 SwDoc
* pDoc
= getSwDoc();
408 uno::Reference
<beans::XPropertySet
> xStandard(getStyles("PageStyles")->getByName("Standard"),
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:
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.
437 SwDoc
* pDoc
= getSwDoc();
438 uno::Reference
<beans::XPropertySet
> xStandard(getStyles("PageStyles")->getByName("Standard"),
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:
457 // Where 2565 is close to the left edge of the text area (2553).
458 assertXPath(pXmlDoc
, "//polyline[@style='solid']/point[1]", "x", "2565");
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
];
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
)
516 for (const auto nLayoutPosition
: aLayoutPositions
)
518 if (std::abs(nBorderPosition
- nLayoutPosition
) <= 30)
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
)
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:
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
)
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
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
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:
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
)
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:
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
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.
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
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
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
)
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
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:
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
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
)
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
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.
859 // Given a document with a top margin and a negative border distance:
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"),
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:
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) =
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.
901 // Given a document with no top margin and a negative border distance:
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"),
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:
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) =
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:
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
);
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
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:
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.
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:
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
);
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
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: */