sc: factor out some more code
[LibreOffice.git] / sw / qa / filter / ww8 / ww8.cxx
blobbecca79534b165ae0480399933e9de201c0b94ed
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <swmodeltestbase.hxx>
12 #include <com/sun/star/awt/CharSet.hpp>
13 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
14 #include <com/sun/star/text/XTextDocument.hpp>
15 #include <com/sun/star/text/WrapTextMode.hpp>
17 #include <comphelper/propertyvalue.hxx>
19 #include <docsh.hxx>
20 #include <formatcontentcontrol.hxx>
21 #include <wrtsh.hxx>
22 #include <itabenum.hxx>
23 #include <frmmgr.hxx>
24 #include <frameformats.hxx>
25 #include <formatflysplit.hxx>
26 #include <IDocumentLayoutAccess.hxx>
27 #include <rootfrm.hxx>
28 #include <pagefrm.hxx>
29 #include <ftnfrm.hxx>
30 #include <IDocumentSettingAccess.hxx>
31 #include <sortedobjs.hxx>
32 #include <fmtwrapinfluenceonobjpos.hxx>
33 #include <ftnidx.hxx>
34 #include <tabfrm.hxx>
35 #include <cntfrm.hxx>
36 #include <colfrm.hxx>
37 #include <fmtftntx.hxx>
39 namespace
41 /**
42 * Covers sw/source/filter/ww8/ fixes.
44 * Note that these tests are meant to be simple: either load a file and assert some result or build
45 * a document model with code, export and assert that result.
47 * Keep using the various sw_<format>import/export suites for multiple filter calls inside a single
48 * test.
50 class Test : public SwModelTestBase
52 public:
53 Test()
54 : SwModelTestBase(u"/sw/qa/filter/ww8/data/"_ustr)
59 CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorderDocImport)
61 // Given a document with a border distance that is larger than the margin, when loading that
62 // document:
63 createSwDoc("negative-page-border.doc");
65 // Then make sure we map that to a negative border distance (move border from the edge of body
66 // frame towards the center of the page, not towards the edge of the page):
67 uno::Reference<container::XNameAccess> xStyleFamily = getStyles(u"PageStyles"_ustr);
68 uno::Reference<beans::XPropertySet> xStyle(xStyleFamily->getByName(u"Standard"_ustr),
69 uno::UNO_QUERY);
70 auto nTopMargin = xStyle->getPropertyValue(u"TopMargin"_ustr).get<sal_Int32>();
71 // Without the accompanying fix in place, this test would have failed with:
72 // - Expected: 501
73 // - Actual : 342
74 // i.e. the border properties influenced the margin, which was 284 twips in the sprmSDyaTop
75 // SPRM.
76 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(501), nTopMargin);
77 auto aTopBorder = xStyle->getPropertyValue(u"TopBorder"_ustr).get<table::BorderLine2>();
78 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(159), aTopBorder.LineWidth);
79 auto nTopBorderDistance = xStyle->getPropertyValue(u"TopBorderDistance"_ustr).get<sal_Int32>();
80 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
83 CPPUNIT_TEST_FIXTURE(Test, testPlainTextContentControlExport)
85 // Given a document with a plain text content control around a text portion:
86 createSwDoc();
87 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
88 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
89 uno::Reference<text::XText> xText = xTextDocument->getText();
90 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
91 xText->insertString(xCursor, u"test"_ustr, /*bAbsorb=*/false);
92 xCursor->gotoStart(/*bExpand=*/false);
93 xCursor->gotoEnd(/*bExpand=*/true);
94 uno::Reference<text::XTextContent> xContentControl(
95 xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
96 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
97 xContentControlProps->setPropertyValue(u"PlainText"_ustr, uno::Any(true));
98 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
100 // When exporting to DOCX:
101 save(u"Office Open XML Text"_ustr);
103 // Then make sure the expected markup is used:
104 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
105 // Without the accompanying fix in place, this test would have failed with:
106 // - Expected: 1
107 // - Actual : 0
108 // - XPath '//w:sdt/w:sdtPr/w:text' number of nodes is incorrect
109 // i.e. the plain text content control was turned into a rich text one on export.
110 assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:text", 1);
113 CPPUNIT_TEST_FIXTURE(Test, testDocxComboBoxContentControlExport)
115 // Given a document with a combo box content control around a text portion:
116 createSwDoc();
117 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
118 pWrtShell->InsertContentControl(SwContentControlType::COMBO_BOX);
120 // When exporting to DOCX:
121 save(u"Office Open XML Text"_ustr);
123 // Then make sure the expected markup is used:
124 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
125 // Without the accompanying fix in place, this test would have failed with:
126 // - Expected: 1
127 // - Actual : 0
128 // - XPath '//w:sdt/w:sdtPr/w:comboBox' number of nodes is incorrect
129 // i.e. the combo box content control was turned into a drop-down one on export.
130 assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:comboBox", 1);
133 CPPUNIT_TEST_FIXTURE(Test, testDocxHyperlinkShape)
135 // Given a document with a hyperlink at char positions 0 -> 6 and a shape with text anchored at
136 // char position 6:
137 createSwDoc();
138 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
139 uno::Reference<text::XText> xText = xTextDocument->getText();
140 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
141 xText->insertString(xCursor, u"beforeafter"_ustr, /*bAbsorb=*/false);
142 xCursor->gotoStart(/*bExpand=*/false);
143 xCursor->goRight(/*nCount=*/6, /*bExpand=*/true);
144 uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
145 xCursorProps->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"http://www.example.com/"_ustr));
146 xCursor->gotoStart(/*bExpand=*/false);
147 xCursor->goRight(/*nCount=*/6, /*bExpand=*/false);
148 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
149 uno::Reference<drawing::XShape> xShape(
150 xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
151 xShape->setSize(awt::Size(5000, 5000));
152 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
153 xShapeProps->setPropertyValue(u"AnchorType"_ustr,
154 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
155 uno::Reference<text::XTextContent> xShapeContent(xShape, uno::UNO_QUERY);
156 xText->insertTextContent(xCursor, xShapeContent, /*bAbsorb=*/false);
157 xShapeProps->setPropertyValue(u"TextBox"_ustr, uno::Any(true));
159 // When saving this document to DOCX, then make sure we don't crash on export (due to an
160 // assertion failure for not-well-formed XML output):
161 save(u"Office Open XML Text"_ustr);
164 CPPUNIT_TEST_FIXTURE(Test, testDocxContentControlDropdownEmptyDisplayText)
166 // Given a document with a dropdown content control, the only list item has no display text
167 // (only a value):
168 createSwDoc();
169 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
170 pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
172 // When saving to DOCX:
173 save(u"Office Open XML Text"_ustr);
175 // Then make sure that no display text attribute is written:
176 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
177 // Without the accompanying fix in place, this test would have failed with:
178 // - XPath '//w:sdt/w:sdtPr/w:dropDownList/w:listItem' unexpected 'displayText' attribute
179 // i.e. we wrote an empty attribute instead of omitting it.
180 assertXPathNoAttribute(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem", "displayText");
183 CPPUNIT_TEST_FIXTURE(Test, testDocxSymbolFontExport)
185 // Create document with symbol character and font Wingdings
186 createSwDoc();
187 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
188 uno::Reference<text::XText> xText = xTextDocument->getText();
189 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
191 xText->insertString(xCursor, u""_ustr, true);
193 uno::Reference<text::XTextRange> xRange = xCursor;
194 uno::Reference<beans::XPropertySet> xTextProps(xRange, uno::UNO_QUERY);
195 xTextProps->setPropertyValue(u"CharFontName"_ustr, uno::Any(u"Wingdings"_ustr));
196 xTextProps->setPropertyValue(u"CharFontNameAsian"_ustr, uno::Any(u"Wingdings"_ustr));
197 xTextProps->setPropertyValue(u"CharFontNameComplex"_ustr, uno::Any(u"Wingdings"_ustr));
198 xTextProps->setPropertyValue(u"CharFontCharSet"_ustr, uno::Any(awt::CharSet::SYMBOL));
200 // When exporting to DOCX:
201 save(u"Office Open XML Text"_ustr);
203 // Then make sure the expected markup is used:
204 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
206 assertXPath(pXmlDoc, "//w:p/w:r/w:sym", 1);
207 assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "font", u"Wingdings");
208 assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "char", u"f0e0");
211 CPPUNIT_TEST_FIXTURE(Test, testDocxFloatingTableExport)
213 // Given a document with a floating table:
214 createSwDoc();
215 SwDoc* pDoc = getSwDoc();
216 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
217 // Insert a table:
218 SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
219 pWrtShell->InsertTable(aTableOptions, 1, 1);
220 pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
221 // Select it:
222 pWrtShell->SelAll();
223 // Wrap in a fly:
224 SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
225 pWrtShell->StartAllAction();
226 aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
227 // Mark it as a floating table:
228 auto& rFlys = *pDoc->GetSpzFrameFormats();
229 auto pFly = rFlys[0];
230 SwAttrSet aSet(pFly->GetAttrSet());
231 aSet.Put(SwFormatFlySplit(true));
232 pDoc->SetAttr(aSet, *pFly);
233 pWrtShell->EndAllAction();
235 // When saving to docx:
236 save(u"Office Open XML Text"_ustr);
238 // Then make sure we write a floating table, not a textframe containing a table:
239 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
240 // Without the accompanying fix in place, this test would have failed with:
241 // - XPath '//w:tbl/w:tblPr/w:tblpPr' number of nodes is incorrect
242 // i.e. no floating table was exported.
243 assertXPath(pXmlDoc, "//w:tbl/w:tblPr/w:tblpPr", 1);
246 CPPUNIT_TEST_FIXTURE(Test, testDocFloatingTableImport)
248 // Given a document with 2 pages:
249 createSwDoc("floattable-compat14.doc");
251 // When laying out that document:
252 calcLayout();
254 // Make sure that the table is split between page 1 and page 2:
255 SwDoc* pDoc = getSwDoc();
256 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
257 auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
258 CPPUNIT_ASSERT(pPage1);
259 // Without the accompanying fix in place, this test would have failed, the fly frame was not
260 // split between page 1 and page 2.
261 CPPUNIT_ASSERT(pPage1->GetNext());
264 CPPUNIT_TEST_FIXTURE(Test, testWrapThroughLayoutInCell)
266 // Given a document with a shape, "keep inside text boundaries" is off, wrap type is set to
267 // "through":
268 createSwDoc();
269 uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
270 uno::Reference<drawing::XShape> xShape(
271 xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
272 xShape->setSize(awt::Size(10000, 10000));
273 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
274 xShapeProps->setPropertyValue(u"AnchorType"_ustr,
275 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
276 xShapeProps->setPropertyValue(u"Surround"_ustr, uno::Any(text::WrapTextMode_THROUGH));
277 xShapeProps->setPropertyValue(u"HoriOrientRelation"_ustr,
278 uno::Any(text::RelOrientation::FRAME));
279 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
280 xDrawPageSupplier->getDrawPage()->add(xShape);
282 // When saving to docx:
283 save(u"Office Open XML Text"_ustr);
285 // Then make sure that layoutInCell is undoing the effect of the import-time tweak:
286 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
287 // Without the accompanying fix in place, this test would have failed with:
288 // - Expected: 1
289 // - Actual : 0
290 // - attribute 'layoutInCell' of '//wp:anchor' incorrect value.
291 // i.e. layoutInCell was disabled, leading to bad layout in Word.
292 assertXPath(pXmlDoc, "//wp:anchor", "layoutInCell", u"1");
295 CPPUNIT_TEST_FIXTURE(Test, test3Endnotes)
297 // Given a DOC file with 3 endnotes:
298 createSwDoc("3endnotes.doc");
300 // When laying out that document:
301 calcLayout();
303 // Then make sure that all 3 endnotes are on the last page, like in Word:
304 SwDoc* pDoc = getSwDoc();
305 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
306 SwPageFrame* pPage = pLayout->GetLastPage();
307 SwContentFrame* pLastContent = pPage->FindLastBodyContent();
308 SwFrame* pSectionFrame = pLastContent->GetNext();
309 auto pColumnFrame = pSectionFrame->GetLower()->DynCastColumnFrame();
310 SwFootnoteContFrame* pFootnoteCont = pColumnFrame->FindFootnoteCont();
311 int nEndnotes = 0;
312 for (SwFrame* pLower = pFootnoteCont->GetLower(); pLower; pLower = pLower->GetNext())
314 ++nEndnotes;
316 // Without the accompanying fix in place, this test would have failed with:
317 // - Expected: 3
318 // - Actual : 1
319 // i.e. only 1 endnote was on the last page, the other 2 was not moved to the end of the
320 // document, which is incorrect.
321 CPPUNIT_ASSERT_EQUAL(3, nEndnotes);
324 CPPUNIT_TEST_FIXTURE(Test, testDoNotBreakWrappedTables)
326 // Given a document with the DO_NOT_BREAK_WRAPPED_TABLES compat mode enabled:
327 createSwDoc();
328 SwDoc* pDoc = getSwDoc();
329 IDocumentSettingAccess& rIDSA = pDoc->getIDocumentSettingAccess();
330 rIDSA.set(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES, true);
332 // When saving to docx:
333 save(u"Office Open XML Text"_ustr);
335 // Then make sure the compat flag is serialized:
336 xmlDocUniquePtr pXmlDoc = parseExport(u"word/settings.xml"_ustr);
337 // Without the accompanying fix in place, this test would have failed with:
338 // - Expected: 1
339 // - Actual : 0
340 // - XPath '/w:settings/w:compat/w:doNotBreakWrappedTables' number of nodes is incorrect
341 // i.e. <w:doNotBreakWrappedTables> was not written.
342 assertXPath(pXmlDoc, "/w:settings/w:compat/w:doNotBreakWrappedTables", 1);
345 CPPUNIT_TEST_FIXTURE(Test, testAllowTextAfterFloatingTableBreak)
347 // Given a document with the ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK compat mode enabled:
348 createSwDoc();
349 SwDoc* pDoc = getSwDoc();
350 IDocumentSettingAccess& rIDSA = pDoc->getIDocumentSettingAccess();
351 rIDSA.set(DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK, true);
353 // When saving to docx:
354 save(u"Office Open XML Text"_ustr);
356 // Then make sure the compat flag is serialized:
357 xmlDocUniquePtr pXmlDoc = parseExport(u"word/settings.xml"_ustr);
358 // Without the accompanying fix in place, this test would have failed with:
359 // - Expected: 1
360 // - Actual : 0
361 // - XPath '/w:settings/w:compat/w:compatSetting[@w:name='allowTextAfterFloatingTableBreak']' number of nodes is incorrect
362 // i.e. the compat flag was lost on export.
363 assertXPath(pXmlDoc,
364 "/w:settings/w:compat/w:compatSetting[@w:name='allowTextAfterFloatingTableBreak']",
365 "val", u"1");
368 CPPUNIT_TEST_FIXTURE(Test, testDOCfDontBreakWrappedTables)
370 // Given a document with fDontBreakWrappedTables:
371 // When importing that document:
372 createSwDoc("dont-break-wrapped-tables.doc");
374 // Then make sure that the matching compat flag is set:
375 SwDoc* pDoc = getSwDoc();
376 IDocumentSettingAccess& rIDSA = pDoc->getIDocumentSettingAccess();
377 bool bDontBreakWrappedTables = rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES);
378 // Without the accompanying fix in place, this test would have failed, the compat flag was not
379 // set.
380 CPPUNIT_ASSERT(bDontBreakWrappedTables);
383 CPPUNIT_TEST_FIXTURE(Test, testDOCFloatingTableHiddenAnchor)
385 // Given a document with a normal table and a floating table with a hidden anchor:
386 createSwDoc("floattable-hidden-anchor.doc");
388 // When laying out that document:
389 xmlDocUniquePtr pLayout = parseLayoutDump();
391 // Then make sure that both tables are visible:
392 // Without the accompanying fix in place, this test would have failed with:
393 // - Expected: 2
394 // - Actual : 1
395 // i.e. the floating table was lost.
396 assertXPath(pLayout, "//tab", 2);
399 CPPUNIT_TEST_FIXTURE(Test, testDOCVerticalFlyOffset)
401 // Given a document with 2 pages, a floating table on the first page and an inline table on the
402 // second page:
403 createSwDoc("floattable-vertical-fly-offset.doc");
405 // When laying out that document:
406 calcLayout();
408 // Then make sure that the tables don't overlap:
409 SwDoc* pDoc = getSwDoc();
410 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
411 auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
412 CPPUNIT_ASSERT(pPage1);
413 CPPUNIT_ASSERT(pPage1->GetSortedObjs());
414 const SwSortedObjs& rPage1Objs = *pPage1->GetSortedObjs();
415 // Page 1 has a floating table:
416 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage1Objs.size());
417 auto pPage2 = dynamic_cast<SwPageFrame*>(pPage1->GetNext());
418 // Without the accompanying fix in place, this test would have failed, there was no second page.
419 CPPUNIT_ASSERT(pPage2);
420 SwFrame* pBody2 = pPage2->GetLower();
421 SwFrame* pTable2 = pBody2->GetLower();
422 CPPUNIT_ASSERT(pTable2);
423 // Page 2 starts with an inline table:
424 CPPUNIT_ASSERT(pTable2->IsTabFrame());
427 CPPUNIT_TEST_FIXTURE(Test, testFloattableThenFloattable)
429 // Given a document that contains a floating table, immediately followed by an other floating
430 // table:
431 // When importing the document & laying it out:
432 createSwDoc("floattable-then-floattable.doc");
433 calcLayout();
435 // Then make sure that the two floating table has different anchors:
436 SwDoc* pDoc = getSwDoc();
437 auto& rFlys = *pDoc->GetSpzFrameFormats();
438 auto pFly1 = rFlys[0];
439 SwNodeOffset nFly1Anchor = pFly1->GetAttrSet().GetAnchor().GetAnchorContentNode()->GetIndex();
440 auto pFly2 = rFlys[1];
441 SwNodeOffset nFly2Anchor = pFly2->GetAttrSet().GetAnchor().GetAnchorContentNode()->GetIndex();
442 // Without the accompanying fix in place, this test would have failed with:
443 // - Expected: 42
444 // - Actual : 41
445 // i.e. the two anchor positions were the same instead of first anchor followed by the second
446 // anchor.
447 CPPUNIT_ASSERT_EQUAL(nFly1Anchor + 1, nFly2Anchor);
450 CPPUNIT_TEST_FIXTURE(Test, testFloattableOverlapNeverDOCXExport)
452 // Given a document with a floating table, overlap is not allowed:
453 createSwDoc();
454 SwDoc* pDoc = getSwDoc();
455 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
456 pWrtShell->Insert2(u"before table"_ustr);
457 // Insert a table:
458 SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
459 pWrtShell->InsertTable(aTableOptions, /*nRows=*/1, /*nCols=*/1);
460 pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
461 // Select table:
462 pWrtShell->SelAll();
463 // Wrap the table in a text frame:
464 SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
465 pWrtShell->StartAllAction();
466 aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
467 pWrtShell->EndAllAction();
468 // Allow the text frame to split:
469 pWrtShell->StartAllAction();
470 auto& rFlys = *pDoc->GetSpzFrameFormats();
471 auto pFly = rFlys[0];
472 SwAttrSet aSet(pFly->GetAttrSet());
473 aSet.Put(SwFormatFlySplit(true));
474 // Don't allow overlap:
475 SwFormatWrapInfluenceOnObjPos aInfluence;
476 aInfluence.SetAllowOverlap(false);
477 aSet.Put(aInfluence);
478 pDoc->SetAttr(aSet, *pFly);
479 pWrtShell->EndAllAction();
481 // When saving to DOCX:
482 save(u"Office Open XML Text"_ustr);
484 // Then make sure that the overlap=never markup is written:
485 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
486 // Without the accompanying fix in place, this test would have failed with:
487 // - Expected: 1
488 // - Actual : 0
489 // - XPath '//w:tblPr/w:tblOverlap' number of nodes is incorrect
490 // i.e. <w:tblOverlap> was not written.
491 assertXPath(pXmlDoc, "//w:tblPr/w:tblOverlap", "val", u"never");
494 CPPUNIT_TEST_FIXTURE(Test, testFloattableOverlapNeverDOCImport)
496 // Given a document with two floating tables, the second has sprmTFNoAllowOverlap=1 set:
497 // When importing that document:
498 createSwDoc("floattable-tbl-overlap.doc");
500 // Then make sure the second table is marked as "can't overlap":
501 SwDoc* pDoc = getSwDoc();
502 sw::FrameFormats<sw::SpzFrameFormat*>& rFlys = *pDoc->GetSpzFrameFormats();
503 sw::SpzFrameFormat* pFly = rFlys[1];
504 // Without the accompanying fix in place, this test would have failed, the fly had the default
505 // "can overlap".
506 CPPUNIT_ASSERT(!pFly->GetAttrSet().GetWrapInfluenceOnObjPos().GetAllowOverlap());
509 CPPUNIT_TEST_FIXTURE(Test, testFloattableFootnote)
511 // Given a document with a floating table and a footnote inside:
512 // When importing that document:
513 createSwDoc("floattable-footnote.doc");
515 // Then make sure we both have a fly frame and a footnote:
516 SwDoc* pDoc = getSwDoc();
517 sw::FrameFormats<sw::SpzFrameFormat*>& rFlys = *pDoc->GetSpzFrameFormats();
518 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rFlys.size());
519 SwFootnoteIdxs& rFootnotes = pDoc->GetFootnoteIdxs();
520 // Without the accompanying fix in place, this test would have failed with:
521 // - Expected: 1
522 // - Actual : 0
523 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rFootnotes.size());
526 CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInInlineTableDOC)
528 // Outer inline table on pages 1 -> 2 -> 3, inner floating table on pages 2 -> 3:
529 // When laying out that document:
530 createSwDoc("floattable-in-inlinetable.doc");
532 SwDoc* pDoc = getSwDoc();
533 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
534 auto pPage1 = pLayout->Lower()->DynCastPageFrame();
535 CPPUNIT_ASSERT(pPage1);
537 SwFrame* pBody = pPage1->FindBodyCont();
538 auto pTab = pBody->GetLower()->DynCastTabFrame();
539 CPPUNIT_ASSERT(!pTab->GetPrecede());
540 CPPUNIT_ASSERT(pTab->GetFollow());
542 auto pPage2 = pPage1->GetNext()->DynCastPageFrame();
543 CPPUNIT_ASSERT(pPage2);
545 SwFrame* pBody = pPage2->FindBodyCont();
546 auto pTab = pBody->GetLower()->DynCastTabFrame();
547 CPPUNIT_ASSERT(pTab->GetPrecede());
548 // Without the accompanying fix in place, this test would have failed, the outer table was
549 // missing on page 3.
550 CPPUNIT_ASSERT(pTab->GetFollow());
552 auto pPage3 = pPage2->GetNext()->DynCastPageFrame();
553 CPPUNIT_ASSERT(pPage3);
555 SwFrame* pBody = pPage3->FindBodyCont();
556 auto pTab = pBody->GetLower()->DynCastTabFrame();
557 CPPUNIT_ASSERT(pTab->GetPrecede());
558 CPPUNIT_ASSERT(!pTab->GetFollow());
562 CPPUNIT_TEST_FIXTURE(Test, testNullPointerDereference)
564 // Given a document with multiple pages:
565 // When loading that document:
566 // Without the accompanying fix in place, this test would have crashed due to null pointer access
567 createSwDoc("null-pointer-dereference.doc");
568 CPPUNIT_ASSERT_EQUAL(6, getPages());
571 CPPUNIT_TEST_FIXTURE(Test, testEndnotesAtSectEnd)
573 // Given a document, endnotes at collected at section end:
574 createSwDoc();
575 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
576 pWrtShell->SplitNode();
577 pWrtShell->Up(/*bSelect=*/false);
578 pWrtShell->Insert(u"x"_ustr);
579 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
580 SwSectionData aSection(SectionType::Content, pWrtShell->GetUniqueSectionName());
581 pWrtShell->StartAction();
582 SfxItemSetFixed<RES_FTN_AT_TXTEND, RES_FRAMEDIR> aSet(pWrtShell->GetAttrPool());
583 aSet.Put(SwFormatEndAtTextEnd(FTNEND_ATTXTEND));
584 pWrtShell->InsertSection(aSection, &aSet);
585 pWrtShell->EndAction();
586 pWrtShell->InsertFootnote(OUString(), /*bEndNote=*/true);
588 // When saving to DOCX:
589 save(u"Office Open XML Text"_ustr);
591 // Then make sure the endnote position is section end:
592 xmlDocUniquePtr pXmlDoc = parseExport(u"word/settings.xml"_ustr);
593 OUString aPos = getXPath(pXmlDoc, "/w:settings/w:endnotePr/w:pos", "val");
594 // Without the accompanying fix in place, this test would have failed with:
595 // - XPath '/w:settings/w:endnotePr/w:pos' number of nodes is incorrect
596 // i.e. the default position was used: document end.
597 CPPUNIT_ASSERT_EQUAL(u"sectEnd"_ustr, aPos);
600 CPPUNIT_TEST_FIXTURE(Test, testContentControlPDFDropDownEmptyItem)
602 // Given a document with a dropdown content control, one item is empty, which can't be saved to
603 // a valid DOCX:
604 createSwDoc();
605 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
606 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
607 uno::Reference<text::XText> xText = xTextDocument->getText();
608 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
609 xText->insertString(xCursor, u"test"_ustr, /*bAbsorb=*/false);
610 xCursor->gotoStart(/*bExpand=*/false);
611 xCursor->gotoEnd(/*bExpand=*/true);
612 uno::Reference<text::XTextContent> xContentControl(
613 xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
614 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
616 uno::Sequence<beans::PropertyValues> aListItems = {
618 comphelper::makePropertyValue(u"DisplayText"_ustr, uno::Any(u"red"_ustr)),
619 comphelper::makePropertyValue(u"Value"_ustr, uno::Any(u"R"_ustr)),
622 comphelper::makePropertyValue(u"DisplayText"_ustr, uno::Any(u""_ustr)),
623 comphelper::makePropertyValue(u"Value"_ustr, uno::Any(u""_ustr)),
626 xContentControlProps->setPropertyValue(u"ListItems"_ustr, uno::Any(aListItems));
628 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
630 // When saving to DOCX:
631 save(u"Office Open XML Text"_ustr);
633 // Then make sure we only emit 1 list item:
634 xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
635 // Without the accompanying fix in place, this test would have failed with:
636 // - Expected: 1
637 // - Actual : 2
638 // i.e. we emitted an empty list item, so the result can't be opened in Word.
639 assertXPath(pXmlDoc, "//w:dropDownList/w:listItem", 1);
642 CPPUNIT_TEST_FIXTURE(Test, tdf71749_with_footnote)
644 // Without the fix in place,
645 // loading the document would hang.
646 createSwDoc("tdf71749_with_footnote.doc");
647 CPPUNIT_ASSERT_EQUAL(1, getPages());
650 CPPUNIT_TEST_FIXTURE(Test, tdf71749_without_footnote)
652 // Without the fix in place,
653 // loading the document would hang.
654 createSwDoc("tdf71749_without_footnote.doc");
655 CPPUNIT_ASSERT_EQUAL(1, getPages());
659 CPPUNIT_PLUGIN_IMPLEMENT();
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */