Kaydet (Commit) 1a48cdaf authored tarafından Miklos Vajna's avatar Miklos Vajna

EPUB export: allow overwriting of document metadata

Pick up overrides from <base directory>/<base name>.xmp as a start.

Change-Id: Ib64a6bbdadc53633fb1f0d4a7efdde2e3c96b5ef
Reviewed-on: https://gerrit.libreoffice.org/45551Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
üst 56d79a2d
......@@ -4467,3 +4467,33 @@ index 3f4bf3c..cbb83b7 100644
--
2.13.6
From 631b21834883aa8f2ee83a20717dd37900331696 Mon Sep 17 00:00:00 2001
From: Miklos Vajna <vmiklos@collabora.co.uk>
Date: Tue, 21 Nov 2017 11:52:03 +0100
Subject: [PATCH] EPUBGenerator: allow overwriting dc:identifier default
All other types had a way to be overwritten.
---
src/lib/EPUBGenerator.cpp | 5 ++++-
src/test/EPUBTextGeneratorTest.cpp | 3 +++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/lib/EPUBGenerator.cpp b/src/lib/EPUBGenerator.cpp
index 62dac6e..1cb1112 100644
--- a/src/lib/EPUBGenerator.cpp
+++ b/src/lib/EPUBGenerator.cpp
@@ -285,7 +285,10 @@ void EPUBGenerator::writeRoot()
boost::uuids::uuid uuid = boost::uuids::random_generator()();
identifierStream << uuid;
std::string identifierCharactrs = identifierStream.str();
- sink.insertCharacters(identifierCharactrs.c_str());
+ RVNGString identifier = identifierCharactrs.c_str();
+ if (m_metadata["dc:identifier"] && !m_metadata["dc:identifier"]->getStr().empty())
+ identifier = m_metadata["dc:identifier"]->getStr();
+ sink.insertCharacters(identifier);
sink.closeElement("dc:identifier");
RVNGString title("Unknown Title");
--
2.13.6
......@@ -61,6 +61,7 @@ public:
void testSpanAutostyle();
void testParaAutostyleCharProps();
void testMeta();
void testMetaXMP();
void testCoverImage();
void testParaNamedstyle();
void testCharNamedstyle();
......@@ -94,6 +95,7 @@ public:
CPPUNIT_TEST(testSpanAutostyle);
CPPUNIT_TEST(testParaAutostyleCharProps);
CPPUNIT_TEST(testMeta);
CPPUNIT_TEST(testMetaXMP);
CPPUNIT_TEST(testCoverImage);
CPPUNIT_TEST(testParaNamedstyle);
CPPUNIT_TEST(testCharNamedstyle);
......@@ -335,6 +337,19 @@ void EPUBExportTest::testMeta()
CPPUNIT_ASSERT(mxZipFile->hasByName("OEBPS/images/image0001.png"));
}
void EPUBExportTest::testMetaXMP()
{
createDoc("meta-xmp.fodt", {});
mpXmlDoc = parseExport("OEBPS/content.opf");
// These were the libepubgen default values, metadata from a matching .xmp file was not picked up.
assertXPathContent(mpXmlDoc, "/opf:package/opf:metadata/dc:identifier", "deadbeef-e394-4cd6-9b83-7172794612e5");
assertXPathContent(mpXmlDoc, "/opf:package/opf:metadata/dc:title", "unknown title from xmp");
assertXPathContent(mpXmlDoc, "/opf:package/opf:metadata/dc:creator", "unknown author from xmp");
assertXPathContent(mpXmlDoc, "/opf:package/opf:metadata/dc:language", "nl");
assertXPathContent(mpXmlDoc, "/opf:package/opf:metadata/opf:meta[@property='dcterms:modified']", "2016-11-20T17:16:07Z");
}
void EPUBExportTest::testCoverImage()
{
OUString aCoverURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "meta.cover-image.png";
......
<?xml version="1.0" encoding="UTF-8"?>
<office:document office:mimetype="application/vnd.oasis.opendocument.text" office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">
<office:body>
<office:text>
<text:p><text:span>Hello world!</text:span></text:p>
</office:text>
</office:body>
</office:document>
<?xml version="1.0" encoding="UTF-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:identifier>deadbeef-e394-4cd6-9b83-7172794612e5</dc:identifier>
<dc:title>
<rdf:Alt>
<rdf:li>unknown title from xmp</rdf:li>
</rdf:Alt>
</dc:title>
<dc:creator>
<rdf:Seq>
<rdf:li>unknown author from xmp</rdf:li>
</rdf:Seq>
</dc:creator>
<dc:language>
<rdf:Bag>
<rdf:li>nl</rdf:li>
</rdf:Bag>
</dc:language>
<dc:date>
<rdf:Seq>
<rdf:li>2016-11-20T17:16:07Z</rdf:li>
</rdf:Seq>
</dc:date>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
......@@ -79,7 +79,7 @@ sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue> &rDe
uno::Reference<frame::XModel> xSourceModel(mxSourceDocument, uno::UNO_QUERY);
if (xSourceModel.is())
aSourceURL = xSourceModel->getURL();
uno::Reference<xml::sax::XDocumentHandler> xExportHandler(new exp::XMLImport(aGenerator, aSourceURL, rDescriptor));
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)});
......
......@@ -12,9 +12,12 @@
#include <initializer_list>
#include <unordered_map>
#include <com/sun/star/xml/sax/InputSource.hpp>
#include <com/sun/star/xml/sax/Parser.hpp>
#include <rtl/uri.hxx>
#include <tools/stream.hxx>
#include <tools/urlobj.hxx>
#include <unotools/streamwrap.hxx>
#include "xmlfmt.hxx"
#include "xmlictxt.hxx"
......@@ -46,26 +49,16 @@ OUString GetMimeType(const OUString &rExtension)
}
/// Picks up a cover image from the base directory.
OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, const uno::Sequence<beans::PropertyValue> &rDescriptor)
OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, const uno::Sequence<beans::PropertyValue> &rFilterData)
{
OUString aRet;
// See if filter data contains a cover image explicitly.
uno::Sequence<beans::PropertyValue> aFilterData;
for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i)
{
if (rDescriptor[i].Name == "FilterData")
{
rDescriptor[i].Value >>= aFilterData;
break;
}
}
for (sal_Int32 i = 0; i < aFilterData.getLength(); ++i)
for (sal_Int32 i = 0; i < rFilterData.getLength(); ++i)
{
if (aFilterData[i].Name == "EPUBCoverImage")
if (rFilterData[i].Name == "EPUBCoverImage")
{
aFilterData[i].Value >>= aRet;
rFilterData[i].Value >>= aRet;
break;
}
}
......@@ -99,7 +92,7 @@ OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, c
}
catch (const rtl::MalformedUriException &rException)
{
SAL_WARN("writerfilter", "FindCoverImage: convertRelToAbs() failed:" << rException.getMessage());
SAL_WARN("writerperfect", "FindCoverImage: convertRelToAbs() failed:" << rException.getMessage());
}
if (!aRet.isEmpty())
......@@ -118,6 +111,57 @@ OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, c
return aRet;
}
/// 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)
{
if (rDocumentBaseURL.isEmpty())
return;
INetURLObject aDocumentBaseURL(rDocumentBaseURL);
OUString aURL;
try
{
aURL = rtl::Uri::convertRelToAbs(rDocumentBaseURL, aDocumentBaseURL.GetBase() + ".xmp");
}
catch (const rtl::MalformedUriException &rException)
{
SAL_WARN("writerperfect", "FindXMPMetadata: convertRelToAbs() failed:" << rException.getMessage());
return;
}
SvFileStream aStream(aURL, StreamMode::READ);
if (!aStream.IsOpen())
return;
xml::sax::InputSource aInputSource;
uno::Reference<io::XInputStream> xStream(new utl::OStreamWrapper(aStream));
aInputSource.aInputStream = xStream;
uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext);
rtl::Reference<XMPParser> xXMP(new XMPParser());
uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xXMP.get());
xParser->setDocumentHandler(xDocumentHandler);
try
{
xParser->parseStream(aInputSource);
}
catch (const uno::Exception &rException)
{
SAL_WARN("writerperfect", "FindXMPMetadata: parseStream() failed:" << rException.Message);
return;
}
if (!xXMP->m_aIdentifier.isEmpty())
rMetaData.insert("dc:identifier", xXMP->m_aIdentifier.toUtf8().getStr());
if (!xXMP->m_aTitle.isEmpty())
rMetaData.insert("dc:title", xXMP->m_aTitle.toUtf8().getStr());
if (!xXMP->m_aCreator.isEmpty())
rMetaData.insert("meta:initial-creator", xXMP->m_aCreator.toUtf8().getStr());
if (!xXMP->m_aLanguage.isEmpty())
rMetaData.insert("dc:language", xXMP->m_aLanguage.toUtf8().getStr());
if (!xXMP->m_aDate.isEmpty())
rMetaData.insert("dc:date", xXMP->m_aDate.toUtf8().getStr());
}
}
/// Handler for <office:body>.
......@@ -170,11 +214,22 @@ rtl::Reference<XMLImportContext> XMLOfficeDocContext::CreateChildContext(const O
return nullptr;
}
XMLImport::XMLImport(librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const uno::Sequence<beans::PropertyValue> &rDescriptor)
: mrGenerator(rGenerator)
XMLImport::XMLImport(const uno::Reference<uno::XComponentContext> &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const uno::Sequence<beans::PropertyValue> &rDescriptor)
: mrGenerator(rGenerator),
mxContext(xContext)
{
uno::Sequence<beans::PropertyValue> aFilterData;
for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i)
{
if (rDescriptor[i].Name == "FilterData")
{
rDescriptor[i].Value >>= aFilterData;
break;
}
}
OUString aMimeType;
OUString aCoverImage = FindCoverImage(rURL, aMimeType, rDescriptor);
OUString aCoverImage = FindCoverImage(rURL, aMimeType, aFilterData);
if (!aCoverImage.isEmpty())
{
librevenge::RVNGBinaryData aBinaryData;
......@@ -187,6 +242,8 @@ XMLImport::XMLImport(librevenge::RVNGTextInterface &rGenerator, const OUString &
aCoverImageProperties.insert("librevenge:mime-type", aMimeType.toUtf8().getStr());
maCoverImages.append(aCoverImageProperties);
}
FindXMPMetadata(mxContext, rURL, aFilterData, maMetaData);
}
const librevenge::RVNGPropertyListVector &XMLImport::GetCoverImages()
......@@ -194,6 +251,11 @@ const librevenge::RVNGPropertyListVector &XMLImport::GetCoverImages()
return maCoverImages;
}
const librevenge::RVNGPropertyList &XMLImport::GetMetaData()
{
return maMetaData;
}
rtl::Reference<XMLImportContext> XMLImport::CreateContext(const OUString &rName, const css::uno::Reference<css::xml::sax::XAttributeList> &/*xAttribs*/)
{
if (rName == "office:document")
......
......@@ -16,6 +16,7 @@
#include <librevenge/librevenge.h>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#include <cppuhelper/implbase.hxx>
......@@ -51,9 +52,12 @@ class XMLImport : public cppu::WeakImplHelper
std::map<OUString, librevenge::RVNGPropertyList> maAutomaticGraphicStyles;
std::map<OUString, librevenge::RVNGPropertyList> maGraphicStyles;
librevenge::RVNGPropertyListVector maCoverImages;
/// 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;
public:
XMLImport(librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const css::uno::Sequence<css::beans::PropertyValue> &rDescriptor);
XMLImport(const css::uno::Reference<css::uno::XComponentContext> &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const css::uno::Sequence<css::beans::PropertyValue> &rDescriptor);
rtl::Reference<XMLImportContext> CreateContext(const OUString &rName, const css::uno::Reference<css::xml::sax::XAttributeList> &xAttribs);
......@@ -73,6 +77,7 @@ public:
std::map<OUString, librevenge::RVNGPropertyList> &GetTableStyles();
std::map<OUString, librevenge::RVNGPropertyList> &GetGraphicStyles();
const librevenge::RVNGPropertyListVector &GetCoverImages();
const librevenge::RVNGPropertyList &GetMetaData();
// XDocumentHandler
void SAL_CALL startDocument() override;
......
......@@ -131,6 +131,9 @@ void XMLMetaInitialCreatorContext::characters(const OUString &rChars)
XMLMetaDocumentContext::XMLMetaDocumentContext(XMLImport &rImport)
: XMLImportContext(rImport)
{
librevenge::RVNGPropertyList::Iter it(mrImport.GetMetaData());
for (it.rewind(); it.next();)
m_aPropertyList.insert(it.key(), it()->clone());
m_aPropertyList.insert("librevenge:cover-images", mrImport.GetCoverImages());
}
......@@ -154,6 +157,94 @@ void XMLMetaDocumentContext::endElement(const OUString &/*rName*/)
mrImport.GetGenerator().setDocumentMetaData(m_aPropertyList);
}
XMPParser::XMPParser() = default;
XMPParser::~XMPParser() = default;
void XMPParser::startDocument()
{
}
void XMPParser::endDocument()
{
}
void XMPParser::startElement(const OUString &rName, const uno::Reference<xml::sax::XAttributeList> &/*xAttribs*/)
{
if (rName == "dc:identifier")
m_bInIdentifier = true;
else if (rName == "dc:title")
m_bInTitle = true;
else if (rName == "dc:creator")
m_bInCreator = true;
else if (rName == "dc:language")
m_bInLanguage = true;
else if (rName == "dc:date")
m_bInDate = true;
else if (rName == "rdf:li")
{
if (m_bInTitle)
m_bInTitleItem = true;
else if (m_bInCreator)
m_bInCreatorItem = true;
else if (m_bInLanguage)
m_bInLanguageItem = true;
else if (m_bInDate)
m_bInDateItem = true;
}
}
void XMPParser::endElement(const OUString &rName)
{
if (rName == "dc:identifier")
m_bInIdentifier = false;
else if (rName == "dc:title")
m_bInTitle = false;
else if (rName == "dc:creator")
m_bInCreator = false;
else if (rName == "dc:language")
m_bInLanguage = false;
else if (rName == "dc:date")
m_bInDate = false;
else if (rName == "rdf:li")
{
if (m_bInTitle)
m_bInTitleItem = false;
else if (m_bInCreator)
m_bInCreatorItem = false;
else if (m_bInLanguage)
m_bInLanguageItem = false;
else if (m_bInDate)
m_bInDateItem = false;
}
}
void XMPParser::characters(const OUString &rChars)
{
if (m_bInIdentifier)
m_aIdentifier += rChars;
else if (m_bInTitleItem)
m_aTitle += rChars;
else if (m_bInCreatorItem)
m_aCreator += rChars;
else if (m_bInLanguageItem)
m_aLanguage += rChars;
else if (m_bInDateItem)
m_aDate += rChars;
}
void XMPParser::ignorableWhitespace(const OUString &/*rWhitespace*/)
{
}
void XMPParser::processingInstruction(const OUString &/*rTarget*/, const OUString &/*rData*/)
{
}
void XMPParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator> &/*xLocator*/)
{
}
} // namespace exp
} // namespace writerperfect
......
......@@ -32,6 +32,51 @@ public:
librevenge::RVNGPropertyList m_aPropertyList;
};
/// Parses an XMP file.
class XMPParser: public cppu::WeakImplHelper
<
css::xml::sax::XDocumentHandler
>
{
public:
explicit XMPParser();
virtual ~XMPParser() override;
// XDocumentHandler
virtual void SAL_CALL startDocument() override;
virtual void SAL_CALL endDocument() override;
virtual void SAL_CALL startElement(const OUString &aName, const css::uno::Reference<css::xml::sax::XAttributeList> &xAttribs) override;
virtual void SAL_CALL endElement(const OUString &aName) override;
virtual void SAL_CALL characters(const OUString &aChars) override;
virtual void SAL_CALL ignorableWhitespace(const OUString &aWhitespaces) override;
virtual void SAL_CALL processingInstruction(const OUString &aTarget, const OUString &aData) override;
virtual void SAL_CALL setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator> &xLocator) override;
OUString m_aIdentifier;
OUString m_aTitle;
OUString m_aCreator;
OUString m_aLanguage;
OUString m_aDate;
private:
bool m_bInIdentifier = false;
bool m_bInTitle = false;
bool m_bInTitleItem = false;
bool m_bInCreator = false;
bool m_bInCreatorItem = false;
bool m_bInLanguage = false;
bool m_bInLanguageItem = false;
bool m_bInDate = false;
bool m_bInDateItem = false;
};
} // namespace exp
} // namespace writerperfect
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment