[Libreoffice-commits] core.git: 4 commits - external/libepubgen writerperfect/Library_wpftwriter.mk writerperfect/qa writerperfect/source writerperfect/uiconfig

Miklos Vajna vmiklos at collabora.co.uk
Fri Dec 1 08:18:41 UTC 2017


 external/libepubgen/libepubgen-epub3.patch.1                       |  857 ++++++++++
 writerperfect/Library_wpftwriter.mk                                |    1 
 writerperfect/qa/uitest/epubexport/epubexport.py                   |   14 
 writerperfect/qa/unit/EPUBExportTest.cxx                           |   57 
 writerperfect/qa/unit/data/writer/epubexport/footnote.fodt         |    8 
 writerperfect/qa/unit/data/writer/epubexport/popup-api.odt         |binary
 writerperfect/qa/unit/data/writer/epubexport/popup.odt             |binary
 writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png |binary
 writerperfect/source/writer/EPUBExportDialog.cxx                   |   23 
 writerperfect/source/writer/EPUBExportDialog.hxx                   |    6 
 writerperfect/source/writer/EPUBExportFilter.cxx                   |   16 
 writerperfect/source/writer/EPUBExportUIComponent.cxx              |    5 
 writerperfect/source/writer/EPUBExportUIComponent.hxx              |    2 
 writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx       |  121 +
 writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx       |   44 
 writerperfect/source/writer/exp/txtparai.cxx                       |   10 
 writerperfect/source/writer/exp/xmlimp.cxx                         |   78 
 writerperfect/source/writer/exp/xmlimp.hxx                         |    5 
 writerperfect/uiconfig/ui/exportepub.ui                            |   89 -
 19 files changed, 1322 insertions(+), 14 deletions(-)

New commits:
commit 3ed60d22c3c2a43d0460520485c3b6ecca00e239
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Nov 30 16:35:04 2017 +0100

    EPUB export: add UI to test custom media directory
    
    Sets the RVNGMediaDir filter data key at UNO level.
    
    Change-Id: I9919e5ca39c9f4f126d1d67946c8bec99ce0381d
    Reviewed-on: https://gerrit.libreoffice.org/45603
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/writerperfect/qa/uitest/epubexport/epubexport.py b/writerperfect/qa/uitest/epubexport/epubexport.py
index 196556b2fb8d..301f090e3362 100644
--- a/writerperfect/qa/uitest/epubexport/epubexport.py
+++ b/writerperfect/qa/uitest/epubexport/epubexport.py
@@ -83,6 +83,20 @@ class EPUBExportTest(UITestCase):
         coverImage = [i.Value for i in filterData if i.Name == "RVNGCoverImage"][0]
         self.assertEqual("cover.png", coverImage)
 
+    def testMediaDir(self):
+        def handleDialog(dialog):
+            dialog.getChild("mediadir").executeAction("TYPE", mkPropertyValues({"TEXT": "file:///foo/bar"}))
+            dialog.getChild("ok").executeAction("CLICK", tuple())
+
+        uiComponent = self.ui_test._xContext.ServiceManager.createInstanceWithContext("com.sun.star.comp.Writer.EPUBExportUIComponent", self.ui_test._xContext)
+
+        self.ui_test.execute_blocking_action(action=uiComponent.execute, dialog_handler=handleDialog)
+        propertyValues = uiComponent.getPropertyValues()
+        filterData = [i.Value for i in propertyValues if i.Name == "FilterData"][0]
+        # The RVNGMediaDir key was missing, EPUBExportDialog::OKClickHdl() did not set it.
+        mediaDir = [i.Value for i in filterData if i.Name == "RVNGMediaDir"][0]
+        self.assertEqual("file:///foo/bar", mediaDir)
+
     def testMeta(self):
         def handleDialog(dialog):
             dialog.getChild("identifier").executeAction("TYPE", mkPropertyValues({"TEXT": "baddcafe-e394-4cd6-9b83-7172794612e5"}))
diff --git a/writerperfect/source/writer/EPUBExportDialog.cxx b/writerperfect/source/writer/EPUBExportDialog.cxx
index c0e316e1f32a..acc1f9a9182d 100644
--- a/writerperfect/source/writer/EPUBExportDialog.cxx
+++ b/writerperfect/source/writer/EPUBExportDialog.cxx
@@ -9,6 +9,8 @@
 
 #include "EPUBExportDialog.hxx"
 
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/FolderPicker.hpp>
 #include <sfx2/opengrf.hxx>
 
 #include "EPUBExportFilter.hxx"
@@ -63,8 +65,9 @@ sal_Int32 PositionToVersion(sal_Int32 nPosition)
 namespace writerperfect
 {
 
-EPUBExportDialog::EPUBExportDialog(vcl::Window *pParent, comphelper::SequenceAsHashMap &rFilterData)
+EPUBExportDialog::EPUBExportDialog(vcl::Window *pParent, comphelper::SequenceAsHashMap &rFilterData, const uno::Reference<uno::XComponentContext> &xContext)
     : ModalDialog(pParent, "EpubDialog", "writerperfect/ui/exportepub.ui"),
+      mxContext(xContext),
       mrFilterData(rFilterData)
 {
     get(m_pVersion, "versionlb");
@@ -98,6 +101,11 @@ EPUBExportDialog::EPUBExportDialog(vcl::Window *pParent, comphelper::SequenceAsH
     get(m_pCoverButton, "coverbutton");
     m_pCoverButton->SetClickHdl(LINK(this, EPUBExportDialog, CoverClickHdl));
 
+    get(m_pMediaDir, "mediadir");
+
+    get(m_pMediaButton, "mediabutton");
+    m_pMediaButton->SetClickHdl(LINK(this, EPUBExportDialog, MediaClickHdl));
+
     get(m_pIdentifier, "identifier");
     get(m_pTitle, "title");
     get(m_pInitialCreator, "author");
@@ -128,11 +136,22 @@ IMPL_LINK_NOARG(EPUBExportDialog, CoverClickHdl, Button *, void)
         m_pCoverPath->SetText(aDlg.GetPath());
 }
 
+IMPL_LINK_NOARG(EPUBExportDialog, MediaClickHdl, Button *, void)
+{
+    uno::Reference<ui::dialogs::XFolderPicker2> xFolderPicker = ui::dialogs::FolderPicker::create(mxContext);
+    if (xFolderPicker->execute() != ui::dialogs::ExecutableDialogResults::OK)
+        return;
+
+    m_pMediaDir->SetText(xFolderPicker->getDirectory());
+}
+
 IMPL_LINK_NOARG(EPUBExportDialog, OKClickHdl, Button *, void)
 {
     // General
     if (!m_pCoverPath->GetText().isEmpty())
         mrFilterData["RVNGCoverImage"] <<= m_pCoverPath->GetText();
+    if (!m_pMediaDir->GetText().isEmpty())
+        mrFilterData["RVNGMediaDir"] <<= m_pMediaDir->GetText();
 
     // Metadata
     if (!m_pIdentifier->GetText().isEmpty())
@@ -166,6 +185,8 @@ void EPUBExportDialog::dispose()
     m_pInitialCreator.clear();
     m_pLanguage.clear();
     m_pDate.clear();
+    m_pMediaDir.clear();
+    m_pMediaButton.clear();
     ModalDialog::dispose();
 }
 
diff --git a/writerperfect/source/writer/EPUBExportDialog.hxx b/writerperfect/source/writer/EPUBExportDialog.hxx
index 4ff67ee6f5e5..1181d096bd03 100644
--- a/writerperfect/source/writer/EPUBExportDialog.hxx
+++ b/writerperfect/source/writer/EPUBExportDialog.hxx
@@ -23,7 +23,7 @@ namespace writerperfect
 class EPUBExportDialog : public ModalDialog
 {
 public:
-    EPUBExportDialog(vcl::Window *pParent, comphelper::SequenceAsHashMap &rFilterData);
+    EPUBExportDialog(vcl::Window *pParent, comphelper::SequenceAsHashMap &rFilterData, const css::uno::Reference<css::uno::XComponentContext> &xContext);
     ~EPUBExportDialog() override;
     void dispose() override;
 
@@ -31,13 +31,17 @@ private:
     DECL_LINK(VersionSelectHdl, ListBox &, void);
     DECL_LINK(SplitSelectHdl, ListBox &, void);
     DECL_LINK(CoverClickHdl, Button *, void);
+    DECL_LINK(MediaClickHdl, Button *, void);
     DECL_LINK(OKClickHdl, Button *, void);
 
+    css::uno::Reference<css::uno::XComponentContext> mxContext;
     comphelper::SequenceAsHashMap &mrFilterData;
     VclPtr<ListBox> m_pVersion;
     VclPtr<ListBox> m_pSplit;
     VclPtr<Edit> m_pCoverPath;
     VclPtr<PushButton> m_pCoverButton;
+    VclPtr<Edit> m_pMediaDir;
+    VclPtr<PushButton> m_pMediaButton;
     VclPtr<PushButton> m_pOKButton;
     VclPtr<Edit> m_pIdentifier;
     VclPtr<Edit> m_pTitle;
diff --git a/writerperfect/source/writer/EPUBExportUIComponent.cxx b/writerperfect/source/writer/EPUBExportUIComponent.cxx
index b38ebf799859..b6fe80ff3604 100644
--- a/writerperfect/source/writer/EPUBExportUIComponent.cxx
+++ b/writerperfect/source/writer/EPUBExportUIComponent.cxx
@@ -23,7 +23,8 @@ using namespace com::sun::star;
 namespace writerperfect
 {
 
-EPUBExportUIComponent::EPUBExportUIComponent(const uno::Reference<uno::XComponentContext> &/*xContext*/)
+EPUBExportUIComponent::EPUBExportUIComponent(const uno::Reference<uno::XComponentContext> &xContext)
+    : mxContext(xContext)
 {
 }
 
@@ -76,7 +77,7 @@ sal_Int16 EPUBExportUIComponent::execute()
 {
     SolarMutexGuard aGuard;
 
-    ScopedVclPtrInstance<EPUBExportDialog> pDialog(Application::GetDefDialogParent(), maFilterData);
+    ScopedVclPtrInstance<EPUBExportDialog> pDialog(Application::GetDefDialogParent(), maFilterData, mxContext);
     if (pDialog->Execute() == RET_OK)
         return ui::dialogs::ExecutableDialogResults::OK;
     return ui::dialogs::ExecutableDialogResults::CANCEL;
diff --git a/writerperfect/source/writer/EPUBExportUIComponent.hxx b/writerperfect/source/writer/EPUBExportUIComponent.hxx
index f20adfd2d997..243e1f8a61fb 100644
--- a/writerperfect/source/writer/EPUBExportUIComponent.hxx
+++ b/writerperfect/source/writer/EPUBExportUIComponent.hxx
@@ -52,6 +52,8 @@ private:
     comphelper::SequenceAsHashMap maMediaDescriptor;
     /// The filter data key.
     comphelper::SequenceAsHashMap maFilterData;
+    /// UNO context.
+    css::uno::Reference<css::uno::XComponentContext> mxContext;
 };
 
 } // namespace writerperfect
diff --git a/writerperfect/uiconfig/ui/exportepub.ui b/writerperfect/uiconfig/ui/exportepub.ui
index bc55aff5eeda..3d7c28af73be 100644
--- a/writerperfect/uiconfig/ui/exportepub.ui
+++ b/writerperfect/uiconfig/ui/exportepub.ui
@@ -172,7 +172,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|splitft">Split method:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">splitlb</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -225,7 +225,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|coverimageft">Custom cover image:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">coverpath</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -279,6 +279,79 @@
                     <property name="position">3</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkAlignment" id="alignment4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkBox" id="box8">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="mediadirft">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_top">6</property>
+                            <property name="label" translatable="yes" context="exportepub|mediadirft">Custom media directory:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">mediadir</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="box9">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="spacing">12</property>
+                            <child>
+                              <object class="GtkEntry" id="mediadir">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButton" id="mediabutton">
+                                <property name="label" translatable="yes" context="exportepub|mediabutton">Browse...</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">4</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -311,7 +384,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkAlignment" id="alignment4">
+                  <object class="GtkAlignment" id="alignment5">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="left_padding">12</property>
@@ -338,7 +411,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|identifierft">Identifier:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">identifier</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -353,7 +426,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|titleft">Title:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">title</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -378,7 +451,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|authorft">Author:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">author</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -403,7 +476,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|languageft">Language:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">language</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
@@ -428,7 +501,7 @@
                             <property name="margin_top">6</property>
                             <property name="label" translatable="yes" context="exportepub|dateft">Date:</property>
                             <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">versionlb</property>
+                            <property name="mnemonic_widget">date</property>
                             <property name="xalign">0</property>
                           </object>
                           <packing>
commit 7f0641de763d2fd425c80f630f3ae6bcde778e7a
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Nov 30 16:34:05 2017 +0100

    EPUB export: allow setting custom media dir explicitly
    
    The default is the same <base directory>/<base name>/ as before.
    
    Change-Id: Idb500193a7f6bd901d861e857147832a3ada91d3
    Reviewed-on: https://gerrit.libreoffice.org/45602
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/writerperfect/qa/unit/EPUBExportTest.cxx b/writerperfect/qa/unit/EPUBExportTest.cxx
index 1f7b64250724..335347de60ff 100644
--- a/writerperfect/qa/unit/EPUBExportTest.cxx
+++ b/writerperfect/qa/unit/EPUBExportTest.cxx
@@ -90,6 +90,7 @@ public:
     void testImageLink();
     void testFootnote();
     void testPopup();
+    void testPopupAPI();
 
     CPPUNIT_TEST_SUITE(EPUBExportTest);
     CPPUNIT_TEST(testOutlineLevel);
@@ -128,6 +129,7 @@ public:
     CPPUNIT_TEST(testImageLink);
     CPPUNIT_TEST(testFootnote);
     CPPUNIT_TEST(testPopup);
+    CPPUNIT_TEST(testPopupAPI);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -724,6 +726,27 @@ void EPUBExportTest::testPopup()
     assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[2]/xhtml:img", 1);
 }
 
+void EPUBExportTest::testPopupAPI()
+{
+    // Make sure that the popup works with data from a media directory.
+    OUString aMediaDir = m_directories.getURLFromSrc(DATA_DIRECTORY) + "popup";
+    uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
+    {
+        {"RVNGMediaDir", uno::makeAny(aMediaDir)}
+    }));
+    createDoc("popup-api.odt", aFilterData);
+
+    // We have a non-empty anchor image.
+    mpXmlDoc = parseExport("OEBPS/sections/section0001.xhtml");
+    OUString aAnchor = getXPath(mpXmlDoc, "//xhtml:body/xhtml:p[1]/xhtml:a/xhtml:img", "src");
+    CPPUNIT_ASSERT(!aAnchor.isEmpty());
+    // We have a non-empty popup image.
+    OUString aData = getXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[1]/xhtml:img", "src");
+    CPPUNIT_ASSERT(!aData.isEmpty());
+    // The anchor is different from the popup image.
+    CPPUNIT_ASSERT(aAnchor != aData);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(EPUBExportTest);
 
 }
