bump product version to 4.1.6.2
[LibreOffice.git] / sw / qa / extras / ooxmlexport / ooxmlexport.cxx
blob21fa48233571bd32da05b6cabfb402112bfc3812
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 <com/sun/star/frame/XStorable.hpp>
11 #include <com/sun/star/drawing/FillStyle.hpp>
12 #include <com/sun/star/awt/Gradient.hpp>
13 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
14 #include <com/sun/star/style/TabStop.hpp>
15 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
16 #include <com/sun/star/text/XTextFrame.hpp>
17 #include <com/sun/star/text/XTextTable.hpp>
18 #include <com/sun/star/text/XTextFramesSupplier.hpp>
19 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
20 #include <com/sun/star/style/ParagraphAdjust.hpp>
21 #include <com/sun/star/view/XSelectionSupplier.hpp>
22 #include <com/sun/star/table/BorderLine2.hpp>
23 #include <com/sun/star/table/ShadowFormat.hpp>
24 #include <com/sun/star/text/XPageCursor.hpp>
25 #include <com/sun/star/awt/FontWeight.hpp>
26 #include <com/sun/star/awt/FontUnderline.hpp>
27 #include <com/sun/star/awt/FontSlant.hpp>
28 #include <com/sun/star/text/WrapTextMode.hpp>
30 #include <unotools/tempfile.hxx>
31 #include <unotools/ucbstreamhelper.hxx>
32 #include <rtl/strbuf.hxx>
33 #include <swmodeltestbase.hxx>
35 #include <libxml/xpathInternals.h>
36 #include <libxml/parserInternals.h>
38 class Test : public SwModelTestBase
40 public:
41 void testZoom();
42 void defaultTabStopNotInStyles();
43 void testFdo38244();
44 void testMathEscape();
45 void testFdo51034();
46 void testMathAccents();
47 void testMathD();
48 void testMathEscaping();
49 void testMathLim();
50 void testMathMalformedXml();
51 void testMathMatrix();
52 void testMathMso2k7();
53 void testMathNary();
54 void testMathOverbraceUnderbrace();
55 void testMathOverstrike();
56 void testMathPlaceholders();
57 void testMathRad();
58 void testMathSubscripts();
59 void testMathVerticalStacks();
60 void testTablePosition();
61 void testFdo47669();
62 void testTableBorders();
63 void testFdo51550();
64 void testN789482();
65 void test1Table1Page();
66 void testTextFrames();
67 void testTextFrameBorders();
68 void testTextframeGradient();
69 void testCellBtlr();
70 void testTableStylerPrSz();
71 void testMathLiteral();
72 void testFdo48557();
73 void testI120928();
74 void testN822175();
75 void testFdo58577();
76 void testFdo60990();
77 void testBnc834035();
78 void testCp1000015();
79 void testFdo70812();
80 void testBnc837302();
81 void testFdo65655();
83 CPPUNIT_TEST_SUITE(Test);
84 #if !defined(MACOSX) && !defined(WNT)
85 CPPUNIT_TEST(run);
86 #endif
87 CPPUNIT_TEST_SUITE_END();
89 private:
90 void run();
91 /**
92 * Given that some problem doesn't affect the result in the importer, we
93 * test the resulting file directly, by opening the zip file, parsing an
94 * xml stream, and asserting an XPath expression. This method returns the
95 * xml stream, so that you can do the asserting.
97 xmlDocPtr parseExport();
98 void assertXPath(xmlDocPtr pXmlDoc, OString aXPath, OString aAttribute = OString(), OUString aExpectedValue = OUString());
101 void Test::run()
103 MethodEntry<Test> aMethods[] = {
104 {"zoom.docx", &Test::testZoom},
105 {"empty.odt", &Test::defaultTabStopNotInStyles},
106 {"fdo38244.docx", &Test::testFdo38244},
107 {"math-escape.docx", &Test::testMathEscape},
108 {"fdo51034.odt", &Test::testFdo51034},
109 {"math-accents.docx", &Test::testMathAccents},
110 {"math-d.docx", &Test::testMathD},
111 {"math-escaping.docx", &Test::testMathEscaping},
112 {"math-lim.docx", &Test::testMathLim},
113 {"math-malformed_xml.docx", &Test::testMathMalformedXml},
114 {"math-matrix.docx", &Test::testMathMatrix},
115 {"math-mso2k7.docx", &Test::testMathMso2k7},
116 {"math-nary.docx", &Test::testMathNary},
117 {"math-overbrace_underbrace.docx", &Test::testMathOverbraceUnderbrace},
118 {"math-overstrike.docx", &Test::testMathOverstrike},
119 {"math-placeholders.docx", &Test::testMathPlaceholders},
120 {"math-rad.docx", &Test::testMathRad},
121 {"math-subscripts.docx", &Test::testMathSubscripts},
122 {"math-vertical_stacks.docx", &Test::testMathVerticalStacks},
123 {"table-position.docx", &Test::testTablePosition},
124 {"fdo47669.docx", &Test::testFdo47669},
125 {"table-borders.docx", &Test::testTableBorders},
126 {"fdo51550.odt", &Test::testFdo51550},
127 {"n789482.docx", &Test::testN789482},
128 // {"1-table-1-page.docx", &Test::test1Table1Page}, // doesn't work on openSUSE12.2 at least
129 {"textframes.odt", &Test::testTextFrames},
130 {"textframe-borders.docx", &Test::testTextFrameBorders},
131 {"textframe-gradient.docx", &Test::testTextframeGradient},
132 {"cell-btlr.docx", &Test::testCellBtlr},
133 {"table-style-rPr-sz.docx", &Test::testTableStylerPrSz},
134 {"math-literal.docx", &Test::testMathLiteral},
135 {"fdo48557.odt", &Test::testFdo48557},
136 {"i120928.docx", &Test::testI120928},
137 {"n822175.odt", &Test::testN822175},
138 {"fdo58577.odt", &Test::testFdo58577},
139 {"fdo60990.odt", &Test::testFdo60990},
140 {"bnc834035.odt", &Test::testBnc834035},
141 {"cp1000015.odt", &Test::testCp1000015},
142 {"fdo70812.docx", &Test::testFdo70812},
143 {"bnc837302.docx", &Test::testBnc837302},
144 {"fdo65655.docx", &Test::testFdo65655},
146 // Don't test the first import of these, for some reason those tests fail
147 const char* aBlacklist[] = {
148 "math-escape.docx",
149 "math-mso2k7.docx",
151 std::vector<const char*> vBlacklist(aBlacklist, aBlacklist + SAL_N_ELEMENTS(aBlacklist));
152 header();
153 for (unsigned int i = 0; i < SAL_N_ELEMENTS(aMethods); ++i)
155 MethodEntry<Test>& rEntry = aMethods[i];
156 load("/sw/qa/extras/ooxmlexport/data/", rEntry.pName);
157 // If the testcase is stored in some other format, it's pointless to test.
158 if (OString(rEntry.pName).endsWith(".docx") && std::find(vBlacklist.begin(), vBlacklist.end(), rEntry.pName) == vBlacklist.end())
159 (this->*rEntry.pMethod)();
160 reload("Office Open XML Text");
161 (this->*rEntry.pMethod)();
162 finish();
166 xmlDocPtr Test::parseExport()
168 // Create the zip file.
169 utl::TempFile aTempFile;
170 save("Office Open XML Text", aTempFile);
172 // Read the XML stream we're interested in.
173 uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), aTempFile.GetURL());
174 uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"), uno::UNO_QUERY);
175 boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, sal_True));
176 pStream->Seek(STREAM_SEEK_TO_END);
177 sal_Size nSize = pStream->Tell();
178 pStream->Seek(0);
179 OStringBuffer aDocument(nSize);
180 char ch;
181 for (sal_Size i = 0; i < nSize; ++i)
183 *pStream >> ch;
184 aDocument.append(ch);
187 // Parse the XML.
188 return xmlParseMemory((const char*)aDocument.getStr(), aDocument.getLength());
191 void Test::assertXPath(xmlDocPtr pXmlDoc, OString aXPath, OString aAttribute, OUString aExpectedValue)
193 xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
194 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
195 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("v"), BAD_CAST("urn:schemas-microsoft-com:vml"));
196 xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
197 xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
198 CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
199 if (aAttribute.isEmpty())
200 return;
201 xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
202 OUString aValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
203 CPPUNIT_ASSERT_EQUAL(aExpectedValue, aValue);
206 void Test::testZoom()
208 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
209 uno::Reference<view::XViewSettingsSupplier> xViewSettingsSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
210 uno::Reference<beans::XPropertySet> xPropertySet(xViewSettingsSupplier->getViewSettings());
211 sal_Int16 nValue = 0;
212 xPropertySet->getPropertyValue("ZoomValue") >>= nValue;
213 CPPUNIT_ASSERT_EQUAL(sal_Int16(42), nValue);
216 void Test::defaultTabStopNotInStyles()
218 // The default tab stop was mistakenly exported to a style.
219 // xray ThisComponent.StyleFamilies(1)(0).ParaTabStop
220 uno::Reference< container::XNameAccess > paragraphStyles = getStyles( "ParagraphStyles" );
221 uno::Reference< beans::XPropertySet > properties( paragraphStyles->getByName( "Standard" ), uno::UNO_QUERY );
222 uno::Sequence< style::TabStop > stops = getProperty< uno::Sequence< style::TabStop > >(
223 paragraphStyles->getByName( "Standard" ), "ParaTabStops" );
224 // There actually be be one tab stop, but it will be the default.
225 CPPUNIT_ASSERT_EQUAL( static_cast<sal_Int32>(1), stops.getLength());
226 CPPUNIT_ASSERT_EQUAL( style::TabAlign_DEFAULT, stops[ 0 ].Alignment );
229 void Test::testFdo38244()
232 * Comments attached to a range was imported without the range, check for the fieldmark start/end positions.
234 * oParas = ThisComponent.Text.createEnumeration
235 * oPara = oParas.nextElement
236 * oRuns = oPara.createEnumeration
237 * oRun = oRuns.nextElement
238 * oRun = oRuns.nextElement 'TextFieldStart
239 * oRun = oRuns.nextElement
240 * oRun = oRuns.nextElement 'TextFieldEnd
241 * xray oRun.TextPortionType
243 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
244 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
245 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
246 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
247 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
248 xRunEnum->nextElement();
249 uno::Reference<beans::XPropertySet> xPropertySet(xRunEnum->nextElement(), uno::UNO_QUERY);
250 CPPUNIT_ASSERT_EQUAL(OUString("TextFieldStart"), getProperty<OUString>(xPropertySet, "TextPortionType"));
251 xRunEnum->nextElement();
252 xPropertySet.set(xRunEnum->nextElement(), uno::UNO_QUERY);
253 CPPUNIT_ASSERT_EQUAL(OUString("TextFieldEnd"), getProperty<OUString>(xPropertySet, "TextPortionType"));
256 * Initials were not imported.
258 * oFields = ThisComponent.TextFields.createEnumeration
259 * oField = oFields.nextElement
260 * xray oField.Initials
262 uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
263 uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields());
264 uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
265 xPropertySet.set(xFields->nextElement(), uno::UNO_QUERY);
266 CPPUNIT_ASSERT_EQUAL(OUString("M"), getProperty<OUString>(xPropertySet, "Initials"));
269 * There was a fake empty paragraph at the end of the comment text.
271 * oFields = ThisComponent.TextFields.createEnumeration
272 * oField = oFields.nextElement
273 * oParas = oField.TextRange.createEnumeration
274 * oPara = oParas.nextElement
275 * oPara = oParas.nextElement
278 xParaEnumAccess.set(getProperty< uno::Reference<container::XEnumerationAccess> >(xPropertySet, "TextRange"), uno::UNO_QUERY);
279 xParaEnum = xParaEnumAccess->createEnumeration();
280 xParaEnum->nextElement();
281 bool bCaught = false;
284 xParaEnum->nextElement();
286 catch (container::NoSuchElementException&)
288 bCaught = true;
290 CPPUNIT_ASSERT_EQUAL(true, bCaught);
293 void Test::testMathEscape()
295 CPPUNIT_ASSERT_EQUAL(OUString("\\{ left [ right ] left ( right ) \\}"), getFormula(getRun(getParagraph(1), 1)));
298 void Test::testFdo51034()
300 // The problem was that the 'l' param of the HYPERLINK field was parsed with = "#", not += "#".
301 CPPUNIT_ASSERT_EQUAL(OUString("http://Www.google.com/#a"), getProperty<OUString>(getRun(getParagraph(1), 1), "HyperLinkURL"));
304 // Construct the expected formula from UTF8, as there may be such characters.
305 // Remove all spaces, as LO export/import may change that.
306 // Replace symbol - (i.e. U+2212) with ASCII - , LO does this change and it shouldn't matter.
307 #define CHECK_FORMULA( expected, actual ) \
308 CPPUNIT_ASSERT_EQUAL( \
309 OUString( expected, strlen( expected ), RTL_TEXTENCODING_UTF8 ) \
310 .replaceAll( " ", "" ).replaceAll( OUString( "−", strlen( "−" ), RTL_TEXTENCODING_UTF8 ), "-" ), \
311 OUString( actual ).replaceAll( " ", "" ).replaceAll( OUString( "−", strlen( "−" ), RTL_TEXTENCODING_UTF8 ), "-" ))
313 void Test::testMathAccents()
315 CHECK_FORMULA(
316 "acute {a} grave {a} check {a} breve {a} circle {a} widevec {a} widetilde {a}"
317 " widehat {a} dot {a} widevec {a} widevec {a} widetilde {a} underline {a}",
318 getFormula( getRun( getParagraph( 1 ), 1 )));
321 void Test::testMathD()
323 CHECK_FORMULA( "left (x mline y mline z right )", getFormula( getRun( getParagraph( 1 ), 1 )));
324 CHECK_FORMULA( "left (1 right )", getFormula( getRun( getParagraph( 1 ), 2 )));
325 CHECK_FORMULA( "left [2 right ]", getFormula( getRun( getParagraph( 1 ), 3 )));
326 CHECK_FORMULA( "left ldbracket 3 right rdbracket", getFormula( getRun( getParagraph( 1 ), 4 )));
327 CHECK_FORMULA( "left lline 4 right rline", getFormula( getRun( getParagraph( 1 ), 5 )));
328 CHECK_FORMULA( "left ldline 5 right rdline", getFormula( getRun( getParagraph( 1 ), 6 )));
329 CHECK_FORMULA( "left langle 6 right rangle", getFormula( getRun( getParagraph( 1 ), 7 )));
330 CHECK_FORMULA( "left langle a mline b right rangle", getFormula( getRun( getParagraph( 1 ), 8 )));
331 CHECK_FORMULA( "left ({x} over {y} right )", getFormula( getRun( getParagraph( 1 ), 9 )));
334 void Test::testMathEscaping()
336 CHECK_FORMULA( "− ∞ < x < ∞", getFormula( getRun( getParagraph( 1 ), 1 )));
339 void Test::testMathLim()
341 CHECK_FORMULA( "lim from {x → 1} {x}", getFormula( getRun( getParagraph( 1 ), 1 )));
344 void Test::testMathMalformedXml()
346 CPPUNIT_ASSERT_EQUAL( 0, getLength());
349 void Test::testMathMatrix()
351 CHECK_FORMULA( "left [matrix {1 # 2 ## 3 # 4} right ]", getFormula( getRun( getParagraph( 1 ), 1 )));
354 void Test::testMathMso2k7()
356 CHECK_FORMULA( "A = π {r} ^ {2}", getFormula( getRun( getParagraph( 1 ), 1 )));
357 // TODO check the stack/binom difference
358 // CHECK_FORMULA( "{left (x+a right )} ^ {n} = sum from {k=0} to {n} {left (binom {n} {k} right ) {x} ^ {k} {a} ^ {n-k}}",
359 CHECK_FORMULA( "{left (x+a right )} ^ {n} = sum from {k=0} to {n} {left (stack {n # k} right ) {x} ^ {k} {a} ^ {n-k}}",
360 getFormula( getRun( getParagraph( 2 ), 1 )));
361 CHECK_FORMULA( "{left (1+x right )} ^ {n} =1+ {nx} over {1!} + {n left (n-1 right ) {x} ^ {2}} over {2!} +…",
362 getFormula( getRun( getParagraph( 3 ), 1 )));
363 // TODO check (cos/sin miss {})
364 // CHECK_FORMULA( "f left (x right ) = {a} rsub {0} + sum from {n=1} to {∞} {left ({a} rsub {n} cos {{nπx} over {L}} + {b} rsub {n} sin {{nπx} over {L}} right )}",
365 CHECK_FORMULA( "f left (x right ) = {a} rsub {0} + sum from {n=1} to {∞} {left ({a} rsub {n} cos {nπx} over {L} + {b} rsub {n} sin {nπx} over {L} right )}",
366 getFormula( getRun( getParagraph( 4 ), 1 )));
367 CHECK_FORMULA( "{a} ^ {2} + {b} ^ {2} = {c} ^ {2}", getFormula( getRun( getParagraph( 5 ), 1 )));
368 CHECK_FORMULA( "x = {- b ± sqrt {{b} ^ {2} -4 ac}} over {2 a}",
369 getFormula( getRun( getParagraph( 6 ), 1 )));
370 CHECK_FORMULA(
371 "{e} ^ {x} =1+ {x} over {1!} + {{x} ^ {2}} over {2!} + {{x} ^ {3}} over {3!} +…, -∞<x<∞",
372 getFormula( getRun( getParagraph( 7 ), 1 )));
373 CHECK_FORMULA(
374 // "sin {α} ± sin {β} =2 sin {{1} over {2} left (α±β right )} cos {{1} over {2} left (α∓β right )}",
375 // TODO check (cos/in miss {})
376 "sin α ± sin β =2 sin {1} over {2} left (α±β right ) cos {1} over {2} left (α∓β right )",
377 getFormula( getRun( getParagraph( 8 ), 1 )));
378 CHECK_FORMULA(
379 // "cos {α} + cos {β} =2 cos {{1} over {2} left (α+β right )} cos {{1} over {2} left (α-β right )}",
380 // TODO check (cos/sin miss {})
381 "cos α + cos β =2 cos {1} over {2} left (α+β right ) cos {1} over {2} left (α-β right )",
382 getFormula( getRun( getParagraph( 9 ), 1 )));
385 void Test::testMathNary()
387 CHECK_FORMULA( "lllint from {1} to {2} {x + 1}", getFormula( getRun( getParagraph( 1 ), 1 )));
388 CHECK_FORMULA( "prod from {a} {b}", getFormula( getRun( getParagraph( 1 ), 2 )));
389 CHECK_FORMULA( "sum to {2} {x}", getFormula( getRun( getParagraph( 1 ), 3 )));
392 void Test::testMathOverbraceUnderbrace()
394 CHECK_FORMULA( "{abcd} overbrace {4}", getFormula( getRun( getParagraph( 1 ), 1 )));
395 CHECK_FORMULA( "{xyz} underbrace {3}", getFormula( getRun( getParagraph( 2 ), 1 )));
398 void Test::testMathOverstrike()
400 CHECK_FORMULA( "overstrike {abc}", getFormula( getRun( getParagraph( 1 ), 1 )));
403 void Test::testMathPlaceholders()
405 CHECK_FORMULA( "sum from <?> to <?> <?>", getFormula( getRun( getParagraph( 1 ), 1 )));
408 void Test::testMathRad()
410 CHECK_FORMULA( "sqrt {4}", getFormula( getRun( getParagraph( 1 ), 1 )));
411 CHECK_FORMULA( "nroot {3} {x + 1}", getFormula( getRun( getParagraph( 1 ), 2 )));
414 void Test::testMathSubscripts()
416 CHECK_FORMULA( "{x} ^ {y} + {e} ^ {x}", getFormula( getRun( getParagraph( 1 ), 1 )));
417 CHECK_FORMULA( "{x} ^ {b}", getFormula( getRun( getParagraph( 1 ), 2 )));
418 CHECK_FORMULA( "{x} rsub {b}", getFormula( getRun( getParagraph( 1 ), 3 )));
419 CHECK_FORMULA( "{a} rsub {c} rsup {b}", getFormula( getRun( getParagraph( 1 ), 4 )));
420 CHECK_FORMULA( "{x} lsub {2} lsup {1}", getFormula( getRun( getParagraph( 1 ), 5 )));
421 CHECK_FORMULA( "{{x csup {6} csub {3}} lsub {4} lsup {5}} rsub {2} rsup {1}",
422 getFormula( getRun( getParagraph( 1 ), 6 )));
425 void Test::testMathVerticalStacks()
427 CHECK_FORMULA( "{a} over {b}", getFormula( getRun( getParagraph( 1 ), 1 )));
428 CHECK_FORMULA( "{a} / {b}", getFormula( getRun( getParagraph( 2 ), 1 )));
429 // TODO check these
430 // CHECK_FORMULA( "binom {a} {b}", getFormula( getRun( getParagraph( 3 ), 1 )));
431 // CHECK_FORMULA( "binom {a} {binom {b} {c}}", getFormula( getRun( getParagraph( 4 ), 1 )));
434 void Test::testTablePosition()
436 sal_Int32 xCoordsFromOffice[] = { 2500, -1000, 0, 0 };
437 sal_Int32 cellLeftMarginFromOffice[] = { 250, 100, 0, 0 };
439 uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
440 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
441 uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables( ), uno::UNO_QUERY);
443 for (int i=0; i<4; i++) {
444 uno::Reference<text::XTextTable> xTable1 (xTables->getByIndex(i), uno::UNO_QUERY);
445 // Verify X coord
446 uno::Reference<view::XSelectionSupplier> xCtrl(xModel->getCurrentController(), uno::UNO_QUERY);
447 xCtrl->select(uno::makeAny(xTable1));
448 uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xCtrl, uno::UNO_QUERY);
449 uno::Reference<text::XTextViewCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
450 awt::Point pos = xCursor->getPosition();
451 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Incorrect X coord computed from docx",
452 xCoordsFromOffice[i], pos.X, 1);
454 // Verify left margin of 1st cell :
455 // * Office left margins are measured relative to the right of the border
456 // * LO left spacing is measured from the center of the border
457 uno::Reference<table::XCell> xCell = xTable1->getCellByName("A1");
458 uno::Reference< beans::XPropertySet > xPropSet(xCell, uno::UNO_QUERY_THROW);
459 sal_Int32 aLeftMargin = -1;
460 xPropSet->getPropertyValue("LeftBorderDistance") >>= aLeftMargin;
461 uno::Any aLeftBorder = xPropSet->getPropertyValue("LeftBorder");
462 table::BorderLine2 aLeftBorderLine;
463 aLeftBorder >>= aLeftBorderLine;
464 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Incorrect left spacing computed from docx cell margin",
465 cellLeftMarginFromOffice[i], aLeftMargin - 0.5 * aLeftBorderLine.LineWidth, 1);
469 void Test::testFdo47669()
472 * Problem: we created imbalance </w:hyperlink> which shouldn't be there,
473 * resulting in loading error: missing last character of hyperlink text
474 * and content after it wasn't loaded.
476 getParagraph(1, "This is a hyperlink with anchor. Also, this sentence should be seen.");
477 getRun(getParagraph(1), 2, "hyperlink with anchor");
478 CPPUNIT_ASSERT_EQUAL(OUString("http://www.google.com/#a"), getProperty<OUString>(getRun(getParagraph(1), 2), "HyperLinkURL"));
481 struct SingleLineBorders {
482 sal_Int16 top, bottom, left, right;
483 SingleLineBorders(int t=0, int b=0, int l=0, int r=0)
484 : top(t), bottom(b), left(l), right(r) {}
485 sal_Int16 getBorder(int i) const
487 switch (i) {
488 case 0: return top;
489 case 1: return bottom;
490 case 2: return left;
491 case 3: return right;
492 default: assert(false); return 0;
496 void Test::testTableBorders() {
497 uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
498 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
499 uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables( ), uno::UNO_QUERY);
500 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
501 uno::Reference<text::XTextTable> xTextTable (xTables->getByIndex(0), uno::UNO_QUERY);
503 std::map<OUString, SingleLineBorders> cellBorders;
504 cellBorders[OUString("A1")] = SingleLineBorders(106, 106, 106, 106);
505 cellBorders[OUString("B1")] = SingleLineBorders(106, 0, 106, 35);
506 cellBorders[OUString("C1")] = SingleLineBorders(106, 106, 35, 106);
507 cellBorders[OUString("A2")] = SingleLineBorders(106, 35, 106, 0);
508 cellBorders[OUString("B2")] = SingleLineBorders(0, 0, 0, 0);
509 cellBorders[OUString("C2")] = SingleLineBorders(106, 106, 0, 106);
510 cellBorders[OUString("A3")] = SingleLineBorders(35, 35, 106, 106);
511 cellBorders[OUString("B3")] = SingleLineBorders(0, 106, 106, 106);
512 cellBorders[OUString("C3")] = SingleLineBorders(106, 106, 106, 106);
513 cellBorders[OUString("A4")] = SingleLineBorders(35, 106, 106, 35);
514 cellBorders[OUString("B4")] = SingleLineBorders(106, 106, 35, 106);
515 cellBorders[OUString("C4")] = SingleLineBorders(106, 106, 106, 106);
517 const OUString borderNames[] = {
518 OUString("TopBorder"),
519 OUString("BottomBorder"),
520 OUString("LeftBorder"),
521 OUString("RightBorder"),
524 uno::Sequence<OUString> const cells = xTextTable->getCellNames();
525 sal_Int32 nLength = cells.getLength();
526 CPPUNIT_ASSERT_EQUAL((sal_Int32)cellBorders.size(), nLength);
528 for (sal_Int32 i = 0; i < nLength; ++i)
530 uno::Reference<table::XCell> xCell = xTextTable->getCellByName(cells[i]);
531 uno::Reference< beans::XPropertySet > xPropSet(xCell, uno::UNO_QUERY_THROW);
532 const SingleLineBorders& borders = cellBorders[cells[i]];
534 for (sal_Int32 j = 0; j < 4; ++j)
536 uno::Any aBorder = xPropSet->getPropertyValue(borderNames[j]);
537 table::BorderLine aBorderLine;
538 if (aBorder >>= aBorderLine)
540 std::stringstream message;
541 message << cells[i] << "'s " << borderNames[j] << " is incorrect";
542 CPPUNIT_ASSERT_EQUAL_MESSAGE(message.str(),
543 borders.getBorder(j), aBorderLine.OuterLineWidth);
549 void Test::testFdo51550()
551 // The problem was that we lacked the fallback to export the replacement graphic for OLE objects.
552 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
553 uno::Reference<container::XIndexAccess> xDraws(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY);
554 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xDraws->getCount());
557 void Test::testN789482()
559 // The problem was that w:del was exported before w:hyperlink, resulting in an invalid XML.
560 uno::Reference<text::XTextRange> xParagraph = getParagraph(1);
561 getRun(xParagraph, 1, "Before. ");
563 CPPUNIT_ASSERT_EQUAL(OUString("Delete"), getProperty<OUString>(getRun(xParagraph, 2), "RedlineType"));
564 CPPUNIT_ASSERT_EQUAL(sal_True, getProperty<sal_Bool>(getRun(xParagraph, 2), "IsStart"));
566 getRun(xParagraph, 3, "www.test.com");
567 CPPUNIT_ASSERT_EQUAL(OUString("http://www.test.com/"), getProperty<OUString>(getRun(xParagraph, 3), "HyperLinkURL"));
569 CPPUNIT_ASSERT_EQUAL(OUString("Delete"), getProperty<OUString>(getRun(xParagraph, 4), "RedlineType"));
570 CPPUNIT_ASSERT_EQUAL(sal_False, getProperty<sal_Bool>(getRun(xParagraph, 4), "IsStart"));
572 getRun(xParagraph, 5, " After.");
575 void Test::test1Table1Page()
577 // 2 problem for this document after export:
578 // - invalid sectPr inserted at the beginning of the page
579 // - font of empty cell is not preserved, leading to change in rows height
580 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
581 uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
582 uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
583 xCursor->jumpToLastPage();
584 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), xCursor->getPage());
587 void Test::testTextFrames()
589 // The frames were simply missing, so let's check if all 3 frames were imported back.
590 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
591 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
592 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xIndexAccess->getCount());
595 void Test::testTextFrameBorders()
597 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
598 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
599 uno::Reference<beans::XPropertySet> xFrame(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
600 CPPUNIT_ASSERT_EQUAL(sal_Int32(0xD99594), getProperty<sal_Int32>(xFrame, "BackColor"));
602 table::BorderLine2 aBorder = getProperty<table::BorderLine2>(xFrame, "TopBorder");
603 CPPUNIT_ASSERT_EQUAL(sal_Int32(0xC0504D), aBorder.Color);
604 CPPUNIT_ASSERT_EQUAL(sal_uInt32(35), aBorder.LineWidth);
606 table::ShadowFormat aShadowFormat = getProperty<table::ShadowFormat>(xFrame, "ShadowFormat");
607 CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadowFormat.Location);
608 CPPUNIT_ASSERT_EQUAL(sal_Int16(48), aShadowFormat.ShadowWidth);
609 CPPUNIT_ASSERT_EQUAL(sal_Int32(0x622423), aShadowFormat.Color);
612 void Test::testTextframeGradient()
614 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
615 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
616 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xIndexAccess->getCount());
618 uno::Reference<beans::XPropertySet> xFrame(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
619 CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_GRADIENT, getProperty<drawing::FillStyle>(xFrame, "FillStyle"));
620 awt::Gradient aGradient = getProperty<awt::Gradient>(xFrame, "FillGradient");
621 CPPUNIT_ASSERT_EQUAL(sal_Int32(0xC0504D), aGradient.StartColor);
622 CPPUNIT_ASSERT_EQUAL(sal_Int32(0xD99594), aGradient.EndColor);
623 CPPUNIT_ASSERT_EQUAL(awt::GradientStyle_AXIAL, aGradient.Style);
625 xFrame.set(xIndexAccess->getByIndex(1), uno::UNO_QUERY);
626 CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_GRADIENT, getProperty<drawing::FillStyle>(xFrame, "FillStyle"));
627 aGradient = getProperty<awt::Gradient>(xFrame, "FillGradient");
628 CPPUNIT_ASSERT_EQUAL(sal_Int32(0x000000), aGradient.StartColor);
629 CPPUNIT_ASSERT_EQUAL(sal_Int32(0x666666), aGradient.EndColor);
630 CPPUNIT_ASSERT_EQUAL(awt::GradientStyle_AXIAL, aGradient.Style);
633 void Test::testCellBtlr()
636 * The problem was that the exporter didn't mirror the workaround of the
637 * importer, regarding the btLr text direction: the <w:textDirection
638 * w:val="btLr"/> token was completely missing in the output.
640 * Given that this doesn't affect the result in the importer, we test the
641 * resulting file directly, by opening the zip file, parsing an xml stream,
642 * and asserting an XPath expression. This can be extracted to a helper
643 * method, once it's clear what is common in such tests.
646 // Create the zip file.
647 utl::TempFile aTempFile;
648 save("Office Open XML Text", aTempFile);
650 // Read the XML stream we're interested in.
651 uno::Sequence<uno::Any> aArgs(1);
652 aArgs[0] <<= OUString(aTempFile.GetURL());
653 uno::Reference<container::XNameAccess> xNameAccess(m_xSFactory->createInstanceWithArguments("com.sun.star.packages.zip.ZipFileAccess", aArgs), uno::UNO_QUERY);
654 uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"), uno::UNO_QUERY);
655 boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, sal_True));
656 pStream->Seek(STREAM_SEEK_TO_END);
657 sal_Size nSize = pStream->Tell();
658 pStream->Seek(0);
659 OStringBuffer aDocument(nSize);
660 char ch;
661 for (sal_Size i = 0; i < nSize; ++i)
663 *pStream >> ch;
664 aDocument.append(ch);
667 // Parse the XML.
668 xmlDocPtr pXmlDoc = xmlParseMemory((const char*)aDocument.getStr(), aDocument.getLength());
670 // Assert the XPath expression.
671 xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
672 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
673 OString aXPath = "/w:document/w:body/w:tbl/w:tr/w:tc/w:tcPr/w:textDirection";
674 xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
675 xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
676 CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
677 xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
678 OString aAttribute = "val";
679 OUString aValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
680 CPPUNIT_ASSERT_EQUAL(OUString("btLr"), aValue);
683 void Test::testTableStylerPrSz()
685 // Verify that font size inside the table is 20pt, despite the sz attribute in the table size.
686 // Also check that other rPr attribute are used: italic, bold, underline
687 // Office has the same behavior
688 uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
689 uno::Reference<container::XIndexAccess> xTables(xTextTablesSupplier->getTextTables(), uno::UNO_QUERY);
690 uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
691 uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY);
692 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell->getText(), uno::UNO_QUERY);
693 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
694 uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), uno::UNO_QUERY);
696 CPPUNIT_ASSERT_EQUAL(20.f, getProperty<float>(getRun(xPara, 1), "CharHeight"));
697 // CPPUNIT_ASSERT_EQUAL(awt::FontUnderline::SINGLE, getProperty<short>(getRun(xPara, 1), "CharUnderline"));
698 // CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD, getProperty<float>(getRun(xPara, 1), "CharWeight"));
699 // CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC, getProperty<awt::FontSlant>(getRun(xPara, 1), "CharPosture"));
702 void Test::testMathLiteral()
704 CHECK_FORMULA( "iiint from {V} to <?> {\"div\" \"F\"} dV= llint from {S} to <?> {\"F\" \"n \" dS}",
705 getFormula( getRun( getParagraph( 1 ), 1 )));
708 void Test::testFdo48557()
710 // Inner margins of the textframe wasn't exported.
711 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
712 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
713 uno::Reference<beans::XPropertySet> xFrame(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
714 CPPUNIT_ASSERT_EQUAL(sal_Int32(150), getProperty<sal_Int32>(xFrame, "LeftBorderDistance"));
715 CPPUNIT_ASSERT_EQUAL(sal_Int32(150), getProperty<sal_Int32>(xFrame, "RightBorderDistance"));
716 CPPUNIT_ASSERT_EQUAL(sal_Int32(150), getProperty<sal_Int32>(xFrame, "TopBorderDistance"));
717 CPPUNIT_ASSERT_EQUAL(sal_Int32(150), getProperty<sal_Int32>(xFrame, "BottomBorderDistance"));
720 void Test::testI120928()
722 // w:numPicBullet was ignored, leading to missing graphic bullet in numbering.
723 uno::Reference<beans::XPropertySet> xPropertySet(getStyles("NumberingStyles")->getByName("WWNum1"), uno::UNO_QUERY);
724 uno::Reference<container::XIndexAccess> xLevels(xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
725 uno::Sequence<beans::PropertyValue> aProps;
726 xLevels->getByIndex(0) >>= aProps; // 1st level
728 bool bIsGraphic = false;
729 for (int i = 0; i < aProps.getLength(); ++i)
731 const beans::PropertyValue& rProp = aProps[i];
733 if (rProp.Name == "NumberingType")
734 CPPUNIT_ASSERT_EQUAL(style::NumberingType::BITMAP, rProp.Value.get<sal_Int16>());
735 else if (rProp.Name == "GraphicURL")
736 bIsGraphic = true;
738 CPPUNIT_ASSERT_EQUAL(true, bIsGraphic);
741 void Test::testN822175()
743 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
744 uno::Reference<container::XIndexAccess> xDraws(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY);
745 uno::Reference<beans::XPropertySet> xFrame(xDraws->getByIndex(0), uno::UNO_QUERY);
746 // Was text::WrapTextMode_THROUGH, due to missing Surround handling in the exporter.
747 CPPUNIT_ASSERT_EQUAL(text::WrapTextMode_PARALLEL, getProperty<text::WrapTextMode>(xFrame, "Surround"));
750 void Test::testFdo58577()
752 // The second frame was simply missing, so let's check if both frames were imported back.
753 uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
754 uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
755 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xIndexAccess->getCount());
758 void Test::testFdo60990()
760 // The shape had no background, no paragraph adjust and no font color.
761 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
762 uno::Reference<container::XIndexAccess> xDraws(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY);
763 uno::Reference<beans::XPropertySet> xShape(xDraws->getByIndex(0), uno::UNO_QUERY);
764 CPPUNIT_ASSERT_EQUAL(sal_Int32(0x00CFE7F5), getProperty<sal_Int32>(xShape, "BackColor"));
765 uno::Reference<text::XText> xText = uno::Reference<text::XTextRange>(xShape, uno::UNO_QUERY)->getText();
766 uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(1, xText);
767 CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_CENTER, static_cast<style::ParagraphAdjust>(getProperty<sal_Int16>(xParagraph, "ParaAdjust")));
768 CPPUNIT_ASSERT_EQUAL(sal_Int32(0x00FF00), getProperty<sal_Int32>(getRun(xParagraph, 1), "CharColor"));
771 void Test::testBnc834035()
773 // This is tricky, when saving manually, there are 2 hyperlinks, here only
774 // one, no idea why. That one still shows that we're not using bookmarks, though.
776 // Illustration index had wrong hyperlinks: anchor was using Writer's
777 // <seqname>!<index>|sequence syntax, not a bookmark name.
778 xmlDocPtr pXmlDoc = parseExport();
779 // This was Figure!1|sequence.
780 assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:hyperlink", "anchor", "_Toc363553908");
783 void Test::testCp1000015()
785 // Redline and hyperlink end got exported in an incorrect order.
786 getParagraph(1, "Hello.");
787 getParagraph(2, "http://www.google.com/");
790 void Test::testFdo70812()
792 // Import just crashed.
793 getParagraph(1, "Sample pages document.");
796 void Test::testBnc837302()
798 // The problem was that text with empty author was not inserted as a redline
799 uno::Reference<text::XTextRange> xParagraph = getParagraph(1);
801 // previously 'AAA' was not an own run
802 getRun(xParagraph, 3, "AAA");
803 // interestingly the 'Insert' is set on the _previous_ run
804 CPPUNIT_ASSERT_EQUAL(OUString("Insert"), getProperty<OUString>(getRun(xParagraph, 2), "RedlineType"));
806 // make sure we don't introduce a redlined delete in the 2nd paragraph
807 xParagraph = getParagraph(2);
808 OUString aProperty;
811 // throws when not present
812 aProperty = getProperty<OUString>(getRun(xParagraph, 1), "RedlineType");
814 catch (const beans::UnknownPropertyException&) {}
815 CPPUNIT_ASSERT_EQUAL(OUString(), aProperty);
818 void Test::testFdo65655()
820 // The problem was that the DOCX had a non-blank odd footer and a blank even footer
821 // The 'Different Odd & Even Pages' was turned on
822 // However - LO assumed that because the 'even' footer is blank - it should ignore the 'Different Odd & Even Pages' flag
823 // So it did not import it and did not export it
824 uno::Reference<beans::XPropertySet> xPropertySet(getStyles("PageStyles")->getByName(DEFAULT_STYLE), uno::UNO_QUERY);
825 sal_Bool bValue = false;
826 xPropertySet->getPropertyValue("HeaderIsShared") >>= bValue;
827 CPPUNIT_ASSERT_EQUAL(false, bool(bValue));
828 xPropertySet->getPropertyValue("FooterIsShared") >>= bValue;
829 CPPUNIT_ASSERT_EQUAL(false, bool(bValue));
832 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
834 CPPUNIT_PLUGIN_IMPLEMENT();
836 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */