EPUB export: allow custom split method
Default is split on heading, but this allows e.g. split on page break instead. Change-Id: Ie392fc76f91d60029c7e5f2a0adc55cb18f6157d Reviewed-on: https://gerrit.libreoffice.org/41577 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
This commit is contained in:
@@ -28,7 +28,9 @@ $(eval $(call gb_CppunitTest_use_libraries,writerperfect_epubexport, \
|
|||||||
))
|
))
|
||||||
|
|
||||||
$(eval $(call gb_CppunitTest_use_externals,writerperfect_epubexport,\
|
$(eval $(call gb_CppunitTest_use_externals,writerperfect_epubexport,\
|
||||||
|
epubgen \
|
||||||
libxml2 \
|
libxml2 \
|
||||||
|
revenge \
|
||||||
))
|
))
|
||||||
|
|
||||||
$(eval $(call gb_CppunitTest_use_external,writerperfect_epubexport,boost_headers))
|
$(eval $(call gb_CppunitTest_use_external,writerperfect_epubexport,boost_headers))
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libepubgen/libepubgen.h>
|
||||||
|
|
||||||
#include <com/sun/star/frame/Desktop.hpp>
|
#include <com/sun/star/frame/Desktop.hpp>
|
||||||
#include <com/sun/star/frame/XStorable.hpp>
|
#include <com/sun/star/frame/XStorable.hpp>
|
||||||
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
|
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
|
||||||
@@ -34,20 +36,26 @@ class EPUBExportTest : public test::BootstrapFixture, public unotest::MacrosTest
|
|||||||
uno::Reference<uno::XComponentContext> mxComponentContext;
|
uno::Reference<uno::XComponentContext> mxComponentContext;
|
||||||
uno::Reference<lang::XComponent> mxComponent;
|
uno::Reference<lang::XComponent> mxComponent;
|
||||||
utl::TempFile maTempFile;
|
utl::TempFile maTempFile;
|
||||||
|
xmlDocPtr mpXmlDoc = nullptr;
|
||||||
|
uno::Reference<packages::zip::XZipFileAccess2> mxZipFile;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setUp() override;
|
void setUp() override;
|
||||||
void tearDown() override;
|
void tearDown() override;
|
||||||
void registerNamespaces(xmlXPathContextPtr &pXmlXpathCtx) override;
|
void registerNamespaces(xmlXPathContextPtr &pXmlXpathCtx) override;
|
||||||
void createDoc(const OUString &rFile, const uno::Sequence<beans::PropertyValue> &rFilterData);
|
void createDoc(const OUString &rFile, const uno::Sequence<beans::PropertyValue> &rFilterData);
|
||||||
|
/// Returns an XML representation of the stream named rName in the exported package.
|
||||||
|
xmlDocPtr parseExport(const OUString &rName);
|
||||||
void testOutlineLevel();
|
void testOutlineLevel();
|
||||||
void testMimetype();
|
void testMimetype();
|
||||||
void testEPUB2();
|
void testEPUB2();
|
||||||
|
void testPageBreakSplit();
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE(EPUBExportTest);
|
CPPUNIT_TEST_SUITE(EPUBExportTest);
|
||||||
CPPUNIT_TEST(testOutlineLevel);
|
CPPUNIT_TEST(testOutlineLevel);
|
||||||
CPPUNIT_TEST(testMimetype);
|
CPPUNIT_TEST(testMimetype);
|
||||||
CPPUNIT_TEST(testEPUB2);
|
CPPUNIT_TEST(testEPUB2);
|
||||||
|
CPPUNIT_TEST(testPageBreakSplit);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,6 +72,12 @@ void EPUBExportTest::tearDown()
|
|||||||
if (mxComponent.is())
|
if (mxComponent.is())
|
||||||
mxComponent->dispose();
|
mxComponent->dispose();
|
||||||
|
|
||||||
|
if (mpXmlDoc)
|
||||||
|
{
|
||||||
|
xmlFreeDoc(mpXmlDoc);
|
||||||
|
mpXmlDoc = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
test::BootstrapFixture::tearDown();
|
test::BootstrapFixture::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +99,14 @@ void EPUBExportTest::createDoc(const OUString &rFile, const uno::Sequence<beans:
|
|||||||
aMediaDescriptor["FilterName"] <<= OUString("EPUB");
|
aMediaDescriptor["FilterName"] <<= OUString("EPUB");
|
||||||
aMediaDescriptor["FilterData"] <<= rFilterData;
|
aMediaDescriptor["FilterData"] <<= rFilterData;
|
||||||
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
||||||
|
mxZipFile = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, maTempFile.GetURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlDocPtr EPUBExportTest::parseExport(const OUString &rName)
|
||||||
|
{
|
||||||
|
uno::Reference<io::XInputStream> xInputStream(mxZipFile->getByName(rName), uno::UNO_QUERY);
|
||||||
|
std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
||||||
|
return parseXmlStream(pStream.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EPUBExportTest::testOutlineLevel()
|
void EPUBExportTest::testOutlineLevel()
|
||||||
@@ -92,11 +114,10 @@ void EPUBExportTest::testOutlineLevel()
|
|||||||
createDoc("outline-level.fodt", {});
|
createDoc("outline-level.fodt", {});
|
||||||
|
|
||||||
// Make sure that the output is split into two.
|
// Make sure that the output is split into two.
|
||||||
uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, maTempFile.GetURL());
|
CPPUNIT_ASSERT(mxZipFile->hasByName("OEBPS/sections/section0001.xhtml"));
|
||||||
CPPUNIT_ASSERT(xNameAccess->hasByName("OEBPS/sections/section0001.xhtml"));
|
|
||||||
// This failed, output was a single section.
|
// This failed, output was a single section.
|
||||||
CPPUNIT_ASSERT(xNameAccess->hasByName("OEBPS/sections/section0002.xhtml"));
|
CPPUNIT_ASSERT(mxZipFile->hasByName("OEBPS/sections/section0002.xhtml"));
|
||||||
CPPUNIT_ASSERT(!xNameAccess->hasByName("OEBPS/sections/section0003.xhtml"));
|
CPPUNIT_ASSERT(!mxZipFile->hasByName("OEBPS/sections/section0003.xhtml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EPUBExportTest::testMimetype()
|
void EPUBExportTest::testMimetype()
|
||||||
@@ -113,6 +134,10 @@ void EPUBExportTest::testMimetype()
|
|||||||
OString aActual(static_cast<const char *>(aMemoryStream.GetBuffer()) + 38, aExpected.getLength());
|
OString aActual(static_cast<const char *>(aMemoryStream.GetBuffer()) + 38, aExpected.getLength());
|
||||||
// This failed: actual data was some garbage, not the uncompressed mime type.
|
// This failed: actual data was some garbage, not the uncompressed mime type.
|
||||||
CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
|
CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
|
||||||
|
|
||||||
|
mpXmlDoc = parseExport("OEBPS/content.opf");
|
||||||
|
// Default is EPUB3.
|
||||||
|
assertXPath(mpXmlDoc, "/opf:package", "version", "3.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
void EPUBExportTest::testEPUB2()
|
void EPUBExportTest::testEPUB2()
|
||||||
@@ -124,13 +149,25 @@ void EPUBExportTest::testEPUB2()
|
|||||||
}));
|
}));
|
||||||
createDoc("hello.fodt", aFilterData);
|
createDoc("hello.fodt", aFilterData);
|
||||||
|
|
||||||
uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, maTempFile.GetURL());
|
mpXmlDoc = parseExport("OEBPS/content.opf");
|
||||||
uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("OEBPS/content.opf"), uno::UNO_QUERY);
|
|
||||||
std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
|
||||||
xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
|
|
||||||
// This was 3.0, EPUBVersion filter option was ignored and we always emitted EPUB3.
|
// This was 3.0, EPUBVersion filter option was ignored and we always emitted EPUB3.
|
||||||
assertXPath(pXmlDoc, "/opf:package", "version", "2.0");
|
assertXPath(mpXmlDoc, "/opf:package", "version", "2.0");
|
||||||
xmlFreeDoc(pXmlDoc);
|
}
|
||||||
|
|
||||||
|
void EPUBExportTest::testPageBreakSplit()
|
||||||
|
{
|
||||||
|
uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
|
||||||
|
{
|
||||||
|
// Explicitly request split on page break (instead of on heading).
|
||||||
|
{"EPUBSplitMethod", uno::makeAny(static_cast<sal_Int32>(libepubgen::EPUB_SPLIT_METHOD_PAGE_BREAK))}
|
||||||
|
}));
|
||||||
|
createDoc("2pages.fodt", aFilterData);
|
||||||
|
|
||||||
|
// Make sure that the output is split into two.
|
||||||
|
CPPUNIT_ASSERT(mxZipFile->hasByName("OEBPS/sections/section0001.xhtml"));
|
||||||
|
// This failed, output was a single section.
|
||||||
|
CPPUNIT_ASSERT(mxZipFile->hasByName("OEBPS/sections/section0002.xhtml"));
|
||||||
|
CPPUNIT_ASSERT(!mxZipFile->hasByName("OEBPS/sections/section0003.xhtml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE_REGISTRATION(EPUBExportTest);
|
CPPUNIT_TEST_SUITE_REGISTRATION(EPUBExportTest);
|
||||||
|
14
writerperfect/qa/unit/data/writer/epubexport/2pages.fodt
Normal file
14
writerperfect/qa/unit/data/writer/epubexport/2pages.fodt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
|
<office:automatic-styles>
|
||||||
|
<style:style style:name="P1" style:family="paragraph">
|
||||||
|
<style:paragraph-properties fo:break-before="page"/>
|
||||||
|
</style:style>
|
||||||
|
</office:automatic-styles>
|
||||||
|
<office:body>
|
||||||
|
<office:text text:use-soft-page-breaks="true">
|
||||||
|
<text:p>First page</text:p>
|
||||||
|
<text:p text:style-name="P1">Second page</text:p>
|
||||||
|
</office:text>
|
||||||
|
</office:body>
|
||||||
|
</office:document>
|
@@ -12,6 +12,7 @@
|
|||||||
#include "config_libepubgen.h"
|
#include "config_libepubgen.h"
|
||||||
|
|
||||||
#include <libepubgen/EPUBTextGenerator.h>
|
#include <libepubgen/EPUBTextGenerator.h>
|
||||||
|
#include <libepubgen/libepubgen-decls.h>
|
||||||
|
|
||||||
#include <com/sun/star/lang/XInitialization.hpp>
|
#include <com/sun/star/lang/XInitialization.hpp>
|
||||||
#include <com/sun/star/uno/XComponentContext.hpp>
|
#include <com/sun/star/uno/XComponentContext.hpp>
|
||||||
@@ -35,6 +36,7 @@ EPUBExportFilter::EPUBExportFilter(const uno::Reference<uno::XComponentContext>
|
|||||||
sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue> &rDescriptor)
|
sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue> &rDescriptor)
|
||||||
{
|
{
|
||||||
sal_Int32 nVersion = 30;
|
sal_Int32 nVersion = 30;
|
||||||
|
sal_Int32 nSplitMethod = libepubgen::EPUB_SPLIT_METHOD_HEADING;
|
||||||
uno::Sequence<beans::PropertyValue> aFilterData;
|
uno::Sequence<beans::PropertyValue> aFilterData;
|
||||||
for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i)
|
for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i)
|
||||||
{
|
{
|
||||||
@@ -49,13 +51,15 @@ sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue> &rDe
|
|||||||
{
|
{
|
||||||
if (aFilterData[i].Name == "EPUBVersion")
|
if (aFilterData[i].Name == "EPUBVersion")
|
||||||
aFilterData[i].Value >>= nVersion;
|
aFilterData[i].Value >>= nVersion;
|
||||||
|
else if (aFilterData[i].Name == "EPUBSplitMethod")
|
||||||
|
aFilterData[i].Value >>= nSplitMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the export filter chain: the package has direct access to the ZIP
|
// Build the export filter chain: the package has direct access to the ZIP
|
||||||
// file, the flat ODF filter has access to the doc model, everything else
|
// file, the flat ODF filter has access to the doc model, everything else
|
||||||
// is in-between.
|
// is in-between.
|
||||||
EPUBPackage aPackage(mxContext, rDescriptor);
|
EPUBPackage aPackage(mxContext, rDescriptor);
|
||||||
libepubgen::EPUBTextGenerator aGenerator(&aPackage, libepubgen::EPUB_SPLIT_METHOD_HEADING
|
libepubgen::EPUBTextGenerator aGenerator(&aPackage, static_cast<libepubgen::EPUBSplitMethod>(nSplitMethod)
|
||||||
#if LIBEPUBGEN_VERSION_SUPPORT
|
#if LIBEPUBGEN_VERSION_SUPPORT
|
||||||
, nVersion
|
, nVersion
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user