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>
14 #include <com/sun/star/awt/FontStrikeout.hpp>
15 #include <com/sun/star/beans/NamedValue.hpp>
16 #include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
17 #include <com/sun/star/drawing/XShapes.hpp>
18 #include <com/sun/star/frame/XStorable.hpp>
19 #include <com/sun/star/text/GraphicCrop.hpp>
20 #include <com/sun/star/text/RelOrientation.hpp>
21 #include <com/sun/star/text/WritingMode2.hpp>
22 #include <com/sun/star/text/XFootnotesSupplier.hpp>
23 #include <com/sun/star/text/XTextDocument.hpp>
24 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
25 #include <com/sun/star/text/XTextField.hpp>
26 #include <com/sun/star/util/XRefreshable.hpp>
29 #include <comphelper/propertysequence.hxx>
30 #include <comphelper/scopeguard.hxx>
31 #include <comphelper/sequenceashashmap.hxx>
32 #include <o3tl/string_view.hxx>
33 #include <comphelper/propertyvalue.hxx>
35 #include <unotxdoc.hxx>
40 class Test
: public SwModelTestBase
43 Test() : SwModelTestBase("/sw/qa/extras/ooxmlexport/data/", "Office Open XML Text") {}
46 CPPUNIT_TEST_FIXTURE(Test
, testTdf150197_predefinedNumbering
)
50 // The exact numbering style doesn't matter - just any non-bullet pre-defined numbering style.
51 uno::Sequence
<beans::PropertyValue
> aPropertyValues
= comphelper::InitPropertySequence({
52 { "Style", uno::Any(OUString("Numbering 123")) },
53 { "FamilyName", uno::Any(OUString("NumberingStyles")) },
55 dispatchCommand(mxComponent
, ".uno:StyleApply", aPropertyValues
);
57 CPPUNIT_ASSERT_EQUAL(OUString("1."), getProperty
<OUString
>(getParagraph(1), "ListLabelString"));
59 saveAndReload("Office Open XML Text");
60 CPPUNIT_ASSERT_EQUAL(OUString("1."), getProperty
<OUString
>(getParagraph(1), "ListLabelString"));
63 CPPUNIT_TEST_FIXTURE(Test
, testInlineSdtHeader
)
65 // Without the accompanying fix in place, this test would have failed with an assertion failure,
66 // we produced not-well-formed XML on save.
67 loadAndSave("inline-sdt-header.docx");
70 CPPUNIT_TEST_FIXTURE(Test
, testCellSdtRedline
)
72 // Without the accompanying fix in place, this test would have failed with an assertion failure,
73 // we produced not-well-formed XML on save.
74 loadAndSave("cell-sdt-redline.docx");
77 DECLARE_OOXMLEXPORT_TEST(testTdf148956_directEndFormatting
, "tdf148956_directEndFormatting.docx")
79 SwDoc
* pDoc
= getSwDoc();
80 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
82 // pWrtShell->EndPara(/*bSelect=*/true);
83 dispatchCommand(mxComponent
, ".uno:GotoEndOfPara", {});
86 CPPUNIT_ASSERT_MESSAGE(
87 "Has direct formatting",
88 pWrtShell
->GetCursor()->GetPoint()->GetNode().GetTextNode()->GetpSwpHints());
92 CPPUNIT_ASSERT_MESSAGE(
93 "Direct formatting cleared",
94 !pWrtShell
->GetCursor()->GetPoint()->GetNode().GetTextNode()->GetpSwpHints());
97 pWrtShell
->SttPara(/*bSelect=*/true);
98 dispatchCommand(mxComponent
, ".uno:ResetAttributes", {});
100 dispatchCommand(mxComponent
, ".uno:GotoEndOfPara", {});
102 CPPUNIT_ASSERT_MESSAGE(
103 "Direct formatting cleared",
104 !pWrtShell
->GetCursor()->GetPoint()->GetNode().GetTextNode()->GetpSwpHints());
107 DECLARE_OOXMLEXPORT_TEST(testTdf147646
, "tdf147646_mergedCellNumbering.docx")
110 //Without the fix in place, it would have failed with
113 CPPUNIT_ASSERT_EQUAL(OUString("2."),parseDump("/root/page/body/tab/row[4]/cell/txt/SwParaPortion/SwLineLayout/child::*[@type='PortionType::Number']","expand"));
116 DECLARE_OOXMLEXPORT_TEST(testTdf153526_commentInNumbering
, "tdf153526_commentInNumbering.docx")
118 // an exception was prematurely ending finishParagraph, losing numbering and CRs
119 // so before the patch, this was 6.
120 CPPUNIT_ASSERT_EQUAL(13, getParagraphs());
123 DECLARE_OOXMLEXPORT_TEST(testTdf153042_largeTab
, "tdf153042_largeTab.docx")
125 // This is not the greatest test because it is slightly weird, and has a different layout
126 // in MS Word 2010/2003 than it does in Word 2019. This tests for the 2019 layout.
127 // Additionally (in Word 2019), going to paragraph properties and hitting OK changes the layout.
128 // It changes back by going to outline numbering properties and hitting OK.
130 // export does not keep the tabstop when exporting non-numbering. (Probably a good thing...)
134 const auto& pLayout
= parseLayoutDump();
135 // Ensure a large tabstop is used in the pseudo-numbering (numbering::NONE followed by tabstop)
136 assertXPath(pLayout
, "//SwFixPortion", "width", "1701");
139 DECLARE_OOXMLEXPORT_TEST(testTdf153042_noTab
, "tdf153042_noTab.docx")
141 // This is not the greatest test because it is slightly weird.
142 // It is the same as the "largeTab" file, except the paragraph properties were viewed
143 // and OK'ed, and now it looks like how Word 2010 and 2003 were laying it out.
144 // Amazingly, LO is handling both documents correctly at the moment, so let's unit test that...
146 // export does not keep the tabstop when exporting non-numbering. (Probably a good thing...)
150 const auto& pLayout
= parseLayoutDump();
151 // Ensure a miniscule tab is used in the pseudo-numbering (numbering::NONE followed by tabstop)
152 assertXPath(pLayout
, "//SwFixPortion", "width", "10");
155 DECLARE_OOXMLEXPORT_TEST(testTdf154751_dualStrikethrough
, "tdf154751_dualStrikethrough.docx")
157 auto nStrike
= getProperty
<sal_Int16
>(getRun(getParagraph(1), 1), "CharStrikeout");
158 CPPUNIT_ASSERT_EQUAL(awt::FontStrikeout::SINGLE
, nStrike
);
161 CPPUNIT_TEST_FIXTURE(Test
, testTdf154478
)
163 loadAndSave("tdf154478.docx");
164 xmlDocUniquePtr pXmlDoc
= parseExport("word/comments.xml");
166 OUString aValues
[5] = { "Comment1 seen.", "Comment2 seen.", "Comment3 NOTseen.", "Comment4 NOTseen.", "Comment5 NOTseen." };
167 for (size_t i
= 1; i
< 6; ++i
)
169 OString sPath
= "/w:comments/w:comment[" + OString::number(i
) + "]/w:p/w:r/w:t";
171 // Without the fix in place, this test would have failed with
172 // - In <>, XPath '/w:comments/w:comment[3]/w:p/w:r/w:t' not found
173 assertXPathContent(pXmlDoc
, sPath
, aValues
[i
- 1]);
177 CPPUNIT_TEST_FIXTURE(Test
, testTdf153592_columnBreaks
)
179 loadAndSave("tdf153592_columnBreaks.docx");
181 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
182 // The two column breaks were lost on import. (I wouldn't complain if they were at 3,5)
183 assertXPath(pXmlDoc
, "//w:br", 2);
186 DECLARE_OOXMLEXPORT_TEST(testTdf104394_lostTextbox
, "tdf104394_lostTextbox.docx")
188 // This was only one page b/c the textbox was missing.
189 CPPUNIT_ASSERT_EQUAL(2, getPages());
192 DECLARE_OOXMLEXPORT_TEST(testTdf146984_anchorInShape
, "tdf146984_anchorInShape.docx")
194 // This was only one page b/c the page break was missing.
195 CPPUNIT_ASSERT_EQUAL(2, getPages());
198 DECLARE_OOXMLEXPORT_TEST(testTdf127622_framePr
, "tdf127622_framePr.docx")
200 // All the paragraphs end up with the same frame definition, so put them all in one frame
201 CPPUNIT_ASSERT_EQUAL(1, getShapes());
204 DECLARE_OOXMLEXPORT_TEST(testTdf105035_framePrB
, "tdf105035_framePrB.docx")
206 // The paragraphs have different frame definitions, so they must be in separate frames,
207 // and the frames must not overlap - even though their vertical positions are identical.
208 const auto& pLayout
= parseLayoutDump();
209 sal_Int32 n1stFlyBottom
210 = getXPath(pLayout
, "//page[1]//anchored/fly[1]/infos/bounds", "bottom").toInt32();
212 = getXPath(pLayout
, "//page[1]//anchored/fly[2]/infos/bounds", "top").toInt32();
213 CPPUNIT_ASSERT_GREATER(n1stFlyBottom
, n2ndFlyTop
); //Top is greater than bottom
215 // Impossible layout TODO: the textboxes are in the wrong order.
216 OUString
sTextBox1("Preparation of Papers for IEEE TRANSACTIONS and JOURNALS (November 2012)");
217 CPPUNIT_ASSERT_MESSAGE("DID YOU FIX ME? Wow - I didn't think this would be possible!",
218 !getXPathContent(pLayout
, "//page[1]//anchored/fly[1]/txt").startsWith(sTextBox1
));
221 DECLARE_OOXMLEXPORT_TEST(testTdf105035_framePrC
, "tdf105035_framePrC.docx")
223 // The paragraphs have different frame definitions, so they must be in separate frames,
224 // and the frames DO overlap this time.
225 const auto& pLayout
= parseLayoutDump();
227 = getXPath(pLayout
, "//page[1]//anchored/fly[1]/infos/bounds", "top").toInt32();
229 = getXPath(pLayout
, "//page[1]//anchored/fly[2]/infos/bounds", "top").toInt32();
230 CPPUNIT_ASSERT_EQUAL(n1stFlyTop
, n2ndFlyTop
); //both frames start at the same position
233 DECLARE_OOXMLEXPORT_TEST(testTdf154129_framePr1
, "tdf154129_framePr1.docx")
235 for (size_t i
= 1; i
< 4; ++i
)
237 uno::Reference
<drawing::XShape
> xTextFrame
= getShape(i
);
238 // The anchor is defined in the style, and only the first style was checked, not the parents
239 auto nAnchor
= getProperty
<sal_Int16
>(xTextFrame
, "HoriOrientRelation");
240 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::PAGE_FRAME
, nAnchor
);
241 nAnchor
= getProperty
<sal_Int16
>(xTextFrame
, "VertOrientRelation");
242 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::PAGE_FRAME
, nAnchor
);
246 DECLARE_OOXMLEXPORT_TEST(testTdf154703_framePr
, "tdf154703_framePr.docx")
248 // the frame conversion had been failing, so it imported as plain text only.
249 CPPUNIT_ASSERT_EQUAL(1, getShapes());
252 DECLARE_OOXMLEXPORT_TEST(testTdf154703_framePr2
, "tdf154703_framePr2.rtf")
254 // framePr frames are always imported as fully transparent
255 CPPUNIT_ASSERT_EQUAL(sal_Int16(100), getProperty
<sal_Int16
>(getShape(1), "FillTransparence"));
257 // as opposed to testLibreOfficeHang (RTF != INVERT_BORDER_SPACING) do not duplicate left/right
258 uno::Reference
<text::XTextRange
> xTextRange(getShape(1), uno::UNO_QUERY
);
259 uno::Reference
<text::XText
> xText
= xTextRange
->getText();
260 CPPUNIT_ASSERT_EQUAL(OUString("framePr"), getParagraphOfText(1, xText
)->getString());
261 sal_Int32 nFrame
= getProperty
<sal_Int32
>(getShape(1), "LeftBorderDistance");
262 sal_Int32 nPara
= getProperty
<sal_Int32
>(getParagraphOfText(1, xText
), "LeftBorderDistance");
263 if (!isExported()) // RTF
264 CPPUNIT_ASSERT_EQUAL(sal_Int32(529), nFrame
+ nPara
);
266 CPPUNIT_ASSERT_EQUAL(sal_Int32(529*2), nFrame
+ nPara
);
270 // Fill the frame with a red background. It should be transferred on export to the paragraph
271 uno::Reference
<beans::XPropertySet
> xFrame(getShape(1), uno::UNO_QUERY
);
272 xFrame
->setPropertyValue("FillColor", uno::Any(COL_RED
));
273 xFrame
->setPropertyValue("FillTransparence", uno::Any(static_cast<sal_Int32
>(0)));
278 // exported: framed paragraphs without a background should now have a red background
279 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
280 assertXPath(pXmlDoc
, "//w:body/w:p[1]/w:pPr/w:shd", "fill", "800000");
281 assertXPath(pXmlDoc
, "//w:body/w:p[2]/w:pPr/w:shd", "fill", "548DD4"); // was blue already, no change
282 assertXPath(pXmlDoc
, "//w:body/w:p[3]/w:pPr/w:shd", "fill", "800000");
285 DECLARE_OOXMLEXPORT_TEST(testTdf154703_framePrWrapSpacing
, "tdf154703_framePrWrapSpacing.docx")
287 CPPUNIT_ASSERT_EQUAL(2, getPages());
291 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
292 // before the fix, this was half of the correct value.
293 assertXPath(pXmlDoc
, "//w:body/w:p/w:pPr/w:framePr", "hSpace", "2552");
296 DECLARE_OOXMLEXPORT_TEST(testTdf154703_framePrTextDirection
, "tdf154703_framePrTextDirection.docx")
298 CPPUNIT_ASSERT_EQUAL(sal_Int16(text::WritingMode2::TB_RL
), getProperty
<sal_Int16
>(getShape(1), "WritingMode"));
302 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
303 assertXPath(pXmlDoc
, "//w:body/w:p/w:pPr/w:textDirection", "val", "tbRl");
306 DECLARE_OOXMLEXPORT_TEST(testTdf153613_anchoredAfterPgBreak
, "tdf153613_anchoredAfterPgBreak.docx")
308 const auto& pLayout
= parseLayoutDump();
309 // An anchored TO character image anchors before the page break.
310 assertXPath(pLayout
, "//page[1]//anchored", 1);
313 DECLARE_OOXMLEXPORT_TEST(testTdf153613_anchoredAfterPgBreak2
, "tdf153613_anchoredAfterPgBreak2.docx")
315 const auto& pLayout
= parseLayoutDump();
316 // An anchored TO character image, followed by more characters moves to the following page
317 assertXPath(pLayout
, "//page[2]//anchored", 1);
320 DECLARE_OOXMLEXPORT_TEST(testTdf153613_anchoredAfterPgBreak3
, "tdf153613_anchoredAfterPgBreak3.docx")
322 const auto& pLayout
= parseLayoutDump();
323 // An anchored TO character image, with setting splitPgBreakAndParaMark moves to the following page
324 assertXPath(pLayout
, "//page[2]//anchored", 1);
327 DECLARE_OOXMLEXPORT_TEST(testTdf153613_anchoredAfterPgBreak6
, "tdf153613_anchoredAfterPgBreak6.docx")
329 // An anchored TO character image, followed by more characters moves to the following page
330 // The difference from test 2 is that it is not the first character run
331 CPPUNIT_ASSERT_EQUAL(2, getPages());
332 CPPUNIT_ASSERT_EQUAL(4, getParagraphs());
334 const auto& pLayout
= parseLayoutDump();
335 CPPUNIT_ASSERT_EQUAL(OUString("y"), getXPathContent(pLayout
, "//page[2]/body/txt[1]"));
336 assertXPath(pLayout
, "//page[1]//anchored", 1); // DID YOU FIX ME? This should be page[2]
339 DECLARE_OOXMLEXPORT_TEST(testTdf153613_inlineAfterPgBreak
, "tdf153613_inlineAfterPgBreak.docx")
341 const auto& pLayout
= parseLayoutDump();
342 // An inline AS character image moves to the following page when after the page break.
343 assertXPath(pLayout
, "//page[2]//anchored", 1);
346 DECLARE_OOXMLEXPORT_TEST(testTdf153613_inlineAfterPgBreak2
, "tdf153613_inlineAfterPgBreak2.docx")
348 // An inline AS character image moves to the following page when after the page break.
349 // The difference from the previous test is that it is not the first character run
350 CPPUNIT_ASSERT_EQUAL(2, getPages());
351 CPPUNIT_ASSERT_EQUAL(4, getParagraphs());
353 const auto& pLayout
= parseLayoutDump();
354 CPPUNIT_ASSERT_EQUAL(OUString("x"), getXPathContent(pLayout
, "//page[1]/body/txt[2]"));
355 assertXPath(pLayout
, "//page[2]//anchored", 1);
358 DECLARE_OOXMLEXPORT_TEST(testTdf153613_textboxAfterPgBreak
, "tdf153613_textboxAfterPgBreak.docx")
360 CPPUNIT_ASSERT_EQUAL(3, getParagraphs());
362 const auto& pLayout
= parseLayoutDump();
363 assertXPathContent(pLayout
, "//page[2]/body/txt", "There should be no prior carriage return.");
366 DECLARE_OOXMLEXPORT_TEST(testTdf153613_textboxAfterPgBreak2
, "tdf153613_textboxAfterPgBreak2.docx")
368 // same as previous example, except that it is the first paragraph in the section.
369 CPPUNIT_ASSERT_EQUAL(2, getParagraphs());
371 const auto& pLayout
= parseLayoutDump();
372 assertXPathContent(pLayout
, "//page[2]/body/txt", "There should be no prior carriage return.");
375 DECLARE_OOXMLEXPORT_TEST(testTdf153613_sdtAfterPgBreak
, "tdf153613_sdtAfterPgBreak.docx")
377 CPPUNIT_ASSERT_EQUAL(2, getPages());
380 DECLARE_OOXMLEXPORT_TEST(testTdf153964_topMarginAfterBreak14
, "tdf153964_topMarginAfterBreak14.docx")
382 //The top margin should only apply once in a split paragraph.
383 //In this compat14 (Windows 2010) version, it applies after the break if no prior visible run.
384 uno::Reference
<beans::XPropertySet
> xPara(getParagraph(2, "a w:br at the start of the document. Does it use 60 point top margin?"), uno::UNO_QUERY
);
385 //CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, "ParaTopMargin"));
387 xPara
.set(getParagraph(3, u
"60 pt spacing before"), uno::UNO_QUERY
);
388 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
389 CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty
<Color
>(xPara
, "ParaBackColor"));
391 // The top margin was applied to paragraph 3, so it shouldn't apply here
392 xPara
.set(getParagraph(4, u
"column break1"), uno::UNO_QUERY
);
393 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
394 CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty
<Color
>(xPara
, "ParaBackColor"));
396 xPara
.set(getParagraph(5, u
"60 pt followed by page break"), uno::UNO_QUERY
);
397 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
398 CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty
<Color
>(xPara
, "ParaBackColor"));
400 // The top margin was applied to paragraph 5, so it shouldn't apply here
401 xPara
.set(getParagraph(6, u
"page break1"), uno::UNO_QUERY
);
402 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
403 CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty
<Color
>(xPara
, "ParaBackColor"));
405 // The top margin was not applied yet, so with compat14 it should apply here.
406 xPara
.set(getParagraph(7, u
"column break2"), uno::UNO_QUERY
);
407 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
408 CPPUNIT_ASSERT_EQUAL(Color(0xe2efd9), getProperty
<Color
>(xPara
, "ParaBackColor"));
410 // In an odd twist, the w:br was actually at the end of the previous w:p, so in that case
411 // we ignore the top margin definition this time.
412 xPara
.set(getParagraph(9, u
"page break2"), uno::UNO_QUERY
);
413 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
415 // The top margin was not applied before the column break, so with compat14 it should apply here
416 xPara
.set(getParagraph(10, u
""), uno::UNO_QUERY
); // after column break
417 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
418 CPPUNIT_ASSERT_EQUAL(Color(0xfff2cc), getProperty
<Color
>(xPara
, "ParaBackColor"));
420 // In an odd twist, the w:br was actually at the end of the previous w:p, so in that case
421 // we ignore the top margin definition this time.
422 xPara
.set(getParagraph(12, u
""), uno::UNO_QUERY
); // after page break
423 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
426 DECLARE_OOXMLEXPORT_TEST(testTdf153964_topMarginAfterBreak15
, "tdf153964_topMarginAfterBreak15.docx")
428 //The top margin should only apply once (at most) in a split paragraph.
429 //In this compat15 (Windows 2013) version, it never applies after the break.
430 uno::Reference
<beans::XPropertySet
> xPara(getParagraph(2, "a w:br at the start of the document. Does it use 60 point top margin?"), uno::UNO_QUERY
);
431 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
433 xPara
.set(getParagraph(3, u
"60 pt spacing before"), uno::UNO_QUERY
);
434 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
435 CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty
<Color
>(xPara
, "ParaBackColor"));
437 // The top margin was applied to paragraph 3, so it shouldn't apply here
438 xPara
.set(getParagraph(4, u
"column break1"), uno::UNO_QUERY
);
439 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
440 CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty
<Color
>(xPara
, "ParaBackColor"));
442 xPara
.set(getParagraph(5, u
"60 pt followed by page break"), uno::UNO_QUERY
);
443 CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
444 CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty
<Color
>(xPara
, "ParaBackColor"));
446 // The top margin was applied to paragraph 5, so it shouldn't apply here
447 xPara
.set(getParagraph(6, u
"page break1"), uno::UNO_QUERY
);
448 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
449 CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty
<Color
>(xPara
, "ParaBackColor"));
451 // The top margin was not applied to paragraph 6, and with compat15 it shouldn't apply here.
452 xPara
.set(getParagraph(7, u
"column break2"), uno::UNO_QUERY
);
453 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
454 CPPUNIT_ASSERT_EQUAL(Color(0xe2efd9), getProperty
<Color
>(xPara
, "ParaBackColor"));
456 // The top margin not was applied to paragraph 8, and with compat15 it shouldn't apply here.
457 xPara
.set(getParagraph(9, u
"page break2"), uno::UNO_QUERY
);
458 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
460 // The top margin was not applied to paragraph 9, and with compat15 it shouldn't apply here.
461 xPara
.set(getParagraph(10, u
""), uno::UNO_QUERY
); // after column break
462 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
463 CPPUNIT_ASSERT_EQUAL(Color(0xfff2cc), getProperty
<Color
>(xPara
, "ParaBackColor"));
465 // The top margin was not applied to paragraph 11, and with compat15 it shouldn't apply here.
466 xPara
.set(getParagraph(12, u
""), uno::UNO_QUERY
); // after page break
467 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaTopMargin"));
470 DECLARE_OOXMLEXPORT_TEST(testTdf153964_numberingAfterBreak14
, "tdf153964_numberingAfterBreak14.docx")
472 //Numbering should only apply once in a split paragraph.
473 uno::Reference
<beans::XPropertySet
> xPara(getParagraph(2, "How numbering affected by a column break?"), uno::UNO_QUERY
);
474 //CPPUNIT_ASSERT_EQUAL(OUString("1."), getProperty<OUString>(xPara, "ListLabelString"));
475 xPara
.set(getParagraph(3, "How is numbering affected by a page break?"), uno::UNO_QUERY
);
476 //CPPUNIT_ASSERT_EQUAL(OUString("2."), getProperty<OUString>(xPara, "ListLabelString"));
477 xPara
.set(getParagraph(4, "x"), uno::UNO_QUERY
);
478 //CPPUNIT_ASSERT_EQUAL(OUString("3."), getProperty<OUString>(xPara, "ListLabelString"));
479 xPara
.set(getParagraph(5, "column break"), uno::UNO_QUERY
);
480 CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty
<OUString
>(xPara
, "ListLabelString"));
481 xPara
.set(getParagraph(6, "y"), uno::UNO_QUERY
);
482 //CPPUNIT_ASSERT_EQUAL(OUString("3."), getProperty<OUString>(xPara, "ListLabelString"));
483 xPara
.set(getParagraph(7, "page break"), uno::UNO_QUERY
);
484 CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty
<OUString
>(xPara
, "ListLabelString"));
487 DECLARE_OOXMLEXPORT_TEST(testTdf153964_firstIndentAfterBreak14
, "tdf153964_firstIndentAfterBreak14.docx")
489 //First line indents should only apply once in a split paragraph.
490 uno::Reference
<beans::XPropertySet
> xPara(getParagraph(2, "How is first line indent affected by a column break?"), uno::UNO_QUERY
);
491 CPPUNIT_ASSERT_EQUAL(sal_Int32(2000), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
492 xPara
.set(getParagraph(3, "How is first line indent affected by a page break?"), uno::UNO_QUERY
);
493 CPPUNIT_ASSERT_EQUAL(sal_Int32(2000), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
494 xPara
.set(getParagraph(4, "x"), uno::UNO_QUERY
);
495 CPPUNIT_ASSERT_EQUAL(sal_Int32(2000), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
496 xPara
.set(getParagraph(5, "column break"), uno::UNO_QUERY
);
497 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
498 xPara
.set(getParagraph(6, "y"), uno::UNO_QUERY
);
499 CPPUNIT_ASSERT_EQUAL(sal_Int32(2000), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
500 xPara
.set(getParagraph(7, "page break"), uno::UNO_QUERY
);
501 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty
<sal_Int32
>(xPara
, "ParaFirstLineIndent"));
504 CPPUNIT_TEST_FIXTURE(Test
, testTdf76022_textboxWrap
)
506 // Granted, this is an ODT with a bit of an anomaly - tables ignore fly wrapping.
507 createSwDoc("tdf76022_textboxWrap.odt");
508 CPPUNIT_ASSERT_EQUAL_MESSAGE("Did you make wrapping sane/interoperable?", 1, getPages());
510 // When saving to DOCX, the table should obey the fly wrapping
511 saveAndReload("Office Open XML Text");
513 // The fly takes up the whole page, so the table needs to shift down to the next page.
514 CPPUNIT_ASSERT_EQUAL(2, getPages());
517 DECLARE_OOXMLEXPORT_TEST(testTdf134114_allowOverlap
, "tdf134114_allowOverlap.docx")
519 // CPPUNIT_ASSERT_EQUAL(1, getPages());
520 CPPUNIT_ASSERT(!getProperty
<bool>(getShape(1), "AllowOverlap"));
521 CPPUNIT_ASSERT(getProperty
<bool>(getShape(2), "AllowOverlap"));
524 CPPUNIT_TEST_FIXTURE(Test
, testTdf149551_mongolianVert
)
526 // Given a docx document with a shape with vert="mongolianVert".
527 createSwDoc("tdf149551_mongolianVert.docx");
529 // The shape is imported as custom shape with attached frame.
530 // Without fix the shape itself had WritingMode = 0 = LR_TB,
531 // the frame in it had WritingMode = 2 = TB_RL.
532 // It should be WritingMode = 3 = TB_LR in both cases.
533 const sal_Int16
eExpected(text::WritingMode2::TB_LR
);
534 CPPUNIT_ASSERT_EQUAL(eExpected
, getProperty
<sal_Int16
>(getShape(1), "WritingMode"));
535 uno::Reference
<beans::XPropertySet
> xShapeProps(getShape(1), uno::UNO_QUERY
);
536 uno::Reference
<beans::XPropertySet
> xFrameProps(xShapeProps
->getPropertyValue("TextBoxContent"),
538 CPPUNIT_ASSERT_EQUAL(eExpected
, getProperty
<sal_Int16
>(xFrameProps
, "WritingMode"));
540 // Such shape must have vert="mongolianVert" again after saving.
541 // Without fix the orientation was vert="vert".
542 save("Office Open XML Text");
543 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
544 assertXPath(pXmlDoc
, "//wps:bodyPr", "vert", "mongolianVert");
547 DECLARE_OOXMLEXPORT_TEST(testTdf151912
, "tdf151912.docx")
549 // For now just ensure roundtrip is successful
551 //tdf#151548 - ensure block SDT preserves id (instead of random re-assignment)
554 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
555 assertXPath(pXmlDoc
, "//w:sdt//w:sdtPr/w:id", "val", "1802566103");
558 DECLARE_OOXMLEXPORT_TEST(testTdf147724
, "tdf147724.docx")
560 const auto& pLayout
= parseLayoutDump();
562 // Ensure we load field value from external XML correctly (it was "HERUNTERLADEN")
563 assertXPathContent(pLayout
, "/root/page[1]/body/txt[1]", "Placeholder -> *ABC*");
565 // This SDT has no storage id, it is not an error, but content can be taken from any suitable XML
566 // There 2 variants possible, both are acceptable
567 OUString sFieldResult
= getXPathContent(pLayout
, "/root/page[1]/body/txt[2]");
568 CPPUNIT_ASSERT(sFieldResult
== "Placeholder -> *HERUNTERLADEN*" || sFieldResult
== "Placeholder -> *ABC*");
571 DECLARE_OOXMLEXPORT_TEST(testTdf130782
, "chart.docx")
573 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xTEOSupplier(mxComponent
, uno::UNO_QUERY
);
574 uno::Reference
<container::XIndexAccess
> xAccess(xTEOSupplier
->getEmbeddedObjects(), uno::UNO_QUERY
);
575 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xAccess
->getCount());
576 uno::Reference
<container::XNamed
> xObj(xAccess
->getByIndex(0), uno::UNO_QUERY
);
578 // these properties were not imported
579 CPPUNIT_ASSERT_EQUAL(OUString("Diagramm 1"), xObj
->getName());
580 CPPUNIT_ASSERT_EQUAL(OUString("uninspired default chart"), getProperty
<OUString
>(xObj
, "Title"));
581 CPPUNIT_ASSERT_EQUAL(OUString("the description is here"), getProperty
<OUString
>(xObj
, "Description"));
584 CPPUNIT_TEST_FIXTURE(Test
, testNumberPortionFormatFromODT
)
586 // Given a document with a single paragraph, direct formatting asks 24pt font size for the
587 // numbering and the text portion:
588 createSwDoc("number-portion-format.odt");
590 // When saving to DOCX:
591 save("Office Open XML Text");
593 // Then make sure that the paragraph marker's char format has that custom font size:
594 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
595 // Without the accompanying fix in place, this test would have failed with:
598 // - XPath '//w:pPr/w:rPr/w:sz' number of nodes is incorrect
599 // i.e. <w:sz> was missing under <w:pPr>'s <w:rPr>.
600 assertXPath(pXmlDoc
, "//w:pPr/w:rPr/w:sz", "val", "48");
603 CPPUNIT_TEST_FIXTURE(Test
, testParaStyleCharPosition
)
605 // Given a loaded document where the Normal paragraph style has <w:position w:val="-1">:
606 createSwDoc("para-style-char-position.docx");
608 // When saving it back to DOCX:
609 save("Office Open XML Text");
611 // Then make sure that is not turned into a normal subscript text:
612 xmlDocUniquePtr pXmlDoc
= parseExport("word/styles.xml");
613 // Without the accompanying fix in place, this test would have failed with:
616 // - XPath '/w:styles/w:style[@w:styleId='Normal']/w:rPr/w:position' number of nodes is incorrect
617 // i.e. we wrote <w:vertAlign w:val="subscript"> instead of <w:position>.
618 assertXPath(pXmlDoc
, "/w:styles/w:style[@w:styleId='Normal']/w:rPr/w:position", "val", "-1");
621 CPPUNIT_TEST_FIXTURE(Test
, testTdf150966_regularInset
)
623 // Given a docx document with a rectangular shape with height cy="900000" (EMU), tIns="180000"
624 // and bIns="360000", resulting in 360000EMU text area height.
625 createSwDoc("tdf150966_regularInset.docx");
627 // The shape is imported as custom shape with attached frame.
628 // The insets are currently imported as margin top="4.99mm" and bottom="10mm".
629 // That should result in tIns="179640" and bIns="360000" on export.
631 // Without fix the insets were tIns="359280" and bIns="539640". The text area had 1080Emu height
632 // and Word displays no text at all.
633 save("Office Open XML Text");
634 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
635 assertXPathAttrs(pXmlDoc
, "//wps:bodyPr", { { "tIns", "179640" }, { "bIns", "360000" } });
638 CPPUNIT_TEST_FIXTURE(Test
, testTdf152636_lostPageBreak
)
640 loadAndReload("tdf152636_lostPageBreak.odt");
642 CPPUNIT_ASSERT_EQUAL(2, getPages());
645 CPPUNIT_TEST_FIXTURE(Test
, testTdf152636_lostPageBreak2
)
647 loadAndReload("tdf152636_lostPageBreak2.docx");
649 CPPUNIT_ASSERT_EQUAL(2, getPages());
652 CPPUNIT_TEST_FIXTURE(Test
, testSdtDuplicatedId
)
654 // Given a document with 2 inline <w:sdt>, with each a <w:id>:
655 createSwDoc("sdt-duplicated-id.docx");
657 // When exporting that back to DOCX:
658 save("Office Open XML Text");
660 // Then make sure we write 2 <w:sdt> and no duplicates:
661 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
662 // Without the accompanying fix in place, this test would have failed with:
665 // i.e. grab-bags introduced 2 unwanted duplicates.
666 assertXPath(pXmlDoc
, "//w:sdt", 2);
669 CPPUNIT_TEST_FIXTURE(Test
, testImageCropping
)
671 loadAndReload("crop-roundtrip.docx");
673 // the image has no cropping after roundtrip, because it has been physically cropped
674 // NB: this test should be fixed when the core feature to show image cropped when it
675 // has the "GraphicCrop" is set is implemented
676 auto aGraphicCropStruct
= getProperty
<text::GraphicCrop
>(getShape(1), "GraphicCrop");
677 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aGraphicCropStruct
.Left
);
678 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aGraphicCropStruct
.Right
);
679 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aGraphicCropStruct
.Top
);
680 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aGraphicCropStruct
.Bottom
);
683 CPPUNIT_TEST_FIXTURE(Test
, testTdf152200
)
685 // Given a document with a fly anchored after a FORMTEXT in the end of the paragraph:
686 createSwDoc("tdf152200-field+textbox.docx");
688 // When exporting that back to DOCX:
689 save("Office Open XML Text");
691 // Then make sure that fldChar with type 'end' goes prior to the at-char anchored fly.
692 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
693 const int nRunsBeforeFldCharEnd
= countXPathNodes(pXmlDoc
, "//w:fldChar[@w:fldCharType='end']/preceding::w:r");
694 CPPUNIT_ASSERT(nRunsBeforeFldCharEnd
);
695 const int nRunsBeforeAlternateContent
= countXPathNodes(pXmlDoc
, "//mc:AlternateContent/preceding::w:r");
696 CPPUNIT_ASSERT(nRunsBeforeAlternateContent
);
697 // Without the accompanying fix in place, this test would have failed with:
698 // - Expected greater than: 6
700 CPPUNIT_ASSERT_GREATER(nRunsBeforeFldCharEnd
, nRunsBeforeAlternateContent
);
701 // Make sure we only have one paragraph in body, and only three field characters overall,
702 // located directly in runs of this paragraph
703 assertXPath(pXmlDoc
, "/w:document/w:body/w:p");
704 assertXPath(pXmlDoc
, "/w:document/w:body/w:p/w:r/w:fldChar", 3);
705 assertXPath(pXmlDoc
, "//w:fldChar", 3); // no field characters elsewhere
708 CPPUNIT_TEST_FIXTURE(Test
, testTdf126477
)
710 loadAndReload("embedded_chart.odt");
712 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xTEOSupplier(mxComponent
, uno::UNO_QUERY
);
713 uno::Reference
<container::XNameAccess
> xAccess(xTEOSupplier
->getEmbeddedObjects());
714 uno::Sequence
<OUString
> aSeq(xAccess
->getElementNames());
716 // Check number of embedded objects.
717 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq
.getLength());
719 uno::Reference
<document::XEmbeddedObjectSupplier
> xEOSupplier(xAccess
->getByName(aSeq
[0]),
721 uno::Reference
<lang::XComponent
> xObj(xEOSupplier
->getEmbeddedObject());
722 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xTEOSupplier2(xObj
, uno::UNO_QUERY
);
723 uno::Reference
<container::XNameAccess
> xAccess2(xTEOSupplier2
->getEmbeddedObjects());
724 uno::Sequence
<OUString
> aSeq2(xAccess2
->getElementNames());
726 // Without the accompanying fix in place, this test would have failed with:
729 // i.e. the chart lost in the embedded document.
730 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq2
.getLength());
733 CPPUNIT_TEST_FIXTURE(Test
, testTdf152425
)
735 loadAndReload("tdf152425.docx");
737 // Check that "List Number" and "List 5" styles don't get merged
738 const OUString Para3Style
= getProperty
<OUString
>(getParagraph(3), "ParaStyleName");
739 CPPUNIT_ASSERT_EQUAL(OUString("Numbering 1"), Para3Style
);
740 const OUString Para4Style
= getProperty
<OUString
>(getParagraph(4), "ParaStyleName");
741 CPPUNIT_ASSERT_EQUAL(OUString("List 5 (WW)"), Para4Style
);
742 // Also check that "List 5" and "List Bullet 5" styles don't get merged
743 const OUString Para5Style
= getProperty
<OUString
>(getParagraph(5), "ParaStyleName");
744 CPPUNIT_ASSERT_EQUAL(OUString("List 5"), Para5Style
);
747 CPPUNIT_TEST_FIXTURE(Test
, testTdf153104
)
749 loadAndReload("tdf153104.docx");
751 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
752 OUString numId
= getXPath(pXmlDoc
, "/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId", "val");
754 xmlDocUniquePtr pXmlNum
= parseExport("word/numbering.xml");
755 OString numPath
= "/w:numbering/w:num[@w:numId='"
756 + OUStringToOString(numId
, RTL_TEXTENCODING_ASCII_US
) + "']/";
758 // Check that first level's w:lvlOverride/w:startOverride is written correctly:
759 // the list defines starting value of 10, which must be kept upon second level
761 // Without the fix, this would fail with
764 // - In <>, XPath '/w:numbering/w:num[@w:numId='3']/w:lvlOverride[@w:ilvl='0']/w:startOverride' number of nodes is incorrect
765 assertXPath(pXmlNum
, numPath
+ "w:lvlOverride[@w:ilvl='0']/w:startOverride", "val", "10");
766 assertXPath(pXmlNum
, numPath
+ "w:lvlOverride[@w:ilvl='1']/w:startOverride", "val", "1");
769 CPPUNIT_TEST_FIXTURE(Test
, testTdf153128
)
771 loadAndReload("tdf153128.docx");
773 sal_Int32 nFirstLineHeight
774 = parseDump("/root/page/body/txt[1]/SwParaPortion/SwLineLayout/SwParaPortion", "height")
776 CPPUNIT_ASSERT_GREATER(sal_Int32(0), nFirstLineHeight
);
778 // The text height is 1 pt, i.e. 20 twip; without the fix, it would fail with
779 // - Expected less than: 30
781 CPPUNIT_ASSERT_LESS(sal_Int32(30), nFirstLineHeight
);
784 CPPUNIT_TEST_FIXTURE(Test
, testExportingUnknownStyleInRedline
)
786 // This must not fail assertions
787 loadAndReload("UnknownStyleInRedline.docx");
788 // Check that the original unknown style name "UnknownStyle" is roundtripped
789 // (maybe this is wrong, because Word does not do this).
790 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
792 "/w:document/w:body/w:p/w:pPr/w:pPrChange/w:pPr/w:pStyle[@w:val='UnknownStyle']");
795 CPPUNIT_TEST_FIXTURE(Test
, testTdf148026
)
797 loadAndReload("tdf148026.fodt");
798 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
799 // Without the accompanying fix in place, this test would have failed with:
802 // - In <>, XPath '//w:hyperlink' number of nodes is incorrect
803 // i.e. a HYPERLINK field was exported instead of the hyperlink XML element.
804 assertXPath(pXmlDoc
, "//w:hyperlink", "tgtFrame", "_self");
807 CPPUNIT_TEST_FIXTURE(Test
, testTdf153664
)
809 loadAndReload("Table-of-Figures.odt");
810 CPPUNIT_ASSERT_EQUAL(1, getPages());
811 xmlDocUniquePtr pXmlStyles
= parseExport("word/styles.xml");
812 CPPUNIT_ASSERT(pXmlStyles
);
813 // Without the fix this was styleId='FigureIndex1' and name was "Figure Index 1"
814 // This led to style settings being reset when ToF was updated in Word
815 // TOF's paragraph style should be exported as "Table of Figures" as that's the default Word style name
816 assertXPath(pXmlStyles
, "/w:styles/w:style[12]", "styleId", "TableofFigures");
817 assertXPath(pXmlStyles
, "/w:styles/w:style[@w:styleId='TableofFigures']/w:name", "val", "Table of Figures");
820 DECLARE_OOXMLEXPORT_TEST(testTdf124472_hyperlink
, "tdf124472.docx")
822 CPPUNIT_ASSERT_EQUAL(OUString("https://www.libreoffice.org/"),
823 getProperty
<OUString
>(getRun(getParagraph(1), 1), "HyperLinkURL"));
824 CPPUNIT_ASSERT_EQUAL(OUString("mailto:info@libreoffice.org"),
825 getProperty
<OUString
>(getRun(getParagraph(2), 1), "HyperLinkURL"));
826 CPPUNIT_ASSERT_EQUAL(OUString(""),
827 getProperty
<OUString
>(getRun(getParagraph(3), 1), "HyperLinkURL"));
830 DECLARE_OOXMLEXPORT_TEST(testTdf135786
, "tdf135786.docx")
832 // Empty first line remain, if the section's initial dummy paragraph is not deleted:
835 CPPUNIT_ASSERT_EQUAL(2, getParagraphs());
838 DECLARE_OOXMLEXPORT_TEST(testTdf155903
, "tdf155903.odt")
840 // Without the accompanying fix in place, this test would have crashed,
841 // because the exported file was corrupted.
844 DECLARE_OOXMLEXPORT_TEST(testTdf155736
, "tdf155736_PageNumbers_footer.docx")
846 CPPUNIT_ASSERT_EQUAL(2, getPages());
848 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
849 assertXPath(pXmlDoc
, "/root/page[1]/footer");
850 assertXPath(pXmlDoc
, "/root/page[2]/footer");
851 //Without the fix in place, it would have failed with
852 //- Expected: Page * of *
854 CPPUNIT_ASSERT_EQUAL(OUString("Page * of *"), parseDump("/root/page[1]/footer/txt/text()"));
855 CPPUNIT_ASSERT_EQUAL(OUString("Page * of *"), parseDump("/root/page[2]/footer/txt/text()"));
858 CPPUNIT_PLUGIN_IMPLEMENT();
860 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */