1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <swmodeltestbase.hxx>
13 #include <string_view>
15 #include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp>
16 #include <com/sun/star/embed/ElementModes.hpp>
17 #include <com/sun/star/io/XActiveDataStreamer.hpp>
18 #include <com/sun/star/io/XSeekable.hpp>
19 #include <com/sun/star/style/ParagraphAdjust.hpp>
20 #include <com/sun/star/awt/FontUnderline.hpp>
21 #include <com/sun/star/table/TableBorder2.hpp>
22 #include <com/sun/star/text/XTextTablesSupplier.hpp>
23 #include <com/sun/star/text/XTextTable.hpp>
24 #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
25 #include <com/sun/star/document/XStorageBasedDocument.hpp>
26 #include <com/sun/star/frame/XStorable.hpp>
27 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
28 #include <com/sun/star/view/XSelectionSupplier.hpp>
30 #include <test/htmltesttools.hxx>
31 #include <tools/urlobj.hxx>
32 #include <svtools/rtfkeywd.hxx>
33 #include <comphelper/propertyvalue.hxx>
34 #include <comphelper/propertysequence.hxx>
35 #include <svtools/parrtf.hxx>
36 #include <rtl/strbuf.hxx>
37 #include <svtools/rtftoken.h>
38 #include <filter/msfilter/rtfutil.hxx>
39 #include <sot/storage.hxx>
40 #include <vcl/svapp.hxx>
41 #include <unotools/mediadescriptor.hxx>
42 #include <svtools/htmlcfg.hxx>
43 #include <unotools/ucbstreamhelper.hxx>
44 #include <comphelper/processfactory.hxx>
45 #include <vcl/graphicfilter.hxx>
46 #include <vcl/dibtools.hxx>
47 #include <o3tl/string_view.hxx>
48 #include <editeng/brushitem.hxx>
50 #include <swmodule.hxx>
52 #include <usrpref.hxx>
57 #include <unotxdoc.hxx>
58 #include <formatlinebreak.hxx>
59 #include <itabenum.hxx>
63 /// Test RTF parser that just extracts a single OLE2 object from a file.
64 class TestReqIfRtfReader
: public SvRTFParser
67 TestReqIfRtfReader(SvStream
& rStream
);
68 void NextToken(int nToken
) override
;
69 bool WriteObjectData(SvStream
& rOLE
);
70 tools::Long
GetObjw() const { return m_nObjw
; }
71 tools::Long
GetObjh() const { return m_nObjh
; }
72 int getWmetafile() const { return m_nWmetafile
; }
75 bool m_bInObjData
= false;
77 tools::Long m_nObjw
= 0;
78 tools::Long m_nObjh
= 0;
82 TestReqIfRtfReader::TestReqIfRtfReader(SvStream
& rStream
)
83 : SvRTFParser(rStream
)
87 void TestReqIfRtfReader::NextToken(int nToken
)
96 m_aHex
.append(OUStringToOString(aToken
, RTL_TEXTENCODING_ASCII_US
));
102 m_nObjw
= nTokenValue
;
105 m_nObjh
= nTokenValue
;
108 m_nWmetafile
= nTokenValue
;
113 bool TestReqIfRtfReader::WriteObjectData(SvStream
& rOLE
)
115 OString aObjdata
= m_aHex
.makeStringAndClear();
117 SvMemoryStream aStream
;
121 // Feed the destination text to a stream.
122 for (int i
= 0; i
< aObjdata
.getLength(); ++i
)
124 char ch
= aObjdata
[i
];
125 if (ch
!= 0x0d && ch
!= 0x0a)
128 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
135 aStream
.WriteChar(b
);
143 rOLE
.WriteStream(aStream
);
147 /// Parser for [MS-OLEDS] 2.2.5 EmbeddedObject, aka OLE1.
150 sal_uInt32 m_nNativeDataSize
;
151 std::vector
<char> m_aNativeData
;
152 sal_uInt32 m_nPresentationDataSize
;
154 OLE1Reader(SvStream
& rStream
);
157 OLE1Reader::OLE1Reader(SvStream
& rStream
)
159 // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
161 CPPUNIT_ASSERT(rStream
.remainingSize());
163 rStream
.ReadUInt32(nData
); // OLEVersion
164 rStream
.ReadUInt32(nData
); // FormatID
165 rStream
.ReadUInt32(nData
); // ClassName
166 rStream
.SeekRel(nData
);
167 rStream
.ReadUInt32(nData
); // TopicName
168 rStream
.SeekRel(nData
);
169 rStream
.ReadUInt32(nData
); // ItemName
170 rStream
.SeekRel(nData
);
172 rStream
.ReadUInt32(m_nNativeDataSize
);
173 m_aNativeData
.resize(m_nNativeDataSize
);
174 rStream
.ReadBytes(m_aNativeData
.data(), m_aNativeData
.size());
176 rStream
.ReadUInt32(nData
); // OLEVersion for presentation data
177 CPPUNIT_ASSERT(rStream
.good());
178 rStream
.ReadUInt32(nData
); // FormatID
179 rStream
.ReadUInt32(nData
); // ClassName
180 rStream
.SeekRel(nData
);
181 rStream
.ReadUInt32(nData
); // Width
182 rStream
.ReadUInt32(nData
); // Height
183 rStream
.ReadUInt32(nData
); // PresentationDataSize
184 m_nPresentationDataSize
= nData
;
188 /// Covers sw/source/filter/html/wrthtml.cxx and related fixes.
189 class HtmlExportTest
: public SwModelTestBase
, public HtmlTestTools
193 : SwModelTestBase("/sw/qa/extras/htmlexport/data/", "HTML (StarWriter)")
197 /// Wraps an RTF fragment into a complete RTF file, so an RTF parser can handle it.
198 static void wrapRtfFragment(const OUString
& rURL
, SvMemoryStream
& rStream
)
200 SvFileStream
aRtfStream(rURL
, StreamMode::READ
);
201 rStream
.WriteOString("{\\rtf1");
202 rStream
.WriteStream(aRtfStream
);
203 rStream
.WriteOString("}");
208 virtual std::unique_ptr
<Resetter
> preTest(const char* filename
) override
210 if (getTestName().indexOf("ReqIf") != -1)
212 if (o3tl::ends_with(filename
, ".xhtml"))
214 setImportFilterOptions("xhtmlns=reqif-xhtml");
215 // Bypass filter detect.
216 setImportFilterName("HTML (StarWriter)");
218 // Export options (implies XHTML).
219 setFilterOptions("xhtmlns=reqif-xhtml");
222 setFilterOptions("");
224 if (filename
== std::string_view("charborder.odt"))
226 // FIXME if padding-top gets exported as inches, not cms, we get rounding errors.
227 SwGlobals::ensure(); // make sure that SW_MOD() is not 0
228 SwMasterUsrPref
* pPref
= const_cast<SwMasterUsrPref
*>(SW_MOD()->GetUsrPref(false));
229 std::unique_ptr
<Resetter
> pResetter(
230 new Resetter([ pPref
, eUnit
= pPref
->GetMetric() ]() { pPref
->SetMetric(eUnit
); }));
231 pPref
->SetMetric(FieldUnit::CM
);
238 #define DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(TestName, filename) \
239 DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, nullptr, HtmlExportTest)
241 /// HTML export of the sw doc model tests.
242 class SwHtmlDomExportTest
: public SwModelTestBase
, public HtmlTestTools
245 SwHtmlDomExportTest()
246 : SwModelTestBase("/sw/qa/extras/htmlexport/data/")
250 /// Get the .ole path, assuming maTempFile is an XHTML export result.
251 OUString
GetOlePath();
252 OUString
GetPngPath();
253 /// Parse the ole1 data out of an RTF fragment URL.
254 void ParseOle1FromRtfUrl(const OUString
& rRtfUrl
, SvMemoryStream
& rOle1
);
255 /// Export using the C++ HTML export filter, with xhtmlns=reqif-xhtml.
256 void ExportToReqif();
257 /// Import using the C++ HTML import filter, with xhtmlns=reqif-xhtml.
258 void ImportFromReqif(const OUString
& rUrl
);
259 /// Export using the C++ HTML export filter
263 OUString
SwHtmlDomExportTest::GetOlePath()
265 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
266 OUString aOlePath
= getXPath(
267 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
268 OUString
aOleSuffix(".ole");
269 CPPUNIT_ASSERT(aOlePath
.endsWith(aOleSuffix
));
270 INetURLObject
aUrl(maTempFile
.GetURL());
271 aUrl
.setBase(aOlePath
.subView(0, aOlePath
.getLength() - aOleSuffix
.getLength()));
272 aUrl
.setExtension(u
"ole");
273 return aUrl
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
276 OUString
SwHtmlDomExportTest::GetPngPath()
278 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
279 OUString aPngPath
= getXPath(
280 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
281 OUString
aPngSuffix(".png");
282 CPPUNIT_ASSERT(aPngPath
.endsWith(aPngSuffix
));
283 INetURLObject
aUrl(maTempFile
.GetURL());
284 aUrl
.setBase(aPngPath
.subView(0, aPngPath
.getLength() - aPngSuffix
.getLength()));
285 aUrl
.setExtension(u
"png");
286 return aUrl
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
289 void SwHtmlDomExportTest::ParseOle1FromRtfUrl(const OUString
& rRtfUrl
, SvMemoryStream
& rOle1
)
292 HtmlExportTest::wrapRtfFragment(rRtfUrl
, aRtf
);
293 tools::SvRef
<TestReqIfRtfReader
> xReader(new TestReqIfRtfReader(aRtf
));
294 CPPUNIT_ASSERT(xReader
->CallParser() != SvParserState::Error
);
295 CPPUNIT_ASSERT(xReader
->WriteObjectData(rOle1
));
296 CPPUNIT_ASSERT(rOle1
.Tell());
299 void SwHtmlDomExportTest::ExportToReqif()
301 setFilterOptions("xhtmlns=reqif-xhtml");
302 save("HTML (StarWriter)");
305 void SwHtmlDomExportTest::ExportToHTML()
307 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
308 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
309 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
311 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
314 void SwHtmlDomExportTest::ImportFromReqif(const OUString
& rUrl
)
316 uno::Sequence
<beans::PropertyValue
> aLoadProperties
= {
317 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
318 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
320 mxComponent
= loadFromDesktop(rUrl
, "com.sun.star.text.TextDocument", aLoadProperties
);
323 DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testFdo81276
, "fdo81276.html")
325 uno::Reference
<container::XNameAccess
> xPageStyles(getStyles("PageStyles"));
326 uno::Reference
<beans::XPropertySet
> xStyle(xPageStyles
->getByName("HTML"), uno::UNO_QUERY
);
327 // some rounding going on here?
328 CPPUNIT_ASSERT(abs(sal_Int32(29700) - getProperty
<sal_Int32
>(xStyle
, "Width")) < 10);
329 CPPUNIT_ASSERT(abs(sal_Int32(21006) - getProperty
<sal_Int32
>(xStyle
, "Height")) < 10);
330 CPPUNIT_ASSERT(abs(sal_Int32(500) - getProperty
<sal_Int32
>(xStyle
, "LeftMargin")) < 10);
331 CPPUNIT_ASSERT(abs(sal_Int32(500) - getProperty
<sal_Int32
>(xStyle
, "RightMargin")) < 10);
332 CPPUNIT_ASSERT(abs(sal_Int32(2000) - getProperty
<sal_Int32
>(xStyle
, "TopMargin")) < 10);
333 CPPUNIT_ASSERT(abs(sal_Int32(500) - getProperty
<sal_Int32
>(xStyle
, "BottomMargin")) < 10);
336 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testFdo62336
)
338 // The problem was essentially a crash during table export as docx/rtf/html
339 // If calc-layout is enabled, the crash does not occur, that's why loadFromURL/save is used
340 loadFromURL(u
"fdo62336.docx");
341 save("HTML (StarWriter)");
344 DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testFdo86857
, "fdo86857.html")
346 // problem was that background color on page style was not exported
347 uno::Reference
<container::XNameAccess
> xPageStyles(getStyles("PageStyles"));
348 uno::Reference
<beans::XPropertySet
> xStyle(xPageStyles
->getByName("HTML"), uno::UNO_QUERY
);
349 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED
, getProperty
<Color
>(xStyle
, "BackColor"));
350 // check that table background color works, which still uses RES_BACKGROUND
351 uno::Reference
<text::XTextTablesSupplier
> xTablesSupplier(mxComponent
, uno::UNO_QUERY
);
352 uno::Reference
<container::XIndexAccess
> xTables(xTablesSupplier
->getTextTables(),
354 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables
->getCount());
355 uno::Reference
<text::XTextTable
> xTable(xTables
->getByIndex(0), uno::UNO_QUERY
);
356 uno::Reference
<text::XTextRange
> xCell(xTable
->getCellByName("A1"), uno::UNO_QUERY
);
357 CPPUNIT_ASSERT_EQUAL(Color(0x66ffff), getProperty
<Color
>(xCell
, "BackColor"));
360 DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testCharacterBorder
, "charborder.odt")
362 CPPUNIT_ASSERT_EQUAL(1, getPages());
364 uno::Reference
<beans::XPropertySet
> xRun(getRun(getParagraph(1), 1), uno::UNO_QUERY
);
367 CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x6666FF, 12, 12, 12, 3, 37),
368 getProperty
<table::BorderLine2
>(xRun
, "CharTopBorder"));
369 CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF9900, 0, 99, 0, 2, 99),
370 getProperty
<table::BorderLine2
>(xRun
, "CharLeftBorder"));
371 CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF0000, 0, 169, 0, 1, 169),
372 getProperty
<table::BorderLine2
>(xRun
, "CharBottomBorder"));
373 CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x0000FF, 0, 169, 0, 0, 169),
374 getProperty
<table::BorderLine2
>(xRun
, "CharRightBorder"));
379 CPPUNIT_ASSERT_EQUAL(sal_Int32(450), getProperty
<sal_Int32
>(xRun
, "CharTopBorderDistance"));
380 CPPUNIT_ASSERT_EQUAL(sal_Int32(550),
381 getProperty
<sal_Int32
>(xRun
, "CharLeftBorderDistance"));
382 CPPUNIT_ASSERT_EQUAL(sal_Int32(150),
383 getProperty
<sal_Int32
>(xRun
, "CharBottomBorderDistance"));
384 CPPUNIT_ASSERT_EQUAL(sal_Int32(250),
385 getProperty
<sal_Int32
>(xRun
, "CharRightBorderDistance"));
391 #define DECLARE_HTMLEXPORT_TEST(TestName, filename) \
392 DECLARE_SW_EXPORT_TEST(TestName, filename, nullptr, HtmlExportTest)
394 DECLARE_HTMLEXPORT_TEST(testExportOfImages
, "textAndImage.docx")
396 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
397 CPPUNIT_ASSERT(pDoc
);
399 assertXPath(pDoc
, "/html/body", 1);
400 assertXPath(pDoc
, "/html/body/p/img", 1);
403 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testExportOfImagesWithSkipImagesEnabled
)
405 createSwDoc("textAndImage.docx");
406 setFilterOptions("SkipImages");
409 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
410 CPPUNIT_ASSERT(pDoc
);
412 assertXPath(pDoc
, "/html/body", 1);
413 assertXPath(pDoc
, "/html/body/p/img", 0);
416 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testSkipImagesEmbedded
)
418 createSwDoc("skipimage-embedded.doc");
419 setFilterOptions("SkipImages");
422 // Embedded spreadsheet was exported as image, so content was lost. Make
423 // sure it's exported as HTML instead.
424 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
425 CPPUNIT_ASSERT(pDoc
);
428 assertXPath(pDoc
, "//table", 1);
429 // This was 2, the HTML header was in the document two times.
430 assertXPath(pDoc
, "//meta[@name='generator']", 1);
431 // This was 0, <table> was directly under <p>, which caused errors in the parser.
432 assertXPath(pDoc
, "//span/table", 1);
435 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testSkipImagesEmbeddedDocument
)
437 createSwDoc("skipimage-embedded-document.docx");
438 setFilterOptions("SkipImages");
441 // Similar to testSkipImagesEmbedded, but with an embedded Writer object,
442 // not a Calc one, and this time OOXML, not WW8.
443 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
444 CPPUNIT_ASSERT(pDoc
);
446 // This was 2, the HTML header was in the document two times.
447 assertXPath(pDoc
, "//meta[@name='generator']", 1);
448 // Text of embedded document was missing.
449 assertXPathContent(pDoc
, "/html/body/p/span/p/span", "Inner.");
452 DECLARE_HTMLEXPORT_TEST(testExportImageProperties
, "HTMLImage.odt")
454 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
455 CPPUNIT_ASSERT(pDoc
);
457 assertXPath(pDoc
, "/html/body", 1);
459 assertXPath(pDoc
, "/html/body/p/map/area", "shape", "poly");
460 assertXPath(pDoc
, "/html/body/p/map/area", "href", "http://www.microsoft.com/");
461 assertXPath(pDoc
, "/html/body/p/map/area", "target", "_self");
462 assertXPath(pDoc
, "/html/body/p/map/area", "alt", "microsoft");
464 assertXPath(pDoc
, "/html/body/p/a", 1);
465 assertXPath(pDoc
, "/html/body/p/a", "href", "http://www.google.com/");
467 assertXPath(pDoc
, "/html/body/p/a/font", 1);
468 assertXPath(pDoc
, "/html/body/p/a/font", "color", "#ff0000");
470 assertXPath(pDoc
, "/html/body/p/a/font/img", 1);
471 assertXPath(pDoc
, "/html/body/p/a/font/img", "name", "Text");
472 assertXPath(pDoc
, "/html/body/p/a/font/img", "alt", "Four colors");
473 assertXPath(pDoc
, "/html/body/p/a/font/img", "align", "middle");
475 // Probably the DPI in OSX is different and Twip -> Pixel conversion produces
476 // different results - so disable OSX for now.
478 // It would make sense to switch to use CSS and use "real world" units instead
479 // i.e. (style="margin: 0cm 1.5cm; width: 1cm; height: 1cm")
481 #if 0 // disabled as it depends that the system DPI is set to 96
482 assertXPath(pDoc
, "/html/body/p/a/font/img", "hspace", "38");
483 assertXPath(pDoc
, "/html/body/p/a/font/img", "vspace", "19");
484 assertXPath(pDoc
, "/html/body/p/a/font/img", "width", "222");
485 assertXPath(pDoc
, "/html/body/p/a/font/img", "height", "222");
486 assertXPath(pDoc
, "/html/body/p/a/font/img", "border", "3");
489 assertXPath(pDoc
, "/html/body/p/a/font/img", "usemap", "#map1");
492 DECLARE_HTMLEXPORT_TEST(testExportCheckboxRadioButtonState
, "checkbox-radiobutton.doc")
494 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
495 CPPUNIT_ASSERT(pDoc
);
497 assertXPath(pDoc
, "/html/body", 1);
498 assertXPath(pDoc
, "/html/body/p[1]/input", "type", "checkbox");
499 assertXPath(pDoc
, "/html/body/p[1]/input", "checked", "checked");
500 assertXPath(pDoc
, "/html/body/p[2]/input", "type", "checkbox");
501 assertXPathNoAttribute(pDoc
, "/html/body/p[2]/input", "checked");
502 assertXPath(pDoc
, "/html/body/form/p[1]/input", "type", "checkbox");
503 assertXPath(pDoc
, "/html/body/form/p[1]/input", "checked", "checked");
504 assertXPath(pDoc
, "/html/body/form/p[2]/input", "type", "checkbox");
505 assertXPathNoAttribute(pDoc
, "/html/body/form/p[2]/input", "checked");
506 assertXPath(pDoc
, "/html/body/form/p[3]/input", "type", "radio");
507 assertXPath(pDoc
, "/html/body/form/p[3]/input", "checked", "checked");
508 assertXPath(pDoc
, "/html/body/form/p[4]/input", "type", "radio");
509 assertXPathNoAttribute(pDoc
, "/html/body/form/p[4]/input", "checked");
512 DECLARE_HTMLEXPORT_TEST(testExportUrlEncoding
, "tdf76291.odt")
514 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
515 CPPUNIT_ASSERT(pDoc
);
517 // Test URI encoded hyperlink with Chinese characters
518 assertXPath(pDoc
, "/html/body/p/a", "href",
519 "http://www.youtube.com/results?search_query=%E7%B2%B5%E8%AA%9Emv&sm=12");
522 DECLARE_HTMLEXPORT_TEST(testExportInternalUrl
, "tdf90905.odt")
524 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
525 CPPUNIT_ASSERT(pDoc
);
527 // Internal url should be valid
528 assertXPath(pDoc
, "/html/body/p[1]/a", "href", "#0.0.1.Text|outline");
529 assertXPath(pDoc
, "/html/body/p[2]/a", "href", "#bookmark");
532 DECLARE_HTMLEXPORT_TEST(testExportImageBulletList
, "tdf66822.odt")
534 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
535 CPPUNIT_ASSERT(pDoc
);
537 // Encoded base64 SVG bullet should match and render on browser
538 assertXPath(pDoc
, "/html/body/ul", 1);
540 pDoc
, "/html/body/ul", "style",
541 "list-style-image: url(data:image/svg+xml;base64,"
542 "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3"
543 "RyYXRvciAxMi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDUxNDQ4KSAg"
544 "LS0+DQo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm"
545 "9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiIFsNCgk8IUVOVElUWSBuc19zdmcgImh0dHA6Ly93d3cu"
546 "dzMub3JnLzIwMDAvc3ZnIj4NCgk8IUVOVElUWSBuc194bGluayAiaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluay"
547 "I+DQpdPg0KPHN2ZyAgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9IiZuc19zdmc7IiB4bWxuczp4bGlu"
548 "az0iJm5zX3hsaW5rOyIgd2lkdGg9IjE0LjAwOCIgaGVpZ2h0PSIxNC4wMSINCgkgdmlld0JveD0iMCAwIDE0LjAwOC"
549 "AxNC4wMSIgb3ZlcmZsb3c9InZpc2libGUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE0LjAwOCAxNC4wMSIg"
550 "eG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cmFkaWFsR3JhZGllbnQgaWQ9IlhNTElEXzRfIiBjeD0iNy4wMD"
551 "Q0IiBjeT0iNy4wMDQ5IiByPSI3LjAwNDQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4NCgkJPHN0b3Ag"
552 "IG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzM1REIzNSIvPg0KCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT"
553 "0ic3RvcC1jb2xvcjojMDBBMDAwIi8+DQoJPC9yYWRpYWxHcmFkaWVudD4NCgk8Y2lyY2xlIGZpbGw9InVybCgjWE1M"
554 "SURfNF8pIiBjeD0iNy4wMDQiIGN5PSI3LjAwNSIgcj0iNy4wMDQiLz4NCgk8ZGVmcz4NCgkJPGZpbHRlciBpZD0iQW"
555 "RvYmVfT3BhY2l0eU1hc2tGaWx0ZXIiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMy40ODEiIHk9IjAu"
556 "NjkzIiB3aWR0aD0iNi45ODgiIGhlaWdodD0iMy44OTMiPg0KCQkJPGZlQ29sb3JNYXRyaXggIHR5cGU9Im1hdHJpeC"
557 "IgdmFsdWVzPSIxIDAgMCAwIDAgIDAgMSAwIDAgMCAgMCAwIDEgMCAwICAwIDAgMCAxIDAiLz4NCgkJPC9maWx0ZXI+"
558 "DQoJPC9kZWZzPg0KCTxtYXNrIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjMuNDgxIiB5PSIwLjY5MyIgd2"
559 "lkdGg9IjYuOTg4IiBoZWlnaHQ9IjMuODkzIiBpZD0iWE1MSURfNV8iPg0KCQk8ZyBmaWx0ZXI9InVybCgjQWRvYmVf"
560 "T3BhY2l0eU1hc2tGaWx0ZXIpIj4NCgkJCTxsaW5lYXJHcmFkaWVudCBpZD0iWE1MSURfNl8iIGdyYWRpZW50VW5pdH"
561 "M9InVzZXJTcGFjZU9uVXNlIiB4MT0iNy4xMjIxIiB5MT0iMC4xMDMiIHgyPSI3LjEyMjEiIHkyPSI1LjIzNDQiPg0K"
562 "CQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNGRkZGRkYiLz4NCgkJCQk8c3RvcCAgb2Zmc2"
563 "V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwIi8+DQoJCQk8L2xpbmVhckdyYWRpZW50Pg0KCQkJPHJlY3Qg"
564 "eD0iMy4xOTkiIHk9IjAuMzM5IiBvcGFjaXR5PSIwLjciIGZpbGw9InVybCgjWE1MSURfNl8pIiB3aWR0aD0iNy44ND"
565 "YiIGhlaWdodD0iNC42MDEiLz4NCgkJPC9nPg0KCTwvbWFzaz4NCgk8ZWxsaXBzZSBtYXNrPSJ1cmwoI1hNTElEXzVf"
566 "KSIgZmlsbD0iI0ZGRkZGRiIgY3g9IjYuOTc1IiBjeT0iMi42NCIgcng9IjMuNDk0IiByeT0iMS45NDYiLz4NCjwvZz"
570 DECLARE_HTMLEXPORT_TEST(testTdf83890
, "tdf83890.odt")
572 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
573 CPPUNIT_ASSERT(pDoc
);
575 assertXPath(pDoc
, "/html/body/ol[2]/ol", "start", "2");
578 DECLARE_HTMLEXPORT_TEST(testExtbChars
, "extb.html")
580 OUString
aExpected(u
"\U00024b62");
581 // Assert that UTF8 encoded non-BMP Unicode character is correct
582 uno::Reference
<text::XTextRange
> xTextRange1
= getRun(getParagraph(1), 1);
583 CPPUNIT_ASSERT_EQUAL(aExpected
, xTextRange1
->getString());
585 // Assert that non-BMP Unicode in character entity format is correct
586 uno::Reference
<text::XTextRange
> xTextRange2
= getRun(getParagraph(2), 1);
587 CPPUNIT_ASSERT_EQUAL(aExpected
, xTextRange2
->getString());
590 DECLARE_HTMLEXPORT_TEST(testNormalImageExport
, "textAndImage.docx")
592 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
593 CPPUNIT_ASSERT(pDoc
);
595 assertXPath(pDoc
, "/html/body", 1);
597 // the HTML export normally does not embed the images
598 OUString imgSrc
= getXPath(pDoc
, "/html/body/p/img", "src");
599 CPPUNIT_ASSERT(imgSrc
.endsWith(".png"));
602 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testEmbedImagesEnabled
)
604 createSwDoc("textAndImage.docx");
605 setFilterOptions("EmbedImages");
608 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
609 CPPUNIT_ASSERT(pDoc
);
611 assertXPath(pDoc
, "/html/body", 1);
613 // the HTML export normally does not embed the images, but here the test
614 // name triggers setting of the "EmbedImages" filter option, meaning the
615 // image will not be a separate PNG, but an embedded base64 encoded
617 OUString imgSrc
= getXPath(pDoc
, "/html/body/p/img", "src");
618 CPPUNIT_ASSERT(imgSrc
.startsWith("data:image/png;base64,"));
621 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testXHTML
)
623 createSwWebDoc("hello.html");
624 setFilterOptions("XHTML");
627 OString
aExpected("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML");
628 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
629 CPPUNIT_ASSERT(pStream
);
630 OString
aActual(read_uInt8s_ToOString(*pStream
, aExpected
.getLength()));
631 // This was HTML, not XHTML.
632 CPPUNIT_ASSERT_EQUAL(aExpected
, aActual
);
634 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
635 CPPUNIT_ASSERT(pDoc
);
636 // This was lang, not xml:lang.
637 assertXPath(pDoc
, "/html/body", "xml:lang", "en-US");
640 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfParagraph
)
642 setImportFilterOptions("xhtmlns=reqif-xhtml");
643 setImportFilterName("HTML (StarWriter)");
644 createSwDoc("reqif-p.xhtml");
645 setFilterOptions("xhtmlns=reqif-xhtml");
648 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
649 CPPUNIT_ASSERT(pStream
);
650 sal_uInt64 nLength
= pStream
->TellEnd();
652 OString aExpected
= "<reqif-xhtml:div><reqif-xhtml:p>aaa<reqif-xhtml:br/>\nbbb"
653 "</reqif-xhtml:p>" SAL_NEWLINE_STRING
654 // This was '<table' instead.
655 "<reqif-xhtml:table";
657 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
659 OString
aActual(read_uInt8s_ToOString(*pStream
, aExpected
.getLength()));
660 // This was a HTML header, like '<!DOCTYPE html ...'.
661 CPPUNIT_ASSERT_EQUAL(aExpected
, aActual
);
663 // This was "<a", was not found.
664 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:a") != -1);
666 // This was "<u>" instead of CSS.
667 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:span style=\"text-decoration: underline\"") != -1);
669 // This was <strong>, namespace prefix was missing.
670 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:strong>") != -1);
672 // This was "<strike>" instead of CSS.
673 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:span style=\"text-decoration: line-through\"")
676 // This was "<font>" instead of CSS + namespace prefix was missing.
677 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:span style=\"color: #ce181e\"") != -1);
679 // This was '<reqif-xhtml:a id="...">': non-unique bookmark name in reqif fragment.
680 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(-1), aStream
.indexOf("<reqif-xhtml:a id="));
683 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfOleData
)
685 auto verify
= [this]() {
686 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
687 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
689 // This was 0, <object> without URL was ignored.
690 // Then this was 0 on export, as data of OLE nodes was ignored.
691 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(1), xObjects
->getCount());
693 setImportFilterOptions("xhtmlns=reqif-xhtml");
694 setImportFilterName("HTML (StarWriter)");
695 createSwDoc("reqif-ole-data.xhtml");
697 setFilterOptions("xhtmlns=reqif-xhtml");
698 saveAndReload("HTML (StarWriter)");
702 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfOleImg
)
704 auto verify
= [this]() {
705 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
706 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
708 uno::Reference
<document::XEmbeddedObjectSupplier2
> xObject(xObjects
->getByIndex(0),
710 // This failed, OLE object had no replacement image.
711 // And then it also failed when the export lost the replacement image.
712 uno::Reference
<graphic::XGraphic
> xGraphic
= xObject
->getReplacementGraphic();
713 // This failed when query and fragment of file:// URLs were not ignored.
714 CPPUNIT_ASSERT(xGraphic
.is());
716 uno::Reference
<drawing::XShape
> xShape(xObject
, uno::UNO_QUERY
);
717 OutputDevice
* pDevice
= Application::GetDefaultDevice();
719 // Expected to be 1693.
720 Size
aLogic(pDevice
->PixelToLogic(aPixel
, MapMode(MapUnit::Map100thMM
)));
721 awt::Size aSize
= xShape
->getSize();
722 // This was only 1247, size was not set explicitly.
723 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(aLogic
.getWidth()), aSize
.Width
);
725 // Check mime/media types.
726 CPPUNIT_ASSERT_EQUAL(OUString("image/png"), getProperty
<OUString
>(xGraphic
, "MimeType"));
728 uno::Reference
<beans::XPropertySet
> xObjectProps(xObject
, uno::UNO_QUERY
);
729 uno::Reference
<io::XActiveDataStreamer
> xStreamProvider(
730 xObjectProps
->getPropertyValue("EmbeddedObject"), uno::UNO_QUERY
);
731 uno::Reference
<io::XSeekable
> xStream(xStreamProvider
->getStream(), uno::UNO_QUERY
);
732 // This was empty when either import or export handling was missing.
733 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int64
>(37888), xStream
->getLength());
735 // Check alternate text (it was empty, for export the 'alt' attribute was used).
736 CPPUNIT_ASSERT_EQUAL(OUString("OLE Object"),
737 getProperty
<OUString
>(xObject
, "Title").trim());
742 // "type" attribute was missing for the inner <object> element.
743 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
744 CPPUNIT_ASSERT(pStream
);
745 sal_uInt64 nLength
= pStream
->TellEnd();
746 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
747 CPPUNIT_ASSERT(aStream
.indexOf("type=\"image/png\"") != -1);
749 setImportFilterOptions("xhtmlns=reqif-xhtml");
750 setImportFilterName("HTML (StarWriter)");
751 createSwDoc("reqif-ole-img.xhtml");
753 setFilterOptions("xhtmlns=reqif-xhtml");
754 saveAndReload("HTML (StarWriter)");
758 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIfPngImg
)
760 auto verify
= [this](bool bExported
) {
761 uno::Reference
<container::XNamed
> xShape(getShape(1), uno::UNO_QUERY
);
762 CPPUNIT_ASSERT(xShape
.is());
766 // Imported PNG image is not an object.
767 CPPUNIT_ASSERT_EQUAL(OUString("Image1"), xShape
->getName());
771 // All images are exported as objects in ReqIF mode.
772 CPPUNIT_ASSERT_EQUAL(OUString("Object1"), xShape
->getName());
774 // This was <img>, not <object>, which is not valid in the reqif-xhtml
776 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
777 CPPUNIT_ASSERT(pStream
);
778 sal_uInt64 nLength
= pStream
->TellEnd();
779 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
780 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:object") != -1);
782 // Make sure that both RTF and PNG versions are written.
783 CPPUNIT_ASSERT(aStream
.indexOf("text/rtf") != -1);
784 // This failed when images with a query in their file:// URL failed to
786 CPPUNIT_ASSERT(aStream
.indexOf("image/png") != -1);
789 ImportFromReqif(createFileURL(u
"reqif-png-img.xhtml"));
790 verify(/*bExported=*/false);
791 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
792 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
793 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
794 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
795 comphelper::makePropertyValue("ExportImagesAsOLE", true),
797 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
798 mxComponent
->dispose();
799 ImportFromReqif(maTempFile
.GetURL());
800 verify(/*bExported=*/true);
803 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfJpgImg
)
805 setImportFilterOptions("xhtmlns=reqif-xhtml");
806 setImportFilterName("HTML (StarWriter)");
807 createSwDoc("reqif-jpg-img.xhtml");
808 setFilterOptions("xhtmlns=reqif-xhtml");
811 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
812 CPPUNIT_ASSERT(pStream
);
813 sal_uInt64 nLength
= pStream
->TellEnd();
814 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
815 // This was image/jpeg, JPG was not converted to PNG in ReqIF mode.
816 CPPUNIT_ASSERT(aStream
.indexOf("type=\"image/png\"") != -1);
819 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfTable
)
821 setImportFilterOptions("xhtmlns=reqif-xhtml");
822 setImportFilterName("HTML (StarWriter)");
823 createSwDoc("reqif-table.xhtml");
824 setFilterOptions("xhtmlns=reqif-xhtml");
827 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
829 // <div> was missing, so the XHTML fragment wasn't a valid
830 // xhtml.BlkStruct.class type anymore.
832 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr/reqif-xhtml:th",
834 // Make sure that the cell background is not written using CSS.
835 assertXPathNoAttribute(
836 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr/reqif-xhtml:th",
838 // The attribute was present, which is not valid in reqif-xhtml.
839 assertXPathNoAttribute(
840 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr/reqif-xhtml:th",
844 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfTable2
)
846 createSwDoc("reqif-table2.odt");
847 setFilterOptions("xhtmlns=reqif-xhtml");
850 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
851 CPPUNIT_ASSERT(pStream
);
852 sal_uInt64 nLength
= pStream
->TellEnd();
853 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
854 // This failed, <reqif-xhtml:td width="..."> was written.
855 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:td>") != -1);
858 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIfTableHeight
)
860 // Given a document with a table in it, with an explicit row height:
862 uno::Sequence
<beans::PropertyValue
> aTableProperties
= {
863 comphelper::makePropertyValue("Rows", static_cast<sal_Int32
>(1)),
864 comphelper::makePropertyValue("Columns", static_cast<sal_Int32
>(1)),
866 dispatchCommand(mxComponent
, ".uno:InsertTable", aTableProperties
);
867 uno::Reference
<text::XTextTablesSupplier
> xTablesSupplier(mxComponent
, uno::UNO_QUERY
);
868 uno::Reference
<container::XIndexAccess
> xTables(xTablesSupplier
->getTextTables(),
870 uno::Reference
<text::XTextTable
> xTable(xTables
->getByIndex(0), uno::UNO_QUERY
);
871 uno::Reference
<beans::XPropertySet
> xRow(xTable
->getRows()->getByIndex(0), uno::UNO_QUERY
);
872 xRow
->setPropertyValue("Height", uno::Any(static_cast<sal_Int32
>(1000)));
874 // When exporting to reqif-xhtml:
877 // Then make sure that the explicit cell height is omitted from the output:
878 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
879 // Without the accompanying fix in place, this test would have failed, explicit height was
880 // written, which is not valid reqif-xhtml.
881 assertXPathNoAttribute(pDoc
, "//reqif-xhtml:td", "height");
884 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testXHTMLUseCSS
)
886 createSwDoc("xhtml-css.odt");
887 setFilterOptions("XHTML");
890 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
891 CPPUNIT_ASSERT(pStream
);
892 sal_uInt64 nLength
= pStream
->TellEnd();
893 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
894 // This failed, <font face="..."> was written.
895 CPPUNIT_ASSERT(aStream
.indexOf("<span style=\"font-family:") != -1);
896 // This failed, <font size="..."> was written.
897 CPPUNIT_ASSERT(aStream
.indexOf("<span style=\"font-size:") != -1);
900 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfList
)
902 setImportFilterOptions("xhtmlns=reqif-xhtml");
903 setImportFilterName("HTML (StarWriter)");
904 createSwDoc("reqif-list.xhtml");
905 setFilterOptions("xhtmlns=reqif-xhtml");
908 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
909 CPPUNIT_ASSERT(pStream
);
910 sal_uInt64 nLength
= pStream
->TellEnd();
911 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
912 // This failed, <ul> was written.
913 CPPUNIT_ASSERT(aStream
.indexOf("<reqif-xhtml:ul>") != -1);
915 // This failed, the 'style' attribute was written, even if the input did
917 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(-1), aStream
.indexOf(" style=\""));
919 // This failed <li> was only opened, not closed.
920 CPPUNIT_ASSERT(aStream
.indexOf("</reqif-xhtml:li>") != -1);
923 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfOle2
)
925 auto verify
= [this]() {
926 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
927 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
929 uno::Reference
<document::XEmbeddedObjectSupplier2
> xObject(xObjects
->getByIndex(0),
931 uno::Reference
<io::XActiveDataStreamer
> xEmbeddedObject(
932 xObject
->getExtendedControlOverEmbeddedObject(), uno::UNO_QUERY
);
933 // This failed, the "RTF fragment" native data was loaded as-is, we had no
934 // filter to handle it, so nothing happened on double-click.
935 CPPUNIT_ASSERT(xEmbeddedObject
.is());
936 uno::Reference
<io::XSeekable
> xStream(xEmbeddedObject
->getStream(), uno::UNO_QUERY
);
937 // This was 38375, msfilter::rtfutil::ExtractOLE2FromObjdata() wrote
938 // everything after the OLE1 header into the OLE2 stream, while the
939 // Presentation field after the OLE2 data doesn't belong there.
940 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int64
>(37888), xStream
->getLength());
941 // Finally the export also failed as it tried to open the stream from the
942 // document storage, but the embedded object already opened it, so an
943 // exception of type com.sun.star.io.IOException was thrown.
947 // Check that the replacement graphic is exported at RTF level.
948 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
949 // Get the path of the RTF data.
950 OUString aOlePath
= getXPath(
951 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
952 OUString
aOleSuffix(".ole");
953 CPPUNIT_ASSERT(aOlePath
.endsWith(aOleSuffix
));
954 INetURLObject
aUrl(maTempFile
.GetURL());
955 aUrl
.setBase(aOlePath
.subView(0, aOlePath
.getLength() - aOleSuffix
.getLength()));
956 aUrl
.setExtension(u
"ole");
957 OUString aOleUrl
= aUrl
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
959 // Search for \result in the RTF data.
960 SvFileStream
aOleStream(aOleUrl
, StreamMode::READ
);
961 CPPUNIT_ASSERT(aOleStream
.IsOpen());
962 OString
aOleString(read_uInt8s_ToOString(aOleStream
, aOleStream
.TellEnd()));
963 // Without the accompanying fix in place, this test would have failed,
964 // replacement graphic was missing at RTF level.
965 CPPUNIT_ASSERT(aOleString
.indexOf(OOO_STRING_SVTOOLS_RTF_RESULT
) != -1);
968 setImportFilterOptions("xhtmlns=reqif-xhtml");
969 setImportFilterName("HTML (StarWriter)");
970 createSwDoc("reqif-ole2.xhtml");
972 setFilterOptions("xhtmlns=reqif-xhtml");
973 saveAndReload(mpFilter
);
977 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testReqIfOle2Odg
)
979 auto verify
= [this]() {
980 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
981 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
983 uno::Reference
<document::XEmbeddedObjectSupplier
> xTextEmbeddedObject(
984 xObjects
->getByIndex(0), uno::UNO_QUERY
);
985 uno::Reference
<lang::XServiceInfo
> xObject(xTextEmbeddedObject
->getEmbeddedObject(),
987 // This failed, both import and export failed to handle OLE2 that contains
989 CPPUNIT_ASSERT(xObject
.is());
990 CPPUNIT_ASSERT(xObject
->supportsService("com.sun.star.drawing.DrawingDocument"));
992 setImportFilterOptions("xhtmlns=reqif-xhtml");
993 setImportFilterName("HTML (StarWriter)");
994 createSwDoc("reqif-ole-odg.xhtml");
996 setFilterOptions("xhtmlns=reqif-xhtml");
997 saveAndReload(mpFilter
);
1001 DECLARE_HTMLEXPORT_TEST(testList
, "list.html")
1003 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
1004 CPPUNIT_ASSERT(pStream
);
1005 sal_uInt64 nLength
= pStream
->TellEnd();
1006 OString
aStream(read_uInt8s_ToOString(*pStream
, nLength
));
1007 // This failed, it was <li/>, i.e. list item was closed before content
1009 CPPUNIT_ASSERT(aStream
.indexOf("<li>") != -1);
1012 DECLARE_HTMLEXPORT_TEST(testTransparentImage
, "transparent-image.odt")
1014 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
1015 CPPUNIT_ASSERT(pDoc
);
1017 OUString aSource
= getXPath(pDoc
, "/html/body/p/img", "src");
1018 OUString aMessage
= "src attribute is: " + aSource
;
1019 // This was a jpeg, transparency was lost.
1020 CPPUNIT_ASSERT_MESSAGE(aMessage
.toUtf8().getStr(), aSource
.endsWith(".gif"));
1023 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTransparentImageReqIf
)
1025 createSwDoc("transparent-image.odt");
1026 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1027 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1028 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1029 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1030 comphelper::makePropertyValue("ExportImagesAsOLE", true),
1032 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1033 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1035 OUString aSource
= getXPath(
1037 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object",
1039 OUString aMessage
= "src attribute is: " + aSource
;
1040 // This was GIF, when the intention was to force PNG.
1041 CPPUNIT_ASSERT_MESSAGE(aMessage
.toUtf8().getStr(), aSource
.endsWith(".png"));
1044 DECLARE_HTMLEXPORT_TEST(testOleNodataReqIf
, "reqif-ole-nodata.odt")
1046 // This failed, io::IOException was thrown during the filter() call.
1047 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1049 // Make sure the native <object> element has the required data attribute.
1050 OUString aSource
= getXPath(
1052 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object",
1054 CPPUNIT_ASSERT(!aSource
.isEmpty());
1057 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testNoLangReqIf
)
1059 createSwDoc("reqif-no-lang.odt");
1060 setFilterOptions("xhtmlns=reqif-xhtml");
1063 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1065 // Make sure that xml:lang is not written in ReqIF mode.
1066 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:h1", "lang");
1069 DECLARE_HTMLEXPORT_TEST(testFieldShade
, "field-shade.odt")
1071 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
1072 CPPUNIT_ASSERT(pDoc
);
1074 // Without the accompanying fix in place, this test would have failed with 'Expected: 1; Actual:
1075 // 0', i.e. shading for the field was lost.
1076 assertXPath(pDoc
, "/html/body/p[1]/span", "style", "background: #c0c0c0");
1078 // Check that field shading is written only in case there is no user-defined span background.
1079 assertXPath(pDoc
, "/html/body/p[2]/span", "style", "background: #ff0000");
1080 // Without the accompanying fix in place, this test would have failed with 'Expected: 0; Actual:
1081 // 1', i.e there was an inner span hiding the wanted background color.
1082 assertXPath(pDoc
, "/html/body/p[2]/span/span", 0);
1085 DECLARE_HTMLEXPORT_TEST(testTdf132739
, "tdf132739.odt")
1087 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
1088 CPPUNIT_ASSERT(pDoc
);
1090 // Without the fix in place, this test would have failed with
1091 // - Expected: background: #5983b0; border: 1px solid #333333; padding: 0.04in
1092 // - Actual : background: #5983b0
1093 assertXPath(pDoc
, "/html/body/table/tr[1]/td", "style",
1094 "background: #5983b0; border: 1px solid #333333; padding: 0.04in");
1097 DECLARE_HTMLEXPORT_TEST(testFieldShadeReqIf
, "field-shade-reqif.odt")
1099 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
1100 CPPUNIT_ASSERT(pDoc
);
1102 // Without the accompanying fix in place, this test would have failed with:
1105 // i.e. the ReqIF subset of xhtml had a background color and a page number field, resulting in
1106 // an invalid ReqIF-XHTML.
1107 assertXPath(pDoc
, "/html/body/div/p[1]/span", 0);
1108 assertXPath(pDoc
, "/html/body/div/p[1]/sdfield", 0);
1111 DECLARE_HTMLEXPORT_TEST(testTdf126879
, "tdf126879.odt")
1113 const OString
aExpected("<!DOCTYPE html>");
1114 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::READ
);
1115 CPPUNIT_ASSERT(pStream
);
1116 const OString
aActual(read_uInt8s_ToOString(*pStream
, aExpected
.getLength()));
1117 // Without the fix in place, this test would have failed with
1118 // - Expected: <!DOCTYPE html>
1119 // - Actual : <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
1120 CPPUNIT_ASSERT_EQUAL(aExpected
, aActual
);
1123 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testBlockQuoteReqIf
)
1125 // Build a document model that uses the Quotations paragraph style.
1127 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
1128 xParagraph
->setPropertyValue("ParaStyleName", uno::Any(OUString("Quotations")));
1131 setFilterOptions("xhtmlns=reqif-xhtml");
1132 save("HTML (StarWriter)");
1133 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1135 // Without the accompanying fix in place, this test would have failed with:
1138 // i.e. <blackquote> had character (direct) children, which is invalid xhtml.
1139 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:blockquote/reqif-xhtml:p", 1);
1142 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testRTFOLEMimeType
)
1144 // Import a document with an embedded object.
1145 OUString
aType("test/rtf");
1146 ImportFromReqif(createFileURL(u
"reqif-ole-data.xhtml"));
1149 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1150 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1151 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1152 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1153 comphelper::makePropertyValue("RTFOLEMimeType", aType
),
1155 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1156 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1158 // Without the accompanying fix in place, this test would have failed with:
1159 // - Expected: test/rtf
1160 // - Actual : text/rtf
1161 // i.e. the MIME type was always text/rtf, not taking the store parameter into account.
1162 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "type",
1166 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testChinese
)
1168 // Load a document with Chinese text in it.
1169 createSwDoc("reqif-chinese.odt");
1173 // Without the accompanying fix in place, this would have failed as the output was not
1175 WrapReqifFromTempFile();
1178 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifComment
)
1180 // Create a document with a comment in it.
1182 uno::Sequence
<beans::PropertyValue
> aPropertyValues
= comphelper::InitPropertySequence({
1183 { "Text", uno::Any(OUString("some text")) },
1184 { "Author", uno::Any(OUString("me")) },
1186 dispatchCommand(mxComponent
, ".uno:InsertAnnotation", aPropertyValues
);
1190 // Without the accompanying fix in place, this would have failed as the output was not
1192 WrapReqifFromTempFile();
1195 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifFontNameSize
)
1197 // Create a document with a custom font name and size in it.
1199 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
1200 xParagraph
->setPropertyValue("CharFontName", uno::Any(OUString("Liberation Serif")));
1201 float fCharHeight
= 14.0;
1202 xParagraph
->setPropertyValue("CharHeight", uno::Any(fCharHeight
));
1203 sal_Int32 nCharColor
= 0xff0000;
1204 xParagraph
->setPropertyValue("CharColor", uno::Any(nCharColor
));
1205 uno::Reference
<text::XTextRange
> xTextRange(xParagraph
, uno::UNO_QUERY
);
1206 xTextRange
->setString("x");
1210 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1212 // Without the accompanying fix in place, this test would have failed with:
1215 // i.e. font name and size was written, even if that's not relevant for ReqIF.
1216 assertXPath(pDoc
, "//reqif-xhtml:span", 1);
1219 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifParagraphAlignment
)
1221 // Create a document with an explicitly aligned paragraph.
1223 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
1224 xParagraph
->setPropertyValue("ParaAdjust",
1225 uno::Any(static_cast<sal_Int16
>(style::ParagraphAdjust_RIGHT
)));
1229 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
1231 // Without the accompanying fix in place, this test would have failed with:
1234 // i.e. the <reqif-xhtml:p align="..."> markup was used, which is invalid.
1235 assertXPathNoAttribute(pDoc
, "//reqif-xhtml:p", "align");
1238 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOle1PDF
)
1240 // Save to reqif-xhtml.
1241 createSwDoc("pdf-ole.odt");
1244 OUString aRtfUrl
= GetOlePath();
1245 SvMemoryStream aOle1
;
1246 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1248 // Check the content of the ole1 data.
1249 OLE1Reader
aOle1Reader(aOle1
);
1251 // Without the accompanying fix in place, this test would have failed with:
1252 // - Expected: 39405
1254 // i.e. we did not work with the Ole10Native stream, rather created an OLE1 wrapper around the
1255 // OLE1-in-OLE2 data, resulting in additional size.
1256 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(0x99ed), aOle1Reader
.m_nNativeDataSize
);
1258 // Now import this back and check the ODT result.
1259 mxComponent
->dispose();
1260 mxComponent
.clear();
1261 ImportFromReqif(maTempFile
.GetURL());
1263 uno::Reference
<packages::zip::XZipFileAccess2
> xNameAccess
1264 = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory
),
1265 maTempFile
.GetURL());
1266 uno::Reference
<io::XInputStream
> xInputStream(xNameAccess
->getByName("Object 2"),
1268 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(xInputStream
, true));
1269 tools::SvRef
<SotStorage
> pStorage
= new SotStorage(*pStream
);
1270 tools::SvRef
<SotStorageStream
> pOleNative
= pStorage
->OpenSotStream("\1Ole10Native");
1271 // Without the accompanying fix in place, this test would have failed with:
1272 // - Expected: 39409
1274 // i.e. we didn't handle the case when the ole1 payload was not an ole2 container. Note how the
1275 // expected value is the same as nData above + 4 bytes, since this data is length-prefixed.
1276 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(39409), pOleNative
->GetSize());
1279 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOle1Paint
)
1281 // Load the bug document, which has OLE1 data in it, which is not a wrapper around OLE2 data.
1282 ImportFromReqif(createFileURL(u
"paint-ole.xhtml"));
1284 // Save it as ODT to inspect the result of the OLE1 -> OLE2 conversion.
1286 uno::Reference
<packages::zip::XZipFileAccess2
> xNameAccess
1287 = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory
),
1288 maTempFile
.GetURL());
1289 uno::Reference
<io::XInputStream
> xInputStream(xNameAccess
->getByName("Object 2"),
1291 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(xInputStream
, true));
1292 tools::SvRef
<SotStorage
> pStorage
= new SotStorage(*pStream
);
1293 // Check the clsid of the root stream of the OLE2 storage.
1294 SvGlobalName aActual
= pStorage
->GetClassName();
1295 SvGlobalName
aExpected(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46);
1297 // Without the accompanying fix in place, this test would have failed with:
1298 // - Expected: 0003000A-0000-0000-c000-000000000046
1299 // - Actual : 0003000C-0000-0000-c000-000000000046
1300 // i.e. the "Package" clsid was used on the OLE2 storage unconditionally, even for an mspaint
1301 // case, which has its own clsid.
1302 CPPUNIT_ASSERT_EQUAL(aExpected
.GetHexName(), aActual
.GetHexName());
1305 OUString aRtfUrl
= GetOlePath();
1306 SvMemoryStream aOle1
;
1307 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1309 // Check the content of the ole1 data.
1310 // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
1313 aOle1
.ReadUInt32(nData
); // OLEVersion
1314 aOle1
.ReadUInt32(nData
); // FormatID
1315 aOle1
.ReadUInt32(nData
); // ClassName
1316 CPPUNIT_ASSERT(nData
);
1317 OString aClassName
= read_uInt8s_ToOString(aOle1
, nData
- 1);
1318 // Without the accompanying fix in place, this test would have failed with:
1319 // - Expected: PBrush
1320 // - Actual : Package
1321 // i.e. a hardcoded class name was written.
1322 CPPUNIT_ASSERT_EQUAL(OString("PBrush"), aClassName
);
1325 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOle1PaintBitmapFormat
)
1327 // Given a document with a 8bpp bitmap:
1328 createSwDoc("paint-ole-bitmap-format.odt");
1330 // When exporting to reqif-xhtml with ExportImagesAsOLE enabled:
1331 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1332 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1333 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1334 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1335 comphelper::makePropertyValue("ExportImagesAsOLE", true),
1337 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1339 // Then make sure the resulting bitmap is 24bpp:
1340 OUString aRtfUrl
= GetOlePath();
1341 SvMemoryStream aOle1
;
1342 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1343 OLE1Reader
aOle1Reader(aOle1
);
1345 SvMemoryStream aMemory
;
1346 aMemory
.WriteBytes(aOle1Reader
.m_aNativeData
.data(), aOle1Reader
.m_aNativeData
.size());
1348 CPPUNIT_ASSERT(ReadDIB(aBitmap
, aMemory
, true));
1349 // Without the accompanying fix in place, this test would have failed with:
1352 // i.e. it was not a pixel format ms paint could handle in OLE mode.
1353 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP
, aBitmap
.getPixelFormat());
1356 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testMultiParaListItem
)
1358 // Create a document with 3 list items: A, B&C and D.
1360 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1361 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
1362 pWrtShell
->Insert("A");
1363 SwDoc
* pDoc
= pWrtShell
->GetDoc();
1365 // Enable numbering.
1366 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
1367 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
1368 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1369 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1370 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1372 pWrtShell
->SplitNode();
1373 pWrtShell
->Insert("B");
1374 pWrtShell
->SplitNode();
1375 pWrtShell
->Insert("C");
1377 // C is in the same list item as B.
1378 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1379 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1380 rTextNode
.SetCountedInList(false);
1382 pWrtShell
->SplitNode();
1383 pWrtShell
->Insert("D");
1387 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1388 assertXPathContent(pXmlDoc
, "//reqif-xhtml:ol/reqif-xhtml:li[1]/reqif-xhtml:p", "A");
1389 assertXPathContent(pXmlDoc
, "//reqif-xhtml:ol/reqif-xhtml:li[2]/reqif-xhtml:p[1]", "B");
1390 // Without the accompanying fix in place, this test would have failed with:
1391 // XPath '//reqif-xhtml:ol/reqif-xhtml:li[2]/reqif-xhtml:p[2]' not found
1392 // i.e. </li> was written before "C", not after "C", so "C" was not in the 2nd list item.
1393 assertXPathContent(pXmlDoc
, "//reqif-xhtml:ol/reqif-xhtml:li[2]/reqif-xhtml:p[2]", "C");
1394 assertXPathContent(pXmlDoc
, "//reqif-xhtml:ol/reqif-xhtml:li[3]/reqif-xhtml:p", "D");
1397 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testUnderlineNone
)
1399 // Create a document with a single paragraph: its underlying is set to an explicit 'none' value.
1401 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
1402 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
1403 xText
->insertString(xText
->getEnd(), "x", /*bAbsorb=*/false);
1404 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
1405 xParagraph
->setPropertyValue("CharUnderline", uno::Any(sal_Int16(awt::FontUnderline::NONE
)));
1407 // Export to reqif-xhtml.
1410 // Make sure that the paragraph has no explicit style, because "text-decoration: none" is
1412 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1413 assertXPathNoAttribute(pXmlDoc
, "//reqif-xhtml:div/reqif-xhtml:p", "style");
1416 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOle1PresDataNoOle2
)
1418 // Save to reqif-xhtml.
1419 createSwDoc("no-ole2-pres-data.odt");
1421 OUString aRtfUrl
= GetOlePath();
1422 SvMemoryStream aOle1
;
1423 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1425 // Check the content of the ole1 data.
1426 // Without the accompanying fix in place, this test would have failed as there was no
1427 // presentation data after the native data in the OLE1 container. The result was not editable in
1429 OLE1Reader
aOle1Reader(aOle1
);
1432 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOle1PresDataWmfOnly
)
1434 // Save to reqif-xhtml.
1435 createSwDoc("ole1-pres-data-wmf.odt");
1437 OUString aRtfUrl
= GetOlePath();
1438 SvMemoryStream aOle1
;
1439 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1441 OLE1Reader
aOle1Reader(aOle1
);
1442 // Without the accompanying fix in place, this test would have failed with:
1443 // - Expected: 135660
1444 // - Actual : 272376
1445 // i.e. we wrote some additional EMF data into the WMF output, which broke Word.
1446 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(135660), aOle1Reader
.m_nPresentationDataSize
);
1449 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifAscharObjsize
)
1451 // Given a document with an as-char anchored embedded object:
1452 createSwDoc("reqif-aschar-objsize.odt");
1454 // When exporting to reqif-xhtml:
1457 // Then make sure that the RTF snippet has the correct aspect ratio:
1458 OUString aRtfUrl
= GetOlePath();
1459 SvMemoryStream aRtf
;
1460 HtmlExportTest::wrapRtfFragment(aRtfUrl
, aRtf
);
1461 tools::SvRef
<TestReqIfRtfReader
> xReader(new TestReqIfRtfReader(aRtf
));
1462 CPPUNIT_ASSERT(xReader
->CallParser() != SvParserState::Error
);
1463 // Without the accompanying fix in place, this test would have failed with:
1466 // i.e. the aspect ratio was 1:1, while the PNG aspect ratio was correctly not 1:1.
1467 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(7344), xReader
->GetObjw());
1468 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4116), xReader
->GetObjh());
1471 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifObjdataPresentationDataSize
)
1473 // Given a document with an OLE2 embedded object, containing a preview:
1474 createSwDoc("reqif-objdata-presentationdatasize.odt");
1476 // When exporting to ReqIF:
1479 // Then make sure that the PresentationDataSize in the RTF's objdata blob is correct:
1480 OUString aRtfUrl
= GetOlePath();
1481 SvMemoryStream aOle1
;
1482 ParseOle1FromRtfUrl(aRtfUrl
, aOle1
);
1483 OLE1Reader
aOle1Reader(aOle1
);
1484 // Without the accompanying fix in place, this test would have failed with:
1485 // - Expected: 565994
1486 // - Actual : 330240 (Linux)
1487 // - Actual : 566034 (Windows, when Word is installed)
1488 // because PresentationData was taken from the OLE2 stream but its size was taken from RTF.
1489 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(565994), aOle1Reader
.m_nPresentationDataSize
);
1492 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testListHeading
)
1494 // Given a document with a list heading:
1496 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1497 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
1498 pWrtShell
->Insert("list header");
1499 SwDoc
* pDoc
= pWrtShell
->GetDoc();
1500 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
1501 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
1502 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1503 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1504 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1505 rTextNode
.SetCountedInList(false);
1507 // When exporting to ReqIF:
1510 // Then make sure the output is valid xhtml:
1511 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1513 // Without the accompanying fix in place, this test would have failed:
1514 // - expected: <div><ol><li style="display: block"><p>...</p></li></ol></div>
1515 // - actual : <div><ol><p>...</p></li></ol></div>
1516 // because a </li> but no <li> is not well-formed and <ol> with a non-li children is invalid.
1518 = getXPathContent(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/"
1519 "reqif-xhtml:li[@style='display: block']/reqif-xhtml:p");
1520 CPPUNIT_ASSERT_EQUAL(OUString("list header"), aContent
.trim());
1523 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testPartiallyNumberedList
)
1525 // Given a document with a list, first para is numbered, second is not:
1527 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1528 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
1529 pWrtShell
->Insert("list header");
1530 SwDoc
* pDoc
= pWrtShell
->GetDoc();
1531 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
1532 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
1534 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1535 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1536 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1538 pWrtShell
->Insert2("numbered");
1539 pWrtShell
->SplitNode();
1540 pWrtShell
->Insert2("not numbered");
1542 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1543 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1544 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1545 rTextNode
.SetCountedInList(false);
1548 // When exporting to ReqIF:
1551 // Then make sure the output is well-formed xhtml:
1552 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1553 // Without the accompanying fix in place, this test would have failed:
1554 // - expected: <li><p>...</p><p>...</p></li>
1555 // - actual : <li><p>...</p><p>...</p>
1556 // because a <li> without a matching </li> is not well-formed, and the </li> was omitted because
1557 // the second para was not numbered.
1558 assertXPath(pXmlDoc
,
1559 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p", 2);
1562 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testPartiallyNumberedListHTML
)
1564 // Given a document with a list, first para is numbered, second is not:
1566 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1567 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
1568 pWrtShell
->Insert("list header");
1569 SwDoc
* pDoc
= pWrtShell
->GetDoc();
1570 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
1571 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
1573 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->nNode
.GetNode();
1574 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1575 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1577 pWrtShell
->Insert2("numbered");
1578 pWrtShell
->SplitNode();
1579 pWrtShell
->Insert2("not numbered");
1581 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->nNode
.GetNode();
1582 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1583 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1584 rTextNode
.SetCountedInList(false);
1587 // When exporting to HTML:
1590 SvMemoryStream aStream
;
1591 WrapFromTempFile(aStream
);
1592 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
1593 CPPUNIT_ASSERT(pXmlDoc
); // if we have missing closing marks - parse error
1595 // Without the accompanying fix in place, this test would have failed:
1596 // - expected: <li><p>...</p><p>...</p></li>
1597 // - actual : <li><p>...</p><p>...</p>
1598 // because a <li> without a matching </li> is not well-formed, and the </li> was omitted because
1599 // the second para was not numbered.
1601 assertXPath(pXmlDoc
, "/html/body/ol/li/p", 2);
1604 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testListHeaderAndItem
)
1606 // Given a document with a list, first para is not numbered, but the second is:
1608 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1609 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
1610 pWrtShell
->Insert("not numbered");
1611 SwDoc
* pDoc
= pWrtShell
->GetDoc();
1612 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
1613 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
1615 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1616 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1617 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1618 rTextNode
.SetCountedInList(false);
1620 pWrtShell
->SplitNode();
1621 pWrtShell
->Insert2("numbered");
1623 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
1624 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
1625 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
1628 // When exporting to ReqIF:
1631 // Then make sure the output is well-formed xhtml:
1632 // Without the accompanying fix in place, this would have failed:
1633 // Entity: line 3: parser error : Opening and ending tag mismatch: ol line 3 and li
1634 // <reqif-xhtml:ol><reqif-xhtml:p>not numbered</reqif-xhtml:p></reqif-xhtml:li>
1635 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1636 // Make sure that in case the list has a header and an item, then both are wrapped in an <li>
1638 assertXPath(pXmlDoc
,
1639 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p", 2);
1642 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testBlockQuoteNoMargin
)
1644 // Given a document with some text, para style set to Quotations, no bottom margin:
1646 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
1647 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
1648 xText
->insertString(xText
->getEnd(), "string", /*bAbsorb=*/false);
1649 uno::Reference
<beans::XPropertySet
> xQuotations(
1650 getStyles("ParagraphStyles")->getByName("Quotations"), uno::UNO_QUERY
);
1651 xQuotations
->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32
>(0)));
1652 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
1653 xParagraph
->setPropertyValue("ParaStyleName", uno::Any(OUString("Quotations")));
1655 // When exporting to XHTML:
1658 // Then make sure the output is valid xhtml:
1659 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1660 // Without the accompanying fix in place, this test would have failed:
1661 // - expected: <blockquote><p>...</p></blockquote>
1662 // - actual : <blockquote>...</blockquote>
1663 // i.e. <blockquote> is can't have character children, but it had.
1664 assertXPathContent(pXmlDoc
,
1665 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:blockquote/reqif-xhtml:p",
1669 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifImageToOle
)
1671 // Given a document with an image:
1673 uno::Sequence
<beans::PropertyValue
> aArgs
= {
1674 comphelper::makePropertyValue("FileName", createFileURL(u
"ole2.png")),
1676 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aArgs
);
1678 // When exporting to XHTML:
1679 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1680 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1681 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1682 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1683 comphelper::makePropertyValue("ExportImagesAsOLE", true),
1685 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1687 // Then make sure we export that PNG as WMF in ReqIF mode:
1688 OUString aRtfUrl
= GetOlePath();
1689 SvMemoryStream aRtf
;
1690 HtmlExportTest::wrapRtfFragment(aRtfUrl
, aRtf
);
1691 tools::SvRef
<TestReqIfRtfReader
> xReader(new TestReqIfRtfReader(aRtf
));
1692 CPPUNIT_ASSERT(xReader
->CallParser() != SvParserState::Error
);
1693 // Without the accompanying fix in place, this test would have failed:
1696 // i.e. the image was exported as PNG, not as WMF (with a version).
1697 CPPUNIT_ASSERT_EQUAL(8, xReader
->getWmetafile());
1699 // Make sure that the native data byte array is not empty.
1700 SvMemoryStream aOle1
;
1701 CPPUNIT_ASSERT(xReader
->WriteObjectData(aOle1
));
1702 // Without the accompanying fix in place, this test would have failed, as aOle1 was empty.
1703 OLE1Reader
aOle1Reader(aOle1
);
1704 CPPUNIT_ASSERT(aOle1Reader
.m_nNativeDataSize
);
1706 // Make sure that the presentation data byte array is not empty.
1707 // Without the accompanying fix in place, this test would have failed, as aOle1 only contained
1709 CPPUNIT_ASSERT(aOle1Reader
.m_nPresentationDataSize
);
1712 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedPNGDirectly
)
1714 // Given a document with an image:
1716 uno::Sequence
<beans::PropertyValue
> aArgs
= {
1717 comphelper::makePropertyValue("FileName", createFileURL(u
"ole2.png")),
1719 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aArgs
);
1721 // When exporting to XHTML:
1724 // Then make sure the PNG is embedded directly, without an RTF wrapper:
1725 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1726 // Without the accompanying fix in place, this test would have failed with:
1727 // - Expected: image/png
1728 // - Actual : text/rtf
1729 // i.e. even PNG was wrapped in an RTF.
1730 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
1733 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedJPGDirectly
)
1735 // Given a document with an image:
1737 uno::Sequence
<beans::PropertyValue
> aArgs
= {
1738 comphelper::makePropertyValue("FileName", createFileURL(u
"reqif-ole-img.jpg")),
1740 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aArgs
);
1742 // When exporting to XHTML:
1745 // Then make sure the JPG is embedded directly, without an RTF wrapper:
1746 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1747 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/jpeg");
1748 // Without the accompanying fix in place, this test would have failed with:
1749 // - Expected: image/jpeg
1750 // - Actual : image/png
1751 // i.e. first the original JPG data was lost, then the inner PNG fallback was missing.
1752 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object", "type",
1756 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedPNGShapeDirectly
)
1758 // Given a document with an image shape:
1760 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1761 uno::Reference
<drawing::XShape
> xShape(
1762 xFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY
);
1763 xShape
->setSize(awt::Size(10000, 10000));
1764 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
1765 xShapeProps
->setPropertyValue("GraphicURL", uno::Any(createFileURL(u
"ole2.png")));
1766 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1767 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1769 // When exporting to XHTML:
1772 // Then make sure the PNG is embedded directly, without an RTF wrapper:
1773 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1774 // Without the accompanying fix in place, this test would have failed with:
1775 // - no attribute 'type' exist
1776 // i.e. the PNG was exported as GIF, without an explicit type.
1777 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
1780 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedJPGShapeDirectly
)
1782 // Given a document with an image:
1784 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1785 uno::Reference
<drawing::XShape
> xShape(
1786 xFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY
);
1787 xShape
->setSize(awt::Size(10000, 10000));
1788 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
1789 xShapeProps
->setPropertyValue("GraphicURL", uno::Any(createFileURL(u
"reqif-ole-img.jpg")));
1790 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1791 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1793 // When exporting to XHTML:
1796 // Then make sure the JPG is embedded directly, without an RTF wrapper:
1797 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1798 // Without the accompanying fix in place, this test would have failed with:
1799 // - Expected: image/jpeg
1800 // - Actual : image/png
1801 // i.e. first the original JPG data was lost, then the inner PNG fallback was missing.
1802 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/jpeg");
1803 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object", "type",
1807 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedPNGShapeAsOLE
)
1809 // Given a document with an image shape:
1811 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1812 uno::Reference
<drawing::XShape
> xShape(
1813 xFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY
);
1814 xShape
->setSize(awt::Size(10000, 10000));
1815 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
1816 xShapeProps
->setPropertyValue("GraphicURL", uno::Any(createFileURL(u
"ole2.png")));
1817 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1818 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1820 // When exporting to XHTML:
1821 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1822 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1823 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1824 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1825 comphelper::makePropertyValue("ExportImagesAsOLE", true),
1827 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1829 // Then make sure the PNG is embedded with an RTF wrapper:
1830 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1831 // Without the accompanying fix in place, this test would have failed with:
1832 // - Expected: text/rtf
1833 // - Actual : image/png
1834 // i.e. the OLE wrapper around the PNG was missing.
1835 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "text/rtf");
1838 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedShapeAsPNG
)
1840 // Given a document with a shape:
1842 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1843 uno::Reference
<drawing::XShape
> xShape(
1844 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1845 xShape
->setSize(awt::Size(10000, 10000));
1846 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1847 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1849 // When exporting to XHTML:
1852 // Then make sure the shape is embedded as a PNG:
1853 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1854 // Without the accompanying fix in place, this test would have failed with:
1855 // - Expected: image/png
1856 // - Actual : image/x-vclgraphic
1857 // i.e. the result was invalid ReqIF.
1858 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
1860 // Then check the pixel size of the shape:
1861 Size
aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(10000, 10000),
1862 MapMode(MapUnit::Map100thMM
)));
1863 // Without the accompanying fix in place, this test would have failed with:
1864 // - no attribute 'width' exist
1865 // i.e. shapes had no width.
1866 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "width",
1867 OUString::number(aPixelSize
.getWidth()));
1870 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testShapeAsImageHtml
)
1872 // Given a document with a shape:
1874 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1875 uno::Reference
<drawing::XShape
> xShape(
1876 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1877 xShape
->setSize(awt::Size(5080, 2540));
1878 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1879 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1881 // When exporting to plain HTML:
1882 saveAndReload("HTML (StarWriter)");
1884 // Without the accompanying fix in place, this test would have failed with:
1887 // i.e. the output was not well-formed.
1888 CPPUNIT_ASSERT_EQUAL(OUString(" "), getParagraph(1)->getString());
1891 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testJson
)
1893 // Given a document with a shape:
1895 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1896 uno::Reference
<drawing::XShape
> xShape(
1897 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1898 xShape
->setSize(awt::Size(2540, 2540));
1899 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1900 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1902 // When exporting to HTML, and specifying options as JSON:
1903 setFilterOptions("{\"XhtmlNs\":{\"type\":\"string\", \"value\":\"reqif-xhtml\"},"
1904 "\"ShapeDPI\":{\"type\":\"long\",\"value\":\"192\"}}");
1905 save("HTML (StarWriter)");
1907 // Then make sure those options are not ignored:
1908 // Without the accompanying fix in place, this test would have failed, as GetPngPath() expects
1909 // XML output, but xhtmlns=reqif-xhtml was ignored.
1910 OUString aPngUrl
= GetPngPath();
1911 SvFileStream
aFileStream(aPngUrl
, StreamMode::READ
);
1912 GraphicDescriptor
aDescriptor(aFileStream
, nullptr);
1913 aDescriptor
.Detect(/*bExtendedInfo=*/true);
1914 // Make sure that the increased DPI is taken into account:
1915 tools::Long nExpected
= 192;
1916 CPPUNIT_ASSERT_EQUAL(nExpected
, aDescriptor
.GetSizePixel().getWidth());
1919 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifEmbedShapeAsPNGCustomDPI
)
1921 // Given a document with a shape:
1923 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1924 uno::Reference
<drawing::XShape
> xShape(
1925 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1926 xShape
->setSize(awt::Size(5080, 2540));
1927 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1928 xDrawPageSupplier
->getDrawPage()->add(xShape
);
1929 sal_Int32 nDPI
= 600;
1931 // When exporting to XHTML:
1932 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1933 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1934 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1935 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1936 comphelper::makePropertyValue("ShapeDPI", nDPI
),
1938 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1940 // Then make sure the shape is embedded as a PNG:
1941 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
1942 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
1944 // Then check the pixel size of the shape:
1945 Size
aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(5080, 2540),
1946 MapMode(MapUnit::Map100thMM
)));
1947 tools::Long nPNGWidth
= 1200;
1948 OUString aPngUrl
= GetPngPath();
1949 SvFileStream
aFileStream(aPngUrl
, StreamMode::READ
);
1950 GraphicDescriptor
aDescriptor(aFileStream
, nullptr);
1951 aDescriptor
.Detect(/*bExtendedInfo=*/true);
1952 // Without the accompanying fix in place, this test would have failed with:
1955 // i.e. first setting a double DPI didn't result in larger pixel width of the PNG, then it was
1956 // limited to 1000 pixels (because the pixel limit was 500k).
1957 CPPUNIT_ASSERT_EQUAL(nPNGWidth
, aDescriptor
.GetSizePixel().getWidth());
1959 // Then make sure the shape's logic size (in CSS pixels) don't change:
1960 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object", "width",
1961 OUString::number(aPixelSize
.getWidth()));
1964 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqifOleBmpTransparent
)
1966 // Given a document with a transparent image:
1968 uno::Sequence
<beans::PropertyValue
> aArgs
= {
1969 comphelper::makePropertyValue("FileName", createFileURL(u
"transparent.png")),
1971 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aArgs
);
1973 // When exporting to reqif with ExportImagesAsOLE=true:
1974 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
1975 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
1976 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
1977 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
1978 comphelper::makePropertyValue("ExportImagesAsOLE", true),
1980 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
1982 // Then make sure the transparent pixel turns into white:
1983 SvMemoryStream aOle1
;
1984 ParseOle1FromRtfUrl(GetOlePath(), aOle1
);
1985 OLE1Reader
aOle1Reader(aOle1
);
1986 SvMemoryStream
aBitmapStream(aOle1Reader
.m_aNativeData
.data(), aOle1Reader
.m_aNativeData
.size(),
1989 ReadDIB(aBitmap
, aBitmapStream
, /*bFileHeader=*/true);
1990 Size aBitmapSize
= aBitmap
.GetSizePixel();
1991 BitmapEx
aBitmapEx(aBitmap
);
1993 = aBitmapEx
.GetPixelColor(aBitmapSize
.getWidth() - 1, aBitmapSize
.getHeight() - 1);
1994 // Without the accompanying fix in place, this test would have failed with:
1995 // - Expected: Color: R:255 G:255 B:255 A:0
1996 // - Actual : Color: R:0 G:0 B:0 A:0
1997 // i.e. the bitmap without an alpha channel was black, not white.
1998 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, nActualColor
);
2001 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testListsHeading
)
2003 // Given a document with lh, lh, li, li, lh and lh nodes:
2005 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
2006 SwWrtShell
* pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
2007 pWrtShell
->Insert("list 1, header 1");
2008 pWrtShell
->SplitNode();
2009 pWrtShell
->Insert("list 1, header 2");
2010 pWrtShell
->SplitNode();
2011 pWrtShell
->Insert("list 2, item 1");
2012 pWrtShell
->SplitNode();
2013 pWrtShell
->Insert("list 2, item 2");
2014 pWrtShell
->SplitNode();
2015 pWrtShell
->Insert("list 3, header 1");
2016 pWrtShell
->SplitNode();
2017 pWrtShell
->Insert("list 3, header 2");
2018 SwDoc
* pDoc
= pWrtShell
->GetDoc();
2019 pWrtShell
->Up(false, 5);
2021 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
2022 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
2024 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2025 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2026 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2027 rTextNode
.SetCountedInList(false);
2029 pWrtShell
->Down(false, 1);
2031 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2032 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2033 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2034 rTextNode
.SetCountedInList(false);
2037 pWrtShell
->Down(false, 1);
2039 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
2040 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
2042 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2043 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2044 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2046 pWrtShell
->Down(false, 1);
2048 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2049 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2050 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2053 pWrtShell
->Down(false, 1);
2055 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
2056 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
2058 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2059 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2060 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2061 rTextNode
.SetCountedInList(false);
2063 pWrtShell
->Down(false, 1);
2065 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2066 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2067 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2068 rTextNode
.SetCountedInList(false);
2072 // When exporting to ReqIF:
2075 // Then make sure the output is valid xhtml:
2076 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2079 = getXPathContent(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/"
2080 "reqif-xhtml:li[@style='display: block']/reqif-xhtml:p");
2081 CPPUNIT_ASSERT_EQUAL(OUString("list 1, header 1"), aContent
.trim());
2084 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testOleEmfPreviewToHtml
)
2086 // Given a document containing an embedded object, with EMF preview:
2087 createSwDoc("ole2.odt");
2089 // When exporting to HTML:
2090 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
2091 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
2092 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
2094 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2096 // Then make sure the <img> tag has matching file extension and data:
2097 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
2098 OUString aPath
= getXPath(pDoc
, "/html/body/p/img", "src");
2099 // Without the accompanying fix in place, this test would have failed, as aPath was
2100 // ole_html_3978e5f373402b43.JPG, with EMF data.
2101 CPPUNIT_ASSERT(aPath
.endsWith("gif"));
2104 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testNestedBullets
)
2106 // Given a documented with nested lists:
2108 SwDoc
* pDoc
= getSwDoc();
2109 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2110 pWrtShell
->Insert("first");
2111 sal_uInt16 nPos
= pDoc
->MakeNumRule(pDoc
->GetUniqueNumRuleName());
2112 SwNumRule
* pNumRule
= pDoc
->GetNumRuleTable()[nPos
];
2114 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2115 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2116 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2117 rTextNode
.SetAttrListLevel(0);
2119 pWrtShell
->SplitNode();
2120 pWrtShell
->Insert("second");
2122 SwNode
& rNode
= pWrtShell
->GetCursor()->GetPoint()->GetNode();
2123 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
2124 rTextNode
.SetAttr(SwNumRuleItem(pNumRule
->GetName()));
2125 rTextNode
.SetAttrListLevel(1);
2128 // When exporting to xhtml:
2131 // Then make sure that there is a <li> between the outer and the inner <ol>:
2132 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2133 // Without the accompanying fix in place, this test would have failed with:
2134 // - XPath '//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p' not found
2135 // i.e. the <li> inside the outer <ol> was missing.
2137 pXmlDoc
, "//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
2141 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTrailingLineBreak
)
2143 // Given a document with a trailing line-break:
2145 SwDoc
* pDoc
= getSwDoc();
2146 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2147 pWrtShell
->Insert("test\n");
2149 // When exporting to reqif-xhtml:
2152 // Then make sure that we still have a single line-break:
2153 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2154 // Without the accompanying fix in place, this test would have failed with:
2157 // - XPath '//reqif-xhtml:br' number of nodes is incorrect
2158 assertXPath(pXmlDoc
, "//reqif-xhtml:br", 1);
2160 // Then test the import side:
2162 // Given an empty document:
2163 mxComponent
->dispose();
2165 // When importing a <br> from reqif-xhtml:
2166 ImportFromReqif(maTempFile
.GetURL());
2168 // Then make sure that line-break is not lost:
2169 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
2170 CPPUNIT_ASSERT(pTextDoc
);
2171 pDoc
= pTextDoc
->GetDocShell()->GetDoc();
2172 pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2173 OUString aActual
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode()->GetText();
2174 // Without the accompanying fix in place, this test would have failed, as the trailing
2175 // line-break was lost.
2176 CPPUNIT_ASSERT_EQUAL(OUString("test\n"), aActual
);
2179 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testLeadingTab
)
2181 // Given a document with leading tabs:
2183 SwDoc
* pDoc
= getSwDoc();
2184 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2185 pWrtShell
->Insert("\t first");
2186 pWrtShell
->SplitNode();
2187 pWrtShell
->Insert("\t\t second");
2188 pWrtShell
->SplitNode();
2189 pWrtShell
->Insert("thi \t rd");
2191 // When exporting to HTML, using LeadingTabWidth=2:
2192 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
2193 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
2194 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
2195 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
2196 comphelper::makePropertyValue("LeadingTabWidth", static_cast<sal_Int32
>(2)),
2198 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2200 // Then make sure that leading tabs are replaced with 2 nbsps:
2201 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2202 // Without the accompanying fix in place, this test would have failed with:
2203 // - Expected: <nbsp><nbsp><space>first
2204 // - Actual : <tab><space>first
2205 // i.e. the leading tab was not replaced by 2 nbsps.
2206 assertXPathContent(pXmlDoc
, "//reqif-xhtml:p[1]", u
"\xa0\xa0 first");
2207 // Test a leading tab that is not at the start of the paragraph:
2208 assertXPathContent(pXmlDoc
, "//reqif-xhtml:p[2]", u
"\xa0\xa0\xa0\xa0 second");
2209 // Test a tab which is not leading:
2210 assertXPathContent(pXmlDoc
, "//reqif-xhtml:p[3]", u
"thi \t rd");
2213 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testLeadingTabHTML
)
2215 // Given a document with leading tabs:
2217 SwDoc
* pDoc
= getSwDoc();
2218 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2219 pWrtShell
->Insert("\t test");
2221 // When exporting to plain HTML, using LeadingTabWidth=2:
2222 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
2223 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
2224 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
2225 comphelper::makePropertyValue("LeadingTabWidth", static_cast<sal_Int32
>(2)),
2227 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2229 // Then make sure that leading tabs are replaced with 2 nbsps:
2230 htmlDocUniquePtr pHtmlDoc
= parseHtml(maTempFile
);
2231 CPPUNIT_ASSERT(pHtmlDoc
);
2232 // Without the accompanying fix in place, this test would have failed with:
2233 // - Expected: <newline><nbsp><nbsp><space>test
2234 // - Actual : <newline><tab><space>test
2235 // i.e. the leading tab was not replaced by 2 nbsps.
2236 assertXPathContent(pHtmlDoc
, "/html/body/p", SAL_NEWLINE_STRING u
"\xa0\xa0 test");
2239 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testClearingBreak
)
2241 auto verify
= [this]() {
2242 uno::Reference
<container::XEnumerationAccess
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
2243 uno::Reference
<container::XEnumeration
> xPortions
= xParagraph
->createEnumeration();
2244 uno::Reference
<beans::XPropertySet
> xPortion
;
2245 OUString aPortionType
;
2248 // Ignore leading comments.
2249 xPortion
.set(xPortions
->nextElement(), uno::UNO_QUERY
);
2250 xPortion
->getPropertyValue("TextPortionType") >>= aPortionType
;
2251 if (aPortionType
!= "Annotation")
2257 // Without the accompanying fix in place, this test would have failed with:
2258 // An uncaught exception of type com.sun.star.container.NoSuchElementException
2259 // i.e. the first para was just comments + text portion, the clearing break was lost.
2260 xPortion
.set(xPortions
->nextElement(), uno::UNO_QUERY
);
2261 xPortion
->getPropertyValue("TextPortionType") >>= aPortionType
;
2262 CPPUNIT_ASSERT_EQUAL(OUString("LineBreak"), aPortionType
);
2263 uno::Reference
<text::XTextContent
> xLineBreak
;
2264 xPortion
->getPropertyValue("LineBreak") >>= xLineBreak
;
2266 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
2267 xLineBreakProps
->getPropertyValue("Clear") >>= eClear
;
2268 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16
>(SwLineBreakClear::ALL
), eClear
);
2271 // Given a document with an at-para anchored image + a clearing break:
2272 // When loading that file:
2273 createSwWebDoc("clearing-break.html");
2274 // Then make sure that the clear property of the break is not ignored:
2276 saveAndReload("HTML (StarWriter)");
2277 // Make sure that the clear property of the break is not ignored during export:
2281 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTableBackground
)
2283 // Given a document with two tables: first stable has a background, second table has a
2284 // background in its first row:
2286 SwDoc
* pDoc
= getSwDoc();
2287 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2288 SwInsertTableOptions
aInsertTableOptions(SwInsertTableFlags::DefaultBorder
,
2289 /*nRowsToRepeat=*/0);
2290 pWrtShell
->InsertTable(aInsertTableOptions
, /*nRows=*/1, /*nCols=*/1);
2291 pWrtShell
->MoveTable(GotoPrevTable
, fnTableStart
);
2292 SvxBrushItem
aBrush(Color(0xff0000), RES_BACKGROUND
);
2293 pWrtShell
->SetTabBackground(aBrush
);
2294 pWrtShell
->Down(/*bSelect=*/false);
2295 pWrtShell
->SplitNode();
2296 pWrtShell
->InsertTable(aInsertTableOptions
, /*nRows=*/2, /*nCols=*/1);
2297 pWrtShell
->MoveTable(GotoPrevTable
, fnTableStart
);
2298 aBrush
.SetColor(0x00ff00);
2299 pWrtShell
->SetRowBackground(aBrush
);
2300 pWrtShell
->Down(/*bSelect=*/false);
2301 // Second row has an explicit transparent background.
2302 aBrush
.SetColor(COL_TRANSPARENT
);
2303 pWrtShell
->SetRowBackground(aBrush
);
2305 // When exporting to reqif-xhtml:
2308 // Then make sure that CSS markup is used, not HTML one:
2309 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2310 // Without the accompanying fix in place, this test would have failed with:
2311 // - XPath '//reqif-xhtml:table[1]' no attribute 'style' exist
2312 // i.e. HTML markup was used for the table background color.
2313 assertXPath(pXmlDoc
, "//reqif-xhtml:table[1]", "style", "background: #ff0000");
2314 assertXPathNoAttribute(pXmlDoc
, "//reqif-xhtml:table[1]", "bgcolor");
2315 assertXPath(pXmlDoc
, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "style",
2316 "background: #00ff00");
2317 assertXPathNoAttribute(pXmlDoc
, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "bgcolor");
2318 // Second row has no explicit style, the default is not written.
2319 assertXPathNoAttribute(pXmlDoc
, "//reqif-xhtml:table[2]/reqif-xhtml:tr[2]", "style");
2322 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testImageKeepRatio
)
2324 // Given a document with an image: width is relative, height is "keep ratio":
2326 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
2327 uno::Reference
<beans::XPropertySet
> xTextGraphic(
2328 xFactory
->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY
);
2329 xTextGraphic
->setPropertyValue("AnchorType",
2330 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
2331 xTextGraphic
->setPropertyValue("RelativeWidth", uno::Any(static_cast<sal_Int16
>(42)));
2332 xTextGraphic
->setPropertyValue("IsSyncHeightToWidth", uno::Any(true));
2333 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
2334 uno::Reference
<text::XText
> xBodyText
= xTextDocument
->getText();
2335 uno::Reference
<text::XTextCursor
> xCursor(xBodyText
->createTextCursor());
2336 uno::Reference
<text::XTextContent
> xTextContent(xTextGraphic
, uno::UNO_QUERY
);
2337 xBodyText
->insertTextContent(xCursor
, xTextContent
, false);
2339 // When exporting to HTML:
2340 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
2341 uno::Sequence
<beans::PropertyValue
> aStoreProperties
= {
2342 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
2344 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2346 // Then make sure that the width is not a fixed size, that would break on resizing the browser
2348 htmlDocUniquePtr pDoc
= parseHtml(maTempFile
);
2349 // Without the accompanying fix in place, this test would have failed with:
2352 // i.e. a static (CSS pixel) height was written.
2353 assertXPath(pDoc
, "/html/body/p/img", "height", "auto");
2356 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testSectionDir
)
2358 // Given a document with a section:
2360 SwDoc
* pDoc
= getSwDoc();
2361 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2362 pWrtShell
->Insert("test");
2363 pWrtShell
->SelAll();
2364 SwSectionData
aSectionData(SectionType::Content
, "mysect");
2365 pWrtShell
->InsertSection(aSectionData
);
2367 // When exporting to (reqif-)xhtml:
2370 // Then make sure CSS is used to export the text direction of the section:
2371 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2372 // Without the accompanying fix in place, this test would have failed with:
2373 // - XPath '//reqif-xhtml:div[@id='mysect']' no attribute 'style' exist
2374 // i.e. the dir="ltr" HTML attribute was used instead.
2375 assertXPath(pXmlDoc
, "//reqif-xhtml:div[@id='mysect']", "style", "dir: ltr");
2378 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testTdf114769
)
2380 // Create document from scratch since relative urls to filesystem can be replaced
2381 // by absolute during save/load
2383 SwDoc
* pDoc
= getSwDoc();
2384 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
2385 pWrtShell
->Insert("Hyperlink1");
2386 pWrtShell
->SplitNode();
2387 pWrtShell
->Insert("Hyperlink2");
2388 pWrtShell
->SplitNode();
2389 pWrtShell
->Insert("Hyperlink3");
2390 pWrtShell
->SplitNode();
2391 pWrtShell
->Insert("Hyperlink4");
2392 pWrtShell
->SplitNode();
2393 pWrtShell
->Insert("Hyperlink5");
2394 pWrtShell
->SplitNode();
2396 // Normal external URL
2397 uno::Reference
<beans::XPropertySet
> xRun(getRun(getParagraph(1), 1), uno::UNO_QUERY
);
2398 xRun
->setPropertyValue("HyperLinkURL", uno::Any(OUString("http://libreoffice.org/")));
2400 // Bookmark reference
2401 xRun
.set(getRun(getParagraph(2), 1), uno::UNO_QUERY
);
2402 xRun
->setPropertyValue("HyperLinkURL", uno::Any(OUString("#some_bookmark")));
2404 // Filesystem absolute link
2405 xRun
.set(getRun(getParagraph(3), 1), uno::UNO_QUERY
);
2406 xRun
->setPropertyValue("HyperLinkURL", uno::Any(OUString("C:\\test.txt")));
2408 // Filesystem relative link
2409 xRun
.set(getRun(getParagraph(4), 1), uno::UNO_QUERY
);
2410 xRun
->setPropertyValue("HyperLinkURL", uno::Any(OUString("..\\..\\test.odt")));
2412 // Filesystem relative link
2413 xRun
.set(getRun(getParagraph(5), 1), uno::UNO_QUERY
);
2414 xRun
->setPropertyValue("HyperLinkURL", uno::Any(OUString(".\\another.odt")));
2417 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
2418 uno::Sequence
<beans::PropertyValue
> aStoreProperties
2419 = { comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")) };
2420 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2422 htmlDocUniquePtr pHtmlDoc
= parseHtml(maTempFile
);
2423 CPPUNIT_ASSERT(pHtmlDoc
);
2425 CPPUNIT_ASSERT_EQUAL(OUString("http://libreoffice.org/"),
2426 getXPath(pHtmlDoc
, "/html/body/p[1]/a", "href"));
2427 CPPUNIT_ASSERT_EQUAL(OUString("#some_bookmark"),
2428 getXPath(pHtmlDoc
, "/html/body/p[2]/a", "href"));
2429 CPPUNIT_ASSERT_EQUAL(OUString("C:\\test.txt"), getXPath(pHtmlDoc
, "/html/body/p[3]/a", "href"));
2430 CPPUNIT_ASSERT_EQUAL(OUString("..\\..\\test.odt"),
2431 getXPath(pHtmlDoc
, "/html/body/p[4]/a", "href"));
2432 CPPUNIT_ASSERT_EQUAL(OUString(".\\another.odt"),
2433 getXPath(pHtmlDoc
, "/html/body/p[5]/a", "href"));
2436 CPPUNIT_TEST_FIXTURE(HtmlExportTest
, testTdf153923
)
2438 createSwDoc("TableWithIndent.fodt");
2439 save("HTML (StarWriter)");
2441 // Parse it as XML (strict!)
2442 xmlDocUniquePtr pDoc
= parseXml(maTempFile
);
2443 // Without the fix in place, this would fail
2444 CPPUNIT_ASSERT(pDoc
);
2446 assertXPath(pDoc
, "/html/body//dl", 3);
2447 // The 'dd' tag was not closed
2448 assertXPath(pDoc
, "/html/body//dd", 3);
2451 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf153923_ReqIF
)
2453 createSwDoc("TableWithIndent.fodt");
2456 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2458 assertXPath(pDoc
, "//reqif-xhtml:table");
2459 // There should be no 'dd' or 'dl' tags, used as a hack for table indentation
2460 assertXPath(pDoc
, "//reqif-xhtml:dl", 0);
2461 assertXPath(pDoc
, "//reqif-xhtml:dd", 0);
2464 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIfTransparentTifImg
)
2466 // reqIf export must keep the TIF encoding of the image
2467 createSwDoc("reqif-transparent-tif-img.odt");
2470 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2471 assertXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object[1]", "type", "image/tiff");
2472 OUString imageName
= getXPath(pXmlDoc
, "//reqif-xhtml:p/reqif-xhtml:object[1]", "data");
2473 // Without the accompanying fix in place, this test would have failed,
2474 // ending with .gif, because XOutFlags::UseGifIfSensible flag combined
2475 // with the transparent image would result in GIF export
2476 CPPUNIT_ASSERT(imageName
.endsWith(".tif"));
2478 INetURLObject
aURL(maTempFile
.GetURL());
2479 aURL
.setName(imageName
);
2480 GraphicDescriptor
aDescriptor(aURL
);
2481 aDescriptor
.Detect();
2482 CPPUNIT_ASSERT_EQUAL(GraphicFileFormat::TIF
, aDescriptor
.GetFileFormat());
2485 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf155387
)
2487 createSwDoc("sub_li_and_ctd.fodt");
2490 // Without the fix in place, this would fail
2491 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2493 // Single top-level list
2494 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul");
2495 // Single top-level item
2496 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li");
2497 // 4 top-level paragraphs in the item
2499 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p", 4);
2500 // 2 sublists in the item
2502 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul", 2);
2503 // 2 items in the first sublist
2505 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[1]/"
2508 // Check the last (most nested) subitem's text
2511 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[2]/"
2512 "reqif-xhtml:li/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p",
2516 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf155496
)
2518 createSwDoc("listItemSubheader.fodt");
2521 // Without the fix in place, this would fail
2522 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2524 // Two top-level lists
2525 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul", 2);
2526 // Single top-level item
2527 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li");
2528 // One top-level paragraph in the item
2530 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:p");
2531 // One sublist in the item
2533 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul");
2534 // One item in the sublist
2536 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
2539 OUString aContent
= getXPathContent(
2540 pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
2541 "reqif-xhtml:li/reqif-xhtml:p");
2542 CPPUNIT_ASSERT_EQUAL(OUString("list 1 item 1\n\t\tsub-header"), aContent
.trim());
2545 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIF_RightAlignedTable
)
2547 createSwDoc("tableRight.fodt");
2550 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2552 // No 'align' attribute must be present in 'div'
2553 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:div", "align");
2556 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIF_ListsWithNumFormat
)
2558 createSwDoc("listsWithNumFormat.fodt");
2561 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2563 // No 'type' attribute must be present in 'ol'
2564 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[1]", "type");
2565 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[2]", "type");
2566 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[3]", "type");
2567 assertXPathNoAttribute(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[4]", "type");
2570 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf155871
)
2572 createSwDoc("tdf155871.fodt");
2575 // Without the fix in place, this would fail
2576 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2579 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIF_ListsNoStartAttribute
)
2581 createSwDoc("twoListsWithSameStyle.fodt");
2584 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2586 // No 'start' attribute must be present in 'ol'
2587 assertXPath(pDoc
, "//reqif-xhtml:ol[@start]", 0);
2590 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIF_FrameTextAsObjectAltText
)
2592 createSwDoc("frameWithText.fodt");
2595 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2597 // Without the fix, this would fail with
2598 // - Expected: Some text in frame & <foo>
2599 // - Actual : Frame1
2600 // i.e., frame name was used as the object element content, not frame text
2601 assertXPathContent(pDoc
,
2602 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]/reqif-xhtml:object",
2603 "Some text in frame & <foo>");
2606 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testSingleOleExport
)
2608 // Given a document containing an embedded OLE object:
2609 createSwDoc("ole2.odt");
2611 // Create a selection for that object:
2612 auto xDrawPageSupplier(mxComponent
.queryThrow
<css::drawing::XDrawPageSupplier
>());
2613 auto xDrawPage(xDrawPageSupplier
->getDrawPage());
2614 auto xModel(mxComponent
.queryThrow
<css::frame::XModel
>());
2615 auto xController(xModel
->getCurrentController().queryThrow
<css::view::XSelectionSupplier
>());
2616 xController
->select(xDrawPage
->getByIndex(0));
2618 // Store only the selection
2619 auto xStorable(mxComponent
.queryThrow
<css::frame::XStorable
>());
2620 css::uno::Sequence
<css::beans::PropertyValue
> aStoreProperties
= {
2621 comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
2622 comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
2623 comphelper::makePropertyValue("RTFOLEMimeType", OUString("text/rtf")),
2624 comphelper::makePropertyValue("SelectionOnly", true),
2626 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreProperties
);
2628 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2630 // The root element must be reqif-xhtml:object
2631 assertXPath(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:object", "type", "text/rtf");
2632 // It has no children
2633 assertXPathChildren(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:object", 0);
2634 // And the content is empty
2635 assertXPathContent(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:object", "");
2637 OUString aRtfData
= getXPath(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:object", "data");
2638 INetURLObject
aUrl(maTempFile
.GetURL());
2639 aUrl
.setName(aRtfData
);
2640 SvMemoryStream aRtf
;
2641 HtmlExportTest::wrapRtfFragment(aUrl
.GetMainURL(INetURLObject::DecodeMechanism::NONE
), aRtf
);
2642 tools::SvRef
<TestReqIfRtfReader
> xReader(new TestReqIfRtfReader(aRtf
));
2643 // The RTF OLE exports correctly
2644 CPPUNIT_ASSERT(xReader
->CallParser() != SvParserState::Error
);
2645 CPPUNIT_ASSERT_EQUAL(tools::Long(9358), xReader
->GetObjw());
2646 CPPUNIT_ASSERT_EQUAL(tools::Long(450), xReader
->GetObjh());
2649 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testReqIF_Tdf156602
)
2651 createSwDoc("NestingInA1.fodt");
2654 xmlDocUniquePtr pDoc
= WrapReqifFromTempFile();
2656 // The outer table must be kept in the document where the outer table is the first element,
2657 // and its A1 starts with a nested table
2659 // Only two sub-elements must be inside the div: an outer table and a trailing paragraph
2660 assertXPathChildren(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div", 2);
2661 // The outer table must have exactly two rows
2662 assertXPath(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
2663 // First outer table cell must have two sub-elements: an inner table and a trailing paragraph
2664 assertXPathChildren(
2666 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]",
2668 // The inner table must have exactly two rows
2671 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2672 "reqif-xhtml:table/reqif-xhtml:tr",
2674 // Check all the elements' content
2677 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2678 "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/reqif-xhtml:p",
2682 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2683 "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/reqif-xhtml:p",
2687 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2688 "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/reqif-xhtml:p",
2692 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2693 "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/reqif-xhtml:p",
2697 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2702 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/"
2707 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/"
2712 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/"
2715 assertXPathContent(pDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "Following text");
2718 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf156647_CellPaddingRoundtrip
)
2720 // Given a document with a table with cell padding:
2721 createSwDoc("table_cell_padding.fodt");
2723 auto xTable
= getParagraphOrTable(1);
2724 auto aTableBorder
= getProperty
<css::table::TableBorder2
>(xTable
, "TableBorder2");
2725 CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder
.Distance
);
2726 CPPUNIT_ASSERT(aTableBorder
.IsDistanceValid
);
2728 // When exporting to reqif-xhtml:
2730 // Make sure that we export it:
2731 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2732 assertXPath(pXmlDoc
, "//reqif-xhtml:table", "cellpadding", "48"); // px
2734 mxComponent
->dispose();
2735 ImportFromReqif(maTempFile
.GetURL());
2736 // Then make sure that padding is not lost:
2738 auto xTable
= getParagraphOrTable(1);
2739 auto aTableBorder
= getProperty
<css::table::TableBorder2
>(xTable
, "TableBorder2");
2740 // Without the accompanying fix in place, this test would have failed:
2743 // as the padding was lost, and the default 55 twip padding was used.
2744 CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder
.Distance
);
2745 CPPUNIT_ASSERT(aTableBorder
.IsDistanceValid
);
2749 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest
, testTdf157643_WideHBorder
)
2751 // Given a document with a table with a wide border between its two rows:
2752 createSwDoc("table_with_wide_horizontal_border.fodt");
2753 // When exporting to reqif-xhtml:
2755 // Make sure that there's no extra tr's:
2756 xmlDocUniquePtr pXmlDoc
= WrapReqifFromTempFile();
2757 assertXPath(pXmlDoc
, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
2760 CPPUNIT_PLUGIN_IMPLEMENT();
2762 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */