1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
12 #include <test/unoapixml_test.hxx>
14 #include <string_view>
16 #include <libepubgen/libepubgen.h>
18 #include <com/sun/star/document/XFilter.hpp>
19 #include <com/sun/star/frame/XStorable.hpp>
20 #include <com/sun/star/lang/XServiceInfo.hpp>
21 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
23 #include <comphelper/propertysequence.hxx>
24 #include <comphelper/string.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <unotools/docinfohelper.hxx>
27 #include <unotools/mediadescriptor.hxx>
28 #include <unotools/tempfile.hxx>
29 #include <unotools/ucbstreamhelper.hxx>
30 #include <o3tl/string_view.hxx>
32 using namespace ::com::sun::star
;
36 /// Tests the EPUB export filter.
37 class EPUBExportTest
: public UnoApiXmlTest
40 uno::Reference
<packages::zip::XZipFileAccess2
> mxZipFile
;
41 OUString maFilterOptions
;
45 : UnoApiXmlTest(u
"/writerperfect/qa/unit/data/writer/epubexport/"_ustr
)
49 void registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
) override
;
50 void createDoc(std::u16string_view rFile
,
51 const uno::Sequence
<beans::PropertyValue
>& rFilterData
);
52 /// Parses a CSS representation of the stream named rName and returns it.
53 std::map
<OUString
, std::vector
<OUString
>> parseCss(const OUString
& rName
);
54 /// Looks up a key of a class in rCss.
55 static OUString
getCss(std::map
<OUString
, std::vector
<OUString
>>& rCss
, const OUString
& rClass
,
56 std::u16string_view rKey
);
59 void EPUBExportTest::registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
)
61 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("dc"), BAD_CAST("http://purl.org/dc/elements/1.1/"));
62 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("opf"), BAD_CAST("http://www.idpf.org/2007/opf"));
63 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("xhtml"), BAD_CAST("http://www.w3.org/1999/xhtml"));
64 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg"));
67 void EPUBExportTest::createDoc(std::u16string_view rFile
,
68 const uno::Sequence
<beans::PropertyValue
>& rFilterData
)
70 // Import the bugdoc and export as EPUB.
72 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
73 utl::MediaDescriptor aMediaDescriptor
;
74 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"EPUB"_ustr
;
75 if (maFilterOptions
.isEmpty())
76 aMediaDescriptor
[u
"FilterData"_ustr
] <<= rFilterData
;
78 aMediaDescriptor
[u
"FilterOptions"_ustr
] <<= maFilterOptions
;
79 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
80 mxZipFile
= packages::zip::ZipFileAccess::createWithURL(m_xContext
, maTempFile
.GetURL());
83 std::map
<OUString
, std::vector
<OUString
>> EPUBExportTest::parseCss(const OUString
& rName
)
85 std::map
<OUString
, std::vector
<OUString
>> aRet
;
87 uno::Reference
<io::XInputStream
> xInputStream(mxZipFile
->getByName(rName
), uno::UNO_QUERY
);
88 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(xInputStream
, true));
90 // Minimal CSS handler till orcus is up to our needs.
93 while (!pStream
->eof())
95 pStream
->ReadLine(aLine
);
96 if (aLine
.endsWith("{"))
97 // '.name {' -> 'name'
98 aRuleName
= OUString::fromUtf8(aLine
.subView(1, aLine
.getLength() - 3));
99 else if (aLine
.endsWith(";"))
100 aRet
[aRuleName
].push_back(OUString::fromUtf8(aLine
));
106 OUString
EPUBExportTest::getCss(std::map
<OUString
, std::vector
<OUString
>>& rCss
,
107 const OUString
& rClass
, std::u16string_view rKey
)
111 auto it
= rCss
.find(rClass
);
112 CPPUNIT_ASSERT(it
!= rCss
.end());
114 for (const auto& rKeyValue
: it
->second
)
116 OUString aKeyValue
= rKeyValue
.trim();
117 std::vector
<OUString
> aTokens
= comphelper::string::split(aKeyValue
, ':');
118 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTokens
.size());
119 if (o3tl::trim(aTokens
[0]) == rKey
)
121 aRet
= aTokens
[1].trim();
122 if (aRet
.endsWith(";"))
123 // Ignore trailing semicolon.
124 aRet
= aRet
.copy(0, aRet
.getLength() - 1);
132 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testOutlineLevel
)
134 createDoc(u
"outline-level.fodt", {});
136 // Make sure that the output is split into two.
137 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0001.xhtml"_ustr
));
138 // This failed, output was a single section.
139 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0002.xhtml"_ustr
));
140 CPPUNIT_ASSERT(!mxZipFile
->hasByName(u
"OEBPS/sections/section0003.xhtml"_ustr
));
143 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testMimetype
)
145 createDoc(u
"hello.fodt", {});
147 // Check that the mime type is written uncompressed at the expected location.
148 SvFileStream
aFileStream(maTempFile
.GetURL(), StreamMode::READ
);
149 SvMemoryStream aMemoryStream
;
150 aMemoryStream
.WriteStream(aFileStream
);
151 OString
aExpected("application/epub+zip"_ostr
);
152 CPPUNIT_ASSERT(aMemoryStream
.GetSize() > static_cast<sal_uInt64
>(aExpected
.getLength()) + 38);
154 OString
aActual(static_cast<const char*>(aMemoryStream
.GetData()) + 38, aExpected
.getLength());
155 // This failed: actual data was some garbage, not the uncompressed mime type.
156 CPPUNIT_ASSERT_EQUAL(aExpected
, aActual
);
158 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
160 assertXPath(mpXmlDoc
, "/opf:package", "version", u
"3.0");
162 // This was just "libepubgen/x.y.z", i.e. the LO version was missing.
164 = getXPath(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@name='generator']", "content");
165 CPPUNIT_ASSERT(aGenerator
.startsWith(utl::DocInfoHelper::GetGeneratorString()));
167 uno::Reference
<lang::XMultiServiceFactory
> xMSF(m_xContext
->getServiceManager(),
169 static constexpr OUString
aServiceName(u
"com.sun.star.comp.Writer.EPUBExportFilter"_ustr
);
170 uno::Reference
<document::XFilter
> xFilter(xMSF
->createInstance(aServiceName
), uno::UNO_QUERY
);
171 // Should result in no errors.
173 // We got back what we expected.
174 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xFilter
, uno::UNO_QUERY
);
175 CPPUNIT_ASSERT_EQUAL(aServiceName
, xServiceInfo
->getImplementationName());
176 CPPUNIT_ASSERT(xServiceInfo
->supportsService(u
"com.sun.star.document.ExportFilter"_ustr
));
179 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testEPUB2
)
181 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
182 { // Explicitly request EPUB2.
183 { "EPUBVersion", uno::Any(static_cast<sal_Int32
>(20)) } }));
184 createDoc(u
"hello.fodt", aFilterData
);
186 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
187 // This was 3.0, EPUBVersion filter option was ignored and we always emitted EPUB3.
188 assertXPath(mpXmlDoc
, "/opf:package", "version", u
"2.0");
191 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testEPUBFixedLayout
)
193 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
194 { // Explicitly request fixed layout.
195 { "EPUBLayoutMethod",
196 uno::Any(static_cast<sal_Int32
>(libepubgen::EPUB_LAYOUT_METHOD_FIXED
)) } }));
197 createDoc(u
"hello.fodt", aFilterData
);
199 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
200 // This was missing, EPUBLayoutMethod filter option was ignored and we always emitted reflowable layout.
201 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@property='rendition:layout']",
205 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testEPUBFixedLayoutOption
)
207 // Explicitly request fixed layout, this time via FilterOptions.
208 maFilterOptions
= "layout=fixed";
209 createDoc(u
"hello.fodt", {});
211 // This failed, fixed layout was only working via the FilterData map.
212 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
213 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@property='rendition:layout']",
217 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testEPUBFixedLayoutImplicitBreak
)
219 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
220 { // Explicitly request fixed layout.
221 { "EPUBLayoutMethod",
222 uno::Any(static_cast<sal_Int32
>(libepubgen::EPUB_LAYOUT_METHOD_FIXED
)) } }));
223 createDoc(u
"fxl-2page.fodt", aFilterData
);
225 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0001.xhtml"_ustr
));
226 // This was missing, implicit page break (as calculated by the layout) was lost on export.
227 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0002.xhtml"_ustr
));
228 CPPUNIT_ASSERT(!mxZipFile
->hasByName(u
"OEBPS/sections/section0003.xhtml"_ustr
));
230 // Make sure that fixed layout has chapter names in the navigation
232 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/toc.xhtml"_ustr
);
233 // This was 'Page 1' instead.
234 assertXPathContent(mpXmlDoc
, "//xhtml:li[1]/xhtml:a", u
"First chapter");
235 assertXPathContent(mpXmlDoc
, "//xhtml:li[2]/xhtml:a", u
"Second chapter");
238 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testPageBreakSplit
)
240 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
241 { // Explicitly request split on page break (instead of on heading).
243 uno::Any(static_cast<sal_Int32
>(libepubgen::EPUB_SPLIT_METHOD_PAGE_BREAK
)) } }));
244 createDoc(u
"2pages.fodt", aFilterData
);
246 // Make sure that the output is split into two.
247 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0001.xhtml"_ustr
));
248 // This failed, output was a single section.
249 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/sections/section0002.xhtml"_ustr
));
250 CPPUNIT_ASSERT(!mxZipFile
->hasByName(u
"OEBPS/sections/section0003.xhtml"_ustr
));
253 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testSpanAutostyle
)
255 createDoc(u
"span-autostyle.fodt", {});
257 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
258 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[1]", "class", u
"span0");
259 // This failed, it was still span1, i.e. the bold and the italic formatting
261 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[2]", "class", u
"span1");
262 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[3]", "class", u
"span2");
265 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testParaAutostyleCharProps
)
267 createDoc(u
"para-autostyle-char-props.fodt", {});
269 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
270 // This failed, para-level char props were not exported.
271 assertXPath(mpXmlDoc
, "//xhtml:p[1]/xhtml:span", "class", u
"span0");
272 assertXPath(mpXmlDoc
, "//xhtml:p[2]/xhtml:span", "class", u
"span1");
275 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testMeta
)
277 createDoc(u
"meta.fodt", {});
279 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
280 // This was "Unknown Author", <meta:initial-creator> was not handled.
281 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:creator", u
"A U Thor");
282 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:title", u
"Title");
283 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:language", u
"hu");
284 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@property='dcterms:modified']",
285 u
"2017-09-27T09:51:19Z");
287 // Make sure that cover image next to the source document is picked up.
288 assertXPath(mpXmlDoc
, "/opf:package/opf:manifest/opf:item[@href='images/image0001.png']",
289 "properties", u
"cover-image");
290 assertXPath(mpXmlDoc
, "/opf:package/opf:manifest/opf:item[@href='images/image0001.png']",
291 "media-type", u
"image/png");
292 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/images/image0001.png"_ustr
));
295 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testMetaXMP
)
297 createDoc(u
"meta-xmp.fodt", {});
298 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
300 // These were the libepubgen default values, metadata from a matching .xmp file was not picked up.
301 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:identifier",
302 u
"deadbeef-e394-4cd6-9b83-7172794612e5");
303 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:title", u
"unknown title from xmp");
304 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:creator",
305 u
"unknown author from xmp");
306 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:language", u
"nl");
307 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@property='dcterms:modified']",
308 u
"2016-11-20T17:16:07Z");
311 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testMetaAPI
)
313 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
314 { { "RVNGIdentifier", uno::Any(u
"deadc0de-e394-4cd6-9b83-7172794612e5"_ustr
) },
315 { "RVNGTitle", uno::Any(u
"unknown title from api"_ustr
) },
316 { "RVNGInitialCreator", uno::Any(u
"unknown author from api"_ustr
) },
317 { "RVNGLanguage", uno::Any(u
"hu"_ustr
) },
318 { "RVNGDate", uno::Any(u
"2015-11-20T17:16:07Z"_ustr
) } }));
319 createDoc(u
"meta-xmp.fodt", aFilterData
);
320 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
322 // These were values from XMP (deadbeef, etc.), not from API.
323 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:identifier",
324 u
"deadc0de-e394-4cd6-9b83-7172794612e5");
325 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:title", u
"unknown title from api");
326 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:creator",
327 u
"unknown author from api");
328 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/dc:language", u
"hu");
329 assertXPathContent(mpXmlDoc
, "/opf:package/opf:metadata/opf:meta[@property='dcterms:modified']",
330 u
"2015-11-20T17:16:07Z");
333 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testCoverImage
)
335 OUString aCoverURL
= createFileURL(u
"meta.cover-image.png");
336 uno::Sequence
<beans::PropertyValue
> aFilterData(
337 comphelper::InitPropertySequence({ { "RVNGCoverImage", uno::Any(aCoverURL
) } }));
338 createDoc(u
"hello.fodt", aFilterData
);
339 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
341 // Make sure that the explicitly set cover image is used.
342 // This failed, as the image was not part of the package.
343 assertXPath(mpXmlDoc
, "/opf:package/opf:manifest/opf:item[@href='images/image0001.png']",
344 "properties", u
"cover-image");
345 assertXPath(mpXmlDoc
, "/opf:package/opf:manifest/opf:item[@href='images/image0001.png']",
346 "media-type", u
"image/png");
347 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/images/image0001.png"_ustr
));
350 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testParaNamedstyle
)
352 createDoc(u
"para-namedstyle.fodt", {});
354 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
355 assertXPath(mpXmlDoc
, "//xhtml:p[1]", "class", u
"para0");
356 // This failed, paragraph properties from style were not exported.
357 assertXPath(mpXmlDoc
, "//xhtml:p[2]", "class", u
"para1");
359 // Test character properties from named paragraph style.
360 assertXPath(mpXmlDoc
, "//xhtml:p[1]/xhtml:span", "class", u
"span0");
361 // This failed, character properties from paragraph style were not exported.
362 assertXPath(mpXmlDoc
, "//xhtml:p[2]/xhtml:span", "class", u
"span1");
365 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testCharNamedstyle
)
367 createDoc(u
"char-namedstyle.fodt", {});
369 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
371 // Test character properties from named text style.
372 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[1]", "class", u
"span0");
373 // This failed, character properties from text style were not exported.
374 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[2]", "class", u
"span1");
377 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testNamedStyleInheritance
)
379 createDoc(u
"named-style-inheritance.fodt", {});
381 // Find the CSS rule for the blue text.
382 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
383 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
384 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
385 OUString aBlue
= getXPath(mpXmlDoc
, "//xhtml:p[2]/xhtml:span[2]", "class");
387 CPPUNIT_ASSERT_EQUAL(u
"#0000ff"_ustr
, EPUBExportTest::getCss(aCssDoc
, aBlue
, u
"color"));
388 // This failed, the span only had the properties from its style, but not
389 // from the style's parent(s).
390 CPPUNIT_ASSERT_EQUAL(u
"'Liberation Mono'"_ustr
,
391 EPUBExportTest::getCss(aCssDoc
, aBlue
, u
"font-family"));
394 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testNestedSpan
)
396 createDoc(u
"nested-span.fodt", {});
398 // Check textural content of nested span.
399 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
400 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
401 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
402 // This crashed, span had no content.
403 assertXPathContent(mpXmlDoc
, "//xhtml:p/xhtml:span[2]", u
"red");
405 // Check formatting of nested span.
406 OUString aRed
= getXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[2]", "class");
407 // This failed, direct formatting on top of named style was lost.
408 CPPUNIT_ASSERT_EQUAL(u
"#ff0000"_ustr
, EPUBExportTest::getCss(aCssDoc
, aRed
, u
"color"));
409 CPPUNIT_ASSERT_EQUAL(u
"'Liberation Mono'"_ustr
,
410 EPUBExportTest::getCss(aCssDoc
, aRed
, u
"font-family"));
413 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testLineBreak
)
415 createDoc(u
"line-break.fodt", {});
417 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
418 // This was 0, line break was not handled.
419 assertXPath(mpXmlDoc
, "//xhtml:p[1]/xhtml:span/xhtml:br", 1);
420 // This was 0, line break inside span was not handled.
421 assertXPath(mpXmlDoc
, "//xhtml:p[2]/xhtml:span/xhtml:br", 1);
424 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testEscape
)
426 createDoc(u
"escape.fodt", {});
428 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
430 assertXPathContent(mpXmlDoc
, "//xhtml:p[1]/xhtml:span[1]", OUString::fromUtf8("\xc2\xa0"));
431 // Make sure escaping happens only once.
432 assertXPathContent(mpXmlDoc
, "//xhtml:p[1]/xhtml:span[2]", u
"a&b");
433 // This was also lost.
435 mpXmlDoc
, "//xhtml:p[1]/xhtml:span[3]",
436 OUString::fromUtf8("\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2"
437 "\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0 "));
440 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testParaCharProps
)
442 createDoc(u
"para-char-props.fodt", {});
444 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
445 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
446 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
447 // Check formatting of the middle span.
448 OUString aMiddle
= getXPath(mpXmlDoc
, "//xhtml:p/xhtml:span[2]", "class");
449 CPPUNIT_ASSERT_EQUAL(u
"italic"_ustr
, EPUBExportTest::getCss(aCssDoc
, aMiddle
, u
"font-style"));
450 // Direct para formatting was lost, only direct char formatting was
451 // written, so this failed.
452 CPPUNIT_ASSERT_EQUAL(u
"bold"_ustr
, EPUBExportTest::getCss(aCssDoc
, aMiddle
, u
"font-weight"));
455 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testSection
)
457 createDoc(u
"section.fodt", {});
459 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
460 // This was "After.", i.e. in-section content was ignored.
461 assertXPathContent(mpXmlDoc
, "//xhtml:p[2]/xhtml:span", u
"In section.");
464 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testList
)
466 createDoc(u
"list.fodt", {});
468 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
469 // This was "C", i.e. in-list content was ignored.
470 assertXPathContent(mpXmlDoc
, "//xhtml:p[2]/xhtml:span", u
"B");
471 // Test nested list content.
472 assertXPathContent(mpXmlDoc
, "//xhtml:p[6]/xhtml:span", u
"F");
475 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testImage
)
477 createDoc(u
"image.fodt", {});
479 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
480 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:img", 1);
483 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testImageBorder
)
485 createDoc(u
"image-border.fodt", {});
487 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
488 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
489 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
491 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:img", "class");
492 // This failed, image had no border.
493 CPPUNIT_ASSERT_EQUAL(u
"0.99pt dashed #ed1c24"_ustr
,
494 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"border"));
497 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testImageNospan
)
499 createDoc(u
"image-nospan.fodt", {});
501 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
502 // Image outside a span was lost.
503 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:img", 1);
506 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTable
)
508 createDoc(u
"table.fodt", {});
510 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
511 assertXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr/xhtml:td", 4);
514 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTableRowSpan
)
516 createDoc(u
"table-row-span.fodt", {});
518 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
519 // This failed, row span wasn't exported.
520 assertXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]/xhtml:td[1]", "rowspan", u
"2");
523 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTableCellBorder
)
525 createDoc(u
"table-cell-border.fodt", {});
527 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
528 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
529 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
532 = getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]/xhtml:td[1]", "class");
533 // This failed, cell border wasn't exported.
534 CPPUNIT_ASSERT_EQUAL(u
"0.05pt solid #000000"_ustr
,
535 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"border-left"));
538 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTableCellWidth
)
540 createDoc(u
"table-cell-width.fodt", {});
542 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
543 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
544 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
546 = getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]/xhtml:td[1]", "class");
548 = getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]/xhtml:td[2]", "class");
550 = getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]/xhtml:td[3]", "class");
551 // These failed, all widths were 0.
552 CPPUNIT_ASSERT_GREATER(EPUBExportTest::getCss(aCssDoc
, aClass2
, u
"width").toDouble(),
553 EPUBExportTest::getCss(aCssDoc
, aClass1
, u
"width").toDouble());
554 CPPUNIT_ASSERT_GREATER(EPUBExportTest::getCss(aCssDoc
, aClass3
, u
"width").toDouble(),
555 EPUBExportTest::getCss(aCssDoc
, aClass1
, u
"width").toDouble());
558 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTableRowHeight
)
560 createDoc(u
"table-row-height.fodt", {});
562 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
563 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
564 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
565 OUString aClass1
= getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[1]", "class");
566 OUString aClass2
= getXPath(mpXmlDoc
, "//xhtml:table/xhtml:tbody/xhtml:tr[2]", "class");
567 // These failed, both heights were 0.
568 CPPUNIT_ASSERT_GREATER(EPUBExportTest::getCss(aCssDoc
, aClass2
, u
"height").toDouble(),
569 EPUBExportTest::getCss(aCssDoc
, aClass1
, u
"height").toDouble());
572 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testLink
)
574 createDoc(u
"link.fodt", {});
576 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
577 assertXPathContent(mpXmlDoc
, "//xhtml:p/xhtml:a/xhtml:span", u
"https://libreoffice.org/");
578 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:a", "href", u
"https://libreoffice.org/");
581 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testLinkInvalid
)
583 createDoc(u
"link-invalid.odt", {});
585 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
586 // This was 1, invalid relative link was not filtered out.
587 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:a", 0);
590 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testLinkCharFormat
)
592 createDoc(u
"link-charformat.fodt", {});
594 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
595 // <span> was lost, link text having a char format was missing.
596 assertXPathContent(mpXmlDoc
, "//xhtml:p/xhtml:a/xhtml:span", u
"https://libreoffice.org/");
597 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:a", "href", u
"https://libreoffice.org/");
600 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testLinkNamedCharFormat
)
602 // Character properties from named character style on hyperlink was lost.
603 createDoc(u
"link-namedcharformat.fodt", {});
605 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
606 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
607 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
608 // This failed, there was no span inside the hyperlink.
609 assertXPathContent(mpXmlDoc
, "//xhtml:p/xhtml:a/xhtml:span", u
"http://libreoffice.org");
610 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:a", "href", u
"http://libreoffice.org/");
612 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:p/xhtml:a/xhtml:span", "class");
613 CPPUNIT_ASSERT_EQUAL(u
"#ff0000"_ustr
, EPUBExportTest::getCss(aCssDoc
, aClass
, u
"color"));
616 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTableWidth
)
618 createDoc(u
"table-width.fodt", {});
620 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
621 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
622 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
624 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:table", "class");
625 // This failed, relative total width of table was lost.
626 CPPUNIT_ASSERT_EQUAL(u
"50%"_ustr
, EPUBExportTest::getCss(aCssDoc
, aClass
, u
"width"));
629 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTextBox
)
631 createDoc(u
"text-box.fodt", {});
633 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
634 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
635 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
637 // This failed, image with caption was lost.
638 assertXPath(mpXmlDoc
, "//xhtml:img", "class", u
"frame1");
640 // 1) break after the image
641 // 2) "Illustration "
642 // 3) The sequence field, this was missing (was ": foo" instead).
643 assertXPathContent(mpXmlDoc
, "//xhtml:div/xhtml:p/xhtml:span[3]", u
"1");
645 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:div/xhtml:p/xhtml:span[3]", "class");
646 // This failed, the 3rd span was not italic.
647 CPPUNIT_ASSERT_EQUAL(u
"italic"_ustr
, EPUBExportTest::getCss(aCssDoc
, aClass
, u
"font-style"));
650 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testFontEmbedding
)
653 createDoc(u
"font-embedding.fodt", {});
655 // Make sure that the params of defineEmbeddedFont() are all handled.
657 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
658 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
659 // 'SketchFlow Print' or ''SketchFlow Print1'
660 CPPUNIT_ASSERT(EPUBExportTest::getCss(aCssDoc
, u
"font-face"_ustr
, u
"font-family")
661 .startsWith("'SketchFlow Print"));
662 // librevenge:mime-type
663 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/content.opf"_ustr
);
664 assertXPath(mpXmlDoc
, "/opf:package/opf:manifest/opf:item[@href='fonts/font0001.otf']",
665 "media-type", u
"application/vnd.ms-opentype");
666 // office:binary-data
667 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/fonts/font0001.otf"_ustr
));
668 // librevenge:font-style
669 CPPUNIT_ASSERT_EQUAL(u
"normal"_ustr
,
670 EPUBExportTest::getCss(aCssDoc
, u
"font-face"_ustr
, u
"font-style"));
671 // librevenge:font-weight
672 CPPUNIT_ASSERT_EQUAL(u
"normal"_ustr
,
673 EPUBExportTest::getCss(aCssDoc
, u
"font-face"_ustr
, u
"font-weight"));
677 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testImageLink
)
679 createDoc(u
"image-link.fodt", {});
681 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
682 // This failed, image was missing.
683 assertXPath(mpXmlDoc
, "//xhtml:p/xhtml:a/xhtml:img", 1);
686 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testFootnote
)
688 createDoc(u
"footnote.fodt", {});
690 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
691 // These were missing, footnote was lost.
692 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p/xhtml:sup/xhtml:a", "type", u
"noteref");
693 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside", "type", u
"footnote");
696 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testPopup
)
698 createDoc(u
"popup.odt", {});
700 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
701 // Test image popup anchor.
702 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[1]/xhtml:a", "type", u
"noteref");
703 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[1]/xhtml:a/xhtml:img", 1);
704 // Test image popup content.
705 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside[1]", "type", u
"footnote");
706 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside[1]/xhtml:img", 1);
708 // Test text popup anchor.
709 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[2]/xhtml:span/xhtml:a", "type", u
"noteref");
710 assertXPathContent(mpXmlDoc
, "//xhtml:body/xhtml:p[2]/xhtml:span/xhtml:a", u
"link");
711 // Test text popup content.
712 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside[2]", "type", u
"footnote");
713 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside[2]/xhtml:img", 1);
716 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testPopupMedia
)
718 // This is the same as testPopup(), but the links point to images in the
719 // default media directory, not in the document directory.
720 createDoc(u
"popup-media.odt", {});
722 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
723 // Test image popup anchor. This failed, number of XPath nodes was 0.
724 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[1]/xhtml:a", "type", u
"noteref");
725 assertXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[1]/xhtml:a/xhtml:img", 1);
728 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testPopupAPI
)
730 // Make sure that the popup works with data from a media directory.
731 OUString aMediaDir
= createFileURL(u
"popup");
732 uno::Sequence
<beans::PropertyValue
> aFilterData(
733 comphelper::InitPropertySequence({ { "RVNGMediaDir", uno::Any(aMediaDir
) } }));
734 createDoc(u
"popup-api.odt", aFilterData
);
736 // We have a non-empty anchor image.
737 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
738 OUString aAnchor
= getXPath(mpXmlDoc
, "//xhtml:body/xhtml:p[1]/xhtml:a/xhtml:img", "src");
739 CPPUNIT_ASSERT(!aAnchor
.isEmpty());
740 // We have a non-empty popup image.
741 OUString aData
= getXPath(mpXmlDoc
, "//xhtml:body/xhtml:aside[1]/xhtml:img", "src");
742 CPPUNIT_ASSERT(!aData
.isEmpty());
743 // The anchor is different from the popup image.
744 CPPUNIT_ASSERT(aAnchor
!= aData
);
747 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testPageSize
)
749 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
750 { { "EPUBLayoutMethod",
751 uno::Any(static_cast<sal_Int32
>(libepubgen::EPUB_LAYOUT_METHOD_FIXED
)) } }));
752 createDoc(u
"hello.fodt", aFilterData
);
754 // This failed, viewport was empty, so page size was lost.
755 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
756 // 21,59cm x 27.94cm (letter).
757 assertXPath(mpXmlDoc
, "/xhtml:html/xhtml:head/xhtml:meta[@name='viewport']", "content",
758 u
"width=816, height=1056");
760 mpXmlDoc
= parseExport(u
"OEBPS/images/image0001.svg"_ustr
);
761 // This was 288mm, logic->logic conversion input was a pixel value.
762 assertXPath(mpXmlDoc
, "/svg:svg", "width", u
"216mm");
763 assertXPath(mpXmlDoc
, "/svg:svg", "height", u
"279mm");
766 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testSVG
)
768 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
769 { { "EPUBLayoutMethod",
770 uno::Any(static_cast<sal_Int32
>(libepubgen::EPUB_LAYOUT_METHOD_FIXED
)) } }));
771 createDoc(u
"hello.fodt", aFilterData
);
773 CPPUNIT_ASSERT(mxZipFile
->hasByName(u
"OEBPS/images/image0001.svg"_ustr
));
774 uno::Reference
<io::XInputStream
> xInputStream(
775 mxZipFile
->getByName(u
"OEBPS/images/image0001.svg"_ustr
), uno::UNO_QUERY
);
776 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(xInputStream
, true));
778 SvMemoryStream aMemoryStream
;
779 aMemoryStream
.WriteStream(*pStream
);
780 OString
aExpected("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<svg"_ostr
);
781 CPPUNIT_ASSERT(aMemoryStream
.GetSize() > o3tl::make_unsigned(aExpected
.getLength()));
783 // This failed, there was a '<!DOCTYPE' line between the xml and the svg
784 // one, causing a validation error.
785 OString
aActual(static_cast<const char*>(aMemoryStream
.GetData()), aExpected
.getLength());
786 CPPUNIT_ASSERT_EQUAL(aExpected
, aActual
);
788 // This failed, we used the xlink attribute namespace, but we did not
790 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/images/image0001.svg"_ustr
);
791 assertXPathNSDef(mpXmlDoc
, "/svg:svg", "xlink", "http://www.w3.org/1999/xlink");
794 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTdf115623SingleWritingMode
)
796 // Simple page that has single writing mode should work.
797 createDoc(u
"tdf115623-single-writing-mode.odt", {});
798 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
799 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
800 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
801 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:body", "class");
802 CPPUNIT_ASSERT_EQUAL(u
"vertical-rl"_ustr
,
803 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"writing-mode"));
806 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTdf115623SplitByChapter
)
808 createDoc(u
"tdf115623-split-by-chapter.odt", {});
809 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
810 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
812 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
813 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:body", "class");
814 CPPUNIT_ASSERT_EQUAL(u
"vertical-rl"_ustr
,
815 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"writing-mode"));
817 // Split HTML should keep the same writing-mode.
819 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0002.xhtml"_ustr
);
820 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:body", "class");
821 CPPUNIT_ASSERT_EQUAL(u
"vertical-rl"_ustr
,
822 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"writing-mode"));
826 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testTdf115623ManyPageSpans
)
828 createDoc(u
"tdf115623-many-pagespans.odt", {});
829 std::map
<OUString
, std::vector
<OUString
>> aCssDoc
830 = parseCss(u
"OEBPS/styles/stylesheet.css"_ustr
);
831 // Two pages should have different writing modes.
833 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
834 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:body", "class");
835 CPPUNIT_ASSERT_EQUAL(u
"vertical-rl"_ustr
,
836 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"writing-mode"));
839 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0002.xhtml"_ustr
);
840 OUString aClass
= getXPath(mpXmlDoc
, "//xhtml:body", "class");
841 CPPUNIT_ASSERT_EQUAL(u
"horizontal-tb"_ustr
,
842 EPUBExportTest::getCss(aCssDoc
, aClass
, u
"writing-mode"));
846 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testSimpleRuby
)
848 createDoc(u
"simple-ruby.odt", {});
849 xmlDocUniquePtr mpXmlDoc
= parseExport(u
"OEBPS/sections/section0001.xhtml"_ustr
);
850 assertXPathContent(mpXmlDoc
, "//xhtml:body/xhtml:p/xhtml:ruby/xhtml:span", u
"base text");
851 assertXPathContent(mpXmlDoc
, "//xhtml:body/xhtml:p/xhtml:ruby/xhtml:rt", u
"ruby text");
854 CPPUNIT_TEST_FIXTURE(EPUBExportTest
, testAbi11105
)
856 // This crashed because the paragraph style "P5" which had a master-page-name
857 // appeared in a table cell messed up page spans.
858 createDoc(u
"abi11105.abw", {});
862 CPPUNIT_PLUGIN_IMPLEMENT();
864 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */