tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sw / qa / extras / ww8export / ww8export.cxx
blobacfc4b674bc5c04f0f921e7ae388046c84938276
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 <sal/config.h>
12 #include <string_view>
14 #include <swmodeltestbase.hxx>
16 #include <com/sun/star/awt/FontWeight.hpp>
17 #include <com/sun/star/awt/Size.hpp>
18 #include <com/sun/star/beans/XPropertyState.hpp>
19 #include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
20 #include <com/sun/star/frame/XStorable.hpp>
21 #include <com/sun/star/graphic/XGraphic.hpp>
22 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
23 #include <com/sun/star/drawing/XControlShape.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
26 #include <com/sun/star/table/BorderLine2.hpp>
27 #include <com/sun/star/table/ShadowFormat.hpp>
28 #include <com/sun/star/table/TableBorder.hpp>
29 #include <com/sun/star/table/TableBorder2.hpp>
30 #include <com/sun/star/text/GraphicCrop.hpp>
31 #include <com/sun/star/text/WrapTextMode.hpp>
32 #include <com/sun/star/text/XFormField.hpp>
33 #include <com/sun/star/text/XTextField.hpp>
34 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
35 #include <com/sun/star/text/XTextFramesSupplier.hpp>
36 #include <com/sun/star/text/XTextTablesSupplier.hpp>
37 #include <com/sun/star/view/DocumentZoomType.hpp>
38 #include <com/sun/star/rdf/URI.hpp>
39 #include <com/sun/star/rdf/Statement.hpp>
40 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
41 #include <com/sun/star/text/XPageCursor.hpp>
43 #include <config_fonts.h>
44 #include <editeng/brushitem.hxx>
45 #include <editeng/ulspitem.hxx>
46 #include <sfx2/bindings.hxx>
47 #include <sfx2/request.hxx>
48 #include <comphelper/processfactory.hxx>
49 #include <tools/UnitConversion.hxx>
51 #include <cmdid.h>
52 #include <envimg.hxx>
53 #include <swmodule.hxx>
54 #include <view.hxx>
55 #include <wrtsh.hxx>
56 #include <fmtsrnd.hxx>
57 #include <frameformats.hxx>
58 #include <grfatr.hxx>
59 #include <pagedesc.hxx>
60 #include <ndgrf.hxx>
61 #include <bordertest.hxx>
62 #include <IDocumentSettingAccess.hxx>
63 #include <docsh.hxx>
64 #include <unotxdoc.hxx>
65 #include <o3tl/string_view.hxx>
67 class Test : public SwModelTestBase
69 public:
70 Test() : SwModelTestBase(u"/sw/qa/extras/ww8export/data/"_ustr, u"MS Word 97"_ustr) {}
73 DECLARE_WW8EXPORT_TEST(testN757910, "n757910.doc")
75 // The internal margin was larger than 0.28cm
76 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
77 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
78 uno::Reference<beans::XPropertySet> xPropertySet(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
79 sal_Int32 nValue = 0;
80 xPropertySet->getPropertyValue(u"LeftBorderDistance"_ustr) >>= nValue;
81 CPPUNIT_ASSERT_EQUAL(sal_Int32(280), nValue);
83 // The border width was zero
84 table::BorderLine2 aBorder;
85 xPropertySet->getPropertyValue(u"LeftBorder"_ustr) >>= aBorder;
86 CPPUNIT_ASSERT(aBorder.LineWidth > 0);
89 DECLARE_WW8EXPORT_TEST(testN760294, "n760294.doc")
91 uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
92 uno::Reference<container::XIndexAccess> xIndexAccess(xTextTablesSupplier->getTextTables(), uno::UNO_QUERY);
93 uno::Reference<beans::XPropertySet> xTable(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
94 table::TableBorder aTableBorder;
95 xTable->getPropertyValue(u"TableBorder"_ustr) >>= aTableBorder;
96 CPPUNIT_ASSERT_EQUAL(aTableBorder.TopLine.InnerLineWidth, aTableBorder.TopLine.OuterLineWidth);
97 CPPUNIT_ASSERT_EQUAL(aTableBorder.TopLine.InnerLineWidth, aTableBorder.TopLine.LineDistance);
100 DECLARE_WW8EXPORT_TEST(testN750255, "n750255.doc")
103 Column break without columns on the page is a page break, so check those paragraphs
104 are on page 2 (page style 'Convert 1') and page 3 (page style 'Convert 2')
105 enum = ThisComponent.Text.createEnumeration
106 enum.nextElement
107 para1 = enum.nextElement
108 xray para1.String
109 xray para1.PageStyleName
110 para2 = enum.nextElement
111 xray para2.String
112 xray para2.PageStyleName
114 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
115 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
116 // list of paragraphs
117 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
118 // go to 1st paragraph
119 (void) paraEnum->nextElement();
120 // get the 2nd and 3rd paragraph
121 uno::Reference<uno::XInterface> paragraph1(paraEnum->nextElement(), uno::UNO_QUERY);
122 uno::Reference<uno::XInterface> paragraph2(paraEnum->nextElement(), uno::UNO_QUERY);
123 uno::Reference<text::XTextRange> text1(paragraph1, uno::UNO_QUERY);
124 uno::Reference<text::XTextRange> text2(paragraph2, uno::UNO_QUERY);
125 CPPUNIT_ASSERT_EQUAL( u"one"_ustr, text1->getString());
126 CPPUNIT_ASSERT_EQUAL( u"two"_ustr, text2->getString());
127 uno::Reference<beans::XPropertySet> paragraphProperties1(paragraph1, uno::UNO_QUERY);
128 uno::Reference<beans::XPropertySet> paragraphProperties2(paragraph2, uno::UNO_QUERY);
129 OUString pageStyle1, pageStyle2;
130 paragraphProperties1->getPropertyValue( u"PageStyleName"_ustr ) >>= pageStyle1;
131 paragraphProperties2->getPropertyValue( u"PageStyleName"_ustr ) >>= pageStyle2;
132 CPPUNIT_ASSERT_EQUAL( u"Convert 1"_ustr, pageStyle1 );
133 CPPUNIT_ASSERT_EQUAL( u"Convert 2"_ustr, pageStyle2 );
137 DECLARE_WW8EXPORT_TEST(testN652364, "n652364.doc")
140 Related to 750255 above, column break with columns on the page however should be a column break.
141 enum = ThisComponent.Text.createEnumeration
142 enum.nextElement
143 para1 = enum.nextElement
144 xray para1.String
145 xray para1.PageStyleName
146 enum.nextElement
147 para2 = enum.nextElement
148 xray para2.String
149 xray para2.PageStyleName
151 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
152 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
153 // list of paragraphs
154 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
155 // get the 2nd and 4th paragraph
156 (void) paraEnum->nextElement();
157 uno::Reference<uno::XInterface> paragraph1(paraEnum->nextElement(), uno::UNO_QUERY);
158 (void) paraEnum->nextElement();
159 uno::Reference<uno::XInterface> paragraph2(paraEnum->nextElement(), uno::UNO_QUERY);
160 uno::Reference<text::XTextRange> text1(paragraph1, uno::UNO_QUERY);
161 uno::Reference<text::XTextRange> text2(paragraph2, uno::UNO_QUERY);
162 CPPUNIT_ASSERT_EQUAL( u"text1"_ustr, text1->getString());
163 CPPUNIT_ASSERT_EQUAL( u"text2"_ustr, text2->getString());
164 uno::Reference<beans::XPropertySet> paragraphProperties1(paragraph1, uno::UNO_QUERY);
165 uno::Reference<beans::XPropertySet> paragraphProperties2(paragraph2, uno::UNO_QUERY);
166 OUString pageStyle1, pageStyle2;
167 paragraphProperties1->getPropertyValue( u"PageStyleName"_ustr ) >>= pageStyle1;
168 paragraphProperties2->getPropertyValue( u"PageStyleName"_ustr ) >>= pageStyle2;
169 // "Standard" is the style for the first page (2nd is "Convert 1").
170 CPPUNIT_ASSERT_EQUAL( u"Standard"_ustr, pageStyle1 );
171 CPPUNIT_ASSERT_EQUAL( u"Standard"_ustr, pageStyle2 );
174 DECLARE_WW8EXPORT_TEST(testN757118, "n757118.doc")
177 Two pairs of horizontal rules (one absolute width, one relative width)
178 have the same width (full page width, half page width).
179 xray ThisComponent.DrawPage.getByIndex(0).BoundRect
181 uno::Reference<drawing::XShape> rule1 = getShape(1), rule2 = getShape(2), rule3 = getShape(3), rule4 = getShape(4);
182 uno::Reference<beans::XPropertySet> ruleProperties1(rule1, uno::UNO_QUERY);
183 uno::Reference<beans::XPropertySet> ruleProperties2(rule2, uno::UNO_QUERY);
184 uno::Reference<beans::XPropertySet> ruleProperties3(rule3, uno::UNO_QUERY);
185 uno::Reference<beans::XPropertySet> ruleProperties4(rule4, uno::UNO_QUERY);
186 awt::Rectangle boundRect1, boundRect2, boundRect3, boundRect4;
187 ruleProperties1->getPropertyValue( u"BoundRect"_ustr ) >>= boundRect1;
188 ruleProperties2->getPropertyValue( u"BoundRect"_ustr ) >>= boundRect2;
189 ruleProperties3->getPropertyValue( u"BoundRect"_ustr ) >>= boundRect3;
190 ruleProperties4->getPropertyValue( u"BoundRect"_ustr ) >>= boundRect4;
191 // compare, allow for < 5 differences because of rounding errors
192 CPPUNIT_ASSERT( abs( boundRect1.Width - boundRect3.Width ) < 5 );
193 CPPUNIT_ASSERT( abs( boundRect2.Width - boundRect4.Width ) < 5 );
196 DECLARE_WW8EXPORT_TEST(testTdf75539_relativeWidth, "tdf75539_relativeWidth.doc")
198 //divide everything by 10 to give a margin of error for rounding etc.
199 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
200 sal_Int32 pageWidth = getXPath(pXmlDoc, "/root/page[1]/body/infos/bounds", "width").toInt32()/10;
201 CPPUNIT_ASSERT_EQUAL_MESSAGE("Page width", sal_Int32(9354/10), pageWidth);
202 CPPUNIT_ASSERT_EQUAL_MESSAGE("100% width line", pageWidth, getXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout/SwLinePortion", "width").toInt32()/10);
203 CPPUNIT_ASSERT_EQUAL_MESSAGE("50% width line", pageWidth/2, getXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout/SwLinePortion", "width").toInt32()/10);
204 CPPUNIT_ASSERT_EQUAL_MESSAGE("25% width line", pageWidth/4, getXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout/SwLinePortion", "width").toInt32()/10);
205 CPPUNIT_ASSERT_EQUAL_MESSAGE("10% width line", pageWidth/10, getXPath(pXmlDoc ,"/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout/SwLinePortion", "width").toInt32()/10);
208 DECLARE_WW8EXPORT_TEST(testN757905, "n757905.doc")
210 // The problem was that the paragraph had only a single fly
211 // (as-character-anchored), and the height of that was smaller than the
212 // paragraph height. When in Word-compat mode, we should take the max of
213 // the two, not just the height of the fly.
215 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
216 OUString aHeight = getXPath(pXmlDoc, "/root/page/body/txt/infos/bounds", "height");
217 CPPUNIT_ASSERT(sal_Int32(31) < aHeight.toInt32());
220 DECLARE_WW8EXPORT_TEST(testAllGapsWord, "all_gaps_word.doc")
222 BorderTest borderTest;
223 BorderTest::testTheBorders(mxComponent, true);
226 DECLARE_WW8EXPORT_TEST(testI120158, "i120158.doc")
228 // See https://bz.apache.org/ooo/show_bug.cgi?id=120158
229 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
230 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
231 // list of paragraphs
232 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
233 // get contents of 1st paragraph as text
234 uno::Reference<uno::XInterface> paragraph0(paraEnum->nextElement(), uno::UNO_QUERY);
235 uno::Reference<text::XTextRange> text0(paragraph0, uno::UNO_QUERY);
236 OUString sFieldResult = text0->getString();
237 CPPUNIT_ASSERT(sFieldResult.endsWith("AM") || sFieldResult.endsWith("PM"));
240 DECLARE_WW8EXPORT_TEST(testN816603, "n816603.doc")
242 // Bugdoc was 5 pages in Word, 1 in Writer due to pointlessly wrapping the
243 // table in a frame. Exact layout may depend on fonts available, etc. --
244 // but at least make sure that our table spans over multiple pages now.
245 CPPUNIT_ASSERT(getPages() > 1);
248 DECLARE_WW8EXPORT_TEST(testPageBorder, "page-border.doc")
250 // Page border was missing (LineWidth was 0), due to wrong interpretation of pgbApplyTo.
251 table::BorderLine2 aBorder = getProperty<table::BorderLine2>(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), u"TopBorder"_ustr);
252 CPPUNIT_ASSERT_EQUAL(sal_uInt32(convertTwipToMm100(6 * 20)), aBorder.LineWidth);
255 DECLARE_WW8EXPORT_TEST(testN823651, "n823651.doc")
257 // Character height was 10pt instead of 7.5pt in the header.
258 uno::Reference<beans::XPropertySet> xStyle(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
259 uno::Reference<text::XText> xText = getProperty< uno::Reference<text::XTextRange> >(xStyle, u"HeaderTextFirst"_ustr)->getText();
260 CPPUNIT_ASSERT_EQUAL(7.5f, getProperty<float>(getParagraphOfText(1, xText), u"CharHeight"_ustr));
263 DECLARE_WW8EXPORT_TEST(testFdo36868, "fdo36868.doc")
265 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
266 OUString aText = getXPath(pXmlDoc, "/root/page/body/txt[3]/SwParaPortion/SwLineLayout/child::*[@type='PortionType::Number']", "expand");
267 // This was 1.1.
268 CPPUNIT_ASSERT_EQUAL(u"2.1"_ustr, aText);
271 DECLARE_WW8EXPORT_TEST(testListNolevel, "list-nolevel.doc")
273 // Similar to fdo#36868, numbering portions had wrong values.
274 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
275 OUString aText = getXPath(pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout/child::*[@type='PortionType::Number']", "expand");
276 // PortionType::Number was completely missing.
277 CPPUNIT_ASSERT_EQUAL(u"1."_ustr, aText);
280 DECLARE_WW8EXPORT_TEST(testHeaderApoTable, "ooo92948-1.doc")
282 // the problem was that a table anchored in the header was split across
283 // 3 text frames and quite messed up
285 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
286 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
287 uno::Reference<text::XText> xFrame(xIndexAccess->getByIndex(1), uno::UNO_QUERY);
289 // uno::Reference<text::XText> xHeaderText = getProperty<uno::Reference<text::XText>>(getStyles("PageStyles")->getByName("Standard"), "HeaderTextFirst");
290 // uno::Reference<text::XTextRange> xPara(getParagraphOfText(9, xHeaderText));
291 //TODO why does this not work
292 // uno::Reference<beans::XPropertySet> xFrame(getParagraphAnchoredObject(1, xPara));
294 uno::Reference<text::XTextContent> xTable(getParagraphOrTable(1, xFrame));
295 getCell(xTable, u"A1"_ustr, u"Aan" SAL_NEWLINE_STRING "Recipient" SAL_NEWLINE_STRING "Recipient" SAL_NEWLINE_STRING ""_ustr);
296 getCell(xTable, u"A2"_ustr, u"Kopie aan" SAL_NEWLINE_STRING ""_ustr);
297 getCell(xTable, u"A3"_ustr, u"Datum" SAL_NEWLINE_STRING "31 juli 2008"_ustr);
298 getCell(xTable, u"A4"_ustr, u"Locatie" SAL_NEWLINE_STRING "Locationr"_ustr);
299 getCell(xTable, u"A5"_ustr, u"Van" SAL_NEWLINE_STRING "Sender "_ustr);
300 getCell(xTable, u"A6"_ustr, u"Directie" SAL_NEWLINE_STRING "Department"_ustr);
301 getCell(xTable, u"A7"_ustr, u"Telefoon" SAL_NEWLINE_STRING "Phone"_ustr);
304 DECLARE_WW8EXPORT_TEST(testBnc821208, "bnc821208.doc")
306 // WW8Num1z0 earned a Symbol font, turning numbers into rectangles.
307 uno::Reference<beans::XPropertyState> xPropertyState(getStyles(u"CharacterStyles"_ustr)->getByName(u"WW8Num1z0"_ustr), uno::UNO_QUERY);
308 beans::PropertyState ePropertyState = xPropertyState->getPropertyState(u"CharFontName"_ustr);
309 // This was beans::PropertyState_DIRECT_VALUE.
310 CPPUNIT_ASSERT_EQUAL(beans::PropertyState_DEFAULT_VALUE, ePropertyState);
312 // Background of the numbering itself should have been the default, was yellow (0xffff00).
313 CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), getProperty<sal_Int32>(xPropertyState, u"CharBackColor"_ustr));
316 DECLARE_WW8EXPORT_TEST(testCp1000044, "cp1000044.doc")
318 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
319 // It wasn't possible to fill out this form.
320 CPPUNIT_ASSERT_EQUAL(false, bool(xStorable->isReadonly()));
322 SwDoc* pDoc = getSwDoc();
323 CPPUNIT_ASSERT_EQUAL( true, pDoc->getIDocumentSettingAccess().get( DocumentSettingId::PROTECT_FORM ) );
325 uno::Sequence<beans::PropertyValue> aGrabBag = getProperty< uno::Sequence<beans::PropertyValue> >(mxComponent, u"InteropGrabBag"_ustr);
326 sal_Int32 nPasswordHash = 0;
327 for ( sal_Int32 i = 0; i < aGrabBag.getLength(); ++i )
329 if ( aGrabBag[i].Name == "FormPasswordHash" )
330 aGrabBag[i].Value >>= nPasswordHash;
332 CPPUNIT_ASSERT_EQUAL_MESSAGE("Password Hash", sal_Int32(609995782), nPasswordHash);
335 DECLARE_WW8EXPORT_TEST(testBorderColours, "bordercolours.doc")
337 // The following 6 colours can only be represented with WW9 (Word 2000)
338 // BRC (BoRder Control) structures. We can tell that they have been
339 // exported/imported using a WW8 (Word '97) BRC if they instead come
340 // through as one of the 16 colours listed at this link:
341 // http://msdn.microsoft.com/en-us/library/dd773060.aspx
342 table::BorderLine2 expectedTop(0xFA670C, 0, 53, 0, 1, 53);
343 table::BorderLine2 expectedLeft(0xD99594, 0, 79, 0, 0, 79);
344 table::BorderLine2 expectedRight(0xB2A1C7, 53, 53, 53, 3, 159);
345 table::BorderLine2 expectedBottom(0xB6DDE8, 0, 106, 0, 14, 106);
346 table::BorderLine2 expectedDashedRed(0xFA670C, 0, 53, 0, 2, 53);
347 table::BorderLine2 expectedDoubleGreen(0xC2D69B, 26, 106, 26, 4, 159);
349 // Paragraph border
350 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(mxComponent,
351 uno::UNO_QUERY);
352 uno::Reference<container::XNameAccess> bookmarks =
353 bookmarksSupplier->getBookmarks();
354 uno::Reference<text::XTextContent> bookmark(
355 bookmarks->getByName(u"ParagraphBorder"_ustr), uno::UNO_QUERY);
356 uno::Reference<text::XTextRange> anchor(bookmark->getAnchor());
357 table::BorderLine2 border;
358 border = getProperty<table::BorderLine2>(anchor, u"TopBorder"_ustr);
359 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
360 border = getProperty<table::BorderLine2>(anchor, u"LeftBorder"_ustr);
361 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
362 border = getProperty<table::BorderLine2>(anchor, u"RightBorder"_ustr);
363 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
364 border = getProperty<table::BorderLine2>(anchor, u"BottomBorder"_ustr);
365 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
367 // Page border
368 OUString pageStyleName = getProperty<OUString>(anchor, u"PageStyleName"_ustr);
369 uno::Reference<style::XStyle> pageStyle(
370 getStyles(u"PageStyles"_ustr)->getByName(pageStyleName), uno::UNO_QUERY);
371 border = getProperty<table::BorderLine2>(pageStyle, u"TopBorder"_ustr);
372 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
373 border = getProperty<table::BorderLine2>(pageStyle, u"LeftBorder"_ustr);
374 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
375 border = getProperty<table::BorderLine2>(pageStyle, u"RightBorder"_ustr);
376 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
377 border = getProperty<table::BorderLine2>(pageStyle, u"BottomBorder"_ustr);
378 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
380 // Character border
381 bookmark.set(bookmarks->getByName(u"CharBorder"_ustr), uno::UNO_QUERY);
382 anchor = bookmark->getAnchor();
383 border = getProperty<table::BorderLine2>(anchor, u"CharTopBorder"_ustr);
384 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
385 border = getProperty<table::BorderLine2>(anchor, u"CharLeftBorder"_ustr);
386 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
387 border = getProperty<table::BorderLine2>(anchor, u"CharRightBorder"_ustr);
388 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
389 border = getProperty<table::BorderLine2>(anchor, u"CharBottomBorder"_ustr);
390 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
392 // Table border
393 uno::Reference<text::XTextTablesSupplier> tablesSupplier(mxComponent,
394 uno::UNO_QUERY);
395 uno::Reference<container::XNameAccess> tables =
396 tablesSupplier->getTextTables();
397 uno::Reference<text::XTextTable> table(
398 tables->getByName(u"Table1"_ustr), uno::UNO_QUERY);
399 table::TableBorder2 tableBorder = getProperty<table::TableBorder2>(
400 table, u"TableBorder2"_ustr);
401 CPPUNIT_ASSERT_EQUAL(expectedTop.Color, tableBorder.TopLine.Color);
402 CPPUNIT_ASSERT_EQUAL(expectedLeft.Color, tableBorder.LeftLine.Color);
403 CPPUNIT_ASSERT_EQUAL(expectedRight.Color, tableBorder.RightLine.Color);
404 CPPUNIT_ASSERT_EQUAL(expectedBottom.Color, tableBorder.BottomLine.Color);
406 // Table cells
407 uno::Reference<table::XCell> cell =
408 table->getCellByName(u"A2"_ustr);
409 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
410 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
411 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
412 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
413 border = getProperty<table::BorderLine2>(cell, u"BottomBorder"_ustr);
414 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
416 cell = table->getCellByName(u"B2"_ustr);
417 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
418 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
419 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
420 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
421 border = getProperty<table::BorderLine2>(cell, u"BottomBorder"_ustr);
422 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
424 cell = table->getCellByName(u"C2"_ustr);
425 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
426 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
427 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
428 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDashedRed, border);
429 border = getProperty<table::BorderLine2>(cell, u"RightBorder"_ustr);
430 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
431 border = getProperty<table::BorderLine2>(cell, u"BottomBorder"_ustr);
432 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
434 // Picture border
435 // (#if'd out as they are not yet imported with correct colours)
436 #if 0
437 bookmark.set(bookmarks->getByName("PictureBorder"),uno::UNO_QUERY);
438 anchor = bookmark->getAnchor();
439 border = getProperty<table::BorderLine2>(anchor, "TopBorder");
440 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
441 border = getProperty<table::BorderLine2>(anchor, "LeftBorder");
442 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
443 border = getProperty<table::BorderLine2>(anchor, "RightBorder");
444 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
445 border = getProperty<table::BorderLine2>(anchor, "BottomBorder");
446 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
447 #endif
450 DECLARE_WW8EXPORT_TEST(testMsoBrightnessContrast, "msobrightnesscontrast.doc")
452 uno::Reference<drawing::XShape> image = getShape(1);
453 uno::Reference<beans::XPropertySet> imageProperties(image, uno::UNO_QUERY);
454 uno::Reference<graphic::XGraphic> graphic;
455 imageProperties->getPropertyValue( u"Graphic"_ustr ) >>= graphic;
456 Graphic vclGraphic(graphic);
457 BitmapEx bitmap(vclGraphic.GetBitmapEx());
458 CPPUNIT_ASSERT_EQUAL( tools::Long(58), bitmap.GetSizePixel().Width());
459 CPPUNIT_ASSERT_EQUAL( tools::Long(320), bitmap.GetSizePixel().Height());
460 CPPUNIT_ASSERT_EQUAL( Color(206,206,206), bitmap.GetPixelColor(16,27));
461 CPPUNIT_ASSERT_EQUAL( Color(206,206,206), bitmap.GetPixelColor(22,48));
464 DECLARE_WW8EXPORT_TEST(testTdf95321, "tdf95321.doc")
466 // The problem was that there should be content in the second cell
467 // but there wasn't.
468 uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
469 uno::Reference<container::XIndexAccess> xIndexAccess(xTextTablesSupplier->getTextTables(), uno::UNO_QUERY);
470 uno::Reference<text::XTextTable> xTable(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
471 CPPUNIT_ASSERT_EQUAL(u"Second Column"_ustr, uno::Reference<text::XTextRange>(xTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY_THROW)->getString());
474 DECLARE_WW8EXPORT_TEST(testFdo77844, "fdo77844.doc")
476 uno::Reference<container::XNameAccess> pageStyles = getStyles(u"PageStyles"_ustr);
478 // get a page cursor
479 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
480 uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(
481 xModel->getCurrentController(), uno::UNO_QUERY);
482 uno::Reference<text::XPageCursor> xCursor(
483 xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
485 // check that the first page has no header
486 xCursor->jumpToFirstPage();
487 OUString pageStyleName = getProperty<OUString>(xCursor, u"PageStyleName"_ustr);
488 uno::Reference<style::XStyle> pageStyle(
489 pageStyles->getByName(pageStyleName), uno::UNO_QUERY);
490 bool headerIsOn = getProperty<bool>(pageStyle, u"HeaderIsOn"_ustr);
491 CPPUNIT_ASSERT(!headerIsOn);
493 // check that the second page has a header
494 xCursor->jumpToPage(2);
495 pageStyleName = getProperty<OUString>(xCursor, u"PageStyleName"_ustr);
496 pageStyle.set(
497 pageStyles->getByName(pageStyleName), uno::UNO_QUERY);
498 headerIsOn = getProperty<bool>(pageStyle, u"HeaderIsOn"_ustr);
499 CPPUNIT_ASSERT(headerIsOn);
501 // check that the third page has a header
502 xCursor->jumpToPage(3);
503 pageStyleName = getProperty<OUString>(xCursor, u"PageStyleName"_ustr);
504 pageStyle.set(
505 pageStyles->getByName(pageStyleName), uno::UNO_QUERY);
506 headerIsOn = getProperty<bool>(pageStyle, u"HeaderIsOn"_ustr);
507 CPPUNIT_ASSERT(headerIsOn);
509 // check that the fourth page has no header
510 // (#if'd out as this is not yet imported correctly)
511 #if 0
512 xCursor->jumpToPage(4);
513 pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
514 pageStyle.set(
515 pageStyles->getByName(pageStyleName), uno::UNO_QUERY);
516 headerIsOn = getProperty<bool>(pageStyle, "HeaderIsOn");
517 CPPUNIT_ASSERT(!headerIsOn);
518 #endif
521 DECLARE_WW8EXPORT_TEST(testFdp80333, "fdo80333.doc")
523 // Despite there is no character border, border shadow was imported
524 uno::Reference<beans::XPropertySet> xRun(getRun(getParagraph(1),1), uno::UNO_QUERY);
525 const table::ShadowFormat aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr);
526 CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location);
529 DECLARE_WW8EXPORT_TEST(testFdo81102, "fdo81102.doc")
531 // get page style at beginning of document
532 uno::Reference<text::XTextDocument> textDocument(
533 mxComponent, uno::UNO_QUERY);
534 uno::Reference<text::XTextRange> start =
535 textDocument->getText()->getStart();
536 OUString pageStyleName = getProperty<OUString>(start, u"PageStyleName"_ustr);
537 uno::Reference<style::XStyle> pageStyle(
538 getStyles(u"PageStyles"_ustr)->getByName(pageStyleName), uno::UNO_QUERY);
540 // check that left and right pages do not share the same header
541 bool headerIsShared = getProperty<bool>(pageStyle, u"HeaderIsShared"_ustr);
542 CPPUNIT_ASSERT(!headerIsShared);
545 DECLARE_WW8EXPORT_TEST(testBnc787942, "bnc787942.doc")
547 // The frame ended up on the second page instead of first.
548 // this is on page 1 in Word
549 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
550 assertXPathContent(pXmlDoc, "/root/page[1]/body/txt[4]/anchored", u"Kralicek UsacekV lese 3Zviratkov 47025");
552 CPPUNIT_ASSERT_EQUAL(text::WrapTextMode_PARALLEL, getProperty<text::WrapTextMode>(getShape(1), u"Surround"_ustr));
553 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::PAGE_FRAME, getProperty<sal_Int16>(getShape(1), u"HoriOrientRelation"_ustr));
555 // The frame is at the bottom of the text area, not at the bottom of the paper.
556 uno::Reference<drawing::XShape> xShape = getShape(1);
557 CPPUNIT_ASSERT_EQUAL(text::VertOrientation::BOTTOM,
558 getProperty<sal_Int16>(xShape, u"VertOrient"_ustr));
559 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::PAGE_PRINT_AREA,
560 getProperty<sal_Int16>(xShape, u"VertOrientRelation"_ustr));
563 DECLARE_WW8EXPORT_TEST(testTdf133504_wrapNotBeside, "tdf133504_wrapNotBeside.doc")
565 CPPUNIT_ASSERT_EQUAL(text::WrapTextMode_NONE, getProperty<text::WrapTextMode>(getShape(1), u"Surround"_ustr));
568 DECLARE_WW8EXPORT_TEST(testTdf36711_inlineFrames, "tdf36711_inlineFrames.doc")
570 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::FRAME, getProperty<sal_Int16>(getShape(1), u"VertOrientRelation"_ustr));
573 DECLARE_WW8EXPORT_TEST(testLayoutHanging, "fdo68967.doc")
575 // This must not hang in layout
578 #if HAVE_MORE_FONTS
579 DECLARE_WW8EXPORT_TEST(testfdo68963, "fdo68963.doc")
581 // The problem was that the text was not displayed.
582 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
583 CPPUNIT_ASSERT ( !getXPath(pXmlDoc, "/root/page/body/tab/row[2]/cell[1]/txt/SwParaPortion/SwLineLayout/SwFieldPortion", "expand").isEmpty() );
584 assertXPath(pXmlDoc, "/root/page/body/tab/row[2]/cell[1]/txt/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"Topic 1");
585 // all crossreference bookmarks should have a target. Shouldn't be any "Reference source not found" in the xml
586 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-1), getXPath(pXmlDoc, "/root/page/body/txt[24]/SwParaPortion/SwLineLayout/SwFieldPortion[2]","expand").indexOf("Reference source not found"));
588 #endif
590 DECLARE_WW8EXPORT_TEST(testTdf99100, "tdf99100.doc")
592 uno::Reference<text::XText> xHeaderText = getProperty< uno::Reference<text::XText> >(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), u"HeaderText"_ustr);
593 auto xField = getProperty< uno::Reference<lang::XServiceInfo> >(getRun(getParagraphOfText(1, xHeaderText), 2), u"TextField"_ustr);
594 // This failed: the second text portion wasn't a field.
595 CPPUNIT_ASSERT(xField.is());
596 CPPUNIT_ASSERT(xField->supportsService(u"com.sun.star.text.textfield.Chapter"_ustr));
599 DECLARE_WW8EXPORT_TEST(testTdf74328, "tdf74328.doc")
602 reading page numbers at sections > 255, in this case 256
604 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
605 uno::Reference<text::XTextCursor> xTextCursor = textDocument->getText()->createTextCursor( );
606 uno::Reference<beans::XPropertySet> xProps(xTextCursor, uno::UNO_QUERY);
607 uno::Any aOffset = xProps->getPropertyValue(u"PageNumberOffset"_ustr);
608 sal_Int16 nOffset = 0;
609 aOffset >>= nOffset;
610 CPPUNIT_ASSERT_EQUAL(sal_Int16(256), nOffset);
613 DECLARE_WW8EXPORT_TEST(testTdf95576, "tdf95576.doc")
615 // The first three paragraphs in this document (which are headings)
616 // should have zero indent and first line indent
617 for (int nPara = 1; nPara <= 3; ++nPara) {
618 std::cout << "nPara = " << nPara << "\n";
619 auto xPara = getParagraph(nPara);
621 // get the numbering rules effective at this paragraph
622 uno::Reference<container::XIndexReplace> xNumRules =
623 getProperty< uno::Reference<container::XIndexReplace> >(
624 xPara, u"NumberingRules"_ustr);
626 // get the numbering level of this paragraph, and the properties
627 // associated with that numbering level
628 int numLevel = getProperty<sal_Int32>(xPara, u"NumberingLevel"_ustr);
629 uno::Sequence< beans::PropertyValue > aPropertyValues;
630 xNumRules->getByIndex(numLevel) >>= aPropertyValues;
632 // Now look through these properties for the indent and
633 // first line indent settings
634 sal_Int32 nIndentAt = -1;
635 sal_Int32 nFirstLineIndent = -1;
636 for(int j = 0 ; j< aPropertyValues.getLength() ; ++j)
638 auto aProp = aPropertyValues[j];
639 std::cout << "Prop.Name: " << aProp.Name << "\n";
640 if (aProp.Name == "FirstLineIndent") {
641 nFirstLineIndent = aProp.Value.get<sal_Int32>();
642 } else if (aProp.Name == "IndentAt") {
643 nIndentAt = aProp.Value.get<sal_Int32>();
647 // The indent and first line indent should be zero
648 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nIndentAt);
649 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nFirstLineIndent);
653 DECLARE_WW8EXPORT_TEST(testTdf59896, "tdf59896.doc")
655 // This was awt::FontWeight::NORMAL, i.e. the first run wasn't bold, when it should be bold
656 CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD, getProperty<float>(getRun(getParagraph(1), 1), u"CharWeight"_ustr));
659 DECLARE_WW8EXPORT_TEST(testTdf102334, "tdf102334.doc")
661 // This was false, i.e. the first run wasn't hidden, when it should have been
662 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(getRun(getParagraph(7), 1), u"CharHidden"_ustr));
665 DECLARE_WW8EXPORT_TEST(testTdf128605, "tdf128605.doc")
667 OUString aPara1PageStyleName = getProperty<OUString>(getParagraph(1), u"PageStyleName"_ustr);
668 OUString aPara2PageStyleName = getProperty<OUString>(getParagraph(2), u"PageStyleName"_ustr);
669 // Without the accompanying fix in place, this test would have failed with:
670 // - Expected: Standard
671 // - Actual : Convert 1
672 // i.e. the continuous section break resulted in an unwanted page break.
673 CPPUNIT_ASSERT_EQUAL(aPara1PageStyleName, aPara2PageStyleName);
676 DECLARE_WW8EXPORT_TEST(testTdf112535, "tdf112535.doc")
678 SwDoc* pDoc = getSwDoc();
679 CPPUNIT_ASSERT(pDoc->GetSpzFrameFormats());
681 auto& rFormats = *pDoc->GetSpzFrameFormats();
682 CPPUNIT_ASSERT(!rFormats.empty());
684 const auto pFormat = rFormats[0];
685 CPPUNIT_ASSERT(pFormat);
687 // Without the accompanying fix in place, this test would have failed: auto-contour was enabled
688 // in Writer, but not in Word.
689 CPPUNIT_ASSERT(!pFormat->GetSurround().IsContour());
692 DECLARE_WW8EXPORT_TEST(testTdf106291, "tdf106291.doc")
694 // Table cell was merged vertically instead of horizontally -> had incorrect dimensions
695 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
696 OUString cellWidth = getXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell[1]/infos/bounds", "width");
697 OUString cellHeight = getXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell[1]/infos/bounds", "height");
698 CPPUNIT_ASSERT_EQUAL(sal_Int32(8650), cellWidth.toInt32());
699 CPPUNIT_ASSERT(cellHeight.toInt32() > 200); // height might depend on font size
702 DECLARE_WW8EXPORT_TEST(testTransparentText, "transparent-text.doc")
704 uno::Reference<text::XText> xHeaderText = getProperty<uno::Reference<text::XText>>(
705 getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), u"HeaderText"_ustr);
706 uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(3, xHeaderText);
707 // Without the accompanying fix in place, this test would have failed: transparency was set to
708 // 100%, so the text was not readable.
709 sal_Int32 nExpected(COL_BLACK);
710 sal_Int32 nActual(getProperty<sal_Int16>(xParagraph, u"CharTransparence"_ustr));
711 CPPUNIT_ASSERT_EQUAL(nExpected, nActual);
714 DECLARE_WW8EXPORT_TEST( testTdf105570, "tdf105570.doc" )
716 /*****
717 * MS-DOC specification ( https://msdn.microsoft.com/en-us/library/cc313153 )
718 * ch. 2.6.3, sprmTTableHeader:
719 * A Bool8 value that specifies that the current table row is a header row.
720 * If the value is 0x01 but sprmTTableHeader is not applied with a value of 0x01
721 * for a previous row in the same table, then this property MUST be ignored.
723 * The document have three tables with three rows.
724 * Table 1 has { 1, 0, 0 } values of the "repeat as header row" property for each row
725 * Table 2 has { 1, 1, 0 }
726 * Table 3 has { 0, 1, 1 }
727 ****/
728 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
729 SwShellCursor* pShellCursor = pWrtShell->getShellCursor( false );
730 SwNodeIndex aIdx( pShellCursor->Start()->GetNode() );
732 // Find first table
733 SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
735 CPPUNIT_ASSERT_EQUAL( sal_uInt16(1), pTableNd->GetTable().GetRowsToRepeat() );
737 // Go to next table
738 aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
739 while ( nullptr == (pTableNd = aIdx.GetNode().GetTableNode()) ) ++aIdx;
741 CPPUNIT_ASSERT_EQUAL( sal_uInt16(2), pTableNd->GetTable().GetRowsToRepeat() );
743 // Go to next table
744 aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
745 while ( nullptr == (pTableNd = aIdx.GetNode().GetTableNode()) ) ++aIdx;
747 // As first row hasn't sprmTTableHeader set, all following must be ignored, so no rows must be repeated
748 CPPUNIT_ASSERT_EQUAL( sal_uInt16(0), pTableNd->GetTable().GetRowsToRepeat() );
751 CPPUNIT_TEST_FIXTURE(Test, testTdf112346)
753 auto verify = [this]() {
754 // Multi-page table was imported as a single page.
755 CPPUNIT_ASSERT_EQUAL(2, getPages());
757 createSwDoc("tdf112346.doc");
758 verify();
759 saveAndReload(u"MS Word 97"_ustr);
760 verify();
763 DECLARE_WW8EXPORT_TEST(testTdf79639, "tdf79639.doc")
765 // Without the accompanying fix in place, this test would have failed with:
766 // - Expected: 1
767 // - Actual : 0
768 // as the floating table in the header wasn't converted to a TextFrame.
769 CPPUNIT_ASSERT_EQUAL(1, getShapes());
772 DECLARE_WW8EXPORT_TEST(testTdf122425_2, "tdf122425_2.doc")
774 // This is for graphic objects in headers/footers
775 SwDoc* pDoc = getSwDoc();
776 SwPosFlyFrames aPosFlyFrames = pDoc->GetAllFlyFormats(nullptr, false);
777 // There is one fly frame in the document: the text box
778 CPPUNIT_ASSERT_EQUAL(size_t(1), aPosFlyFrames.size());
779 for (const SwPosFlyFrame& rPosFlyFrame : aPosFlyFrames)
781 const SwFrameFormat& rFormat = rPosFlyFrame.GetFormat();
782 const SfxPoolItem* pItem = nullptr;
784 // Check for correct explicitly-set values of UL spacings. Previously this was "DEFAULT",
785 // and resulted in inherited values (114 = 2 mm) used.
786 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, rFormat.GetItemState(RES_UL_SPACE, false, &pItem));
787 auto pUL = static_cast<const SvxULSpaceItem*>(pItem);
788 CPPUNIT_ASSERT(pUL);
789 CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), pUL->GetUpper());
790 CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), pUL->GetLower());
794 DECLARE_WW8EXPORT_TEST(testTdf130262, "tdf130262.doc")
796 // We had an infinite layout loop
799 DECLARE_WW8EXPORT_TEST(testTdf38778, "tdf38778_properties_in_run_for_field.doc")
801 CPPUNIT_ASSERT_EQUAL(10.0f, getProperty<float>(getRun(getParagraph(1), 1), u"CharHeight"_ustr));
802 CPPUNIT_ASSERT_EQUAL(u"Courier New"_ustr, getProperty<OUString>(getRun(getParagraph(1), 1), u"CharFontName"_ustr));
805 DECLARE_WW8EXPORT_TEST(testN325936, "n325936.doc")
808 * The problem was that the transparent background of the drawing in the
809 * header was exported as non-transparent.
811 * xray ThisComponent.DrawPage(0).BackColorTransparency
814 CPPUNIT_ASSERT_EQUAL(Color(0x000064), getProperty< Color >(getShape(1), u"BackColorTransparency"_ustr));
817 DECLARE_WW8EXPORT_TEST(testTscp, "tscp.doc")
819 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
820 uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, u"urn:bails"_ustr);
821 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(mxComponent, uno::UNO_QUERY);
822 uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = xDocumentMetadataAccess->getMetadataGraphsWithType(xType);
823 // This failed, no graphs had the urn:bails type.
824 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aGraphNames.getLength());
825 uno::Reference<rdf::XURI> xGraphName = aGraphNames[0];
826 uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
828 // No RDF statement on the first paragraph.
829 uno::Reference<rdf::XResource> xParagraph(getParagraph(1), uno::UNO_QUERY);
830 uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(xParagraph, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
831 CPPUNIT_ASSERT_EQUAL(false, static_cast<bool>(xStatements->hasMoreElements()));
833 // 3 RDF statements on the second paragraph.
834 xParagraph.set(getParagraph(2), uno::UNO_QUERY);
835 std::map<OUString, OUString> aExpectedStatements =
837 {"urn:bails:ExportControl:BusinessAuthorization:Identifier", "urn:example:tscp:1"},
838 {"urn:bails:ExportControl:BusinessAuthorizationCategory:Identifier", "urn:example:tscp:1:confidential"},
839 {"urn:bails:ExportControl:Authorization:StartValidity", "2015-11-27"}
841 std::map<OUString, OUString> aActualStatements;
842 xStatements = xGraph->getStatements(xParagraph, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
843 while (xStatements->hasMoreElements())
845 rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
846 aActualStatements[aStatement.Predicate->getNamespace() + aStatement.Predicate->getLocalName()] = aStatement.Object->getStringValue();
848 CPPUNIT_ASSERT(bool(aExpectedStatements == aActualStatements));
850 // No RDF statement on the third paragraph.
851 xParagraph.set(getParagraph(3), uno::UNO_QUERY);
852 xStatements = xGraph->getStatements(xParagraph, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
853 CPPUNIT_ASSERT_EQUAL(false, static_cast<bool>(xStatements->hasMoreElements()));
856 CPPUNIT_TEST_FIXTURE(Test, testFdo45724)
858 loadAndReload("fdo45724.odt");
859 CPPUNIT_ASSERT_EQUAL(1, getShapes());
860 CPPUNIT_ASSERT_EQUAL(1, getPages());
861 // The text and background color of the control shape was not correct.
862 uno::Reference<drawing::XControlShape> xControlShape(getShape(1), uno::UNO_QUERY);
863 uno::Reference<form::validation::XValidatableFormComponent> xComponent(xControlShape->getControl(), uno::UNO_QUERY);
864 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getProperty<Color>(xComponent, u"BackgroundColor"_ustr));
865 CPPUNIT_ASSERT_EQUAL(u"xxx"_ustr, xComponent->getCurrentValue().get<OUString>());
868 CPPUNIT_TEST_FIXTURE(Test, testTdf136620)
870 loadAndReload("tdf136620.odt");
871 CPPUNIT_ASSERT_EQUAL(1, getShapes());
872 CPPUNIT_ASSERT_EQUAL(1, getPages());
874 uno::Reference<drawing::XShape> xShape = getShape(1);
876 CPPUNIT_ASSERT_EQUAL(sal_Int32(5636), xShape->getPosition().X);
877 CPPUNIT_ASSERT_EQUAL(sal_Int32(1826), xShape->getPosition().Y);
878 CPPUNIT_ASSERT_EQUAL(sal_Int32(6630), xShape->getSize().Height);
880 // Without the fix in place, this test would have failed with
881 // - Expected: 5853
882 // - Actual : 850
883 CPPUNIT_ASSERT_EQUAL(sal_Int32(5853), xShape->getSize().Width);
886 CPPUNIT_TEST_FIXTURE(Test, testFdo46020)
888 loadAndReload("fdo46020.odt");
889 CPPUNIT_ASSERT_EQUAL(1, getPages());
890 // The footnote in that document wasn't exported, check that it is actually exported
891 uno::Reference<text::XFootnotesSupplier> xFootnotesSupplier(mxComponent, uno::UNO_QUERY);
892 uno::Reference<container::XIndexAccess> xFootnotes = xFootnotesSupplier->getFootnotes();
893 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xFootnotes->getCount());
896 DECLARE_WW8EXPORT_TEST(testZoom, "zoom.doc")
898 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
899 uno::Reference<view::XViewSettingsSupplier> xViewSettingsSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
900 uno::Reference<beans::XPropertySet> xPropertySet(xViewSettingsSupplier->getViewSettings());
901 sal_Int16 nValue = 0;
902 xPropertySet->getPropertyValue(u"ZoomValue"_ustr) >>= nValue;
903 CPPUNIT_ASSERT_EQUAL(sal_Int16(42), nValue);
906 DECLARE_WW8EXPORT_TEST(testZoomType, "zoomtype.doc")
908 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
909 uno::Reference<view::XViewSettingsSupplier> xViewSettingsSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
910 uno::Reference<beans::XPropertySet> xPropertySet(xViewSettingsSupplier->getViewSettings());
911 sal_Int16 nValue = 0;
912 xPropertySet->getPropertyValue(u"ZoomType"_ustr) >>= nValue;
913 CPPUNIT_ASSERT_EQUAL(sal_Int16(view::DocumentZoomType::PAGE_WIDTH), nValue);
916 DECLARE_WW8EXPORT_TEST(test56513, "fdo56513.doc")
918 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
919 assertXPathContent(pXmlDoc, "/root/page[1]/header/txt/text()", u"This is the header of the first section");
920 assertXPathContent(pXmlDoc, "/root/page[2]/header/txt/text()", u"This is the first page header of the second section");
921 assertXPathContent(pXmlDoc, "/root/page[3]/header/txt/text()", u"This is the non-first-page header of the second section");
924 DECLARE_WW8EXPORT_TEST(testNewPageStylesTable, "new-page-styles.doc")
926 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
927 assertXPathContent(pXmlDoc, "/root/page[1]/header/txt/text()", u"Sigma Space Performance Goals and Results (Page 1)*");
928 assertXPathContent(pXmlDoc, "/root/page[2]/header/txt/text()", u"Sigma Space Performance Assessment (Page 2)****");
929 assertXPathContent(pXmlDoc, "/root/page[3]/header/txt/text()", u"Sigma Space Performance Goals: Next Year (Page 3)*******");
932 CPPUNIT_TEST_FIXTURE(Test, testFdo42144)
934 loadAndReload("fdo42144.odt");
935 CPPUNIT_ASSERT_EQUAL(1, getPages());
936 // Footer wasn't disabled -- instead empty footer was exported.
937 uno::Reference<beans::XPropertySet> xStyle(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
938 CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xStyle, u"FooterIsOn"_ustr));
941 CPPUNIT_TEST_FIXTURE(Test, testCharacterBorder)
943 loadAndReload("charborder.odt");
944 CPPUNIT_ASSERT_EQUAL(1, getPages());
945 uno::Reference<beans::XPropertySet> xRun(getRun(getParagraph(1),1), uno::UNO_QUERY);
946 // WW8 has just one border attribute (sprmCBrc) for text border so all side has
947 // the same border
948 // Border
950 const table::BorderLine2 aTopBorder = getProperty<table::BorderLine2>(xRun,u"CharTopBorder"_ustr);
951 CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF3333,0,318,0,0,318), aTopBorder);
952 CPPUNIT_ASSERT_BORDER_EQUAL(aTopBorder, getProperty<table::BorderLine2>(xRun,u"CharLeftBorder"_ustr));
953 CPPUNIT_ASSERT_BORDER_EQUAL(aTopBorder, getProperty<table::BorderLine2>(xRun,u"CharBottomBorder"_ustr));
954 CPPUNIT_ASSERT_BORDER_EQUAL(aTopBorder, getProperty<table::BorderLine2>(xRun,u"CharRightBorder"_ustr));
957 // Padding (dptSpace) it is constant 0
959 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xRun,u"CharTopBorderDistance"_ustr));
960 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xRun,u"CharLeftBorderDistance"_ustr));
961 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xRun,u"CharBottomBorderDistance"_ustr));
962 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xRun,u"CharRightBorderDistance"_ustr));
965 // Shadow (fShadow)
966 /* WW8 use just one bool value for shadow so the next conversions
967 are made during an export-import round
968 color: any -> black
969 location: any -> bottom-right
970 width: any -> border width */
972 const table::ShadowFormat aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr);
973 CPPUNIT_ASSERT_EQUAL(COL_BLACK, Color(ColorTransparency, aShadow.Color));
974 CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadow.Location);
975 CPPUNIT_ASSERT_EQUAL(sal_Int16(318), aShadow.ShadowWidth);
979 CPPUNIT_TEST_FIXTURE(Test, testTdf41542_imagePadding)
981 loadAndReload("tdf41542_imagePadding.odt");
982 CPPUNIT_ASSERT_EQUAL(3, getShapes());
983 CPPUNIT_ASSERT_EQUAL(1, getPages());
984 // borderlessImage - image WITHOUT BORDERS : simulate padding with -crop
985 text::GraphicCrop crop = getProperty<text::GraphicCrop>(getShape(2), u"GraphicCrop"_ustr);
986 CPPUNIT_ASSERT( crop.Left != 0 );
987 CPPUNIT_ASSERT( crop.Right != 0 );
988 CPPUNIT_ASSERT_EQUAL( crop.Left, crop.Top );
989 CPPUNIT_ASSERT_EQUAL( crop.Right, crop.Bottom );
990 CPPUNIT_ASSERT_EQUAL( crop.Left, crop.Right );
992 // borderedImage - image WITH BORDERS : simulate padding with -crop
993 crop = getProperty<text::GraphicCrop>(getShape(3), u"GraphicCrop"_ustr);
994 CPPUNIT_ASSERT( crop.Left != 0 );
995 CPPUNIT_ASSERT( crop.Right != 0 );
996 CPPUNIT_ASSERT_EQUAL( crop.Left, crop.Top );
997 CPPUNIT_ASSERT_EQUAL( crop.Right, crop.Bottom );
998 CPPUNIT_ASSERT_EQUAL( crop.Left, crop.Right );
1000 // tdf#147819 - page background should not be forced to white color
1001 const SwFrameFormat &rFormat = getSwDoc()->GetPageDesc(0).GetMaster();
1002 CPPUNIT_ASSERT(!rFormat.GetItemIfSet(RES_BACKGROUND));
1005 DECLARE_WW8EXPORT_TEST(testFdo77454, "fdo77454.doc")
1008 // check negative crops round-trip (with border/padding of 1)
1009 text::GraphicCrop const crop =
1010 getProperty<text::GraphicCrop>(getShape(1), u"GraphicCrop"_ustr);
1011 CPPUNIT_ASSERT(abs(sal_Int32( -439) - crop.Left) <= 2);
1012 CPPUNIT_ASSERT(abs(sal_Int32(-7040) - crop.Right) <= 2);
1013 CPPUNIT_ASSERT(abs(sal_Int32( -220) - crop.Top) <= 2);
1014 CPPUNIT_ASSERT(abs(sal_Int32(-7040) - crop.Bottom) <= 2);
1018 // check positive crops round-trip (with padding of 1)
1019 text::GraphicCrop const crop =
1020 getProperty<text::GraphicCrop>(getShape(2), u"GraphicCrop"_ustr);
1021 CPPUNIT_ASSERT(abs(sal_Int32( 326) - crop.Left) <= 3);
1022 CPPUNIT_ASSERT(abs(sal_Int32(1208) - crop.Right) <= 3);
1023 CPPUNIT_ASSERT(abs(sal_Int32(1635) - crop.Top) <= 3);
1024 CPPUNIT_ASSERT(abs(sal_Int32( 95) - crop.Bottom) <= 3);
1028 DECLARE_WW8EXPORT_TEST(testFdo59530, "fdo59530.doc")
1030 // See ooxmlexport's testFdo38244().
1031 // Test comment range feature.
1032 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
1033 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
1034 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
1035 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
1036 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1037 xRunEnum->nextElement();
1038 uno::Reference<beans::XPropertySet> xPropertySet(xRunEnum->nextElement(), uno::UNO_QUERY);
1039 CPPUNIT_ASSERT_EQUAL(u"Annotation"_ustr, getProperty<OUString>(xPropertySet, u"TextPortionType"_ustr));
1040 xRunEnum->nextElement();
1041 xPropertySet.set(xRunEnum->nextElement(), uno::UNO_QUERY);
1042 CPPUNIT_ASSERT_EQUAL(u"AnnotationEnd"_ustr, getProperty<OUString>(xPropertySet, u"TextPortionType"_ustr));
1044 // Test initials.
1045 uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
1046 uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields());
1047 uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
1048 xPropertySet.set(xFields->nextElement(), uno::UNO_QUERY);
1049 CPPUNIT_ASSERT_EQUAL(u"M"_ustr, getProperty<OUString>(xPropertySet, u"Initials"_ustr));
1051 // Test commented text range which spans over more text nodes
1052 // Comment starts in the second paragraph
1053 xRunEnumAccess.set(xParaEnum->nextElement(), uno::UNO_QUERY);
1054 xRunEnum = xRunEnumAccess->createEnumeration();
1055 xRunEnum->nextElement();
1056 xPropertySet.set(xRunEnum->nextElement(), uno::UNO_QUERY);
1057 CPPUNIT_ASSERT_EQUAL(u"Annotation"_ustr, getProperty<OUString>(xPropertySet, u"TextPortionType"_ustr));
1058 // Comment ends in the third paragraph
1059 xRunEnumAccess.set(xParaEnum->nextElement(), uno::UNO_QUERY);
1060 xRunEnum = xRunEnumAccess->createEnumeration();
1061 xRunEnum->nextElement();
1062 xPropertySet.set(xRunEnum->nextElement(), uno::UNO_QUERY);
1063 CPPUNIT_ASSERT_EQUAL(u"AnnotationEnd"_ustr, getProperty<OUString>(xPropertySet, u"TextPortionType"_ustr));
1066 DECLARE_WW8EXPORT_TEST(testCommentsNested, "comments-nested.doc")
1068 uno::Reference<beans::XPropertySet> xOuter = getProperty< uno::Reference<beans::XPropertySet> >(getRun(getParagraph(1), 2), u"TextField"_ustr);
1069 CPPUNIT_ASSERT_EQUAL(u"Outer"_ustr, getProperty<OUString>(xOuter, u"Content"_ustr));
1071 uno::Reference<beans::XPropertySet> xInner = getProperty< uno::Reference<beans::XPropertySet> >(getRun(getParagraph(1), 4), u"TextField"_ustr);
1072 CPPUNIT_ASSERT_EQUAL(u"Inner"_ustr, getProperty<OUString>(xInner, u"Content"_ustr));
1075 CPPUNIT_TEST_FIXTURE(Test, testBorderColoursExport)
1077 loadAndReload("bordercolours.odt");
1078 CPPUNIT_ASSERT_EQUAL(1, getShapes());
1079 CPPUNIT_ASSERT_EQUAL(1, getPages());
1080 // This is very close to testBorderColours in ww8import.cxx, but for export
1082 // The following 6 colours can only be represented with WW9 (Word 2000)
1083 // BRC (BoRder Control) structures. We can tell that they have been
1084 // exported/imported using a WW8 (Word '97) BRC if they instead come
1085 // through as one of the 16 colours listed at this link:
1086 // http://msdn.microsoft.com/en-us/library/dd773060.aspx
1087 table::BorderLine2 expectedTop(0xFA670C, 0, 53, 0, 1, 53);
1088 table::BorderLine2 expectedLeft(0xD99594, 0, 79, 0, 0, 79);
1089 table::BorderLine2 expectedRight(0xB2A1C7, 53, 53, 53, 3, 159);
1090 table::BorderLine2 expectedBottom(0xB6DDE8, 0, 106, 0, 14, 106);
1091 table::BorderLine2 expectedDashedRed(0xFA670C, 0, 53, 0, 2, 53);
1092 table::BorderLine2 expectedDoubleGreen(0xC2D69B, 26, 106, 26, 4, 159);
1094 // Paragraph border
1095 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(mxComponent,
1096 uno::UNO_QUERY);
1097 uno::Reference<container::XNameAccess> bookmarks =
1098 bookmarksSupplier->getBookmarks();
1099 uno::Reference<text::XTextContent> bookmark(
1100 bookmarks->getByName(u"ParagraphBorder"_ustr), uno::UNO_QUERY);
1101 uno::Reference<text::XTextRange> anchor(bookmark->getAnchor());
1102 table::BorderLine2 border;
1103 border = getProperty<table::BorderLine2>(anchor, u"TopBorder"_ustr);
1104 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1105 border = getProperty<table::BorderLine2>(anchor, u"LeftBorder"_ustr);
1106 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
1107 border = getProperty<table::BorderLine2>(anchor, u"RightBorder"_ustr);
1108 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
1109 border = getProperty<table::BorderLine2>(anchor, u"BottomBorder"_ustr);
1110 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
1112 // Page border
1113 OUString pageStyleName = getProperty<OUString>(anchor, u"PageStyleName"_ustr);
1114 uno::Reference<style::XStyle> pageStyle(
1115 getStyles(u"PageStyles"_ustr)->getByName(pageStyleName), uno::UNO_QUERY);
1116 border = getProperty<table::BorderLine2>(pageStyle, u"TopBorder"_ustr);
1117 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1118 border = getProperty<table::BorderLine2>(pageStyle, u"LeftBorder"_ustr);
1119 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
1120 border = getProperty<table::BorderLine2>(pageStyle, u"RightBorder"_ustr);
1121 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
1122 border = getProperty<table::BorderLine2>(pageStyle, u"BottomBorder"_ustr);
1123 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
1125 // Character border
1126 bookmark.set(bookmarks->getByName(u"CharBorder"_ustr), uno::UNO_QUERY);
1127 anchor = bookmark->getAnchor();
1128 border = getProperty<table::BorderLine2>(anchor, u"CharTopBorder"_ustr);
1129 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1130 border = getProperty<table::BorderLine2>(anchor, u"CharLeftBorder"_ustr);
1131 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1132 border = getProperty<table::BorderLine2>(anchor, u"CharRightBorder"_ustr);
1133 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1134 border = getProperty<table::BorderLine2>(anchor, u"CharBottomBorder"_ustr);
1135 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1137 // Table border
1138 uno::Reference<text::XTextTablesSupplier> tablesSupplier(mxComponent,
1139 uno::UNO_QUERY);
1140 uno::Reference<container::XNameAccess> tables =
1141 tablesSupplier->getTextTables();
1142 uno::Reference<text::XTextTable> table(
1143 tables->getByName(u"Table1"_ustr), uno::UNO_QUERY);
1144 table::TableBorder2 tableBorder = getProperty<table::TableBorder2>(
1145 table, u"TableBorder2"_ustr);
1146 CPPUNIT_ASSERT_EQUAL(expectedTop.Color, tableBorder.TopLine.Color);
1147 CPPUNIT_ASSERT_EQUAL(expectedLeft.Color, tableBorder.LeftLine.Color);
1148 CPPUNIT_ASSERT_EQUAL(expectedRight.Color, tableBorder.RightLine.Color);
1149 #if 0
1150 // #if'd out because the "fine dashed" border line style for table borders
1151 // does not seem to save or load correctly in odt format at present
1152 CPPUNIT_ASSERT_EQUAL(expectedBottom.Color, tableBorder.BottomLine.Color);
1153 #endif
1155 // Table cells
1156 uno::Reference<table::XCell> cell =
1157 table->getCellByName(u"A2"_ustr);
1158 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
1159 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1160 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
1161 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
1162 #if 0
1163 // #if'd out because the "fine dashed" border line style for table borders
1164 // does not seem to save or load correctly in odt format at present
1165 border = getProperty<table::BorderLine2>(cell, "BottomBorder");
1166 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
1167 #endif
1169 cell = table->getCellByName(u"B2"_ustr);
1170 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
1171 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
1172 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
1173 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
1174 border = getProperty<table::BorderLine2>(cell, u"BottomBorder"_ustr);
1175 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
1177 cell = table->getCellByName(u"C2"_ustr);
1178 border = getProperty<table::BorderLine2>(cell, u"TopBorder"_ustr);
1179 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
1180 border = getProperty<table::BorderLine2>(cell, u"LeftBorder"_ustr);
1181 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDashedRed, border);
1182 border = getProperty<table::BorderLine2>(cell, u"RightBorder"_ustr);
1183 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
1184 border = getProperty<table::BorderLine2>(cell, u"BottomBorder"_ustr);
1185 CPPUNIT_ASSERT_BORDER_EQUAL(expectedDoubleGreen, border);
1187 // Picture border
1188 // (#if'd out as they are not yet exported with correct colours)
1189 #if 0
1190 bookmark.set(bookmarks->getByName("PictureBorder"),uno::UNO_QUERY);
1191 anchor = bookmark->getAnchor();
1192 border = getProperty<table::BorderLine2>(anchor, "TopBorder");
1193 CPPUNIT_ASSERT_BORDER_EQUAL(expectedTop, border);
1194 border = getProperty<table::BorderLine2>(anchor, "LeftBorder");
1195 CPPUNIT_ASSERT_BORDER_EQUAL(expectedLeft, border);
1196 border = getProperty<table::BorderLine2>(anchor, "RightBorder");
1197 CPPUNIT_ASSERT_BORDER_EQUAL(expectedRight, border);
1198 border = getProperty<table::BorderLine2>(anchor, "BottomBorder");
1199 CPPUNIT_ASSERT_BORDER_EQUAL(expectedBottom, border);
1200 #endif
1203 CPPUNIT_TEST_FIXTURE(Test, testRedlineExport1)
1205 loadAndReload("redline-export-1.odt");
1206 CPPUNIT_ASSERT_EQUAL(1, getPages());
1207 uno::Reference<text::XTextRange> xParagraph = getParagraph(1);
1208 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
1209 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1210 //there must be no redline information on the first line before and after reloading
1211 while (xRunEnum->hasMoreElements())
1213 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
1214 CPPUNIT_ASSERT_EQUAL(false, hasProperty(xRun, u"RedlineType"_ustr));
1218 CPPUNIT_TEST_FIXTURE(Test, testRedlineExport2)
1220 loadAndReload("redline-export-2.odt");
1221 CPPUNIT_ASSERT_EQUAL(1, getPages());
1222 //there must be redline information on the first portion of the third paragraph before and after reloading
1223 CPPUNIT_ASSERT_EQUAL(true, hasProperty(getRun(getParagraph(3), 1), u"RedlineType"_ustr));
1226 CPPUNIT_TEST_FIXTURE(Test, testRedlineExport3)
1228 loadAndReload("redline-export-3.odt");
1229 CPPUNIT_ASSERT_EQUAL(1, getPages());
1230 //there must be redline information just on the para-break boundary between para one and two
1231 CPPUNIT_ASSERT_EQUAL(false, hasProperty(getRun(getParagraph(1), 1), u"RedlineType"_ustr));
1232 CPPUNIT_ASSERT_EQUAL(true, hasProperty(getRun(getParagraph(1), 2), u"RedlineType"_ustr));
1233 CPPUNIT_ASSERT_EQUAL(true, hasProperty(getRun(getParagraph(2), 1), u"RedlineType"_ustr));
1234 CPPUNIT_ASSERT_EQUAL(false, hasProperty(getRun(getParagraph(2), 2), u"RedlineType"_ustr));
1237 CPPUNIT_TEST_FIXTURE(Test, testCellBgColor)
1239 loadAndReload("cell-bg-color.odt");
1240 CPPUNIT_ASSERT_EQUAL(1, getPages());
1241 uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
1242 uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
1243 uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
1244 CPPUNIT_ASSERT_EQUAL(Color(0xCC0000), getProperty<Color>(xTable->getCellByName(u"A1"_ustr), u"BackColor"_ustr));
1247 DECLARE_WW8EXPORT_TEST(testBnc636128, "bnc636128.doc")
1249 // Import / export of FFData.cch was missing.
1250 uno::Reference<text::XFormField> xFormField = getProperty< uno::Reference<text::XFormField> >(getRun(getParagraph(1), 2), u"Bookmark"_ustr);
1251 uno::Reference<container::XNameContainer> xParameters = xFormField->getParameters();
1252 // This resulted in a container.NoSuchElementException.
1253 CPPUNIT_ASSERT_EQUAL(sal_uInt16(5), xParameters->getByName(u"MaxLength"_ustr).get<sal_uInt16>());
1257 DECLARE_WW8EXPORT_TEST(testWw8Cjklist30, "cjklist30.doc")
1259 sal_Int16 numFormat = getNumberingTypeOfParagraph(1);
1260 CPPUNIT_ASSERT_EQUAL(style::NumberingType::TIAN_GAN_ZH, numFormat);
1263 DECLARE_WW8EXPORT_TEST(testWw8Cjklist31, "cjklist31.doc")
1265 sal_Int16 numFormat = getNumberingTypeOfParagraph(1);
1266 CPPUNIT_ASSERT_EQUAL(style::NumberingType::DI_ZI_ZH, numFormat);
1269 DECLARE_WW8EXPORT_TEST(testWw8Cjklist34, "cjklist34.doc")
1271 sal_Int16 numFormat = getNumberingTypeOfParagraph(1);
1272 CPPUNIT_ASSERT_EQUAL(style::NumberingType::NUMBER_UPPER_ZH_TW, numFormat);
1275 DECLARE_WW8EXPORT_TEST(testWw8Cjklist35, "cjklist35.doc")
1277 sal_Int16 numFormat = getNumberingTypeOfParagraph(1);
1278 CPPUNIT_ASSERT_EQUAL(style::NumberingType::NUMBER_LOWER_ZH, numFormat);
1281 DECLARE_WW8EXPORT_TEST(testTdf118564, "tdf118564.doc")
1283 sal_Int16 numFormat = getNumberingTypeOfParagraph(3);
1284 CPPUNIT_ASSERT_EQUAL(style::NumberingType::NUMBER_LOWER_ZH, numFormat);
1287 DECLARE_WW8EXPORT_TEST(testTdf92281, "tdf92281.doc")
1289 uno::Reference<beans::XPropertySet> xRun(getRun(getParagraph(1),1), uno::UNO_QUERY);
1290 CPPUNIT_ASSERT_EQUAL(u"Wingdings"_ustr, getProperty<OUString>(xRun, u"CharFontName"_ustr));
1291 CPPUNIT_ASSERT_EQUAL(u"Wingdings"_ustr, getProperty<OUString>(xRun, u"CharFontNameAsian"_ustr));
1292 CPPUNIT_ASSERT_EQUAL(u"Wingdings"_ustr, getProperty<OUString>(xRun, u"CharFontNameComplex"_ustr));
1294 uno::Reference<text::XText> xXText = getParagraph(1)->getText();
1295 uno::Reference<text::XTextCursor> xCursor = xXText->createTextCursor();
1297 xCursor->goRight( 5 , false );
1298 uno::Reference< beans::XPropertySet > xPropSet(xCursor, uno::UNO_QUERY);
1299 static constexpr OUStringLiteral aFontname = u"\u65B0\u7D30\u660E\u9AD4;PMingLiU";
1300 CPPUNIT_ASSERT_EQUAL(u"Calibri"_ustr, getProperty<OUString>(xPropSet, u"CharFontName"_ustr));
1301 CPPUNIT_ASSERT_EQUAL(OUString(aFontname), getProperty<OUString>(xPropSet, u"CharFontNameAsian"_ustr));
1302 CPPUNIT_ASSERT_EQUAL(u"Times New Roman"_ustr, getProperty<OUString>(xPropSet, u"CharFontNameComplex"_ustr));
1305 DECLARE_WW8EXPORT_TEST(testCommentedTable, "commented-table.doc")
1307 // Document has a non-trivial commented text range, as the range contains a table.
1308 uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
1309 uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields());
1310 uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
1311 uno::Reference<text::XTextContent> xField(xFields->nextElement(), uno::UNO_QUERY);
1312 // After first import, there was an off-by-one during import, so this was "efore.\nA1\nB1\nAfte". (Notice the additional "e" prefix.)
1313 // After export and import, things got worse, this was "\nA1\nB1\nAfte".
1314 CPPUNIT_ASSERT_EQUAL(u"fore." SAL_NEWLINE_STRING "A1" SAL_NEWLINE_STRING "B1" SAL_NEWLINE_STRING "Afte"_ustr, xField->getAnchor()->getString());
1317 DECLARE_WW8EXPORT_TEST(testTextVerticalAdjustment, "tdf36117_verticalAdjustment.doc")
1319 //Preserve the page vertical alignment setting for .doc
1320 SwDoc* pDoc = getSwDoc();
1322 SwPageDesc* pDesc = &pDoc->GetPageDesc( 0 );
1323 drawing::TextVerticalAdjust nVA = pDesc->GetVerticalAdjustment();
1324 CPPUNIT_ASSERT_EQUAL( drawing::TextVerticalAdjust_CENTER, nVA );
1326 pDesc = &pDoc->GetPageDesc( 1 );
1327 nVA = pDesc->GetVerticalAdjustment();
1328 CPPUNIT_ASSERT_EQUAL( drawing::TextVerticalAdjust_TOP, nVA );
1330 pDesc = &pDoc->GetPageDesc( 2 );
1331 nVA = pDesc->GetVerticalAdjustment();
1332 CPPUNIT_ASSERT_EQUAL( drawing::TextVerticalAdjust_BOTTOM, nVA );
1334 pDesc = &pDoc->GetPageDesc( 3 );
1335 nVA = pDesc->GetVerticalAdjustment();
1336 CPPUNIT_ASSERT_EQUAL( drawing::TextVerticalAdjust_BLOCK, nVA );
1338 // The frame is at the left of the text area, not at the left of the paper.
1339 uno::Reference<drawing::XShape> xShape = getShape(2);
1340 CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::LEFT,
1341 getProperty<sal_Int16>(xShape, u"HoriOrient"_ustr));
1342 CPPUNIT_ASSERT_EQUAL(text::RelOrientation::PAGE_PRINT_AREA,
1343 getProperty<sal_Int16>(xShape, u"HoriOrientRelation"_ustr));
1347 DECLARE_WW8EXPORT_TEST(testRES_MIRROR_GRAPH_BOTH, "tdf56321_flipImage_both.doc")
1349 SwDoc* pDoc = getSwDoc();
1351 for (SwNodeOffset n(0); ; n++)
1353 SwNode* pNode = pDoc->GetNodes()[ n ];
1354 if (SwGrfNode *pGrfNode = pNode->GetGrfNode())
1356 CPPUNIT_ASSERT_EQUAL(int(MirrorGraph::Both), static_cast<int>(pGrfNode->GetSwAttrSet().GetMirrorGrf().GetValue()));
1357 break;
1362 CPPUNIT_TEST_FIXTURE(Test, testCommentExport)
1364 loadAndReload("comment-export.odt");
1365 CPPUNIT_ASSERT_EQUAL(1, getPages());
1366 struct TextPortionInfo {
1367 OUString sKind;
1368 OUString sText;
1369 int nAnnotationID;
1372 const TextPortionInfo aTextPortions[] = {
1373 {u"Annotation"_ustr, u"Comment on [A...A]"_ustr, 0},
1374 {u"Text"_ustr, u"[A xx "_ustr, 0},
1375 {u"Annotation"_ustr, u"Comment on [B...B]"_ustr, 1},
1376 {u"Text"_ustr, u"[B x "_ustr, 0},
1377 {u"Annotation"_ustr, u"Comment on [C..C]"_ustr, 2},
1378 {u"Text"_ustr, u"[C x B]"_ustr, 0},
1379 {u"AnnotationEnd"_ustr, OUString(), 1},
1380 {u"Text"_ustr, u" x C]"_ustr, 0},
1381 {u"AnnotationEnd"_ustr, OUString(), 2},
1382 {u"Text"_ustr, u" xx A]"_ustr, 0},
1383 {u"AnnotationEnd"_ustr, OUString(), 0},
1384 {u"Text"_ustr, u" Comment on a point"_ustr, 0},
1385 {u"Annotation"_ustr, u"Comment on point"_ustr, 3},
1386 {u"Text"_ustr, u"x "_ustr, 0},
1387 {u"Annotation"_ustr, u"Comment on AA...BB"_ustr, 4},
1388 {u"Annotation"_ustr, u"Comment on AAAAAA"_ustr, 5},
1389 {u"Text"_ustr, u"AAAAAA"_ustr, 0},
1390 {u"AnnotationEnd"_ustr, OUString(), 5},
1391 {u"Text"_ustr, u" BBBBBB"_ustr, 0},
1392 {u"AnnotationEnd"_ustr, OUString(), 4}
1395 OUString sNames[6];
1397 const int nNumberOfTextPortions = SAL_N_ELEMENTS(aTextPortions);
1399 uno::Reference<text::XTextRange> xPara = getParagraph(1);
1401 for (int i = 0; i < nNumberOfTextPortions; ++i)
1403 OUString sKind = aTextPortions[i].sKind;
1404 uno::Reference<text::XTextRange> xRun = getRun(xPara, i + 1);
1405 uno::Reference<beans::XPropertySet> xPropertySet(xRun, uno::UNO_QUERY);
1406 CPPUNIT_ASSERT_EQUAL(sKind, getProperty<OUString>(xPropertySet, u"TextPortionType"_ustr));
1408 if (sKind == "Text")
1410 // Check if textportion has the correct text
1411 CPPUNIT_ASSERT_EQUAL(aTextPortions[i].sText, xRun->getString());
1413 else if (sKind == "Annotation")
1415 // Check if the comment text is correct and save the name of the comment
1416 uno::Reference<beans::XPropertySet> xComment = getProperty< uno::Reference<beans::XPropertySet> >(xRun, u"TextField"_ustr);
1417 CPPUNIT_ASSERT_EQUAL(aTextPortions[i].sText, getProperty<OUString>(xComment, u"Content"_ustr));
1418 sNames[aTextPortions[i].nAnnotationID] = getProperty<OUString>(xComment, u"Name"_ustr);
1420 else // if (sKind == OUString("AnnotationEnd"))
1422 // Check if the correct Annotation ends here (by Name)
1423 uno::Reference<container::XNamed> xBookmark(getProperty< uno::Reference<beans::XPropertySet> >(xRun, u"Bookmark"_ustr), uno::UNO_QUERY);
1424 CPPUNIT_ASSERT_EQUAL(sNames[aTextPortions[i].nAnnotationID], xBookmark->getName());
1428 // tdf#139759 import character highlight and shade for comment text
1429 uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
1430 auto xFieldsAccess(xTextFieldsSupplier->getTextFields());
1431 uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
1432 uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY);
1433 uno::Reference<text::XText> xText = getProperty<uno::Reference<text::XText>>(xField, u"TextRange"_ustr);
1434 uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(1, xText);
1435 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getProperty<Color>(getRun(xParagraph, 1), u"CharBackColor"_ustr));
1438 #if HAVE_MORE_FONTS
1439 CPPUNIT_TEST_FIXTURE(Test, testTableKeep)
1441 loadAndReload("tdf91083.odt");
1442 CPPUNIT_ASSERT_EQUAL(7, getPages());
1443 //emulate table "keep with next" -do not split table
1444 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
1445 assertXPathContent(pXmlDoc, "/root/page[3]/body/tab[1]/row[2]/cell[1]/txt[1]", u"Row 1");
1446 assertXPathContent(pXmlDoc, "/root/page[6]/body/tab[1]/row[2]/cell[1]/txt[1]", u"Row 1");
1448 #endif
1450 CPPUNIT_TEST_FIXTURE(Test, tesTdf91083_tableKeep2)
1452 loadAndReload("tdf91083_tableKeep2.odt");
1453 //emulate table "keep with next" - split large row in order to keep with previous paragraph
1454 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
1455 CPPUNIT_ASSERT_EQUAL_MESSAGE("Table doesn't split, so it starts on page 2",
1456 u"0"_ustr, getXPathContent(pXmlDoc, "count(//page[1]//tab)") );
1457 CPPUNIT_ASSERT_EQUAL_MESSAGE("Page 2 starts with a paragraph/title, not a table",
1458 u"KeepWithNext"_ustr, getXPathContent(pXmlDoc, "//page[2]/body/txt[1]") );
1459 CPPUNIT_ASSERT_EQUAL_MESSAGE("Table sticks with previous paragraph, so it starts on page 2",
1460 u"1"_ustr, getXPathContent(pXmlDoc, "count(//page[2]//tab)") );
1461 CPPUNIT_ASSERT_MESSAGE("Row itself splits, not the table at a row boundary",
1462 "Cell 2" != getXPathContent(pXmlDoc, "//page[3]//tab//row[2]/cell[1]/txt[1]") );
1465 CPPUNIT_TEST_FIXTURE(Test, tesTdf91083_tableKeep3)
1467 loadAndReload("tdf91083_tableKeep3.odt");
1468 CPPUNIT_ASSERT_EQUAL(3, getPages());
1469 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
1470 //emulate table "keep with next" - split single row table in order to keep with previous paragraph
1471 CPPUNIT_ASSERT_EQUAL_MESSAGE("Table doesn't split, so it starts on page 2",
1472 u"0"_ustr, getXPathContent(pXmlDoc, "count(//page[1]//tab)") );
1473 CPPUNIT_ASSERT_EQUAL_MESSAGE("Table sticks with previous paragraph, so it starts on page 2",
1474 u"1"_ustr, getXPathContent(pXmlDoc, "count(//page[2]//tab)") );
1477 DECLARE_WW8EXPORT_TEST(testTdf76349_textboxMargins, "tdf76349_textboxMargins.doc")
1479 // textboxes without borders were losing their spacing items in round-tripping
1480 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
1481 CPPUNIT_ASSERT( 0 < getXPath(pXmlDoc, "/root/page/body/txt/anchored/fly/infos/prtBounds", "left").toInt32() );
1483 uno::Reference<drawing::XShape> xShape = getShape(1);
1484 CPPUNIT_ASSERT_EQUAL_MESSAGE("Textbox background color", Color(0xD8, 0xD8, 0xD8), getProperty<Color>(xShape, u"BackColor"_ustr));
1487 CPPUNIT_TEST_FIXTURE(Test, testMoveRange)
1489 loadAndReload("fdo66304-1.odt");
1490 //the save must survive without asserting
1493 CPPUNIT_TEST_FIXTURE(Test, testClearFramePams)
1495 loadAndReload("tdf46441-2.odt");
1496 CPPUNIT_ASSERT_EQUAL(1, getPages());
1497 //the save must survive without asserting
1500 CPPUNIT_TEST_FIXTURE(Test, testTdf94386)
1502 createSwDoc("tdf94386.odt");
1503 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1505 // emulate the behavior from tdf#94386 - insert an envelope to the
1506 // document
1508 SfxItemSet aSet(pWrtShell->GetView().GetCurShell()->GetPool(), svl::Items<FN_ENVELOP, FN_ENVELOP>);
1509 aSet.Put(SwEnvItem());
1510 SfxRequest aRequest(FN_ENVELOP, SfxCallMode::SYNCHRON, aSet);
1511 SwModule::get()->ExecOther(aRequest);
1513 saveAndReload(u"MS Word 97"_ustr);
1515 // check that the first and next page use different page styles
1516 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
1517 uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(
1518 xModel->getCurrentController(), uno::UNO_QUERY);
1519 uno::Reference<text::XPageCursor> xCursor(
1520 xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
1522 xCursor->jumpToFirstPage();
1523 OUString firstPageStyleName = getProperty<OUString>(xCursor, u"PageStyleName"_ustr);
1524 xCursor->jumpToLastPage();
1525 OUString lastPageStyleName = getProperty<OUString>(xCursor, u"PageStyleName"_ustr);
1526 CPPUNIT_ASSERT(firstPageStyleName != lastPageStyleName);
1528 uno::Reference<beans::XPropertySet> xFirstPropertySet(getStyles(u"PageStyles"_ustr)->getByName(firstPageStyleName), uno::UNO_QUERY);
1529 awt::Size fSize;
1530 xFirstPropertySet->getPropertyValue(u"Size"_ustr) >>= fSize;
1532 uno::Reference<beans::XPropertySet> xNextPropertySet(getStyles(u"PageStyles"_ustr)->getByName(lastPageStyleName), uno::UNO_QUERY);
1533 awt::Size lSize;
1534 xNextPropertySet->getPropertyValue(u"Size"_ustr) >>= lSize;
1536 CPPUNIT_ASSERT((fSize.Width != lSize.Width));
1537 CPPUNIT_ASSERT((fSize.Height != lSize.Height));
1540 CPPUNIT_TEST_FIXTURE(Test, testTdf99474)
1542 loadAndReload("tdf99474.odt");
1543 CPPUNIT_ASSERT_EQUAL(1, getPages());
1544 // The bullet colour of paragraph #3 should be COL_AUTO
1545 auto xPara = getParagraph(3);
1546 uno::Reference<container::XIndexReplace> xNumRules =
1547 getProperty< uno::Reference<container::XIndexReplace> >(
1548 xPara, u"NumberingRules"_ustr);
1550 int numLevel = getProperty<sal_Int32>(xPara, u"NumberingLevel"_ustr);
1551 uno::Sequence< beans::PropertyValue > aPropertyValues;
1552 xNumRules->getByIndex(numLevel) >>= aPropertyValues;
1553 OUString charStyleName;
1554 for(int j = 0 ; j< aPropertyValues.getLength() ; ++j)
1556 auto aProp = aPropertyValues[j];
1557 if (aProp.Name == "CharStyleName") {
1558 charStyleName = aProp.Value.get<OUString>();
1559 break;
1562 CPPUNIT_ASSERT(charStyleName.getLength());
1563 uno::Reference<beans::XPropertySet> xStyle(
1564 getStyles(u"CharacterStyles"_ustr)->getByName(charStyleName),
1565 uno::UNO_QUERY);
1566 CPPUNIT_ASSERT_EQUAL(COL_AUTO, getProperty<Color>(xStyle, u"CharColor"_ustr));
1569 DECLARE_WW8EXPORT_TEST(testContinuousSectionsNoPageBreak, "continuous-sections.doc")
1571 SwDoc* pDoc = getSwDoc();
1573 // Continuous section breaks should not add new pages
1574 CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetPageDescCnt());
1577 CPPUNIT_PLUGIN_IMPLEMENT();
1579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */