Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <com/sun/star/container/XContentEnumerationAccess.hpp>
11 : #include <com/sun/star/frame/Desktop.hpp>
12 : #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
13 : #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
14 : #include <com/sun/star/style/XAutoStylesSupplier.hpp>
15 : #include <com/sun/star/style/XAutoStyleFamily.hpp>
16 : #include <com/sun/star/text/XPageCursor.hpp>
17 : #include <com/sun/star/text/XTextDocument.hpp>
18 : #include <com/sun/star/text/XTextRange.hpp>
19 : #include <com/sun/star/text/XTextTable.hpp>
20 : #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
21 : #include <com/sun/star/table/XCell.hpp>
22 : #include <com/sun/star/table/BorderLine2.hpp>
23 :
24 : #include <test/bootstrapfixture.hxx>
25 : #include <unotest/macros_test.hxx>
26 : #include <unotools/ucbstreamhelper.hxx>
27 : #include <rtl/strbuf.hxx>
28 : #include <rtl/ustrbuf.hxx>
29 : #include <rtl/byteseq.hxx>
30 : #include <comphelper/processfactory.hxx>
31 : #include <unotools/tempfile.hxx>
32 : #include <unotools/mediadescriptor.hxx>
33 :
34 : #include <unotxdoc.hxx>
35 : #include <docsh.hxx>
36 : #include <doc.hxx>
37 : #include <rootfrm.hxx>
38 :
39 : #include <libxml/xmlwriter.h>
40 : #include <libxml/xpath.h>
41 : #include <libxml/xpathInternals.h>
42 : #include <libxml/parserInternals.h>
43 :
44 : using namespace css;
45 :
46 : #define DEFAULT_STYLE "Default Style"
47 : #define EMU_TO_MM100(EMU) (EMU / 360)
48 :
49 : /**
50 : * Macro to declare a new test (with full round-trip. To test
51 : * import only use the DECLARE_SW_IMPORT_TEST macro instead).
52 : * In order to add a new test, one only needs to use this macro
53 : * and then specify the test content, like this:
54 : *
55 : * DECLARE_SW_ROUNDTRIP_TEST(MyTest, "myfilename.docx", Test)
56 : * {
57 : * CPPUNIT_ASSERT_EQUAL(blabla);
58 : * }
59 : *
60 : */
61 : #define DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, BaseClass) \
62 : class TestName : public BaseClass { \
63 : protected:\
64 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
65 : public:\
66 : CPPUNIT_TEST_SUITE(TestName); \
67 : CPPUNIT_TEST(Import); \
68 : CPPUNIT_TEST(Import_Export_Import); \
69 : CPPUNIT_TEST_SUITE_END(); \
70 : \
71 : void Import() { \
72 : executeImportTest(filename);\
73 : }\
74 : void Import_Export_Import() {\
75 : executeImportExportImportTest(filename);\
76 : }\
77 : void verify() SAL_OVERRIDE;\
78 : }; \
79 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
80 : void TestName::verify()
81 :
82 : #define DECLARE_SW_IMPORT_TEST(TestName, filename, BaseClass) \
83 : class TestName : public BaseClass { \
84 : protected:\
85 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
86 : public:\
87 : CPPUNIT_TEST_SUITE(TestName); \
88 : CPPUNIT_TEST(Import); \
89 : CPPUNIT_TEST_SUITE_END(); \
90 : \
91 : void Import() { \
92 : executeImportTest(filename);\
93 : }\
94 : void verify() SAL_OVERRIDE;\
95 : }; \
96 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
97 : void TestName::verify()
98 :
99 : #define DECLARE_SW_EXPORT_TEST(TestName, filename, BaseClass) \
100 : class TestName : public BaseClass { \
101 : protected:\
102 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
103 : public:\
104 : CPPUNIT_TEST_SUITE(TestName); \
105 : CPPUNIT_TEST(Import_Export); \
106 : CPPUNIT_TEST_SUITE_END(); \
107 : \
108 : void Import_Export() {\
109 : executeImportExport(filename);\
110 : }\
111 : void verify() SAL_OVERRIDE;\
112 : }; \
113 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
114 : void TestName::verify()
115 :
116 : /// Base class for filter tests loading or roundtriping a document, then asserting the document model.
117 : class SwModelTestBase : public test::BootstrapFixture, public unotest::MacrosTest
118 : {
119 : OUString maFilterOptions;
120 : protected:
121 0 : virtual OUString getTestName() { return OUString(); }
122 :
123 : public:
124 : OUString& getFilterOptions()
125 : {
126 : return maFilterOptions;
127 : }
128 5 : void setFilterOptions(const OUString &rFilterOptions)
129 : {
130 5 : maFilterOptions = rFilterOptions;
131 5 : }
132 :
133 982 : SwModelTestBase(const char* pTestDocumentPath = "", const char* pFilter = "")
134 : : mpXmlBuffer(0),
135 : mpTestDocumentPath(pTestDocumentPath),
136 : mpFilter(pFilter),
137 : m_nStartTime(0),
138 982 : m_bExported(false)
139 982 : {}
140 :
141 982 : virtual ~SwModelTestBase()
142 982 : {}
143 :
144 982 : virtual void setUp() SAL_OVERRIDE
145 : {
146 982 : test::BootstrapFixture::setUp();
147 :
148 982 : mxDesktop.set( com::sun::star::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())) );
149 982 : }
150 :
151 982 : virtual void tearDown() SAL_OVERRIDE
152 : {
153 982 : if (mxComponent.is())
154 935 : mxComponent->dispose();
155 :
156 982 : test::BootstrapFixture::tearDown();
157 982 : }
158 :
159 : protected:
160 : /**
161 : * Helper func used by each unit test to test the 'import' code.
162 : * (Loads the requested file and then calls 'verify' method)
163 : */
164 639 : void executeImportTest(const char* filename)
165 : {
166 : // If the testcase is stored in some other format, it's pointless to test.
167 639 : if (mustTestImportOf(filename))
168 : {
169 592 : header();
170 592 : preTest(filename);
171 592 : load(mpTestDocumentPath, filename);
172 592 : postTest(filename);
173 592 : verify();
174 592 : finish();
175 : }
176 639 : }
177 :
178 : /**
179 : * Helper func used by each unit test to test the 'export' code.
180 : * (Loads the requested file, save it to temp file, load the
181 : * temp file and then calls 'verify' method)
182 : */
183 332 : void executeImportExportImportTest(const char* filename)
184 : {
185 332 : header();
186 332 : preTest(filename);
187 332 : load(mpTestDocumentPath, filename);
188 332 : reload(mpFilter, filename);
189 332 : postTest(filename);
190 332 : verify();
191 332 : finish();
192 332 : }
193 :
194 : /**
195 : * Helper func used by each unit test to test the 'export' code.
196 : * (Loads the requested file for document base (this represents
197 : * the initial document condition), exports with the desired
198 : * export filter and then calls 'verify' method)
199 : */
200 2 : void executeImportExport(const char* filename)
201 : {
202 2 : header();
203 2 : preTest(filename);
204 2 : load(mpTestDocumentPath, filename);
205 2 : save(OUString::createFromAscii(mpFilter), m_aTempFile);
206 2 : postTest(filename);
207 2 : verify();
208 2 : finish();
209 2 : }
210 :
211 : /**
212 : * Function overloaded by unit test. See DECLARE_SW_*_TEST macros
213 : */
214 0 : virtual void verify()
215 : {
216 0 : CPPUNIT_FAIL( "verify method must be overridden" );
217 0 : }
218 :
219 : /**
220 : * Override this function if interested in skipping import test for this file
221 : */
222 307 : virtual bool mustTestImportOf(const char* /* filename */) const
223 : {
224 307 : return true;
225 : }
226 : /**
227 : * Override this function if some special filename-specific setup is needed
228 : */
229 650 : virtual void preTest(const char* /*filename*/)
230 : {
231 650 : }
232 :
233 : /**
234 : * Override this function if some special filename-specific teardown is needed
235 : */
236 650 : virtual void postTest(const char* /*filename*/)
237 : {
238 650 : }
239 :
240 : /**
241 : * Override this function if not calcing layout is needed
242 : */
243 1260 : virtual bool mustCalcLayoutOf(const char* /*filename*/)
244 : {
245 1260 : return true;
246 : }
247 :
248 : private:
249 23 : void dumpLayout()
250 : {
251 : // create the xml writer
252 23 : mpXmlBuffer = xmlBufferCreate();
253 23 : xmlTextWriterPtr pXmlWriter = xmlNewTextWriterMemory(mpXmlBuffer, 0);
254 23 : xmlTextWriterStartDocument(pXmlWriter, NULL, NULL, NULL);
255 :
256 : // create the dump
257 23 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
258 23 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
259 23 : SwRootFrm* pLayout = pDoc->GetCurrentLayout();
260 23 : pLayout->dumpAsXml(pXmlWriter);
261 :
262 : // delete xml writer
263 23 : xmlTextWriterEndDocument(pXmlWriter);
264 23 : xmlFreeTextWriter(pXmlWriter);
265 23 : }
266 :
267 1265 : void calcLayout()
268 : {
269 1265 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
270 1265 : CPPUNIT_ASSERT(pTxtDoc);
271 1265 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
272 1265 : pDoc->GetCurrentViewShell()->CalcLayout();
273 1265 : }
274 :
275 : protected:
276 : /// Get the length of the whole document.
277 11 : int getLength()
278 : {
279 11 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
280 22 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
281 22 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
282 22 : OUStringBuffer aBuf;
283 34 : while (xParaEnum->hasMoreElements())
284 : {
285 12 : uno::Reference<container::XEnumerationAccess> xRangeEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
286 24 : uno::Reference<container::XEnumeration> xRangeEnum = xRangeEnumAccess->createEnumeration();
287 40 : while (xRangeEnum->hasMoreElements())
288 : {
289 16 : uno::Reference<text::XTextRange> xRange(xRangeEnum->nextElement(), uno::UNO_QUERY);
290 16 : aBuf.append(xRange->getString());
291 16 : }
292 12 : }
293 22 : return aBuf.getLength();
294 : }
295 :
296 : /// Get a family of styles, see com.sun.star.style.StyleFamilies for possible values.
297 78 : uno::Reference<container::XNameAccess> getStyles(const OUString& aFamily)
298 : {
299 78 : uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
300 156 : uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
301 78 : uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(aFamily), uno::UNO_QUERY);
302 156 : return xStyleFamily;
303 : }
304 :
305 : /// Get a family of auto styles, see com.sun.star.style.StyleFamilies for possible values.
306 2 : uno::Reference<style::XAutoStyleFamily> getAutoStyles(const OUString& aFamily)
307 : {
308 2 : uno::Reference< style::XAutoStylesSupplier > xAutoStylesSupplier(mxComponent, uno::UNO_QUERY);
309 4 : uno::Reference< style::XAutoStyles > xAutoStyles(xAutoStylesSupplier->getAutoStyles());
310 2 : uno::Reference< style::XAutoStyleFamily > xAutoStyleFamily(xAutoStyles->getByName(aFamily), uno::UNO_QUERY);
311 4 : return xAutoStyleFamily;
312 : }
313 :
314 : /**
315 : * Extract a value from the layout dump using an XPath expression and an attribute name.
316 : *
317 : * If the attribute is omitted, the text of the node is returned.
318 : */
319 78 : OUString parseDump(const OString& aXPath, const OString& aAttribute = OString())
320 : {
321 78 : if (!mpXmlBuffer)
322 23 : dumpLayout();
323 :
324 78 : xmlDocPtr pXmlDoc = xmlParseMemory((const char*)xmlBufferContent(mpXmlBuffer), xmlBufferLength(mpXmlBuffer));;
325 :
326 78 : xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
327 78 : xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
328 78 : xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
329 78 : CPPUNIT_ASSERT_EQUAL_MESSAGE("parsing dump failed", 1, xmlXPathNodeSetGetLength(pXmlNodes));
330 78 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
331 78 : OUString aRet;
332 78 : if (aAttribute.getLength())
333 15 : aRet = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
334 : else
335 63 : aRet = OUString::createFromAscii((const char*)XML_GET_CONTENT(pXmlNode));
336 :
337 78 : xmlFreeDoc(pXmlDoc);
338 :
339 78 : return aRet;
340 : }
341 :
342 : template< typename T >
343 83 : T getProperty( const uno::Any& obj, const OUString& name ) const
344 : {
345 83 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
346 83 : T data = T();
347 83 : properties->getPropertyValue( name ) >>= data;
348 83 : return data;
349 : }
350 :
351 : template< typename T >
352 1223 : T getProperty( const uno::Reference< uno::XInterface >& obj, const OUString& name ) const
353 : {
354 1223 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
355 1223 : T data = T();
356 1223 : properties->getPropertyValue( name ) >>= data;
357 1223 : return data;
358 : }
359 :
360 3 : bool hasProperty(const uno::Reference<uno::XInterface>& obj, const OUString& name) const
361 : {
362 3 : uno::Reference<beans::XPropertySet> properties(obj, uno::UNO_QUERY_THROW);
363 3 : return properties->getPropertySetInfo()->hasPropertyByName(name);
364 : }
365 :
366 : /// Get number of paragraphs of the document.
367 2 : int getParagraphs()
368 : {
369 2 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
370 4 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
371 4 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
372 2 : int nRet = 0;
373 7 : while (xParaEnum->hasMoreElements())
374 : {
375 3 : xParaEnum->nextElement();
376 3 : nRet++;
377 : }
378 4 : return nRet;
379 : }
380 :
381 357 : uno::Reference<text::XTextContent> getParagraphOrTable(int number, uno::Reference<text::XText> xText = uno::Reference<text::XText>()) const
382 : {
383 357 : uno::Reference<container::XEnumerationAccess> paraEnumAccess;
384 357 : if (xText.is())
385 71 : paraEnumAccess.set(xText, uno::UNO_QUERY);
386 : else
387 : {
388 286 : uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
389 286 : paraEnumAccess.set(textDocument->getText(), uno::UNO_QUERY);
390 : }
391 714 : uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
392 620 : for( int i = 1;
393 : i < number;
394 : ++i )
395 263 : paraEnum->nextElement();
396 357 : uno::Reference< text::XTextContent> const xElem(paraEnum->nextElement(),
397 357 : uno::UNO_QUERY_THROW);
398 713 : return xElem;
399 : }
400 :
401 : // Get paragraph (counted from 1), optionally check it contains the given text.
402 276 : uno::Reference< text::XTextRange > getParagraph( int number, const OUString& content = OUString() ) const
403 : {
404 : uno::Reference<text::XTextRange> const xParagraph(
405 276 : getParagraphOrTable(number), uno::UNO_QUERY_THROW);
406 275 : if( !content.isEmpty())
407 43 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not have expected content", content, xParagraph->getString());
408 275 : return xParagraph;
409 : }
410 :
411 67 : uno::Reference<text::XTextRange> getParagraphOfText(int number, uno::Reference<text::XText> xText, const OUString& content = OUString()) const
412 : {
413 67 : uno::Reference<text::XTextRange> const xParagraph(getParagraphOrTable(number, xText), uno::UNO_QUERY_THROW);
414 67 : if (!content.isEmpty())
415 6 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not contain expected content", content, xParagraph->getString());
416 67 : return xParagraph;
417 : }
418 :
419 : /// Get run (counted from 1) of a paragraph, optionally check it contains the given text.
420 290 : uno::Reference<text::XTextRange> getRun(uno::Reference<text::XTextRange> xParagraph, int number, const OUString& content = OUString()) const
421 : {
422 290 : uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
423 580 : uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
424 806 : for (int i = 1; i < number; ++i)
425 516 : xRunEnum->nextElement();
426 290 : uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
427 290 : if( !content.isEmpty())
428 31 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "run does not contain expected content", content, xRun->getString());
429 580 : return xRun;
430 : }
431 :
432 : /// Get math formula string of a run.
433 114 : OUString getFormula(uno::Reference<text::XTextRange> xRun) const
434 : {
435 114 : uno::Reference<container::XContentEnumerationAccess> xContentEnumAccess(xRun, uno::UNO_QUERY);
436 228 : uno::Reference<container::XEnumeration> xContentEnum(xContentEnumAccess->createContentEnumeration(""), uno::UNO_QUERY);
437 228 : uno::Reference<beans::XPropertySet> xFormula(xContentEnum->nextElement(), uno::UNO_QUERY);
438 228 : return getProperty<OUString>(getProperty< uno::Reference<beans::XPropertySet> >(xFormula, "Model"), "Formula");
439 : }
440 :
441 : /// get cell of a table; table can be retrieved with getParagraphOrTable
442 7 : uno::Reference<table::XCell> getCell(
443 : uno::Reference<uno::XInterface> const& xTableIfc,
444 : OUString const& rCell, OUString const& rContent = OUString())
445 : {
446 : uno::Reference<text::XTextTable> const xTable(xTableIfc,
447 7 : uno::UNO_QUERY_THROW);
448 : uno::Reference<table::XCell> const xCell(
449 7 : xTable->getCellByName(rCell), uno::UNO_SET_THROW);
450 7 : if (!rContent.isEmpty())
451 : {
452 : uno::Reference<text::XText> const xCellText(xCell,
453 3 : uno::UNO_QUERY_THROW);
454 3 : CPPUNIT_ASSERT_EQUAL_MESSAGE("cell does not contain expected content", rContent, xCellText->getString());
455 : }
456 7 : return xCell;
457 : }
458 :
459 : /// Get shape (counted from 1)
460 240 : uno::Reference<drawing::XShape> getShape(int number)
461 : {
462 240 : uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
463 480 : uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
464 240 : uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(number - 1), uno::UNO_QUERY);
465 479 : return xShape;
466 : }
467 :
468 : /// Get TextFrame by name
469 16 : uno::Reference<drawing::XShape> getTextFrameByName(const OUString& aName)
470 : {
471 16 : uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
472 32 : uno::Reference<container::XNameAccess> xNameAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
473 16 : uno::Reference<drawing::XShape> xShape(xNameAccess->getByName(aName), uno::UNO_QUERY);
474 32 : return xShape;
475 : }
476 :
477 926 : void header()
478 : {
479 926 : std::cout << "File tested,Execution Time (ms)" << std::endl;
480 926 : }
481 :
482 935 : void load(const char* pDir, const char* pName)
483 : {
484 935 : if (mxComponent.is())
485 0 : mxComponent->dispose();
486 : // Output name early, so in the case of a hang, the name of the hanging input file is visible.
487 935 : std::cout << pName << ",";
488 935 : m_nStartTime = osl_getGlobalTimer();
489 935 : mxComponent = loadFromDesktop(getURLFromSrc(pDir) + OUString::createFromAscii(pName), "com.sun.star.text.TextDocument");
490 935 : if (mustCalcLayoutOf(pName))
491 934 : calcLayout();
492 935 : }
493 :
494 332 : void reload(const char* pFilter, const char* filename)
495 : {
496 332 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
497 664 : OUString aFilterName = OUString::createFromAscii(pFilter);
498 664 : utl::MediaDescriptor aMediaDescriptor;
499 332 : aMediaDescriptor["FilterName"] <<= aFilterName;
500 332 : if (!maFilterOptions.isEmpty())
501 0 : aMediaDescriptor["FilterOptions"] <<= maFilterOptions;
502 332 : m_aTempFile.EnableKillingFile();
503 332 : xStorable->storeToURL(m_aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
504 664 : uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
505 332 : xComponent->dispose();
506 332 : m_bExported = true;
507 332 : mxComponent = loadFromDesktop(m_aTempFile.GetURL(), "com.sun.star.text.TextDocument");
508 332 : if(aFilterName == "Office Open XML Text")
509 : {
510 : // too many validation errors right now
511 : // validate(m_aTempFile.GetFileName(), test::OOXML);
512 : }
513 76 : else if(aFilterName == "writer8")
514 : {
515 : // still a few validation errors
516 : // validate(m_aTempFile.GetFileName(), test::ODF);
517 : }
518 :
519 332 : if (mpXmlBuffer)
520 : {
521 0 : xmlBufferFree(mpXmlBuffer);
522 0 : mpXmlBuffer = 0;
523 : }
524 332 : if (mustCalcLayoutOf(filename))
525 663 : calcLayout();
526 332 : }
527 :
528 : /// Save the loaded document to a tempfile. Can be used to check the resulting docx/odt directly as a ZIP file.
529 3 : void save(const OUString& aFilterName, utl::TempFile& rTempFile)
530 : {
531 3 : rTempFile.EnableKillingFile();
532 3 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
533 6 : utl::MediaDescriptor aMediaDescriptor;
534 3 : aMediaDescriptor["FilterName"] <<= aFilterName;
535 3 : if (!maFilterOptions.isEmpty())
536 1 : aMediaDescriptor["FilterOptions"] <<= maFilterOptions;
537 6 : xStorable->storeToURL(rTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
538 3 : }
539 :
540 926 : void finish()
541 : {
542 926 : sal_uInt32 nEndTime = osl_getGlobalTimer();
543 926 : std::cout << (nEndTime - m_nStartTime) << std::endl;
544 926 : if (mpXmlBuffer)
545 : {
546 23 : xmlBufferFree(mpXmlBuffer);
547 23 : mpXmlBuffer = 0;
548 : }
549 926 : }
550 :
551 : /// Get page count.
552 13 : int getPages()
553 : {
554 13 : uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
555 26 : uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
556 26 : uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
557 13 : xCursor->jumpToLastPage();
558 26 : return xCursor->getPage();
559 : }
560 :
561 : /**
562 : * Given that some problem doesn't affect the result in the importer, we
563 : * test the resulting file directly, by opening the zip file, parsing an
564 : * xml stream, and asserting an XPath expression. This method returns the
565 : * xml stream, so that you can do the asserting.
566 : */
567 297 : xmlDocPtr parseExport(const OUString& rStreamName = OUString("word/document.xml"))
568 : {
569 297 : if (!m_bExported)
570 145 : return 0;
571 :
572 : // Read the XML stream we're interested in.
573 152 : uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), m_aTempFile.GetURL());
574 304 : uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rStreamName), uno::UNO_QUERY);
575 :
576 304 : boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
577 152 : sal_Size nSize = pStream->remainingSize();
578 :
579 304 : rtl::ByteSequence aBuffer(nSize);
580 152 : pStream->Read(aBuffer.getArray(), nSize);
581 :
582 : // Parse the XML.
583 304 : return xmlParseMemory(reinterpret_cast<const char*>(aBuffer.getArray()), aBuffer.getLength());
584 : }
585 :
586 : /**
587 : * Helper method to return nodes represented by rXPath.
588 : */
589 712 : xmlNodeSetPtr getXPathNode(xmlDocPtr pXmlDoc, const OString& rXPath)
590 : {
591 712 : xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
592 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
593 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("v"), BAD_CAST("urn:schemas-microsoft-com:vml"));
594 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("mc"), BAD_CAST("http://schemas.openxmlformats.org/markup-compatibility/2006"));
595 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wps"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingShape"));
596 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wpg"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"));
597 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"));
598 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"));
599 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("a"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/main"));
600 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("pic"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/picture"));
601 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("rels"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/relationships"));
602 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordml"));
603 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("m"), BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/math"));
604 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("ContentType"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/content-types"));
605 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("lc"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"));
606 712 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("extended-properties"), BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"));
607 712 : xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(rXPath.getStr()), pXmlXpathCtx);
608 712 : return pXmlXpathObj->nodesetval;
609 : }
610 :
611 : /**
612 : * Same as the assertXPath(), but don't assert: return the string instead.
613 : */
614 579 : OUString getXPath(xmlDocPtr pXmlDoc, const OString& rXPath, const OString& rAttribute)
615 : {
616 579 : xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
617 1158 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
618 579 : 1, xmlXPathNodeSetGetLength(pXmlNodes));
619 579 : if (rAttribute.isEmpty())
620 11 : return OUString();
621 568 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
622 568 : return OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(rAttribute.getStr())));
623 : }
624 :
625 : /**
626 : * Same as the assertXPathContent(), but don't assert: return the string instead.
627 : */
628 30 : OUString getXPathContent(xmlDocPtr pXmlDoc, const OString& rXPath)
629 : {
630 30 : xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
631 :
632 60 : CPPUNIT_ASSERT_MESSAGE(OString("XPath '" + rXPath + "' not found").getStr(),
633 30 : xmlXPathNodeSetGetLength(pXmlNodes) > 0);
634 :
635 30 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
636 30 : return OUString::createFromAscii((const char*)((pXmlNode->children[0]).content));
637 : }
638 :
639 : /**
640 : * Assert that rXPath exists, and returns exactly one node.
641 : * In case rAttribute is provided, the rXPath's attribute's value must
642 : * equal to the rExpected value.
643 : */
644 495 : void assertXPath(xmlDocPtr pXmlDoc, const OString& rXPath, const OString& rAttribute = OString(), const OUString& rExpectedValue = OUString())
645 : {
646 495 : OUString aValue = getXPath(pXmlDoc, rXPath, rAttribute);
647 990 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("Attribute '" + rAttribute + "' of '" + rXPath + "' incorrect value.").getStr(),
648 990 : rExpectedValue, aValue);
649 495 : }
650 :
651 : /**
652 : * Assert that rXPath exists, and returns exactly nNumberOfNodes nodes.
653 : * Useful for checking that we do _not_ export some node (nNumberOfNodes == 0).
654 : */
655 87 : void assertXPath(xmlDocPtr pXmlDoc, const OString& rXPath, int nNumberOfNodes)
656 : {
657 87 : xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
658 174 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
659 87 : nNumberOfNodes, xmlXPathNodeSetGetLength(pXmlNodes));
660 87 : }
661 :
662 : /**
663 : * Assert that rXPath exists, and its content equals rContent.
664 : */
665 22 : void assertXPathContent(xmlDocPtr pXmlDoc, const OString& rXPath, const OUString& rContent)
666 : {
667 22 : CPPUNIT_ASSERT_EQUAL_MESSAGE("XPath contents of child does not match", rContent, getXPathContent(pXmlDoc, rXPath));
668 22 : }
669 :
670 : /**
671 : * Assert that rXPath exists, and has exactly nNumberOfChildNodes child nodes.
672 : * Useful for checking that we do have a no child nodes to a specific node (nNumberOfChildNodes == 0).
673 : */
674 1 : void assertXPathChildren(xmlDocPtr pXmlDoc, const OString& rXPath, int nNumberOfChildNodes)
675 : {
676 1 : xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
677 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
678 1 : 1, xmlXPathNodeSetGetLength(pXmlNodes));
679 1 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
680 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of child-nodes is incorrect").getStr(),
681 1 : nNumberOfChildNodes, (int)xmlChildElementCount(pXmlNode));
682 1 : }
683 :
684 : /**
685 : * Get the position of the child named rName of the parent node specified by rXPath.
686 : * Useful for checking relative order of elements.
687 : */
688 11 : int getXPathPosition(xmlDocPtr pXmlDoc, const OString& rXPath, const OUString& rChildName)
689 : {
690 11 : xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
691 22 : CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
692 : 1,
693 11 : xmlXPathNodeSetGetLength(pXmlNodes));
694 11 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
695 11 : int nRet = 0;
696 43 : for (xmlNodePtr pChild = pXmlNode->children; pChild; pChild = pChild->next)
697 : {
698 43 : if (OUString::createFromAscii((const char*)pChild->name) == rChildName)
699 11 : break;
700 32 : ++nRet;
701 : }
702 11 : return nRet;
703 : }
704 :
705 : uno::Reference<lang::XComponent> mxComponent;
706 : xmlBufferPtr mpXmlBuffer;
707 : const char* mpTestDocumentPath;
708 : const char* mpFilter;
709 :
710 : template< typename T >
711 : struct MethodEntry
712 : {
713 : const char* pName;
714 : void (T::*pMethod)();
715 : };
716 : sal_uInt32 m_nStartTime;
717 : utl::TempFile m_aTempFile;
718 : bool m_bExported; ///< Does m_aTempFile already contain something useful?
719 : };
720 :
721 : /**
722 : * Test whether the expected and actual borderline parameters are equal
723 : * and assert if not.
724 : *
725 : * @param[in] rExpected expected borderline object
726 : * @param[in] rActual actual borderline object
727 : * @param[in] rSourceLine line from where the assertion is called
728 : * Note: This method is the implementatition of CPPUNIT_ASSERT_BORDER_EQUAL, so
729 : * use that macro instead.
730 : **/
731 126 : inline void assertBorderEqual(
732 : const table::BorderLine2& rExpected, const table::BorderLine2& rActual,
733 : const CppUnit::SourceLine& rSourceLine )
734 : {
735 126 : CPPUNIT_NS::assertEquals<util::Color>( rExpected.Color, rActual.Color, rSourceLine, "different Color" );
736 126 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.InnerLineWidth, rActual.InnerLineWidth, rSourceLine, "different InnerLineWidth" );
737 126 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.OuterLineWidth, rActual.OuterLineWidth, rSourceLine, "different OuterLineWidth" );
738 126 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineDistance, rActual.LineDistance, rSourceLine, "different LineDistance" );
739 126 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineStyle, rActual.LineStyle, rSourceLine, "different LineStyle" );
740 126 : CPPUNIT_NS::assertEquals<sal_Int32>( rExpected.LineWidth, rActual.LineWidth, rSourceLine, "different LineWidth" );
741 126 : }
742 :
743 : #define CPPUNIT_ASSERT_BORDER_EQUAL(aExpected, aActual) \
744 : assertBorderEqual( aExpected, aActual, CPPUNIT_SOURCELINE() ) \
745 :
746 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|