diff --git a/writerperfect/qa/unit/data/writer/epubexport/popup-api.odt b/writerperfect/qa/unit/data/writer/epubexport/popup-api.odt
new file mode 100644
index 000000000000..30f4cc8f526e
Binary files /dev/null and b/writerperfect/qa/unit/data/writer/epubexport/popup-api.odt differ
diff --git a/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png b/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png
index fdad35484e7c..cc74f136fbdf 100644
Binary files a/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png and b/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png differ
diff --git a/writerperfect/source/writer/exp/xmlimp.cxx b/writerperfect/source/writer/exp/xmlimp.cxx
index fdbc1d398243..f933487a59b8 100644
--- a/writerperfect/source/writer/exp/xmlimp.cxx
+++ b/writerperfect/source/writer/exp/xmlimp.cxx
@@ -113,6 +113,37 @@ OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, c
     return aRet;
 }
 
+/// Determines the base directory for cover images, XMP metadata, popup images.
+OUString FindMediaDir(const OUString &rDocumentBaseURL, const uno::Sequence<beans::PropertyValue> &rFilterData)
+{
+    OUString aMediaDir;
+
+    // See if filter data contains a media directory explicitly.
+    for (sal_Int32 i = 0; i < rFilterData.getLength(); ++i)
+    {
+        if (rFilterData[i].Name == "RVNGMediaDir")
+        {
+            rFilterData[i].Value >>= aMediaDir;
+            break;
+        }
+    }
+
+    if (!aMediaDir.isEmpty())
+        return aMediaDir + "/";
+
+    // Not set explicitly, try to pick it up from the base directory.
+    INetURLObject aURL(rDocumentBaseURL);
+    try
+    {
+        aMediaDir = rtl::Uri::convertRelToAbs(rDocumentBaseURL, aURL.GetBase()) + "/";
+    }
+    catch (const rtl::MalformedUriException &rException)
+    {
+        SAL_WARN("writerperfect", "FindMediaDir: convertRelToAbs() failed:" << rException.getMessage());
+    }
+    return aMediaDir;
+}
+
 /// Picks up XMP metadata from the base directory.
 void FindXMPMetadata(const uno::Reference<uno::XComponentContext> &xContext, const OUString &rDocumentBaseURL, const uno::Sequence<beans::PropertyValue> &rFilterData, librevenge::RVNGPropertyList &rMetaData)
 {
@@ -256,6 +287,8 @@ XMLImport::XMLImport(const uno::Reference<uno::XComponentContext> &xContext, lib
         }
     }
 
+    maMediaDir = FindMediaDir(rURL, aFilterData);
+
     OUString aMimeType;
     OUString aCoverImage = FindCoverImage(rURL, aMimeType, aFilterData);
     if (!aCoverImage.isEmpty())
@@ -303,16 +336,7 @@ bool XMLImport::FillPopupData(const OUString &rURL, librevenge::RVNGPropertyList
     if (!bRelative)
         return false;
 
-    OUString aAbs;
-    INetURLObject aBaseURL(maDocumentBaseURL);
-    try
-    {
-        aAbs = rtl::Uri::convertRelToAbs(maDocumentBaseURL, aBaseURL.GetBase() + "/" + rURL);
-    }
-    catch (const rtl::MalformedUriException &rException)
-    {
-        SAL_WARN("writerperfect", "XMLImport::FillPopupData: convertRelToAbs() failed:" << rException.getMessage());
-    }
+    OUString aAbs = maMediaDir + rURL;
     if (aAbs.isEmpty())
         return false;
 
diff --git a/writerperfect/source/writer/exp/xmlimp.hxx b/writerperfect/source/writer/exp/xmlimp.hxx
index 7b517cbe1325..0777e71953a4 100644
--- a/writerperfect/source/writer/exp/xmlimp.hxx
+++ b/writerperfect/source/writer/exp/xmlimp.hxx
@@ -58,6 +58,7 @@ class XMLImport : public cppu::WeakImplHelper
     const css::uno::Reference<css::uno::XComponentContext> &mxContext;
     css::uno::Reference<css::uri::XUriReferenceFactory> mxUriReferenceFactory;
     OUString maDocumentBaseURL;
+    OUString maMediaDir;
 
 public:
     XMLImport(const css::uno::Reference<css::uno::XComponentContext> &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const css::uno::Sequence<css::beans::PropertyValue> &rDescriptor);
commit 7dcb4fef3b2383f4dd4d5d5c9ebbe8f19f8f4c8e
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Nov 30 16:33:03 2017 +0100

    EPUB export: support image popup for images and text
    
    Handle relative links on image / text as data for a popup. Pick the images up
    from <base directory>/<base name>/<relative url> as a start.
    
    Change-Id: I9b6183d554e3792aa71dfffc19a671a0e4c302cc
    Reviewed-on: https://gerrit.libreoffice.org/45601
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/external/libepubgen/libepubgen-epub3.patch.1 b/external/libepubgen/libepubgen-epub3.patch.1
index 28f9c3771708..e2948c43bd34 100644
--- a/external/libepubgen/libepubgen-epub3.patch.1
+++ b/external/libepubgen/libepubgen-epub3.patch.1
@@ -5053,3 +5053,304 @@ index 96e7623..6b4c7c2 100644
 -- 
 2.13.6
 
+From bc80bf98172e8e0f8c803dec0b8e74e233ac482e Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos at collabora.co.uk>
+Date: Fri, 24 Nov 2017 16:28:58 +0100
+Subject: [PATCH 1/2] EPUBHTMLGenerator: support image popup for images
+
+If the user of the lib uses the librevenge:mime-type and
+office:binary-data keys (instead of xlink:href) for a link around an
+image, then open that image in a popup (using the footnote markup).
+
+The usual assumption is that the footnote anchor is trivial (just a
+string) and the content is complex, but here the situation is the
+opposite: the anchor can be something complex, OTOH the footnote content
+is always a single inline image -- this requires the new
+closeAnchor mode when adding the footnote label.
+---
+ src/lib/EPUBHTMLGenerator.cpp      | 79 ++++++++++++++++++++++++++++++--------
+ src/test/EPUBTextGeneratorTest.cpp | 34 ++++++++++++++++
+ 2 files changed, 96 insertions(+), 17 deletions(-)
+
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 6b4c7c2..75b0866 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -222,7 +222,8 @@ 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, const librevenge::RVNGString &number)
++  //! @param closeAnchor determintes if the anchor on the main sink should be closed or not.
++  void addLabel(EPUBXMLSink &output, const librevenge::RVNGString &number, bool closeAnchor)
+   {
+     // Unique label, e.g. 'F1' for the first footnote.
+     std::string lbl=label();
+@@ -244,9 +245,12 @@ struct TextZoneSink
+         aAttrs.insert("epub:type", "noteref");
+       aAttrs.insert("href", ("#data" + lbl).c_str());
+       output.openElement("a", aAttrs);
+-      output.insertCharacters(uiLabel.c_str());
+-      output.closeElement("a");
+-      output.closeElement("sup");
++      if (closeAnchor)
++      {
++        output.insertCharacters(uiLabel.c_str());
++        output.closeElement("a");
++        output.closeElement("sup");
++      }
+     }
+     flush();
+     if (version == 30)
+@@ -259,13 +263,16 @@ struct TextZoneSink
+     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(uiLabel.c_str());
+-    m_delayedLabel.closeElement("a");
+-    m_delayedLabel.closeElement("sup");
++    if (closeAnchor)
++    {
++      m_delayedLabel.openElement("sup", supAttrs);
++      RVNGPropertyList aAttrs;
++      aAttrs.insert("href", ("#called" + lbl).c_str());
++      m_delayedLabel.openElement("a", aAttrs);
++      m_delayedLabel.insertCharacters(uiLabel.c_str());
++      m_delayedLabel.closeElement("a");
++      m_delayedLabel.closeElement("sup");
++    }
+   }
+   //! flush delayed label, ...
+   void flush()
+@@ -379,6 +386,7 @@ struct EPUBHTMLGeneratorImpl
+     , m_version(version)
+     , m_frameAnchorTypes()
+     , m_framePropertiesStack()
++    , m_linkPropertiesStack()
+     , m_stylesMethod(stylesMethod)
+     , m_actualSink()
+     , m_sinkStack()
+@@ -475,6 +483,8 @@ struct EPUBHTMLGeneratorImpl
+ 
+   std::stack<std::string> m_frameAnchorTypes;
+   std::stack<RVNGPropertyList> m_framePropertiesStack;
++  /// This is used for links which don't have a href.
++  std::stack<RVNGPropertyList> m_linkPropertiesStack;
+ 
+   EPUBStylesMethod m_stylesMethod;
+ 
+@@ -702,14 +712,29 @@ void EPUBHTMLGenerator::openLink(const RVNGPropertyList &propList)
+ 
+     attrs.insert("href", href.c_str());
+   }
+-  m_impl->output(false).openElement("a", attrs);
++  const librevenge::RVNGProperty *binaryDataProp = propList["office:binary-data"];
++  const librevenge::RVNGProperty *mimeTypeProp = propList["librevenge:mime-type"];
++  if (binaryDataProp && mimeTypeProp)
++  {
++    // This is not a real link, but more an additional image on top of an
++    // existing one, map it to footnotes instead.
++    RVNGPropertyList linkProperties;
++    linkProperties.insert("office:binary-data", binaryDataProp->clone());
++    linkProperties.insert("librevenge:mime-type", mimeTypeProp->clone());
++    m_impl->m_linkPropertiesStack.push(linkProperties);
++  }
++  else
++    m_impl->output(false).openElement("a", attrs);
+ }
+ 
+ void EPUBHTMLGenerator::closeLink()
+ {
+   if (m_impl->m_ignore)
+     return;
+-  m_impl->output().closeElement("a");
++  if (!m_impl->m_linkPropertiesStack.empty())
++    m_impl->m_linkPropertiesStack.pop();
++  else
++    m_impl->output().closeElement("a");
+ }
+ 
+ void EPUBHTMLGenerator::insertTab()
+@@ -820,7 +845,8 @@ void EPUBHTMLGenerator::openFootnote(const RVNGPropertyList &propList)
+   librevenge::RVNGString number;
+   if (const librevenge::RVNGProperty *numProp = propList["librevenge:number"])
+     number = numProp->getStr();
+-  m_impl->getSink().addLabel(output, number);
++  bool closeAnchor = m_impl->m_linkPropertiesStack.empty();
++  m_impl->getSink().addLabel(output, number, closeAnchor);
+ }
+ 
+ void EPUBHTMLGenerator::closeFootnote()
+@@ -838,7 +864,7 @@ void EPUBHTMLGenerator::openEndnote(const RVNGPropertyList &)
+     return;
+   EPUBXMLSink &output = m_impl->output();
+   m_impl->push(EPUBHTMLTextZone::Z_EndNote);
+-  m_impl->getSink().addLabel(output, librevenge::RVNGString());
++  m_impl->getSink().addLabel(output, librevenge::RVNGString(), true);
+ }
+ 
+ void EPUBHTMLGenerator::closeEndnote()
+@@ -854,7 +880,7 @@ void EPUBHTMLGenerator::openComment(const RVNGPropertyList & /*propList*/)
+     return;
+   EPUBXMLSink &output = m_impl->output();
+   m_impl->push(EPUBHTMLTextZone::Z_Comment);
+-  m_impl->getSink().addLabel(output, librevenge::RVNGString());
++  m_impl->getSink().addLabel(output, librevenge::RVNGString(), true);
+ }
+ 
+ void EPUBHTMLGenerator::closeComment()
+@@ -1067,7 +1093,26 @@ void EPUBHTMLGenerator::insertBinaryObject(const RVNGPropertyList &propList)
+   attrs.insert("src", path.relativeTo(m_impl->m_path).str().c_str());
+   // FIXME: use alternative repr. if available
+   attrs.insert("alt", path.str().c_str());
+-  m_impl->output().insertEmptyElement("img", attrs);
++  if (!m_impl->m_linkPropertiesStack.empty())
++  {
++    RVNGPropertyList &linkProperties = m_impl->m_linkPropertiesStack.top();
++    // Save the main sink, as m_impl->output() will point to the footnote sink.
++    libepubgen::EPUBXMLSink &main = m_impl->output();
++    openFootnote(RVNGPropertyList());
++    main.insertEmptyElement("img", attrs);
++    main.closeElement("a");
++    main.closeElement("sup");
++    const EPUBPath &linkPath = m_impl->m_imageManager.insert(
++                                 RVNGBinaryData(linkProperties["office:binary-data"]->getStr()),
++                                 linkProperties["librevenge:mime-type"]->getStr());
++    RVNGPropertyList linkAttrs;
++    linkAttrs.insert("src", linkPath.relativeTo(m_impl->m_path).str().c_str());
++    linkAttrs.insert("alt", linkPath.str().c_str());
++    m_impl->output().insertEmptyElement("img", linkAttrs);
++    closeFootnote();
++  }
++  else
++    m_impl->output().insertEmptyElement("img", attrs);
+ 
+   if (!wrapStyle.empty())
+   {
+-- 
+2.13.6
+
+
+From 6e094bbe9fd8c1784ef3c348d04e2add8b48fc67 Mon Sep 17 00:00:00 2001
+From: Miklos Vajna <vmiklos at collabora.co.uk>
+Date: Fri, 24 Nov 2017 17:18:53 +0100
+Subject: [PATCH 2/2] EPUBHTMLGenerator: support image popup for text
+
+And also make sure that the popup anchor is not superscript.
+---
+ src/lib/EPUBHTMLGenerator.cpp      | 44 ++++++++++++++++++++++++++------------
+ src/lib/EPUBHTMLGenerator.h        |  4 ++++
+ src/test/EPUBTextGeneratorTest.cpp | 31 ++++++++++++++++++++++++++-
+ 3 files changed, 64 insertions(+), 15 deletions(-)
+
+diff --git a/src/lib/EPUBHTMLGenerator.cpp b/src/lib/EPUBHTMLGenerator.cpp
+index 75b0866..5c6421c 100644
+--- a/src/lib/EPUBHTMLGenerator.cpp
++++ b/src/lib/EPUBHTMLGenerator.cpp
+@@ -239,7 +239,8 @@ struct TextZoneSink
+     {
+       RVNGPropertyList supAttrs;
+       supAttrs.insert("id", ("called" + lbl).c_str());
+-      output.openElement("sup", supAttrs);
++      if (closeAnchor)
++        output.openElement("sup", supAttrs);
+       RVNGPropertyList aAttrs;
+       if (version == 30)
+         aAttrs.insert("epub:type", "noteref");
+@@ -769,7 +770,9 @@ void EPUBHTMLGenerator::insertText(const RVNGString &text)
+ {
+   if (m_impl->m_ignore)
+     return;
+-  m_impl->output().insertCharacters(text);
++  EPUBXMLSink &sink = openPopup();
++  sink.insertCharacters(text);
++  closePopup(sink);
+   m_impl->m_hasText = true;
+ }
+ 
+@@ -1093,15 +1096,37 @@ void EPUBHTMLGenerator::insertBinaryObject(const RVNGPropertyList &propList)
+   attrs.insert("src", path.relativeTo(m_impl->m_path).str().c_str());
+   // FIXME: use alternative repr. if available
+   attrs.insert("alt", path.str().c_str());
++  EPUBXMLSink &sink = openPopup();
++  sink.insertEmptyElement("img", attrs);
++  closePopup(sink);
++
++  if (!wrapStyle.empty())
++  {
++    attrs.clear();
++    attrs.insert("style", wrapStyle);
++    m_impl->output().insertEmptyElement("br", attrs);
++  }
++}
++
++EPUBXMLSink &EPUBHTMLGenerator::openPopup()
++{
+   if (!m_impl->m_linkPropertiesStack.empty())
+   {
+-    RVNGPropertyList &linkProperties = m_impl->m_linkPropertiesStack.top();
+     // Save the main sink, as m_impl->output() will point to the footnote sink.
+     libepubgen::EPUBXMLSink &main = m_impl->output();
+     openFootnote(RVNGPropertyList());
+-    main.insertEmptyElement("img", attrs);
++    return main;
++  }
++  else
++    return m_impl->output();
++}
++
++void EPUBHTMLGenerator::closePopup(EPUBXMLSink &main)
++{
++  if (!m_impl->m_linkPropertiesStack.empty())
++  {
++    RVNGPropertyList &linkProperties = m_impl->m_linkPropertiesStack.top();
+     main.closeElement("a");
+-    main.closeElement("sup");
+     const EPUBPath &linkPath = m_impl->m_imageManager.insert(
+                                  RVNGBinaryData(linkProperties["office:binary-data"]->getStr()),
+                                  linkProperties["librevenge:mime-type"]->getStr());
+@@ -1111,15 +1136,6 @@ void EPUBHTMLGenerator::insertBinaryObject(const RVNGPropertyList &propList)
+     m_impl->output().insertEmptyElement("img", linkAttrs);
+     closeFootnote();
+   }
+-  else
+-    m_impl->output().insertEmptyElement("img", attrs);
+-
+-  if (!wrapStyle.empty())
+-  {
+-    attrs.clear();
+-    attrs.insert("style", wrapStyle);
+-    m_impl->output().insertEmptyElement("br", attrs);
+-  }
+ }
+ 
+ void EPUBHTMLGenerator::insertEquation(const RVNGPropertyList & /* propList */) {}
+diff --git a/src/lib/EPUBHTMLGenerator.h b/src/lib/EPUBHTMLGenerator.h
+index 11f20cb..3699179 100644
+--- a/src/lib/EPUBHTMLGenerator.h
++++ b/src/lib/EPUBHTMLGenerator.h
+@@ -27,6 +27,7 @@ class EPUBSpanStyleManager;
+ class EPUBParagraphStyleManager;
+ class EPUBTableStyleManager;
+ class EPUBPath;
++class EPUBXMLSink;
+ 
+ class EPUBHTMLGenerator : public librevenge::RVNGTextInterface
+ {
+@@ -112,6 +113,9 @@ public:
+   void insertEquation(const librevenge::RVNGPropertyList &propList) override;
+ 
+ private:
++  EPUBXMLSink &openPopup();
++  void closePopup(EPUBXMLSink &main);
++
+   std::unique_ptr<EPUBHTMLGeneratorImpl> m_impl;
+ 
+   // Unimplemented to prevent compiler from creating crasher ones
+-- 
+2.13.6
+
diff --git a/writerperfect/qa/unit/EPUBExportTest.cxx b/writerperfect/qa/unit/EPUBExportTest.cxx
index 04df33257b6d..1f7b64250724 100644
--- a/writerperfect/qa/unit/EPUBExportTest.cxx
+++ b/writerperfect/qa/unit/EPUBExportTest.cxx
@@ -89,6 +89,7 @@ public:
     void testFontEmbedding();
     void testImageLink();
     void testFootnote();
+    void testPopup();
 
     CPPUNIT_TEST_SUITE(EPUBExportTest);
     CPPUNIT_TEST(testOutlineLevel);
@@ -126,6 +127,7 @@ public:
     CPPUNIT_TEST(testFontEmbedding);
     CPPUNIT_TEST(testImageLink);
     CPPUNIT_TEST(testFootnote);
+    CPPUNIT_TEST(testPopup);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -702,6 +704,26 @@ void EPUBExportTest::testFootnote()
     assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside", "type", "footnote");
 }
 
+void EPUBExportTest::testPopup()
+{
+    createDoc("popup.odt", {});
+
+    mpXmlDoc = parseExport("OEBPS/sections/section0001.xhtml");
+    // Test image popup anchor.
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:p[1]/xhtml:a", "type", "noteref");
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:p[1]/xhtml:a/xhtml:img", 1);
+    // Test image popup content.
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[1]", "type", "footnote");
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[1]/xhtml:img", 1);
+
+    // Test text popup anchor.
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:p[2]/xhtml:span/xhtml:a", "type", "noteref");
+    assertXPathContent(mpXmlDoc, "//xhtml:body/xhtml:p[2]/xhtml:span/xhtml:a", "link");
+    // Test text popup content.
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[2]", "type", "footnote");
+    assertXPath(mpXmlDoc, "//xhtml:body/xhtml:aside[2]/xhtml:img", 1);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(EPUBExportTest);
 
 }
diff --git a/writerperfect/qa/unit/data/writer/epubexport/popup.odt b/writerperfect/qa/unit/data/writer/epubexport/popup.odt
new file mode 100644
index 000000000000..30f4cc8f526e
Binary files /dev/null and b/writerperfect/qa/unit/data/writer/epubexport/popup.odt differ
diff --git a/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png b/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png
new file mode 100644
index 000000000000..fdad35484e7c
Binary files /dev/null and b/writerperfect/qa/unit/data/writer/epubexport/popup/libreoffice.png differ
diff --git a/writerperfect/source/writer/EPUBExportFilter.cxx b/writerperfect/source/writer/EPUBExportFilter.cxx
index 8463c7c88165..ba9e30d85df9 100644
--- a/writerperfect/source/writer/EPUBExportFilter.cxx
+++ b/writerperfect/source/writer/EPUBExportFilter.cxx
@@ -14,11 +14,15 @@
 #include <libepubgen/EPUBTextGenerator.h>
 #include <libepubgen/libepubgen-decls.h>
 
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/frame/XModel.hpp>
 #include <com/sun/star/lang/XInitialization.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>
 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
 
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/propertysetinfo.hxx>
 #include <cppuhelper/supportsservice.hxx>
 
 #include "exp/xmlimp.hxx"
@@ -82,7 +86,17 @@ sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue> &rDe
     uno::Reference<xml::sax::XDocumentHandler> xExportHandler(new exp::XMLImport(mxContext, aGenerator, aSourceURL, rDescriptor));
 
     uno::Reference<lang::XInitialization> xInitialization(mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.Writer.XMLOasisExporter", mxContext), uno::UNO_QUERY);
-    xInitialization->initialize({uno::makeAny(xExportHandler)});
+
+    // A subset of parameters are passed in as a property set.
+    comphelper::PropertyMapEntry const aInfoMap[] =
+    {
+        {OUString("BaseURI"), 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0},
+        {OUString(), 0, css::uno::Type(), 0, 0}
+    };
+    uno::Reference<beans::XPropertySet> xInfoSet(comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+    xInfoSet->setPropertyValue("BaseURI", uno::makeAny(aSourceURL));
+
+    xInitialization->initialize({uno::makeAny(xExportHandler), uno::makeAny(xInfoSet)});
     uno::Reference<document::XExporter> xExporter(xInitialization, uno::UNO_QUERY);
     xExporter->setSourceDocument(mxSourceDocument);
     uno::Reference<document::XFilter> xFilter(xInitialization, uno::UNO_QUERY);
diff --git a/writerperfect/source/writer/exp/txtparai.cxx b/writerperfect/source/writer/exp/txtparai.cxx
index bfd1b2842a83..309f80ce607a 100644
--- a/writerperfect/source/writer/exp/txtparai.cxx
+++ b/writerperfect/source/writer/exp/txtparai.cxx
@@ -265,6 +265,9 @@ void XMLTextFrameHyperlinkContext::startElement(const OUString &/*rName*/, const
             FillStyles(rAttributeValue, mrImport.GetAutomaticTextStyles(), mrImport.GetTextStyles(), m_aPropertyList);
         else
         {
+            if (rAttributeName == "xlink:href" && mrImport.FillPopupData(rAttributeValue, aPropertyList))
+                continue;
+
             // This affects the link's properties.
             OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8);
             OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8);
@@ -331,6 +334,9 @@ void XMLHyperlinkContext::startElement(const OUString &/*rName*/, const css::uno
             FillStyles(rAttributeValue, mrImport.GetAutomaticTextStyles(), mrImport.GetTextStyles(), m_aPropertyList);
         else
         {
+            if (rAttributeName == "xlink:href" && mrImport.FillPopupData(rAttributeValue, aPropertyList))
+                continue;
+
             // This affects the link's properties.
             OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8);
             OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8);
diff --git a/writerperfect/source/writer/exp/xmlimp.cxx b/writerperfect/source/writer/exp/xmlimp.cxx
index bb3d067660b7..fdbc1d398243 100644
--- a/writerperfect/source/writer/exp/xmlimp.cxx
+++ b/writerperfect/source/writer/exp/xmlimp.cxx
@@ -12,6 +12,7 @@
 #include <initializer_list>
 #include <unordered_map>
 
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
 #include <com/sun/star/xml/sax/InputSource.hpp>
 #include <com/sun/star/xml/sax/Parser.hpp>
 #include <rtl/uri.hxx>
@@ -242,7 +243,8 @@ rtl::Reference<XMLImportContext> XMLOfficeDocContext::CreateChildContext(const O
 
 XMLImport::XMLImport(const uno::Reference<uno::XComponentContext> &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const uno::Sequence<beans::PropertyValue> &rDescriptor)
     : mrGenerator(rGenerator),
-      mxContext(xContext)
+      mxContext(xContext),
+      maDocumentBaseURL(rURL)
 {
     uno::Sequence<beans::PropertyValue> aFilterData;
     for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i)
@@ -270,6 +272,8 @@ XMLImport::XMLImport(const uno::Reference<uno::XComponentContext> &xContext, lib
     }
 
     FindXMPMetadata(mxContext, rURL, aFilterData, maMetaData);
+
+    mxUriReferenceFactory = uri::UriReferenceFactory::create(mxContext);
 }
 
 const librevenge::RVNGPropertyListVector &XMLImport::GetCoverImages()
@@ -282,6 +286,54 @@ const librevenge::RVNGPropertyList &XMLImport::GetMetaData()
     return maMetaData;
 }
 
+bool XMLImport::FillPopupData(const OUString &rURL, librevenge::RVNGPropertyList &rPropList)
+{
+    uno::Reference<uri::XUriReference> xUriRef;
+    try
+    {
+        xUriRef = mxUriReferenceFactory->parse(rURL);
+    }
+    catch (const uno::Exception &rException)
+    {
+        SAL_WARN("writerperfect", "XMLImport::FillPopupData: XUriReference::parse() failed:" << rException.Message);
+    }
+    bool bRelative = false;
+    if (xUriRef.is())
+        bRelative = !xUriRef->isAbsolute();
+    if (!bRelative)
+        return false;
+
+    OUString aAbs;
+    INetURLObject aBaseURL(maDocumentBaseURL);
+    try
+    {
+        aAbs = rtl::Uri::convertRelToAbs(maDocumentBaseURL, aBaseURL.GetBase() + "/" + rURL);
+    }
+    catch (const rtl::MalformedUriException &rException)
+    {
+        SAL_WARN("writerperfect", "XMLImport::FillPopupData: convertRelToAbs() failed:" << rException.getMessage());
+    }
+    if (aAbs.isEmpty())
+        return false;
+
+    SvFileStream aStream(aAbs, StreamMode::READ);
+    if (aStream.IsOpen())
+    {
+        librevenge::RVNGBinaryData aBinaryData;
+        SvMemoryStream aMemoryStream;
+        aMemoryStream.WriteStream(aStream);
+        aBinaryData.append(static_cast<const unsigned char *>(aMemoryStream.GetBuffer()), aMemoryStream.GetSize());
+        rPropList.insert("office:binary-data", aBinaryData);
+
+        INetURLObject aAbsURL(aAbs);
+        OUString aMimeType = GetMimeType(aAbsURL.GetExtension());
+        rPropList.insert("librevenge:mime-type", aMimeType.toUtf8().getStr());
+        return true;
+    }
+
+    return false;
+}
+
 rtl::Reference<XMLImportContext> XMLImport::CreateContext(const OUString &rName, const css::uno::Reference<css::xml::sax::XAttributeList> &/*xAttribs*/)
 {
     if (rName == "office:document")
diff --git a/writerperfect/source/writer/exp/xmlimp.hxx b/writerperfect/source/writer/exp/xmlimp.hxx
index 90a9762bb9b6..7b517cbe1325 100644
--- a/writerperfect/source/writer/exp/xmlimp.hxx
+++ b/writerperfect/source/writer/exp/xmlimp.hxx
@@ -17,6 +17,7 @@
 
 #include <com/sun/star/beans/PropertyValue.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uri/XUriReferenceFactory.hpp>
 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
 
 #include <cppuhelper/implbase.hxx>
@@ -55,6 +56,8 @@ class XMLImport : public cppu::WeakImplHelper
     /// Author, date, etc -- overwrites what would be from the document out of the box.
     librevenge::RVNGPropertyList maMetaData;
     const css::uno::Reference<css::uno::XComponentContext> &mxContext;
+    css::uno::Reference<css::uri::XUriReferenceFactory> mxUriReferenceFactory;
+    OUString maDocumentBaseURL;
 
 public:
     XMLImport(const css::uno::Reference<css::uno::XComponentContext> &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const css::uno::Sequence<css::beans::PropertyValue> &rDescriptor);
@@ -78,6 +81,7 @@ public:
     std::map<OUString, librevenge::RVNGPropertyList> &GetGraphicStyles();
     const librevenge::RVNGPropertyListVector &GetCoverImages();
     const librevenge::RVNGPropertyList &GetMetaData();
+    bool FillPopupData(const OUString &rURL, librevenge::RVNGPropertyList &rPropList);
 
     // XDocumentHandler
     void SAL_CALL startDocument() override;
commit 553a35bed7a7ffb5bcda98987eb4af8b79bc031f
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Nov 30 16:32:02 2017 +0100

    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 at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

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 at 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 at 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 at 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 at 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:oas
 is: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;
 }
 


More information about the Libreoffice-commits mailing list