summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2017-11-30 16:32:02 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2017-12-01 09:16:41 +0100
commit553a35bed7a7ffb5bcda98987eb4af8b79bc031f (patch)
treec622febc856db2776f1ab5a402ea2839d3c10734
parent8f0f8f80c1712fdc2ebb178cb88c5bd43db50fb7 (diff)
EPUB export: handle footnotes
Stringly speaking just linking from the main text to the footnote content would be enough from an EPUB point of view, but let's do the other direction as well, since Writer provides that. Change-Id: Iba366ef11a8375347d57a650c57baf8e8002e820 Reviewed-on: https://gerrit.libreoffice.org/45600 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r--external/libepubgen/libepubgen-epub3.patch.1556
-rw-r--r--writerperfect/Library_wpftwriter.mk1
-rw-r--r--writerperfect/qa/unit/EPUBExportTest.cxx12
-rw-r--r--writerperfect/qa/unit/data/writer/epubexport/footnote.fodt8
-rw-r--r--writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx121
-rw-r--r--writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx44
-rw-r--r--writerperfect/source/writer/exp/txtparai.cxx4
7 files changed, 746 insertions, 0 deletions
diff --git a/external/libepubgen/libepubgen-epub3.patch.1 b/external/libepubgen/libepubgen-epub3.patch.1
index 39bac59c51ff..28f9c3771708 100644
--- a/external/libepubgen/libepubgen-epub3.patch.1
+++ b/external/libepubgen/libepubgen-epub3.patch.1
@@ -4497,3 +4497,559 @@ index 62dac6e..1cb1112 100644
--
2.13.6
+From 8c447caee18b4400170ecce36ea3714fdc377989 Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos@collabora.co.uk>
+Date: Thu, 23 Nov 2017 16:42:13 +0100
+Subject: [PATCH 1/4] EPUBHTMLGenerator: fix footnotes/endnotes/comments
+
+There were two problems here:
+
+- when working with two sinks (footnote and main), make sure that we
+ save the main one before the push of the sink stack
+
+- when handing out a non-const xml sink reference, make sure there is no
+ parallel empty bool that tracks its size, otherwise these can out of
+ sync (empty is still true, even if there is footnote content)
+---
+ src/lib/EPUBHTMLGenerator.cpp | 24 +++++++-----------------
+ src/lib/EPUBXMLSink.cpp | 5 +++++
+ src/lib/EPUBXMLSink.h | 2 ++
+ src/test/EPUBTextGeneratorTest.cpp | 24 ++++++++++++++++++++++++
+ 4 files changed, 38 insertions(+), 17 deletions(-)
+
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 614dd02..3c8862b 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -51,20 +51,16 @@ public:
+ const EPUBXMLSink &get() const;
+ EPUBXMLSink &get();
+
+- bool empty() const;
+-
+ bool endsInLineBreak() const;
+
+ private:
+ EPUBXMLSink m_sink;
+ std::string m_lastCloseElement;
+- bool m_empty;
+ };
+
+ ZoneSinkImpl::ZoneSinkImpl()
+ : m_sink()
+ , m_lastCloseElement()
+- , m_empty(true)
+ {
+ }
+
+@@ -72,28 +68,24 @@ void ZoneSinkImpl::openElement(const char *const name, const librevenge::RVNGPro
+ {
+ m_sink.openElement(name, attributes);
+ m_lastCloseElement.clear();
+- m_empty = false;
+ }
+
+ void ZoneSinkImpl::closeElement(const char *const name)
+ {
+ m_sink.closeElement(name);
+ m_lastCloseElement = name;
+- m_empty = false;
+ }
+
+ void ZoneSinkImpl::insertCharacters(const librevenge::RVNGString &characters)
+ {
+ m_sink.insertCharacters(characters);
+ m_lastCloseElement.clear();
+- m_empty = false;
+ }
+
+ void ZoneSinkImpl::append(const ZoneSinkImpl &other)
+ {
+ m_sink.append(other.m_sink);
+ m_lastCloseElement = other.m_lastCloseElement;
+- m_empty |= other.m_empty;
+ }
+
+ const EPUBXMLSink &ZoneSinkImpl::get() const
+@@ -106,11 +98,6 @@ EPUBXMLSink &ZoneSinkImpl::get()
+ return m_sink;
+ }
+
+-bool ZoneSinkImpl::empty() const
+-{
+- return m_empty;
+-}
+-
+ bool ZoneSinkImpl::endsInLineBreak() const
+ {
+ return m_lastCloseElement == "p"
+@@ -154,7 +141,7 @@ struct EPUBHTMLTextZone
+ bool isEmpty() const
+ {
+ for (const auto &zoneSink : m_zoneSinks)
+- if (!zoneSink.empty())
++ if (!zoneSink.get().empty())
+ return false;
+ return true;
+ }
+@@ -791,8 +778,9 @@ void EPUBHTMLGenerator::openFootnote(const RVNGPropertyList &)
+ {
+ if (m_impl->m_ignore)
+ return;
++ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_FootNote);
+- m_impl->getSink().addLabel(m_impl->output());
++ m_impl->getSink().addLabel(output);
+ }
+
+ void EPUBHTMLGenerator::closeFootnote()
+@@ -806,8 +794,9 @@ void EPUBHTMLGenerator::openEndnote(const RVNGPropertyList &)
+ {
+ if (m_impl->m_ignore)
+ return;
++ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_EndNote);
+- m_impl->getSink().addLabel(m_impl->output());
++ m_impl->getSink().addLabel(output);
+ }
+
+ void EPUBHTMLGenerator::closeEndnote()
+@@ -821,8 +810,9 @@ void EPUBHTMLGenerator::openComment(const RVNGPropertyList & /*propList*/)
+ {
+ if (m_impl->m_ignore)
+ return;
++ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_Comment);
+- m_impl->getSink().addLabel(m_impl->output());
++ m_impl->getSink().addLabel(output);
+ }
+
+ void EPUBHTMLGenerator::closeComment()
+diff --git a/src/lib/EPUBXMLSink.cpp b/src/lib/EPUBXMLSink.cpp
+index 7c12c6d..db480d7 100644
+--- a/src/lib/EPUBXMLSink.cpp
++++ b/src/lib/EPUBXMLSink.cpp
+@@ -155,6 +155,11 @@ void EPUBXMLSink::append(const EPUBXMLSink &other)
+ m_elements.insert(m_elements.end(), other.m_elements.begin(), other.m_elements.end());
+ }
+
++bool EPUBXMLSink::empty() const
++{
++ return m_elements.empty();
++}
++
+ void EPUBXMLSink::writeTo(EPUBPackage &package, const char *const name)
+ {
+ package.openXMLFile(name);
+diff --git a/src/lib/EPUBXMLSink.h b/src/lib/EPUBXMLSink.h
+index ea7ac7a..a2bf951 100644
+--- a/src/lib/EPUBXMLSink.h
++++ b/src/lib/EPUBXMLSink.h
+@@ -40,6 +40,8 @@ public:
+
+ void writeTo(EPUBPackage &package, const char *name);
+
++ bool empty() const;
++
+ private:
+ std::deque<EPUBXMLElementPtr_t> m_elements;
+ };
+--
+2.13.6
+
+
+From 9c723bbe42673906d8d0faf5083a186cf86d05ce Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos@collabora.co.uk>
+Date: Thu, 23 Nov 2017 17:27:00 +0100
+Subject: [PATCH 2/4] EPUBHTMLGenerator: avoid <title> in EPUB3 XHTML content
+ documents
+
+- there were multiple of them, which is invalid
+- just don't write them, content.opf has the same info
+---
+ src/lib/EPUBGenerator.cpp | 2 +-
+ src/lib/EPUBHTMLGenerator.cpp | 18 ++++++++++++------
+ src/lib/EPUBHTMLGenerator.h | 2 +-
+ src/lib/EPUBHTMLManager.cpp | 4 ++--
+ src/lib/EPUBHTMLManager.h | 2 +-
+ src/test/EPUBTextGeneratorTest.cpp | 5 ++++-
+ 6 files changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/src/lib/EPUBGenerator.cpp b/src/lib/EPUBGenerator.cpp
+index 1cb1112..571f0eb 100644
+--- a/src/lib/EPUBGenerator.cpp
++++ b/src/lib/EPUBGenerator.cpp
+@@ -117,7 +117,7 @@ void EPUBGenerator::startNewHtmlFile()
+
+ m_splitGuard.onSplit();
+
+- m_currentHtml = m_htmlManager.create(m_imageManager, m_fontManager, m_listStyleManager, m_paragraphStyleManager, m_spanStyleManager, m_tableStyleManager, m_stylesheetPath, m_stylesMethod);
++ m_currentHtml = m_htmlManager.create(m_imageManager, m_fontManager, m_listStyleManager, m_paragraphStyleManager, m_spanStyleManager, m_tableStyleManager, m_stylesheetPath, m_stylesMethod, m_version);
+
+ // restore state in the new file
+ m_currentHtml->startDocument(m_documentProps);
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 3c8862b..209e7e1 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -338,7 +338,7 @@ std::string EPUBHTMLTextZone::label(int id) const
+ struct EPUBHTMLGeneratorImpl
+ {
+ //! constructor
+- EPUBHTMLGeneratorImpl(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod)
++ EPUBHTMLGeneratorImpl(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod, int version)
+ : m_document(document)
+ , m_imageManager(imageManager)
+ , m_fontManager(fontManager)
+@@ -351,6 +351,7 @@ struct EPUBHTMLGeneratorImpl
+ , m_actualPage(0)
+ , m_ignore(false)
+ , m_hasText(false)
++ , m_version(version)
+ , m_frameAnchorTypes()
+ , m_framePropertiesStack()
+ , m_stylesMethod(stylesMethod)
+@@ -442,6 +443,7 @@ struct EPUBHTMLGeneratorImpl
+ bool m_ignore;
+ /// Does the currently opened paragraph have some text?
+ bool m_hasText;
++ int m_version;
+
+ std::stack<std::string> m_frameAnchorTypes;
+ std::stack<RVNGPropertyList> m_framePropertiesStack;
+@@ -458,8 +460,8 @@ private:
+ EPUBHTMLGeneratorImpl operator=(EPUBHTMLGeneratorImpl const &orig);
+ };
+
+-EPUBHTMLGenerator::EPUBHTMLGenerator(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod)
+- : m_impl(new EPUBHTMLGeneratorImpl(document, imageManager, fontManager, listStyleManager, paragraphStyleManager, spanStyleManager, tableStyleManager, path, stylesheetPath, stylesMethod))
++EPUBHTMLGenerator::EPUBHTMLGenerator(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod, int version)
++ : m_impl(new EPUBHTMLGeneratorImpl(document, imageManager, fontManager, listStyleManager, paragraphStyleManager, spanStyleManager, tableStyleManager, path, stylesheetPath, stylesMethod, version))
+ {
+ }
+
+@@ -509,14 +511,18 @@ void EPUBHTMLGenerator::endDocument()
+ htmlAttrs.insert("xmlns", "http://www.w3.org/1999/xhtml");
+ m_impl->m_document.openElement("html", htmlAttrs);
+ m_impl->m_document.openElement("head", RVNGPropertyList());
+- m_impl->m_document.openElement("title", RVNGPropertyList());
+- m_impl->m_document.closeElement("title");
++ if (m_impl->m_version != 30)
++ {
++ m_impl->m_document.openElement("title", RVNGPropertyList());
++ m_impl->m_document.closeElement("title");
++ }
+ RVNGPropertyList metaAttrs;
+ metaAttrs.insert("http-equiv", "content-type");
+ metaAttrs.insert("content", "text/html; charset=UTF-8");
+ m_impl->m_document.openElement("meta", metaAttrs);
+ m_impl->m_document.closeElement("meta");
+- m_impl->sendMetaData(m_impl->m_document);
++ if (m_impl->m_version != 30)
++ m_impl->sendMetaData(m_impl->m_document);
+ RVNGPropertyList linkAttrs;
+ linkAttrs.insert("href", m_impl->m_stylesheetPath.relativeTo(m_impl->m_path).str().c_str());
+ linkAttrs.insert("type", "text/css");
+diff --git a/src/lib/EPUBHTMLGenerator.h b/src/lib/EPUBHTMLGenerator.h
+index 49f76a3..11f20cb 100644
+--- a/src/lib/EPUBHTMLGenerator.h
++++ b/src/lib/EPUBHTMLGenerator.h
+@@ -31,7 +31,7 @@ class EPUBPath;
+ class EPUBHTMLGenerator : public librevenge::RVNGTextInterface
+ {
+ public:
+- EPUBHTMLGenerator(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod);
++ EPUBHTMLGenerator(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod, int version);
+ ~EPUBHTMLGenerator() override;
+
+ void setDocumentMetaData(const librevenge::RVNGPropertyList &propList) override;
+diff --git a/src/lib/EPUBHTMLManager.cpp b/src/lib/EPUBHTMLManager.cpp
+index 9d4c507..d2c21da 100644
+--- a/src/lib/EPUBHTMLManager.cpp
++++ b/src/lib/EPUBHTMLManager.cpp
+@@ -41,7 +41,7 @@ EPUBHTMLManager::EPUBHTMLManager(EPUBManifest &manifest)
+ {
+ }
+
+-const EPUBHTMLGeneratorPtr_t EPUBHTMLManager::create(EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod)
++const EPUBHTMLGeneratorPtr_t EPUBHTMLManager::create(EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod, int version)
+ {
+ std::ostringstream nameBuf;
+ nameBuf << "section" << std::setw(4) << std::setfill('0') << m_number.next();
+@@ -55,7 +55,7 @@ const EPUBHTMLGeneratorPtr_t EPUBHTMLManager::create(EPUBImageManager &imageMana
+ m_contents.push_back(EPUBXMLSink());
+
+ const EPUBHTMLGeneratorPtr_t gen(
+- new EPUBHTMLGenerator(m_contents.back(), imageManager, fontManager, listStyleManager, paragraphStyleManager, spanStyleManager, tableStyleManager, m_paths.back(), stylesheetPath, stylesMethod));
++ new EPUBHTMLGenerator(m_contents.back(), imageManager, fontManager, listStyleManager, paragraphStyleManager, spanStyleManager, tableStyleManager, m_paths.back(), stylesheetPath, stylesMethod, version));
+
+ return gen;
+ }
+diff --git a/src/lib/EPUBHTMLManager.h b/src/lib/EPUBHTMLManager.h
+index ef56a52..e1205e6 100644
+--- a/src/lib/EPUBHTMLManager.h
++++ b/src/lib/EPUBHTMLManager.h
+@@ -41,7 +41,7 @@ class EPUBHTMLManager
+ public:
+ explicit EPUBHTMLManager(EPUBManifest &manifest);
+
+- const EPUBHTMLGeneratorPtr_t create(EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod);
++ const EPUBHTMLGeneratorPtr_t create(EPUBImageManager &imageManager, EPUBFontManager &fontManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &stylesheetPath, EPUBStylesMethod stylesMethod, int version);
+
+ void writeTo(EPUBPackage &package);
+
+--
+2.13.6
+
+
+From 502948041df07729572bf4f2b222e03073baa6c8 Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos@collabora.co.uk>
+Date: Fri, 24 Nov 2017 10:29:15 +0100
+Subject: [PATCH 3/4] EPUBHTMLGenerator: implement EPUB3 footnote markup
+
+The main difference is that in the EPUB3 case the footnote has an
+explicit end.
+---
+ src/lib/EPUBHTMLGenerator.cpp | 56 ++++++++++++++++++++++++++++----------
+ src/test/EPUBTextGeneratorTest.cpp | 26 ++++++++++++++++++
+ 2 files changed, 68 insertions(+), 14 deletions(-)
+
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 209e7e1..96e7623 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -122,7 +122,7 @@ struct EPUBHTMLTextZone
+ //! the different zone
+ enum Type { Z_Comment=0, Z_EndNote, Z_FootNote, Z_Main, Z_MetaData, Z_TextBox, Z_Unknown, Z_NumZones= Z_Unknown+1};
+ //! constructor for basic stream
+- EPUBHTMLTextZone(Type tp=Z_Unknown) : m_type(tp), m_actualId(0), m_zoneSinks()
++ EPUBHTMLTextZone(Type tp=Z_Unknown) : m_type(tp), m_actualId(0), m_zoneSinks(), m_version(20)
+ {
+ }
+ //! the type
+@@ -135,6 +135,14 @@ struct EPUBHTMLTextZone
+ {
+ m_type=tp;
+ }
++ void setVersion(int version)
++ {
++ m_version = version;
++ }
++ int getVersion() const
++ {
++ return m_version;
++ }
+ //! returns a new sink corresponding to this zone
+ std::unique_ptr<TextZoneSink> getNewSink();
+ //! returns true if there is no data
+@@ -150,7 +158,7 @@ struct EPUBHTMLTextZone
+ {
+ if (isEmpty() || m_type==Z_Unknown || m_type==Z_Main)
+ return;
+- if (m_type!=Z_MetaData)
++ if (m_type!=Z_MetaData && m_version < 30)
+ {
+ out.openElement("hr", RVNGPropertyList());
+ out.closeElement("hr");
+@@ -197,6 +205,7 @@ protected:
+ mutable int m_actualId;
+ //! the list of data string
+ std::vector<ZoneSinkImpl> m_zoneSinks;
++ int m_version;
+ private:
+ EPUBHTMLTextZone(EPUBHTMLTextZone const &orig);
+ EPUBHTMLTextZone operator=(EPUBHTMLTextZone const &orig);
+@@ -218,11 +227,16 @@ struct TextZoneSink
+ std::string lbl=label();
+ if (!lbl.length())
+ return;
++ int version = 20;
++ if (m_zone)
++ version = m_zone->getVersion();
+ {
+ RVNGPropertyList supAttrs;
+ supAttrs.insert("id", ("called" + lbl).c_str());
+ output.openElement("sup", supAttrs);
+ RVNGPropertyList aAttrs;
++ if (version == 30)
++ aAttrs.insert("epub:type", "noteref");
+ aAttrs.insert("href", ("#data" + lbl).c_str());
+ output.openElement("a", aAttrs);
+ output.insertCharacters(lbl.c_str());
+@@ -230,17 +244,23 @@ struct TextZoneSink
+ output.closeElement("sup");
+ }
+ flush();
++ if (version == 30)
+ {
+- RVNGPropertyList supAttrs;
+- supAttrs.insert("id", ("data" + lbl).c_str());
+- m_delayedLabel.openElement("sup", supAttrs);
+- RVNGPropertyList aAttrs;
+- aAttrs.insert("href", ("#called" + lbl).c_str());
+- m_delayedLabel.openElement("a", aAttrs);
+- m_delayedLabel.insertCharacters(lbl.c_str());
+- m_delayedLabel.closeElement("a");
+- m_delayedLabel.closeElement("sup");
++ RVNGPropertyList asideAttrs;
++ asideAttrs.insert("epub:type", "footnote");
++ asideAttrs.insert("id", ("data" + lbl).c_str());
++ m_sink.openElement("aside", asideAttrs);
+ }
++ RVNGPropertyList supAttrs;
++ if (version < 30)
++ supAttrs.insert("id", ("data" + lbl).c_str());
++ m_delayedLabel.openElement("sup", supAttrs);
++ RVNGPropertyList aAttrs;
++ aAttrs.insert("href", ("#called" + lbl).c_str());
++ m_delayedLabel.openElement("a", aAttrs);
++ m_delayedLabel.insertCharacters(lbl.c_str());
++ m_delayedLabel.closeElement("a");
++ m_delayedLabel.closeElement("sup");
+ }
+ //! flush delayed label, ...
+ void flush()
+@@ -359,7 +379,10 @@ struct EPUBHTMLGeneratorImpl
+ , m_sinkStack()
+ {
+ for (int i = 0; i < EPUBHTMLTextZone::Z_NumZones; ++i)
++ {
+ m_zones[i].setType(EPUBHTMLTextZone::Type(i));
++ m_zones[i].setVersion(version);
++ }
+ m_actualSink=m_zones[EPUBHTMLTextZone::Z_Main].getNewSink();
+ }
+ //! destructor
+@@ -511,7 +534,7 @@ void EPUBHTMLGenerator::endDocument()
+ htmlAttrs.insert("xmlns", "http://www.w3.org/1999/xhtml");
+ m_impl->m_document.openElement("html", htmlAttrs);
+ m_impl->m_document.openElement("head", RVNGPropertyList());
+- if (m_impl->m_version != 30)
++ if (m_impl->m_version < 30)
+ {
+ m_impl->m_document.openElement("title", RVNGPropertyList());
+ m_impl->m_document.closeElement("title");
+@@ -521,7 +544,7 @@ void EPUBHTMLGenerator::endDocument()
+ metaAttrs.insert("content", "text/html; charset=UTF-8");
+ m_impl->m_document.openElement("meta", metaAttrs);
+ m_impl->m_document.closeElement("meta");
+- if (m_impl->m_version != 30)
++ if (m_impl->m_version < 30)
+ m_impl->sendMetaData(m_impl->m_document);
+ RVNGPropertyList linkAttrs;
+ linkAttrs.insert("href", m_impl->m_stylesheetPath.relativeTo(m_impl->m_path).str().c_str());
+@@ -529,7 +552,10 @@ void EPUBHTMLGenerator::endDocument()
+ linkAttrs.insert("rel", "stylesheet");
+ m_impl->m_document.insertEmptyElement("link", linkAttrs);
+ m_impl->m_document.closeElement("head");
+- m_impl->m_document.openElement("body", RVNGPropertyList());
++ RVNGPropertyList bodyAttrs;
++ if (m_impl->m_version == 30)
++ bodyAttrs.insert("xmlns:epub", "http://www.idpf.org/2007/ops");
++ m_impl->m_document.openElement("body", bodyAttrs);
+ m_impl->flushUnsent(m_impl->m_document);
+ m_impl->m_document.closeElement("body");
+ m_impl->m_document.closeElement("html");
+@@ -793,6 +819,8 @@ void EPUBHTMLGenerator::closeFootnote()
+ {
+ if (m_impl->m_ignore)
+ return;
++ if (m_impl->m_version == 30)
++ m_impl->output().closeElement("aside");
+ m_impl->pop();
+ }
+
+--
+2.13.6
+
+
+From a8444b113df52769849ad45ea440def8d1884b15 Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos@collabora.co.uk>
+Date: Fri, 24 Nov 2017 11:10:34 +0100
+Subject: [PATCH 4/4] EPUBHTMLGenerator: implement custom footnote anchor text
+
+Try to avoid our default F<N> anchor text if possible, which only makes
+sense in English.
+---
+ src/lib/EPUBHTMLGenerator.cpp | 22 +++++++++++++++-------
+ src/test/EPUBTextGeneratorTest.cpp | 25 +++++++++++++++++++++++++
+ 2 files changed, 40 insertions(+), 7 deletions(-)
+
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 96e7623..6b4c7c2 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -222,9 +222,14 @@ struct TextZoneSink
+ //! destructor
+ ~TextZoneSink() { }
+ //! add a label called on main and a label in this ( delayed to allow openParagraph to be called )
+- void addLabel(EPUBXMLSink &output)
++ void addLabel(EPUBXMLSink &output, const librevenge::RVNGString &number)
+ {
++ // Unique label, e.g. 'F1' for the first footnote.
+ std::string lbl=label();
++ // User-visible label, e.g. '1'.
++ std::string uiLabel = lbl;
++ if (!number.empty())
++ uiLabel = number.cstr();
+ if (!lbl.length())
+ return;
+ int version = 20;
+@@ -239,7 +244,7 @@ struct TextZoneSink
+ aAttrs.insert("epub:type", "noteref");
+ aAttrs.insert("href", ("#data" + lbl).c_str());
+ output.openElement("a", aAttrs);
+- output.insertCharacters(lbl.c_str());
++ output.insertCharacters(uiLabel.c_str());
+ output.closeElement("a");
+ output.closeElement("sup");
+ }
+@@ -258,7 +263,7 @@ struct TextZoneSink
+ RVNGPropertyList aAttrs;
+ aAttrs.insert("href", ("#called" + lbl).c_str());
+ m_delayedLabel.openElement("a", aAttrs);
+- m_delayedLabel.insertCharacters(lbl.c_str());
++ m_delayedLabel.insertCharacters(uiLabel.c_str());
+ m_delayedLabel.closeElement("a");
+ m_delayedLabel.closeElement("sup");
+ }
+@@ -806,13 +811,16 @@ void EPUBHTMLGenerator::closeListElement()
+ m_impl->output().closeElement("li");
+ }
+
+-void EPUBHTMLGenerator::openFootnote(const RVNGPropertyList &)
++void EPUBHTMLGenerator::openFootnote(const RVNGPropertyList &propList)
+ {
+ if (m_impl->m_ignore)
+ return;
+ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_FootNote);
+- m_impl->getSink().addLabel(output);
++ librevenge::RVNGString number;
++ if (const librevenge::RVNGProperty *numProp = propList["librevenge:number"])
++ number = numProp->getStr();
++ m_impl->getSink().addLabel(output, number);
+ }
+
+ void EPUBHTMLGenerator::closeFootnote()
+@@ -830,7 +838,7 @@ void EPUBHTMLGenerator::openEndnote(const RVNGPropertyList &)
+ return;
+ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_EndNote);
+- m_impl->getSink().addLabel(output);
++ m_impl->getSink().addLabel(output, librevenge::RVNGString());
+ }
+
+ void EPUBHTMLGenerator::closeEndnote()
+@@ -846,7 +854,7 @@ void EPUBHTMLGenerator::openComment(const RVNGPropertyList & /*propList*/)
+ return;
+ EPUBXMLSink &output = m_impl->output();
+ m_impl->push(EPUBHTMLTextZone::Z_Comment);
+- m_impl->getSink().addLabel(output);
++ m_impl->getSink().addLabel(output, librevenge::RVNGString());
+ }
+
+ void EPUBHTMLGenerator::closeComment()
+--
+2.13.6
+
diff --git a/writerperfect/Library_wpftwriter.mk b/writerperfect/Library_wpftwriter.mk
index bf605e718dc1..5e356304544d 100644
--- a/writerperfect/Library_wpftwriter.mk
+++ b/writerperfect/Library_wpftwriter.mk
@@ -78,6 +78,7 @@ $(eval $(call gb_Library_add_exception_objects,wpftwriter,\
writerperfect/source/writer/StarOfficeWriterImportFilter \
writerperfect/source/writer/WordPerfectImportFilter \
writerperfect/source/writer/exp/XMLBase64ImportContext \
+ writerperfect/source/writer/exp/XMLFootnoteImportContext \
writerperfect/source/writer/exp/XMLSectionContext \
writerperfect/source/writer/exp/XMLTextFrameContext \
writerperfect/source/writer/exp/XMLTextListContext \
diff --git a/writerperfect/qa/unit/EPUBExportTest.cxx b/writerperfect/qa/unit/EPUBExportTest.cxx
index d04c2eeeaaa3..04df33257b6d 100644
--- a/writerperfect/qa/unit/EPUBExportTest.cxx
+++ b/writerperfect/qa/unit/EPUBExportTest.cxx
@@ -88,6 +88,7 @@ public:
void testTextBox();
void testFontEmbedding();
void testImageLink();
+ void testFootnote();
CPPUNIT_TEST_SUITE(EPUBExportTest);
CPPUNIT_TEST(testOutlineLevel);
@@ -124,6 +125,7 @@ public:
CPPUNIT_TEST(testTextBox);
CPPUNIT_TEST(testFontEmbedding);
CPPUNIT_TEST(testImageLink);
+ CPPUNIT_TEST(testFootnote);
CPPUNIT_TEST_SUITE_END();
};
@@ -690,6 +692,16 @@ void EPUBExportTest::testImageLink()
assertXPath(mpXmlDoc, "//xhtml:p/xhtml:a/xhtml:img", 1);
}
+void EPUBExportTest::testFootnote()
+{
+ createDoc("footnote.fodt", {});
+
+ mpXmlDoc = parseExport("OEBPS/sections/section0001.xhtml");
+ // These were missing, footnote was lost.
+ assertXPath(mpXmlDoc, "//xhtml:body/xhtml:p/xhtml:sup/xhtml:a", "type", "noteref");
+ assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside", "type", "footnote");
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(EPUBExportTest);
}
diff --git a/writerperfect/qa/unit/data/writer/epubexport/footnote.fodt b/writerperfect/qa/unit/data/writer/epubexport/footnote.fodt
new file mode 100644
index 000000000000..a846d64ed03d
--- /dev/null
+++ b/writerperfect/qa/unit/data/writer/epubexport/footnote.fodt
@@ -0,0 +1,8 @@
+<?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:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+ <office:text>
+ <text:p>before<text:note text:id="ftn0" text:note-class="footnote"><text:note-citation>1</text:note-citation><text:note-body><text:p>Footnote content</text:p></text:note-body></text:note>after</text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx b/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx
new file mode 100644
index 000000000000..e23f60932773
--- /dev/null
+++ b/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "XMLFootnoteImportContext.hxx"
+
+#include "xmlimp.hxx"
+#include "xmltext.hxx"
+
+using namespace com::sun::star;
+
+namespace writerperfect
+{
+namespace exp
+{
+/// Handler for <text:note-citation>.
+class XMLTextNoteCitationContext : public XMLImportContext
+{
+public:
+ XMLTextNoteCitationContext(XMLImport& rImport, librevenge::RVNGPropertyList& rProperties);
+
+ void SAL_CALL characters(const OUString& rCharacters) override;
+ void SAL_CALL endElement(const OUString& rName) override;
+
+private:
+ librevenge::RVNGPropertyList& m_rProperties;
+ OUString m_aCharacters;
+};
+
+XMLTextNoteCitationContext::XMLTextNoteCitationContext(XMLImport& rImport,
+ librevenge::RVNGPropertyList& rProperties)
+ : XMLImportContext(rImport)
+ , m_rProperties(rProperties)
+{
+}
+
+void XMLTextNoteCitationContext::endElement(const OUString& /*rName*/)
+{
+ m_rProperties.insert("librevenge:number", m_aCharacters.toUtf8().getStr());
+}
+
+void XMLTextNoteCitationContext::characters(const OUString& rCharacters)
+{
+ m_aCharacters += rCharacters;
+}
+
+/// Handler for <text:note-body>.
+class XMLFootnoteBodyImportContext : public XMLImportContext
+{
+public:
+ XMLFootnoteBodyImportContext(XMLImport& rImport,
+ const librevenge::RVNGPropertyList& rProperties);
+
+ rtl::Reference<XMLImportContext>
+ CreateChildContext(const OUString& rName,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+
+ void SAL_CALL
+ startElement(const OUString& rName,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+ void SAL_CALL endElement(const OUString& rName) override;
+
+private:
+ const librevenge::RVNGPropertyList& m_rProperties;
+};
+
+XMLFootnoteBodyImportContext::XMLFootnoteBodyImportContext(
+ XMLImport& rImport, const librevenge::RVNGPropertyList& rProperties)
+ : XMLImportContext(rImport)
+ , m_rProperties(rProperties)
+{
+}
+
+rtl::Reference<XMLImportContext> XMLFootnoteBodyImportContext::CreateChildContext(
+ const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/)
+{
+ return CreateTextChildContext(mrImport, rName);
+}
+
+void XMLFootnoteBodyImportContext::startElement(
+ const OUString& /*rName*/,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/)
+{
+ mrImport.GetGenerator().openFootnote(m_rProperties);
+}
+
+void XMLFootnoteBodyImportContext::endElement(const OUString& /*rName*/)
+{
+ mrImport.GetGenerator().closeFootnote();
+}
+
+XMLFootnoteImportContext::XMLFootnoteImportContext(XMLImport& rImport)
+ : XMLImportContext(rImport)
+{
+}
+
+rtl::Reference<XMLImportContext> XMLFootnoteImportContext::CreateChildContext(
+ const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/)
+{
+ if (rName == "text:note-citation")
+ return new XMLTextNoteCitationContext(mrImport, m_aProperties);
+ if (rName == "text:note-body")
+ return new XMLFootnoteBodyImportContext(mrImport, m_aProperties);
+ SAL_WARN("writerperfect", "XMLFootnoteImportContext::CreateChildContext: unhandled " << rName);
+ return nullptr;
+}
+
+void XMLFootnoteImportContext::startElement(
+ const OUString& /*rName*/,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/)
+{
+}
+} // namespace exp
+} // namespace writerperfect
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx b/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx
new file mode 100644
index 000000000000..7bd221162bb9
--- /dev/null
+++ b/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_WRITERPERFECT_SOURCE_WRITER_EXP_XMLFOOTNOTEIMPORTCONTEXT_HXX
+#define INCLUDED_WRITERPERFECT_SOURCE_WRITER_EXP_XMLFOOTNOTEIMPORTCONTEXT_HXX
+
+#include <rtl/ref.hxx>
+
+#include "xmlictxt.hxx"
+
+namespace writerperfect
+{
+namespace exp
+{
+/// Handler for <text:note>.
+class XMLFootnoteImportContext : public XMLImportContext
+{
+public:
+ XMLFootnoteImportContext(XMLImport& rImport);
+
+ rtl::Reference<XMLImportContext>
+ CreateChildContext(const OUString& rName,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+
+ void SAL_CALL
+ startElement(const OUString& rName,
+ const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+
+private:
+ librevenge::RVNGPropertyList m_aProperties;
+};
+
+} // namespace exp
+} // namespace writerperfect
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerperfect/source/writer/exp/txtparai.cxx b/writerperfect/source/writer/exp/txtparai.cxx
index a7d899547759..bfd1b2842a83 100644
--- a/writerperfect/source/writer/exp/txtparai.cxx
+++ b/writerperfect/source/writer/exp/txtparai.cxx
@@ -9,6 +9,7 @@
#include "txtparai.hxx"
+#include "XMLFootnoteImportContext.hxx"
#include "XMLTextFrameContext.hxx"
#include "xmlimp.hxx"
@@ -425,6 +426,9 @@ rtl::Reference<XMLImportContext> CreateParagraphOrSpanChildContext(XMLImport &rI
return new XMLTextFrameContext(rImport);
if (rName == "text:sequence")
return new XMLTextSequenceContext(rImport, rTextPropertyList);
+ if (rName == "text:note")
+ return new XMLFootnoteImportContext(rImport);
+ SAL_WARN("writerperfect", "CreateParagraphOrSpanChildContext: unhandled " << rName);
return nullptr;
}