Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / qa / extras / htmlexport / htmlexport.cxx
blobd5b94dcc28d3baaf202290b026755ced5412f9a6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <swmodeltestbase.hxx>
12 #include <memory>
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>
51 #include <swdll.hxx>
52 #include <usrpref.hxx>
53 #include <wrtsh.hxx>
54 #include <ndtxt.hxx>
55 #include <paratr.hxx>
56 #include <docsh.hxx>
57 #include <unotxdoc.hxx>
58 #include <formatlinebreak.hxx>
59 #include <itabenum.hxx>
61 namespace
63 /// Test RTF parser that just extracts a single OLE2 object from a file.
64 class TestReqIfRtfReader : public SvRTFParser
66 public:
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; }
74 private:
75 bool m_bInObjData = false;
76 OStringBuffer m_aHex;
77 tools::Long m_nObjw = 0;
78 tools::Long m_nObjh = 0;
79 int m_nWmetafile = 0;
82 TestReqIfRtfReader::TestReqIfRtfReader(SvStream& rStream)
83 : SvRTFParser(rStream)
87 void TestReqIfRtfReader::NextToken(int nToken)
89 switch (nToken)
91 case '}':
92 m_bInObjData = false;
93 break;
94 case RTF_TEXTTOKEN:
95 if (m_bInObjData)
96 m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
97 break;
98 case RTF_OBJDATA:
99 m_bInObjData = true;
100 break;
101 case RTF_OBJW:
102 m_nObjw = nTokenValue;
103 break;
104 case RTF_OBJH:
105 m_nObjh = nTokenValue;
106 break;
107 case RTF_WMETAFILE:
108 m_nWmetafile = nTokenValue;
109 break;
113 bool TestReqIfRtfReader::WriteObjectData(SvStream& rOLE)
115 OString aObjdata = m_aHex.makeStringAndClear();
117 SvMemoryStream aStream;
118 int b = 0;
119 int count = 2;
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)
127 b = b << 4;
128 sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
129 if (parsed == -1)
130 return false;
131 b += parsed;
132 count--;
133 if (!count)
135 aStream.WriteChar(b);
136 count = 2;
137 b = 0;
142 aStream.Seek(0);
143 rOLE.WriteStream(aStream);
144 return true;
147 /// Parser for [MS-OLEDS] 2.2.5 EmbeddedObject, aka OLE1.
148 struct OLE1Reader
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.
160 rStream.Seek(0);
161 CPPUNIT_ASSERT(rStream.remainingSize());
162 sal_uInt32 nData;
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
191 public:
192 HtmlExportTest()
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("}");
204 rStream.Seek(0);
207 private:
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");
221 else
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);
232 return pResetter;
234 return nullptr;
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
244 public:
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
260 void ExportToHTML();
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)
291 SvMemoryStream aRtf;
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(),
353 uno::UNO_QUERY);
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);
365 // Different Border
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"));
377 // Different Padding
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"));
388 // No shadow
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");
407 save(mpFilter);
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");
420 save(mpFilter);
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);
427 // This was 0.
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");
439 save(mpFilter);
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");
487 #endif
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);
539 assertXPath(
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"
567 "4NCjwvc3ZnPg0K);");
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");
606 save(mpFilter);
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
616 // version of that
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");
625 save(mpFilter);
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");
646 save(mpFilter);
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));
658 pStream->Seek(0);
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\"")
674 != -1);
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(),
688 uno::UNO_QUERY);
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");
696 verify();
697 setFilterOptions("xhtmlns=reqif-xhtml");
698 saveAndReload("HTML (StarWriter)");
699 verify();
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(),
707 uno::UNO_QUERY);
708 uno::Reference<document::XEmbeddedObjectSupplier2> xObject(xObjects->getByIndex(0),
709 uno::UNO_QUERY);
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();
718 Size aPixel(64, 64);
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());
739 if (!isExported())
740 return;
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");
752 verify();
753 setFilterOptions("xhtmlns=reqif-xhtml");
754 saveAndReload("HTML (StarWriter)");
755 verify();
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());
764 if (!bExported)
766 // Imported PNG image is not an object.
767 CPPUNIT_ASSERT_EQUAL(OUString("Image1"), xShape->getName());
768 return;
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
775 // subset.
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
785 // import.
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");
809 save(mpFilter);
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");
825 save(mpFilter);
827 xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
829 // <div> was missing, so the XHTML fragment wasn't a valid
830 // xhtml.BlkStruct.class type anymore.
831 assertXPath(pDoc,
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",
837 "style");
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",
841 "bgcolor");
844 CPPUNIT_TEST_FIXTURE(HtmlExportTest, testReqIfTable2)
846 createSwDoc("reqif-table2.odt");
847 setFilterOptions("xhtmlns=reqif-xhtml");
848 save(mpFilter);
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:
861 createSwDoc();
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(),
869 uno::UNO_QUERY);
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:
875 ExportToReqif();
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");
888 save(mpFilter);
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");
906 save(mpFilter);
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
916 // not had one.
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(),
928 uno::UNO_QUERY);
929 uno::Reference<document::XEmbeddedObjectSupplier2> xObject(xObjects->getByIndex(0),
930 uno::UNO_QUERY);
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.
945 if (isExported())
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");
971 verify();
972 setFilterOptions("xhtmlns=reqif-xhtml");
973 saveAndReload(mpFilter);
974 verify();
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(),
982 uno::UNO_QUERY);
983 uno::Reference<document::XEmbeddedObjectSupplier> xTextEmbeddedObject(
984 xObjects->getByIndex(0), uno::UNO_QUERY);
985 uno::Reference<lang::XServiceInfo> xObject(xTextEmbeddedObject->getEmbeddedObject(),
986 uno::UNO_QUERY);
987 // This failed, both import and export failed to handle OLE2 that contains
988 // just ODF.
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");
995 verify();
996 setFilterOptions("xhtmlns=reqif-xhtml");
997 saveAndReload(mpFilter);
998 verify();
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
1008 // started.
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(
1036 pDoc,
1037 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object",
1038 "data");
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(
1051 pDoc,
1052 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object",
1053 "data");
1054 CPPUNIT_ASSERT(!aSource.isEmpty());
1057 CPPUNIT_TEST_FIXTURE(HtmlExportTest, testNoLangReqIf)
1059 createSwDoc("reqif-no-lang.odt");
1060 setFilterOptions("xhtmlns=reqif-xhtml");
1061 save(mpFilter);
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:
1103 // - Expected: 0
1104 // - Actual : 2
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.
1126 createSwDoc();
1127 uno::Reference<beans::XPropertySet> xParagraph(getParagraph(1), uno::UNO_QUERY);
1128 xParagraph->setPropertyValue("ParaStyleName", uno::Any(OUString("Quotations")));
1130 // Export it.
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:
1136 // - Expected: 1
1137 // - Actual : 0
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"));
1148 // Export it.
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",
1163 aType);
1166 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testChinese)
1168 // Load a document with Chinese text in it.
1169 createSwDoc("reqif-chinese.odt");
1171 // Export it.
1172 ExportToReqif();
1173 // Without the accompanying fix in place, this would have failed as the output was not
1174 // well-formed.
1175 WrapReqifFromTempFile();
1178 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifComment)
1180 // Create a document with a comment in it.
1181 createSwDoc();
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);
1188 // Export it.
1189 ExportToReqif();
1190 // Without the accompanying fix in place, this would have failed as the output was not
1191 // well-formed.
1192 WrapReqifFromTempFile();
1195 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifFontNameSize)
1197 // Create a document with a custom font name and size in it.
1198 createSwDoc();
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");
1208 // Export it.
1209 ExportToReqif();
1210 xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
1212 // Without the accompanying fix in place, this test would have failed with:
1213 // - Expected: 1
1214 // - Actual : 3
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.
1222 createSwDoc();
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)));
1227 // Export it.
1228 ExportToReqif();
1229 xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
1231 // Without the accompanying fix in place, this test would have failed with:
1232 // - Expected:
1233 // - Actual : right
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");
1243 ExportToReqif();
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
1253 // - Actual : 43008
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());
1262 save("writer8");
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"),
1267 uno::UNO_QUERY);
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
1273 // - Actual : 0
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.
1285 save("writer8");
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"),
1290 uno::UNO_QUERY);
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());
1304 ExportToReqif();
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.
1311 aOle1.Seek(0);
1312 sal_uInt32 nData;
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);
1344 Bitmap aBitmap;
1345 SvMemoryStream aMemory;
1346 aMemory.WriteBytes(aOle1Reader.m_aNativeData.data(), aOle1Reader.m_aNativeData.size());
1347 aMemory.Seek(0);
1348 CPPUNIT_ASSERT(ReadDIB(aBitmap, aMemory, true));
1349 // Without the accompanying fix in place, this test would have failed with:
1350 // - Expected: 24
1351 // - Actual : 8
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.
1359 createSwDoc();
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");
1385 ExportToReqif();
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.
1400 createSwDoc();
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.
1408 ExportToReqif();
1410 // Make sure that the paragraph has no explicit style, because "text-decoration: none" is
1411 // filtered out.
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");
1420 ExportToReqif();
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
1428 // Word.
1429 OLE1Reader aOle1Reader(aOle1);
1432 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOle1PresDataWmfOnly)
1434 // Save to reqif-xhtml.
1435 createSwDoc("ole1-pres-data-wmf.odt");
1436 ExportToReqif();
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:
1455 ExportToReqif();
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:
1464 // - Expected: 7344
1465 // - Actual : 2836
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:
1477 ExportToReqif();
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:
1495 createSwDoc();
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:
1508 ExportToReqif();
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.
1517 OUString aContent
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:
1526 createSwDoc();
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:
1549 ExportToReqif();
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:
1565 createSwDoc();
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:
1588 ExportToHTML();
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:
1607 createSwDoc();
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:
1629 ExportToReqif();
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>
1637 // element.
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:
1645 createSwDoc();
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:
1656 ExportToReqif();
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",
1666 "string");
1669 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifImageToOle)
1671 // Given a document with an image:
1672 createSwDoc();
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:
1694 // - Expected: 8
1695 // - Actual : 0
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
1708 // the native data.
1709 CPPUNIT_ASSERT(aOle1Reader.m_nPresentationDataSize);
1712 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGDirectly)
1714 // Given a document with an image:
1715 createSwDoc();
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:
1722 ExportToReqif();
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:
1736 createSwDoc();
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:
1743 ExportToReqif();
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",
1753 "image/png");
1756 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeDirectly)
1758 // Given a document with an image shape:
1759 createSwDoc();
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:
1770 ExportToReqif();
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:
1783 createSwDoc();
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:
1794 ExportToReqif();
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",
1804 "image/png");
1807 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE)
1809 // Given a document with an image shape:
1810 createSwDoc();
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:
1841 createSwDoc();
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:
1850 ExportToReqif();
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:
1873 createSwDoc();
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:
1885 // - Expected:
1886 // - Actual : />
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:
1894 createSwDoc();
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:
1922 createSwDoc();
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:
1953 // - Expected: 1200
1954 // - Actual : 1000
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:
1967 createSwDoc();
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(),
1987 StreamMode::READ);
1988 Bitmap aBitmap;
1989 ReadDIB(aBitmap, aBitmapStream, /*bFileHeader=*/true);
1990 Size aBitmapSize = aBitmap.GetSizePixel();
1991 BitmapEx aBitmapEx(aBitmap);
1992 Color nActualColor
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:
2004 createSwDoc();
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:
2073 ExportToReqif();
2075 // Then make sure the output is valid xhtml:
2076 xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
2078 OUString aContent
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:
2107 createSwDoc();
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:
2129 ExportToReqif();
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.
2136 assertXPathContent(
2137 pXmlDoc, "//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
2138 "second");
2141 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTrailingLineBreak)
2143 // Given a document with a trailing line-break:
2144 createSwDoc();
2145 SwDoc* pDoc = getSwDoc();
2146 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
2147 pWrtShell->Insert("test\n");
2149 // When exporting to reqif-xhtml:
2150 ExportToReqif();
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:
2155 // - Expected: 1
2156 // - Actual : 2
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:
2182 createSwDoc();
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:
2216 createSwDoc();
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;
2246 while (true)
2248 // Ignore leading comments.
2249 xPortion.set(xPortions->nextElement(), uno::UNO_QUERY);
2250 xPortion->getPropertyValue("TextPortionType") >>= aPortionType;
2251 if (aPortionType != "Annotation")
2253 break;
2256 // Skip "foo".
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;
2265 sal_Int16 eClear{};
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:
2275 verify();
2276 saveAndReload("HTML (StarWriter)");
2277 // Make sure that the clear property of the break is not ignored during export:
2278 verify();
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:
2285 createSwDoc();
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:
2306 ExportToReqif();
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":
2325 createSwDoc();
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
2347 // window:
2348 htmlDocUniquePtr pDoc = parseHtml(maTempFile);
2349 // Without the accompanying fix in place, this test would have failed with:
2350 // - Expected: auto
2351 // - Actual : 2
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:
2359 createSwDoc();
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:
2368 ExportToReqif();
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
2382 createSwDoc();
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")));
2416 // Export
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");
2454 ExportToReqif();
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");
2468 ExportToReqif();
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");
2488 ExportToReqif();
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
2498 assertXPath(pDoc,
2499 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p", 4);
2500 // 2 sublists in the item
2501 assertXPath(
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
2504 assertXPath(pDoc,
2505 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[1]/"
2506 "reqif-xhtml:li",
2508 // Check the last (most nested) subitem's text
2509 assertXPathContent(
2510 pDoc,
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",
2513 "l3");
2516 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155496)
2518 createSwDoc("listItemSubheader.fodt");
2519 ExportToReqif();
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
2529 assertXPath(pDoc,
2530 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:p");
2531 // One sublist in the item
2532 assertXPath(
2533 pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul");
2534 // One item in the sublist
2535 assertXPath(pDoc,
2536 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
2537 "reqif-xhtml:li");
2538 // Check its text
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");
2548 ExportToReqif();
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");
2559 ExportToReqif();
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");
2573 ExportToReqif();
2575 // Without the fix in place, this would fail
2576 xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
2579 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsNoStartAttribute)
2581 createSwDoc("twoListsWithSameStyle.fodt");
2582 ExportToReqif();
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");
2593 ExportToReqif();
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");
2652 ExportToReqif();
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(
2665 pDoc,
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
2669 assertXPath(
2670 pDoc,
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
2675 assertXPathContent(
2676 pDoc,
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",
2679 "Inner.A1");
2680 assertXPathContent(
2681 pDoc,
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",
2684 "Inner.B1");
2685 assertXPathContent(
2686 pDoc,
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",
2689 "Inner.A2");
2690 assertXPathContent(
2691 pDoc,
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",
2694 "Inner.B2");
2695 assertXPathContent(
2696 pDoc,
2697 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
2698 "reqif-xhtml:p",
2699 "Outer.A1");
2700 assertXPathContent(
2701 pDoc,
2702 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/"
2703 "reqif-xhtml:p",
2704 "Outer.B1");
2705 assertXPathContent(
2706 pDoc,
2707 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/"
2708 "reqif-xhtml:p",
2709 "Outer.A2");
2710 assertXPathContent(
2711 pDoc,
2712 "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/"
2713 "reqif-xhtml:p",
2714 "Outer.B2");
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:
2729 ExportToReqif();
2730 // Make sure that we export it:
2731 xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
2732 assertXPath(pXmlDoc, "//reqif-xhtml:table", "cellpadding", "48"); // px
2733 // Now import it
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:
2741 // - Expected: 1270
2742 // - Actual : 97
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:
2754 ExportToReqif();
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: */