1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <swmodeltestbase.hxx>
11 
12 #include <com/sun/star/style/PageStyleLayout.hpp>
13 
14 #include <vcl/gdimtf.hxx>
15 #include <svx/svdpage.hxx>
16 #include <svx/unopage.hxx>
17 
18 #include <wrtsh.hxx>
19 #include <docsh.hxx>
20 #include <unotxdoc.hxx>
21 #include <drawdoc.hxx>
22 #include <IDocumentDrawModelAccess.hxx>
23 #include <IDocumentState.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <rootfrm.hxx>
26 
27 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/layout/data/";
28 
29 /// Covers sw/source/core/layout/ fixes.
30 class SwCoreLayoutTest : public SwModelTestBase
31 {
32 };
33 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTableFlyOverlap)34 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlap)
35 {
36     // Load a document that has an image anchored in the header.
37     // It also has a table which has the wrap around the image.
38     load(DATA_DIRECTORY, "table-fly-overlap.docx");
39     SwTwips nFlyTop = parseDump("//header/txt/anchored/fly/infos/bounds", "top").toInt32();
40     SwTwips nFlyHeight = parseDump("//header/txt/anchored/fly/infos/bounds", "height").toInt32();
41     SwTwips nFlyBottom = nFlyTop + nFlyHeight;
42     SwTwips nTableFrameTop = parseDump("//tab/infos/bounds", "top").toInt32();
43     SwTwips nTablePrintTop = parseDump("//tab/infos/prtBounds", "top").toInt32();
44     SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
45     // Without the accompanying fix in place, this test would have failed with:
46     // - Expected greater or equal than: 3579
47     // - Actual  : 2210
48     // i.e. the table's top border overlapped with the image, even if the image's wrap mode was set
49     // to parallel.
50     CPPUNIT_ASSERT_GREATEREQUAL(nFlyBottom, nTableTop);
51 }
52 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTdf128195)53 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf128195)
54 {
55     // Load a document that has two paragraphs in the header.
56     // The second paragraph should have its bottom spacing applied.
57     load(DATA_DIRECTORY, "tdf128195.docx");
58     sal_Int32 nTxtHeight = parseDump("//header/txt[2]/infos/bounds", "height").toInt32();
59     sal_Int32 nTxtBottom = parseDump("//header/txt[2]/infos/bounds", "bottom").toInt32();
60     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2269), nTxtHeight);
61     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3529), nTxtBottom);
62 }
63 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testBIRT)64 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBIRT)
65 {
66     // this looped
67     load(DATA_DIRECTORY, "birt_min.odt");
68 }
69 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testBorderCollapseCompat)70 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBorderCollapseCompat)
71 {
72     // Load a document with a border conflict: top cell has a dotted bottom border, bottom cell has
73     // a solid upper border.
74     load(DATA_DIRECTORY, "border-collapse-compat.docx");
75     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
76     SwDocShell* pShell = pTextDoc->GetDocShell();
77     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
78     MetafileXmlDump aDumper;
79     xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
80 
81     // Make sure the solid border has priority.
82     // Without the accompanying fix in place, this test would have failed with:
83     // - Expected: 1
84     // - Actual  : 48
85     // i.e. there was no single cell border with width=20, rather there were 48 border parts
86     // (forming a dotted border), all with width=40.
87     assertXPath(pXmlDoc, "//polyline[@style='solid']", "width", "20");
88 }
89 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testBtlrTableRowSpan)90 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrTableRowSpan)
91 {
92     // Load a document which has a table. The A1 cell has btlr text direction, and the A1..A3 cells
93     // are merged.
94     load(DATA_DIRECTORY, "btlr-table-row-span.odt");
95     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
96     SwDocShell* pShell = pTextDoc->GetDocShell();
97     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
98     MetafileXmlDump aDumper;
99     xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
100 
101     // Without the accompanying fix in place, this test would have failed with:
102     // - Expected: USA
103     // - Actual  : West
104     // i.e. the "USA" text completely disappeared.
105     assertXPathContent(pXmlDoc, "//textarray[1]/text", "USA");
106 }
107 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTableFlyOverlapSpacing)108 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlapSpacing)
109 {
110     // Load a document that has an image on the right of a table.  The table wraps around the image.
111     load(DATA_DIRECTORY, "table-fly-overlap-spacing.docx");
112     SwTwips nFlyTop = parseDump("//body/txt/anchored/fly/infos/bounds", "top").toInt32();
113     SwTwips nFlyHeight = parseDump("//body/txt/anchored/fly/infos/bounds", "height").toInt32();
114     SwTwips nFlyBottom = nFlyTop + nFlyHeight;
115     SwTwips nTableFrameTop = parseDump("//tab/infos/bounds", "top").toInt32();
116     SwTwips nTablePrintTop = parseDump("//tab/infos/prtBounds", "top").toInt32();
117     SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
118     // Without the accompanying fix in place, this test would have failed with:
119     // - Expected greater or equal than: 3993
120     // - Actual  : 3993
121     // i.e. the table was below the image, not on the left of the image.
122     CPPUNIT_ASSERT_LESS(nFlyBottom, nTableTop);
123 }
124 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTablesMoveBackwards)125 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTablesMoveBackwards)
126 {
127     // Load a document with 1 pages: empty content on first page, then 21 tables on the second page.
128     load(DATA_DIRECTORY, "tables-move-backwards.odt");
129     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
130     SwDocShell* pDocShell = pTextDoc->GetDocShell();
131     SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
132 
133     // Delete the content on the first page.
134     pWrtShell->SttEndDoc(/*bStart=*/true);
135     pWrtShell->EndPg(/*bSelect=*/true);
136     pWrtShell->DelLeft();
137 
138     // Calc the layout and check the number of pages.
139     pWrtShell->CalcLayout();
140     xmlDocUniquePtr pLayout = parseLayoutDump();
141     // Without the accompanying fix in place, this test would have failed with:
142     // - Expected: 1
143     // - Actual  : 2
144     // i.e. there was an unexpected 2nd page, as only 20 out of 21 tables were moved to the first
145     // page.
146     assertXPath(pLayout, "//page", 1);
147 }
148 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testContinuousEndnotesMoveBackwards)149 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testContinuousEndnotesMoveBackwards)
150 {
151     // Load a document with the ContinuousEndnotes flag turned on.
152     load(DATA_DIRECTORY, "continuous-endnotes-move-backwards.doc");
153     xmlDocUniquePtr pLayout = parseLayoutDump();
154     // We have 2 pages.
155     assertXPath(pLayout, "/root/page", 2);
156     // No endnote container on page 1.
157     // Without the accompanying fix in place, this test would have failed with:
158     // - Expected: 0
159     // - Actual  : 1
160     // i.e. there were unexpected endnotes on page 1.
161     assertXPath(pLayout, "/root/page[1]/ftncont", 0);
162     // All endnotes are in a container on page 2.
163     assertXPath(pLayout, "/root/page[2]/ftncont", 1);
164 }
165 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testAnchorPositionBasedOnParagraph)166 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testAnchorPositionBasedOnParagraph)
167 {
168     // tdf#134783 check whether position of shape is good if it is anchored to paragraph and
169     // the "Don't add space between paragraphs of the same style" option is set
170     load(DATA_DIRECTORY, "tdf134783_testAnchorPositionBasedOnParagraph.fodt");
171     xmlDocUniquePtr pXmlDoc = parseLayoutDump();
172     CPPUNIT_ASSERT(pXmlDoc);
173     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "top", "1671");
174     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "bottom", "1732");
175     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "top", "1947");
176     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "bottom", "2008");
177     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "top", "3783");
178     assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "bottom", "3844");
179 }
180 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTextBoxStaysInsideShape)181 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxStaysInsideShape)
182 {
183     // tdf#135198: check whether text box stays inside shape after moving it upwards
184     load(DATA_DIRECTORY, "shape-textbox.odt");
185     xmlDocUniquePtr pXmlDoc = parseLayoutDump();
186     CPPUNIT_ASSERT(pXmlDoc);
187 
188     // Without the fix in place, this test would have failed with
189     // - Expected: 1932
190     // - Actual  : 7476
191     assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "top", "1932");
192     assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "bottom", "7184");
193 }
194 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTextBoxNotModifiedOnOpen)195 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxNotModifiedOnOpen)
196 {
197     // tdf#138050: a freshly opened document containing a shape with a text box
198     // should not appear to be modified
199     load(DATA_DIRECTORY, "textbox-phantom-change.docx");
200     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
201     SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
202 
203     // Without the fix in place this test would have shown that the document
204     // was modified due to a fix to tdf#135198
205     CPPUNIT_ASSERT(!pDoc->getIDocumentState().IsModified());
206 }
207 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTextBoxAutoGrowVertical)208 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxAutoGrowVertical)
209 {
210     load(DATA_DIRECTORY, "textbox-autogrow-vertical.docx");
211     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
212     SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
213     SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
214     SdrObject* pShape = pPage->GetObj(0);
215     tools::Rectangle aShapeRect = pShape->GetCurrentBoundRect();
216 
217     discardDumpedLayout();
218     xmlDocUniquePtr pLayout = parseLayoutDump();
219     CPPUNIT_ASSERT(pLayout);
220     sal_Int32 nFlyLeft = getXPath(pLayout, "//anchored/fly/infos/bounds", "left").toInt32();
221     sal_Int32 nFlyTop = getXPath(pLayout, "//anchored/fly/infos/bounds", "top").toInt32();
222     sal_Int32 nFlyRight = getXPath(pLayout, "//anchored/fly/infos/bounds", "right").toInt32();
223     sal_Int32 nFlyBottom = getXPath(pLayout, "//anchored/fly/infos/bounds", "bottom").toInt32();
224     tools::Rectangle aFlyRect(nFlyLeft, nFlyTop, nFlyRight, nFlyBottom);
225     // Without the accompanying fix in place, this test would have failed, as aFlyRect was too wide,
226     // so it was not inside aShapeRect anymore.
227     CPPUNIT_ASSERT(aShapeRect.IsInside(aFlyRect));
228 }
229 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTextboxModification)230 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextboxModification)
231 {
232     // Load a document with a textbox in it: the layout will have to position the shape part.
233     load(DATA_DIRECTORY, "textbox-modification.docx");
234     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
235     SwDocShell* pDocShell = pTextDoc->GetDocShell();
236 
237     // Without the accompanying fix in place, this test would have failed, as the document was
238     // marked as modified right after the import.
239     CPPUNIT_ASSERT(!pDocShell->IsModified());
240 }
241 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testBtlrNestedCell)242 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrNestedCell)
243 {
244     // Load a document with a nested table, the inner A1 cell has a btlr text direction.
245     load(DATA_DIRECTORY, "btlr-nested-cell.odt");
246     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
247     SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
248     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
249     SwFrame* pPage = pLayout->GetLower();
250     SwFrame* pBody = pPage->GetLower();
251     SwFrame* pOuterTable = pBody->GetLower()->GetNext();
252     SwFrame* pInnerTable = pOuterTable->GetLower()->GetLower()->GetLower();
253 
254     // Check the paint area of the only text frame in the cell.
255     SwFrame* pTextFrame = pInnerTable->GetLower()->GetLower()->GetLower();
256     tools::Long nFrameBottom = pTextFrame->getFrameArea().Bottom();
257     SwRect aPaintArea = pTextFrame->GetPaintArea();
258 
259     // Without the accompanying fix in place, this test would have failed with:
260     // - Expected greater or equal than: 2829
261     // - Actual  : 2080
262     // i.e. part of the text frame area was not painted, hiding the actual text.
263     CPPUNIT_ASSERT_GREATEREQUAL(nFrameBottom, aPaintArea.Bottom());
264 }
265 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testKeepwithnextFullheight)266 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testKeepwithnextFullheight)
267 {
268     // The document has a heading (keep with next) and a full-page image in the next paragraph, i.e.
269     // conflicting requirements.
270     // Without the accompanying fix in place, this test would have failed with a layout loop in
271     // SwEditShell::CalcLayout().
272     load(DATA_DIRECTORY, "keepwithnext-fullheight.fodt");
273 
274     xmlDocUniquePtr pXmlDoc = parseLayoutDump();
275     CPPUNIT_ASSERT(pXmlDoc);
276     // Make sure the document has 2 pages.
277     assertXPath(pXmlDoc, "//page", 2);
278     // Heading stays on page 1 to avoid a layout loop.
279     assertXPathContent(pXmlDoc, "//page[1]/body/txt[2]", "Heading");
280     // Image stays on page 2.
281     assertXPath(pXmlDoc, "//page[2]/body/txt/anchored/fly", 1);
282 }
283 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testGutterMargin)284 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMargin)
285 {
286     // Create a document, remember the old left edge of the page print area (the rectangle that is
287     // inside margins).
288     SwDoc* pDoc = createSwDoc();
289     uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
290                                                   uno::UNO_QUERY);
291     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
292     SwFrame* pPage = pLayout->GetLower();
293     tools::Long nOldLeft = pPage->getFramePrintArea().Left();
294 
295     // Set the gutter margin to 2cm.
296     sal_Int32 nGutterMm100 = 2000;
297     xStandard->setPropertyValue("GutterMargin", uno::makeAny(nGutterMm100));
298 
299     // Verify that the new left edge is larger.
300     tools::Long nNewLeft = pPage->getFramePrintArea().Left();
301     tools::Long nGutterTwips = convertMm100ToTwip(nGutterMm100);
302     // Without the accompanying fix in place, this test would have failed with:
303     // - Expected: 1134
304     // - Actual  : 0
305     // i.e. the gutter was not added to the left margin.
306     CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
307 }
308 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testGutterTopMargin)309 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterTopMargin)
310 {
311     // Create a document, remember the old top edge of the page print area (the rectangle that is
312     // inside margins).
313     SwDoc* pDoc = createSwDoc();
314     uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
315     uno::Reference<beans::XPropertySet> xSettings(
316         xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
317     xSettings->setPropertyValue("GutterAtTop", uno::makeAny(true));
318     uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
319                                                   uno::UNO_QUERY);
320     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
321     SwFrame* pPage = pLayout->GetLower();
322     tools::Long nOldTop = pPage->getFramePrintArea().Top();
323 
324     // Set the gutter margin to 2cm.
325     sal_Int32 nGutterMm100 = 2000;
326     xStandard->setPropertyValue("GutterMargin", uno::makeAny(nGutterMm100));
327 
328     // Verify that the new top edge is larger.
329     tools::Long nNewTop = pPage->getFramePrintArea().Top();
330     tools::Long nGutterTwips = convertMm100ToTwip(nGutterMm100);
331     // Without the accompanying fix in place, this test would have failed with:
332     // - Expected: 1134
333     // - Actual  : 0
334     // i.e. the gutter was not added to the left margin.
335     CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewTop - nOldTop);
336 }
337 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testGutterMirrorMargin)338 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMirrorMargin)
339 {
340     SwDoc* pDoc = createSwDoc();
341     SwDocShell* pDocShell = pDoc->GetDocShell();
342     SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
343     pWrtShell->InsertPageBreak();
344     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
345     SwFrame* pPage = pLayout->GetLower();
346     tools::Long nOldLeft = pPage->getFramePrintArea().Left();
347     SwFrame* pPage2 = pPage->GetNext();
348     tools::Long nOldRight = pPage2->getFramePrintArea().Right();
349 
350     uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
351                                                   uno::UNO_QUERY);
352     xStandard->setPropertyValue("PageStyleLayout", uno::makeAny(style::PageStyleLayout_MIRRORED));
353     sal_Int32 nGutterMm100 = 2000;
354     xStandard->setPropertyValue("GutterMargin", uno::makeAny(nGutterMm100));
355 
356     tools::Long nNewLeft = pPage->getFramePrintArea().Left();
357     tools::Long nGutterTwips = convertMm100ToTwip(nGutterMm100);
358     CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
359     tools::Long nNewRight = pPage2->getFramePrintArea().Right();
360     // Without the accompanying fix in place, this test would have failed with:
361     // - Expected: 1134
362     // - Actual  : 0
363     // i.e. the gutter was missing on the second, mirrored page.
364     CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
365 }
366 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testRtlGutterMargin)367 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testRtlGutterMargin)
368 {
369     // Given a document with a right margin:
370     SwDoc* pDoc = createSwDoc();
371     uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
372                                                   uno::UNO_QUERY);
373     SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
374     SwFrame* pPage = pLayout->GetLower();
375     tools::Long nOldRight = pPage->getFramePrintArea().Right();
376 
377     // When setting enable RTL gutter mode and setting a gutter margin:
378     xStandard->setPropertyValue("RtlGutter", uno::makeAny(true));
379     sal_Int32 nGutterMm100 = 2000;
380     xStandard->setPropertyValue("GutterMargin", uno::makeAny(nGutterMm100));
381 
382     // Then make sure the new right edge of the print area is decreased:
383     tools::Long nNewRight = pPage->getFramePrintArea().Right();
384     tools::Long nGutterTwips = convertMm100ToTwip(nGutterMm100);
385     // Without the accompanying fix in place, this test would have failed with:
386     // - Expected: 1134
387     // - Actual  : 0
388     // i.e. the gutter was missing on the right side.
389     CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
390 }
391 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testGutterMarginPageBorder)392 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMarginPageBorder)
393 {
394 // FIXME this is 3369 on macOS -- calculate this number dynamically?
395 // FIXME this is random on Windows at the moment (in two subsequent tests without any scaling,
396 //       the actual values were 6346, 10066) - something broke metafile generation on Windows?
397 #if !defined(MACOSX) && !defined(_WIN32)
398     // Given a document with a non-0 gutter margin.
399     SwDoc* pDoc = createSwDoc();
400     uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"),
401                                                   uno::UNO_QUERY);
402     sal_Int32 nGutterMm100 = 2000;
403     xStandard->setPropertyValue("GutterMargin", uno::makeAny(nGutterMm100));
404 
405     // When setting a left border.
406     table::BorderLine2 aBorder;
407     aBorder.LineWidth = 2;
408     aBorder.OuterLineWidth = 2;
409     xStandard->setPropertyValue("LeftBorder", uno::makeAny(aBorder));
410 
411     // Then make sure border is at the left edge of the text area.
412     SwDocShell* pShell = pDoc->GetDocShell();
413     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
414     MetafileXmlDump dumper;
415     xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
416     // Without the accompanying fix in place, this test would have failed with:
417     // - Expected: 2565
418     // - Actual  : 1425
419     // Where 2565 is close to the left edge of the text area (2553).
420     assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "x", "2565");
421 #endif
422 }
423 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testTdf45908_invoice)424 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf45908_invoice)
425 {
426     // without the fix, this was hanging (and slowly consuming memory) on fileopen.
427     load(DATA_DIRECTORY, "tdf45908_invoice.odt");
428 }
429 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testVerticallyMergedCellBorder)430 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testVerticallyMergedCellBorder)
431 {
432     // Given a document with a table: 2 columns, 5 rows. B2 -> B5 is merged:
433     SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "vmerge-cell-border.docx");
434     SwDocShell* pShell = pDoc->GetDocShell();
435 
436     // When rendering the table:
437     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
438 
439     // Make sure that B4->B5 has no borders.
440     MetafileXmlDump dumper;
441     xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
442     // Collect vertical positions of all border points.
443     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
444     xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
445     std::vector<sal_Int32> aBorderPositions;
446     for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); ++i)
447     {
448         xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
449         xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("y"));
450         sal_Int32 nValue = OString(reinterpret_cast<char const*>(pValue)).toInt32();
451         aBorderPositions.push_back(nValue);
452     }
453     xmlXPathFreeObject(pXmlObj);
454     // Collect top and bottom of the B1->B3 rows.
455     xmlDocUniquePtr pLayout = parseLayoutDump();
456     pXmlObj = getXPathNode(pLayout, "//tab/row/infos/bounds");
457     pXmlNodes = pXmlObj->nodesetval;
458     std::vector<sal_Int32> aLayoutPositions;
459     for (int i = 0; i < 3; ++i)
460     {
461         xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
462         if (i == 0)
463         {
464             xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("top"));
465             sal_Int32 nValue = OString(reinterpret_cast<char const*>(pValue)).toInt32();
466             aLayoutPositions.push_back(nValue);
467         }
468         xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("bottom"));
469         sal_Int32 nValue = OString(reinterpret_cast<char const*>(pValue)).toInt32();
470         aLayoutPositions.push_back(nValue);
471     }
472     xmlXPathFreeObject(pXmlObj);
473     // Check if any border is outside the B1->B3 range.
474     for (const auto nBorderPosition : aBorderPositions)
475     {
476         bool bFound = false;
477         for (const auto nLayoutPosition : aLayoutPositions)
478         {
479             if (std::abs(nBorderPosition - nLayoutPosition) <= 15)
480             {
481                 bFound = true;
482                 break;
483             }
484         }
485         std::stringstream ss;
486         ss << "Bad vertical position for border point: " << nBorderPosition;
487         ss << " Expected positions: ";
488         for (size_t i = 0; i < aLayoutPositions.size(); ++i)
489         {
490             if (i > 0)
491             {
492                 ss << ", ";
493             }
494             ss << aLayoutPositions[i];
495         }
496 
497         // Without the accompanying fix in place, this test would have failed with:
498         // - Bad vertical position for border point: 5624 Expected positions: 3022, 3540, 4059, 4578
499         // i.e. the middle vertical border end was the bottom of B5, not bottom of B3.
500         CPPUNIT_ASSERT_MESSAGE(ss.str(), bFound);
501     }
502 }
503 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testCrashRemoveFromLayout)504 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testCrashRemoveFromLayout)
505 {
506     load(DATA_DIRECTORY, "tdf122894-4.doc");
507 }
508 
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest,testLinkedBullet)509 CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testLinkedBullet)
510 {
511     // Given a document with a graphic bullet, where the image is a linked one:
512     load(DATA_DIRECTORY, "linked-bullet.odt");
513     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
514     SwDocShell* pShell = pTextDoc->GetDocShell();
515 
516     // When rendering that document:
517     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
518 
519     // Then make sure the render result contains exactly one bitmap:
520     MetafileXmlDump aDumper;
521     xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
522     // Without the accompanying fix in place, this test would have failed with:
523     // - Expected: 1
524     // - Actual  : 0
525     // i.e. the bullet's bitmap was lost.
526     assertXPath(pXmlDoc, "//bmpexscale", 1);
527 }
528 
529 CPPUNIT_PLUGIN_IMPLEMENT();
530 
531 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
532