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