From 006848cb62225647c418d5143d4e88a9d73829da Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Fri, 22 Dec 2017 16:33:52 +0100
Subject: [PATCH] EPUBHTMLGenerator: avoid <div> inside <p> and/or <span>

This is not allowed in XHTML, but we wrote that markup when a text frame
was inside a span or a paragraph. The closest allowed markup in XHTML
seems to be closing the span/paragraph before opening the text box and
doing the opposite after the text box is closed.
---
 src/lib/EPUBHTMLGenerator.cpp      | 33 +++++++++++++++++++++++++++++++++
 src/test/EPUBTextGeneratorTest.cpp |  4 +++-
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
index 342213e..bc9c1b7 100644
--- a/src/lib/EPUBHTMLGenerator.cpp
+++ b/src/lib/EPUBHTMLGenerator.cpp
@@ -395,6 +395,8 @@ struct EPUBHTMLGeneratorImpl
     , m_frameAnchorTypes()
     , m_framePropertiesStack()
     , m_linkPropertiesStack()
+    , m_paragraphAttributesStack()
+    , m_spanAttributesStack()
     , m_stylesMethod(stylesMethod)
     , m_layoutMethod(layoutMethod)
     , m_actualSink()
@@ -495,6 +497,8 @@ struct EPUBHTMLGeneratorImpl
   std::stack<RVNGPropertyList> m_framePropertiesStack;
   /// This is used for links which don't have a href.
   std::stack<RVNGPropertyList> m_linkPropertiesStack;
+  std::stack<RVNGPropertyList> m_paragraphAttributesStack;
+  std::stack<RVNGPropertyList> m_spanAttributesStack;
 
   EPUBStylesMethod m_stylesMethod;
   EPUBLayoutMethod m_layoutMethod;
@@ -683,6 +687,12 @@ void EPUBHTMLGenerator::openParagraph(const RVNGPropertyList &propList)
   }
   m_impl->output(false).openElement("p", attrs);
   m_impl->m_hasText = false;
+
+  librevenge::RVNGPropertyList::Iter i(attrs);
+  RVNGPropertyList paragraphAttributes;
+  for (i.rewind(); i.next();)
+    paragraphAttributes.insert(i.key(), i()->clone());
+  m_impl->m_paragraphAttributesStack.push(paragraphAttributes);
 }
 
 void EPUBHTMLGenerator::closeParagraph()
@@ -690,6 +700,9 @@ void EPUBHTMLGenerator::closeParagraph()
   if (m_impl->m_ignore)
     return;
 
+  if (!m_impl->m_paragraphAttributesStack.empty())
+    m_impl->m_paragraphAttributesStack.pop();
+
   if (!m_impl->m_hasText)
     insertSpace();
 
@@ -717,12 +730,22 @@ void EPUBHTMLGenerator::openSpan(const RVNGPropertyList &propList)
     break;
   }
   m_impl->output(false).openElement("span", attrs);
+
+  librevenge::RVNGPropertyList::Iter i(attrs);
+  RVNGPropertyList spanAttributes;
+  for (i.rewind(); i.next();)
+    spanAttributes.insert(i.key(), i()->clone());
+  m_impl->m_spanAttributesStack.push(spanAttributes);
 }
 
 void EPUBHTMLGenerator::closeSpan()
 {
   if (m_impl->m_ignore)
     return;
+
+  if (!m_impl->m_spanAttributesStack.empty())
+    m_impl->m_spanAttributesStack.pop();
+
   m_impl->output().closeElement("span");
 }
 
@@ -931,6 +954,11 @@ void EPUBHTMLGenerator::openTextBox(const RVNGPropertyList & /*propList*/)
   if (m_impl->m_ignore)
     return;
 
+  if (!m_impl->m_spanAttributesStack.empty())
+    m_impl->output().closeElement("span");
+  if (!m_impl->m_paragraphAttributesStack.empty())
+    m_impl->output().closeElement("p");
+
   RVNGPropertyList attrs;
 
   if (!m_impl->m_framePropertiesStack.empty())
@@ -968,6 +996,11 @@ void EPUBHTMLGenerator::closeTextBox()
       m_impl->output().insertEmptyElement("br", attrs);
     }
   }
+
+  if (!m_impl->m_paragraphAttributesStack.empty())
+    m_impl->output(false).openElement("p", m_impl->m_paragraphAttributesStack.top());
+  if (!m_impl->m_spanAttributesStack.empty())
+    m_impl->output(false).openElement("span", m_impl->m_spanAttributesStack.top());
 }
 
 void EPUBHTMLGenerator::openTable(const RVNGPropertyList &propList)
-- 
2.13.6

From a97e7f40bddba8e5d572b29811a19f34536190dc Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Fri, 22 Dec 2017 17:16:23 +0100
Subject: [PATCH] EPUBTableStyleManager: avoid vertical-align key without value

ERROR(CSS-008): test.epub/OEBPS/styles/stylesheet.css(1625,19): An error occurred while parsing the CSS: Token ';' not allowed here, expecting a property value.
---
 src/lib/EPUBTableStyleManager.cpp  | 5 +++--
 src/test/EPUBTextGeneratorTest.cpp | 6 +++++-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/lib/EPUBTableStyleManager.cpp b/src/lib/EPUBTableStyleManager.cpp
index a1ce33e..cf08737 100644
--- a/src/lib/EPUBTableStyleManager.cpp
+++ b/src/lib/EPUBTableStyleManager.cpp
@@ -255,8 +255,9 @@ void EPUBTableStyleManager::extractCellProperties(RVNGPropertyList const &pList,
     else
       cssProps["text-align"] = pList["fo:text-align"]->getStr().cstr();
   }
-  if (pList["style:vertical-align"])
-    cssProps["vertical-align"] = pList["style:vertical-align"]->getStr().cstr();
+  const librevenge::RVNGProperty *verticalAlign = pList["style:vertical-align"];
+  if (verticalAlign && !verticalAlign->getStr().empty())
+    cssProps["vertical-align"] = verticalAlign->getStr().cstr();
   else
     cssProps["vertical-align"] = "top";
   if (pList["fo:background-color"])
-- 
2.13.6

From 60baa7fb597cde57c3489d8b5066937e7edb779f Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Fri, 22 Dec 2017 17:39:31 +0100
Subject: [PATCH] EPUBHTMLGenerator: fix invalid XHTML with links at footnote
 start

ERROR(RSC-005): test3.epub/OEBPS/sections/section0001.xhtml(2,568): Error while parsing file: The a element must not appear inside a elements.
---
 src/lib/EPUBHTMLGenerator.cpp      | 4 +++-
 src/test/EPUBTextGeneratorTest.cpp | 9 +++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
index bc9c1b7..59ded90 100644
--- a/src/lib/EPUBHTMLGenerator.cpp
+++ b/src/lib/EPUBHTMLGenerator.cpp
@@ -781,7 +781,9 @@ void EPUBHTMLGenerator::openLink(const RVNGPropertyList &propList)
     m_impl->m_linkPropertiesStack.push(linkProperties);
   }
   else
-    m_impl->output(false).openElement("a", attrs);
+    // Implicit sendDelayed=true, so that in case the link is at the start of a
+    // footnote, links are not nested.
+    m_impl->output().openElement("a", attrs);
 }
 
 void EPUBHTMLGenerator::closeLink()
-- 
2.13.6

From 51e17dc87d85f1dc71b380906f9260de4cd0371c Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Thu, 18 Jan 2018 14:54:06 +0100
Subject: [PATCH] EPUBImageManager: handle relative and absolute width

---
 src/lib/EPUBImageManager.cpp       |  6 +++++
 src/test/EPUBTextGeneratorTest.cpp | 54 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/src/lib/EPUBImageManager.cpp b/src/lib/EPUBImageManager.cpp
index bdf3bf0..cb4efee 100644
--- a/src/lib/EPUBImageManager.cpp
+++ b/src/lib/EPUBImageManager.cpp
@@ -171,6 +171,12 @@ void EPUBImageManager::extractImageProperties(librevenge::RVNGPropertyList const
       continue;
     cssProps[type[i]] =  pList[field.c_str()]->getStr().cstr();
   }
+
+  // Extract size.
+  if (auto pRelWidth = pList["style:rel-width"])
+    cssProps["width"] = pRelWidth->getStr().cstr();
+  else if (auto pWidth = pList["svg:width"])
+    cssProps["width"] = pWidth->getStr().cstr();
 }
 
 std::string EPUBImageManager::getWrapStyle(librevenge::RVNGPropertyList const &pList)
-- 
2.13.6

From c081609849b18113340c39a73b6af432a103a102 Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Mon, 22 Jan 2018 14:39:19 +0100
Subject: [PATCH] fixed layout: allow defining chapter names

Fixed layout normally just works with SVG images (one image / page), but
readable navigation document is still expected, e.g. PDF provides it. So
add a way to mention what chapters start on a given page.
---
 src/lib/EPUBHTMLManager.cpp        | 25 +++++++++++++++++++++++++
 src/lib/EPUBHTMLManager.h          |  3 +++
 src/lib/EPUBPath.cpp               | 11 +++++++++++
 src/lib/EPUBPath.h                 |  4 ++++
 src/lib/EPUBTextGenerator.cpp      | 15 +++++++++++++++
 src/test/EPUBTextGeneratorTest.cpp | 37 +++++++++++++++++++++++++++++++++++++
 6 files changed, 95 insertions(+)

diff --git a/src/lib/EPUBHTMLManager.cpp b/src/lib/EPUBHTMLManager.cpp
index 5e96d1d..d35bc3f 100644
--- a/src/lib/EPUBHTMLManager.cpp
+++ b/src/lib/EPUBHTMLManager.cpp
@@ -93,6 +93,23 @@ void EPUBHTMLManager::writeTocTo(EPUBXMLSink &sink, const EPUBPath &tocPath, int
   {
     for (std::vector<EPUBPath>::size_type i = 0; m_paths.size() != i; ++i)
     {
+      const std::vector<std::string> &chapters = m_paths[i].getChapters();
+      if (!chapters.empty())
+      {
+        for (const auto &chapter : chapters)
+        {
+          sink.openElement("li");
+          librevenge::RVNGPropertyList anchorAttrs;
+          anchorAttrs.insert("href", m_paths[i].relativeTo(tocPath).str().c_str());
+          sink.openElement("a", anchorAttrs);
+          std::ostringstream label;
+          sink.insertCharacters(chapter.c_str());
+          sink.closeElement("a");
+          sink.closeElement("li");
+        }
+        continue;
+      }
+
       sink.openElement("li");
       librevenge::RVNGPropertyList anchorAttrs;
       anchorAttrs.insert("href", m_paths[i].relativeTo(tocPath).str().c_str());
@@ -140,6 +157,14 @@ void EPUBHTMLManager::insertHeadingText(const std::string &text)
   m_paths.back().appendTitle(text);
 }
 
+void EPUBHTMLManager::addChapterName(const std::string &text)
+{
+  if (m_paths.empty())
+    return;
+
+  m_paths.back().addChapter(text);
+}
+
 bool EPUBHTMLManager::hasHeadingText() const
 {
   if (m_paths.empty())
diff --git a/src/lib/EPUBHTMLManager.h b/src/lib/EPUBHTMLManager.h
index 157896b..d48ddf2 100644
--- a/src/lib/EPUBHTMLManager.h
+++ b/src/lib/EPUBHTMLManager.h
@@ -51,6 +51,9 @@ public:
   /// Appends text to the title of the current heading.
   void insertHeadingText(const std::string &text);
 
+  /// Registers a chapter name for the current page (fixed layout case).
+  void addChapterName(const std::string &text);
+
   /// If the current heading has a title.
   bool hasHeadingText() const;
 
diff --git a/src/lib/EPUBPath.cpp b/src/lib/EPUBPath.cpp
index e1c05ed..be24de5 100644
--- a/src/lib/EPUBPath.cpp
+++ b/src/lib/EPUBPath.cpp
@@ -54,6 +54,7 @@ EPUBPath::Relative::Relative(const std::vector<std::string> &components)
 EPUBPath::EPUBPath(const std::string &path)
   : m_components()
   , m_title()
+  , m_chapters()
 {
   const std::string trimmed(algorithm::trim_left_copy_if(path, algorithm::is_any_of("/")));
   algorithm::split(m_components, trimmed, algorithm::is_any_of("/"), algorithm::token_compress_on);
@@ -116,6 +117,16 @@ void EPUBPath::appendTitle(const std::string &title)
   m_title += title;
 }
 
+void EPUBPath::addChapter(const std::string &chapter)
+{
+  m_chapters.push_back(chapter);
+}
+
+const std::vector<std::string> &EPUBPath::getChapters() const
+{
+  return m_chapters;
+}
+
 std::string EPUBPath::getTitle() const
 {
   return m_title;
diff --git a/src/lib/EPUBPath.h b/src/lib/EPUBPath.h
index 12b8f25..76f2d7b 100644
--- a/src/lib/EPUBPath.h
+++ b/src/lib/EPUBPath.h
@@ -51,9 +51,13 @@ public:
   void appendTitle(const std::string &title);
   std::string getTitle() const;
 
+  /// Adds chapter name (fixed layout).
+  void addChapter(const std::string &chapter);
+  const std::vector<std::string> &getChapters() const;
 private:
   std::vector<std::string> m_components;
   std::string m_title;
+  std::vector<std::string> m_chapters;
 };
 
 bool operator==(const EPUBPath &left, const EPUBPath &right);
diff --git a/src/lib/EPUBTextGenerator.cpp b/src/lib/EPUBTextGenerator.cpp
index 8e88adb..38ddcdf 100644
--- a/src/lib/EPUBTextGenerator.cpp
+++ b/src/lib/EPUBTextGenerator.cpp
@@ -270,7 +270,9 @@ void EPUBTextGenerator::openParagraph(const librevenge::RVNGPropertyList &propLi
 {
   const RVNGProperty *const breakBefore = propList["fo:break-before"];
   if (isPageBreak(breakBefore) && m_impl->getSplitGuard().splitOnPageBreak())
+  {
     m_impl->startNewHtmlFile();
+  }
   const RVNGProperty *const breakAfter = propList["fo:break-after"];
   m_impl->m_breakAfterPara = isPageBreak(breakAfter);
   if (m_impl->getSplitGuard().splitOnSize())
@@ -282,6 +284,19 @@ void EPUBTextGenerator::openParagraph(const librevenge::RVNGPropertyList &propLi
     m_impl->startNewHtmlFile();
   m_impl->getSplitGuard().setCurrentHeadingLevel(outlineLevel ? outlineLevel->getInt() : 0);
 
+  if (const librevenge::RVNGPropertyListVector *chapterNames = m_impl->m_pageSpanProps.child("librevenge:chapter-names"))
+  {
+    for (unsigned long i = 0; i < chapterNames->count(); i++)
+    {
+      RVNGPropertyList const &chapter=(*chapterNames)[i];
+      const RVNGProperty *const chapterName = chapter["librevenge:name"];
+      if (!chapterName)
+        continue;
+
+      m_impl->getHtmlManager().addChapterName(chapterName->getStr().cstr());
+    }
+  }
+
   m_impl->getSplitGuard().openLevel();
 
   if (m_impl->m_inHeader || m_impl->m_inFooter)
-- 
2.13.6

From b6081f659e3000d9f3d5851278d8abdd33448353 Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Mon, 22 Jan 2018 15:54:43 +0100
Subject: [PATCH] fixed layout: avoid Page <N> entries when chapter names are
 provided

---
 src/lib/EPUBHTMLManager.cpp        | 31 ++++++++++++++++++-------------
 src/test/EPUBTextGeneratorTest.cpp | 16 ++++++++++++++++
 2 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/src/lib/EPUBHTMLManager.cpp b/src/lib/EPUBHTMLManager.cpp
index d35bc3f..35d82e8 100644
--- a/src/lib/EPUBHTMLManager.cpp
+++ b/src/lib/EPUBHTMLManager.cpp
@@ -7,6 +7,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <algorithm>
 #include <cassert>
 #include <iomanip>
 #include <sstream>
@@ -91,24 +92,28 @@ void EPUBHTMLManager::writeTocTo(EPUBXMLSink &sink, const EPUBPath &tocPath, int
 {
   if (version >= 30)
   {
+    bool hasChapterNames = std::find_if(m_paths.begin(), m_paths.end(), [](const EPUBPath &path)
+    {
+      return !path.getChapters().empty();
+    }) != m_paths.end();
     for (std::vector<EPUBPath>::size_type i = 0; m_paths.size() != i; ++i)
     {
       const std::vector<std::string> &chapters = m_paths[i].getChapters();
-      if (!chapters.empty())
+      for (const auto &chapter : chapters)
       {
-        for (const auto &chapter : chapters)
-        {
-          sink.openElement("li");
-          librevenge::RVNGPropertyList anchorAttrs;
-          anchorAttrs.insert("href", m_paths[i].relativeTo(tocPath).str().c_str());
-          sink.openElement("a", anchorAttrs);
-          std::ostringstream label;
-          sink.insertCharacters(chapter.c_str());
-          sink.closeElement("a");
-          sink.closeElement("li");
-        }
-        continue;
+        sink.openElement("li");
+        librevenge::RVNGPropertyList anchorAttrs;
+        anchorAttrs.insert("href", m_paths[i].relativeTo(tocPath).str().c_str());
+        sink.openElement("a", anchorAttrs);
+        std::ostringstream label;
+        sink.insertCharacters(chapter.c_str());
+        sink.closeElement("a");
+        sink.closeElement("li");
       }
+      if (hasChapterNames)
+        // Chapter names are provided for this document, so never write Page
+        // <N> entries.
+        continue;
 
       sink.openElement("li");
       librevenge::RVNGPropertyList anchorAttrs;
-- 
2.13.6