/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* This file is part of the libepubgen 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 <algorithm>
#include <cassert>
#include <cstring>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <librevenge-stream/librevenge-stream.h>
#include <libepubgen/libepubgen.h>
#include "libepubgen_utils.h"
#include "EPUBGenerator.h"
#include "EPUBTextElements.h"
#include "EPUBPath.h"
using std::shared_ptr;
using librevenge::RVNGBinaryData;
using librevenge::RVNGProperty;
using librevenge::RVNGPropertyFactory;
using librevenge::RVNGPropertyList;
using librevenge::RVNGString;
namespace libepubgen
{
namespace
{
typedef std::unordered_map<std::string, EPUBEmbeddedImage> ImageHandlerMap_t;
const char *CORE_MEDIA_TYPES[] =
{
"image/gif",
"image/jpeg",
"image/png",
"image/svg+xml"
};
/// Determines if this break property a page break one.
bool isPageBreak(const librevenge::RVNGProperty *property)
{
if (!property)
return false;
librevenge::RVNGString str = property->getStr();
return str != "column" && str != "auto";
}
}
struct EPUBTextGenerator::Impl : public EPUBGenerator
{
Impl(EPUBPackage *package, int version);
private:
void startHtmlFile() override;
void endHtmlFile() override;
public:
bool m_inPageSpan;
bool m_inHeader;
bool m_inFooter;
RVNGPropertyList m_pageSpanProps;
shared_ptr<EPUBTextElements> m_currentHeader;
shared_ptr<EPUBTextElements> m_currentFooter;
shared_ptr<EPUBTextElements> m_currentHeaderOrFooter;
ImageHandlerMap_t m_imageHandlers;
bool m_breakAfterPara;
private:
// disable copying
Impl(const Impl &);
Impl &operator=(const Impl &);
};
EPUBTextGenerator::Impl::Impl(EPUBPackage *const package, int version)
: EPUBGenerator(package, version)
, m_inPageSpan(false)
, m_inHeader(false)
, m_inFooter(false)
, m_pageSpanProps()
, m_currentHeader()
, m_currentFooter()
, m_currentHeaderOrFooter()
, m_imageHandlers()
, m_breakAfterPara(false)
{
}
void EPUBTextGenerator::Impl::startHtmlFile()
{
if (m_inPageSpan)
getHtml()->closePageSpan();
}
void EPUBTextGenerator::Impl::endHtmlFile()
{
if (m_inPageSpan)
getHtml()->openPageSpan(m_pageSpanProps);
if (bool(m_currentHeader))
m_currentHeader->write(getHtml().get());
if (bool(m_currentFooter))
m_currentFooter->write(getHtml().get());
}
EPUBTextGenerator::EPUBTextGenerator(EPUBPackage *const package, int version)
: m_impl(new Impl(package, version))
{
}
EPUBTextGenerator::EPUBTextGenerator(EPUBEmbeddingContact &contact, EPUBPackage *const package)
: m_impl(nullptr)
{
// TODO: implement me
(void) contact;
(void) package;
}
EPUBTextGenerator::~EPUBTextGenerator()
{
delete m_impl;
}
void EPUBTextGenerator::setSplitHeadingLevel(const unsigned level)
{
m_impl->getSplitGuard().setSplitHeadingLevel(level);
}
void EPUBTextGenerator::setSplitSize(const unsigned size)
{
m_impl->getSplitGuard().setSplitSize(size);
}
void EPUBTextGenerator::setOption(int key, int value)
{
switch (key)
{
case EPUB_GENERATOR_OPTION_SPLIT:
m_impl->setSplitMethod(static_cast<EPUBSplitMethod>(value));
break;
case EPUB_GENERATOR_OPTION_STYLES:
m_impl->setStylesMethod(static_cast<EPUBStylesMethod>(value));
break;
case EPUB_GENERATOR_OPTION_LAYOUT:
m_impl->setLayoutMethod(static_cast<EPUBLayoutMethod>(value));
break;
}
}
void EPUBTextGenerator::registerEmbeddedImageHandler(const librevenge::RVNGString &mimeType, EPUBEmbeddedImage imageHandler)
{
if (!mimeType.empty() && imageHandler)
m_impl->m_imageHandlers[mimeType.cstr()] = imageHandler;
}
void EPUBTextGenerator::registerEmbeddedObjectHandler(const librevenge::RVNGString &mimeType, EPUBEmbeddedObject objectHandler)
{
// TODO: implement me
(void) mimeType;
(void) objectHandler;
}
void EPUBTextGenerator::setDocumentMetaData(const librevenge::RVNGPropertyList &propList)
{
m_impl->setDocumentMetaData(propList);
m_impl->getHtml()->setDocumentMetaData(propList);
}
void EPUBTextGenerator::startDocument(const librevenge::RVNGPropertyList &propList)
{
m_impl->startDocument(propList);
}
void EPUBTextGenerator::endDocument()
{
m_impl->endDocument();
}
void EPUBTextGenerator::definePageStyle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->definePageStyle(propList);
}
void EPUBTextGenerator::defineEmbeddedFont(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->defineEmbeddedFont(propList);
}
void EPUBTextGenerator::openPageSpan(const librevenge::RVNGPropertyList &propList)
{
assert(!m_impl->m_inPageSpan);
m_impl->m_inPageSpan = true;
m_impl->m_pageSpanProps = propList;
m_impl->getHtml()->openPageSpan(propList);
}
void EPUBTextGenerator::closePageSpan()
{
assert(m_impl->m_inPageSpan);
m_impl->m_inPageSpan = false;
m_impl->m_pageSpanProps = RVNGPropertyList();
m_impl->getHtml()->closePageSpan();
}
void EPUBTextGenerator::openHeader(const librevenge::RVNGPropertyList &propList)
{
m_impl->m_inHeader = true;
m_impl->m_currentHeader.reset(new EPUBTextElements());
m_impl->m_currentHeaderOrFooter = m_impl->m_currentHeader;
m_impl->m_currentHeaderOrFooter->addOpenHeader(propList);
m_impl->getHtml()->openHeader(propList);
}
void EPUBTextGenerator::closeHeader()
{
m_impl->m_inHeader = false;
m_impl->m_currentHeaderOrFooter->addCloseHeader();
m_impl->m_currentHeaderOrFooter.reset();
m_impl->getHtml()->closeHeader();
}
void EPUBTextGenerator::openFooter(const librevenge::RVNGPropertyList &propList)
{
m_impl->m_inFooter = true;
m_impl->m_currentFooter.reset(new EPUBTextElements());
m_impl->m_currentHeaderOrFooter = m_impl->m_currentFooter;
m_impl->m_currentHeaderOrFooter->addOpenHeader(propList);
m_impl->getHtml()->openFooter(propList);
}
void EPUBTextGenerator::closeFooter()
{
m_impl->m_inFooter = false;
m_impl->m_currentHeaderOrFooter->addCloseFooter();
m_impl->m_currentHeaderOrFooter.reset();
m_impl->getHtml()->closeFooter();
}
void EPUBTextGenerator::defineParagraphStyle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->defineParagraphStyle(propList);
}
void EPUBTextGenerator::openParagraph(const librevenge::RVNGPropertyList &propList)
{
const RVNGProperty *const breakBefore = propList["fo:break-before"];
if (isPageBreak(breakBefore) && m_impl->getSplitGuard().splitOnPageBreak())
m_impl->startNewHtmlFile();
const RVNGProperty *const breakAfter = propList["fo:break-after"];
m_impl->m_breakAfterPara = isPageBreak(breakAfter);
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
// Handle split by chapters.
const RVNGProperty *const outlineLevel = propList["text:outline-level"];
if (outlineLevel && m_impl->getSplitGuard().splitOnHeading(outlineLevel->getInt()))
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().setCurrentHeadingLevel(outlineLevel ? outlineLevel->getInt() : 0);
m_impl->getSplitGuard().openLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenParagraph(propList);
m_impl->getHtml()->openParagraph(propList);
}
void EPUBTextGenerator::closeParagraph()
{
m_impl->getSplitGuard().closeLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseParagraph();
m_impl->getHtml()->closeParagraph();
if (m_impl->m_breakAfterPara && m_impl->getSplitGuard().splitOnPageBreak())
m_impl->startNewHtmlFile();
m_impl->m_breakAfterPara = false;
}
void EPUBTextGenerator::defineCharacterStyle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->defineCharacterStyle(propList);
}
void EPUBTextGenerator::openSpan(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenSpan(propList);
m_impl->getHtml()->openSpan(propList);
}
void EPUBTextGenerator::closeSpan()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseSpan();
m_impl->getHtml()->closeSpan();
}
void EPUBTextGenerator::openLink(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenLink(propList);
m_impl->getHtml()->openLink(propList);
}
void EPUBTextGenerator::closeLink()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseLink();
m_impl->getHtml()->closeLink();
}
void EPUBTextGenerator::defineSectionStyle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->defineSectionStyle(propList);
}
void EPUBTextGenerator::openSection(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getHtml()->openSection(propList);
}
void EPUBTextGenerator::closeSection()
{
m_impl->getHtml()->closeSection();
}
void EPUBTextGenerator::insertTab()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertTab();
m_impl->getSplitGuard().incrementSize(1);
m_impl->getHtml()->insertTab();
}
void EPUBTextGenerator::insertSpace()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertSpace();
m_impl->getSplitGuard().incrementSize(1);
m_impl->getHtml()->insertSpace();
}
void EPUBTextGenerator::insertText(const librevenge::RVNGString &text)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertText(text);
if (m_impl->getSplitGuard().inHeading(!m_impl->getHtmlManager().hasHeadingText()))
m_impl->getHtmlManager().insertHeadingText(text.cstr());
m_impl->getSplitGuard().incrementSize(text.len());
m_impl->getHtml()->insertText(text);
}
void EPUBTextGenerator::insertLineBreak()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertLineBreak();
m_impl->getSplitGuard().incrementSize(1);
m_impl->getHtml()->insertLineBreak();
}
void EPUBTextGenerator::insertField(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertField(propList);
m_impl->getHtml()->insertField(propList);
}
void EPUBTextGenerator::openOrderedListLevel(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenOrderedListLevel(propList);
m_impl->getHtml()->openOrderedListLevel(propList);
}
void EPUBTextGenerator::openUnorderedListLevel(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenUnorderedListLevel(propList);
m_impl->getHtml()->openUnorderedListLevel(propList);
}
void EPUBTextGenerator::closeOrderedListLevel()
{
m_impl->getSplitGuard().closeLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseOrderedListLevel();
m_impl->getHtml()->closeOrderedListLevel();
}
void EPUBTextGenerator::closeUnorderedListLevel()
{
m_impl->getSplitGuard().closeLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseUnorderedListLevel();
m_impl->getHtml()->closeUnorderedListLevel();
}
void EPUBTextGenerator::openListElement(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenListElement(propList);
m_impl->getHtml()->openListElement(propList);
}
void EPUBTextGenerator::closeListElement()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseListElement();
m_impl->getHtml()->closeListElement();
}
void EPUBTextGenerator::openFootnote(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenFootnote(propList);
m_impl->getHtml()->openFootnote(propList);
}
void EPUBTextGenerator::closeFootnote()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseFootnote();
m_impl->getHtml()->closeFootnote();
}
void EPUBTextGenerator::openEndnote(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenEndnote(propList);
m_impl->getHtml()->openEndnote(propList);
}
void EPUBTextGenerator::closeEndnote()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseEndnote();
m_impl->getHtml()->closeEndnote();
}
void EPUBTextGenerator::openComment(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenComment(propList);
m_impl->getHtml()->openComment(propList);
}
void EPUBTextGenerator::closeComment()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseComment();
m_impl->getHtml()->closeComment();
}
void EPUBTextGenerator::openTextBox(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
m_impl->getHtml()->openTextBox(propList);
}
void EPUBTextGenerator::closeTextBox()
{
m_impl->getSplitGuard().closeLevel();
m_impl->getHtml()->closeTextBox();
}
void EPUBTextGenerator::openTable(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenTable(propList);
m_impl->getHtml()->openTable(propList);
}
void EPUBTextGenerator::openTableRow(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenTableRow(propList);
m_impl->getHtml()->openTableRow(propList);
}
void EPUBTextGenerator::closeTableRow()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseTableRow();
m_impl->getHtml()->closeTableRow();
}
void EPUBTextGenerator::openTableCell(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addOpenTableCell(propList);
m_impl->getHtml()->openTableCell(propList);
}
void EPUBTextGenerator::closeTableCell()
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseTableCell();
m_impl->getHtml()->closeTableCell();
}
void EPUBTextGenerator::insertCoveredTableCell(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertCoveredTableCell(propList);
m_impl->getHtml()->insertCoveredTableCell(propList);
}
void EPUBTextGenerator::closeTable()
{
m_impl->getSplitGuard().closeLevel();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addCloseTable();
m_impl->getHtml()->closeTable();
}
void EPUBTextGenerator::openFrame(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
m_impl->getHtml()->openFrame(propList);
}
void EPUBTextGenerator::closeFrame()
{
m_impl->getSplitGuard().closeLevel();
m_impl->getHtml()->closeFrame();
}
/// Checks if the media type is an EPUB 3 Core Media Type or not.
static bool isValidMimeType(const RVNGString &mediaType)
{
// Defined at <https://idpf.github.io/epub-cmt/v3/#sec-cmt-supported>.
static char const *(types[])=
{
"image/gif",
"image/png",
"image/jpeg",
"image/svg+xml"
};
for (const auto &i : types)
{
if (mediaType == i)
return true;
}
return false;
}
void EPUBTextGenerator::insertBinaryObject(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
RVNGPropertyList newPropList;
shared_ptr<RVNGProperty> mimetype;
shared_ptr<RVNGProperty> data;
for (RVNGPropertyList::Iter iter(propList); !iter.last(); iter.next())
{
if (RVNGString("librevenge:mime-type") == iter.key())
{
if (isValidMimeType(iter()->getStr()))
mimetype.reset(iter()->clone());
}
else if (RVNGString("office:binary-data") == iter.key())
data.reset(iter()->clone());
else
newPropList.insert(iter.key(), iter()->clone());
}
if (!mimetype || mimetype->getStr().empty() || !data)
{
EPUBGEN_DEBUG_MSG(("invalid binary object dropped"));
return;
}
const ImageHandlerMap_t::const_iterator it = m_impl->m_imageHandlers.find(mimetype->getStr().cstr());
if (m_impl->m_imageHandlers.end() != it)
{
RVNGBinaryData outData;
EPUBImageType outType;
const EPUBEmbeddedImage imageHandler = it->second;
if (imageHandler(RVNGBinaryData(data->getStr()), outData, outType))
{
mimetype.reset(RVNGPropertyFactory::newStringProp(CORE_MEDIA_TYPES[outType]));
data.reset(RVNGPropertyFactory::newBinaryDataProp(outData));
}
else
{
EPUBGEN_DEBUG_MSG(("image conversion failed"));
}
}
newPropList.insert("librevenge:mime-type", mimetype->clone());
newPropList.insert("office:binary-data", data->clone());
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertBinaryObject(newPropList);
m_impl->getSplitGuard().incrementSize(1);
m_impl->getHtml()->insertBinaryObject(newPropList);
}
void EPUBTextGenerator::insertEquation(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
if (m_impl->m_inHeader || m_impl->m_inFooter)
m_impl->m_currentHeaderOrFooter->addInsertEquation(propList);
m_impl->getHtml()->insertEquation(propList);
}
void EPUBTextGenerator::openGroup(const librevenge::RVNGPropertyList &propList)
{
if (m_impl->getSplitGuard().splitOnSize())
m_impl->startNewHtmlFile();
m_impl->getSplitGuard().openLevel();
m_impl->getHtml()->openGroup(propList);
}
void EPUBTextGenerator::closeGroup()
{
m_impl->getSplitGuard().closeLevel();
m_impl->getHtml()->closeGroup();
}
void EPUBTextGenerator::defineGraphicStyle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->defineGraphicStyle(propList);
}
void EPUBTextGenerator::drawRectangle(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawRectangle(propList);
}
void EPUBTextGenerator::drawEllipse(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawEllipse(propList);
}
void EPUBTextGenerator::drawPolygon(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawPolygon(propList);
}
void EPUBTextGenerator::drawPolyline(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawPolyline(propList);
}
void EPUBTextGenerator::drawPath(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawPath(propList);
}
void EPUBTextGenerator::drawConnector(const librevenge::RVNGPropertyList &propList)
{
m_impl->getHtml()->drawConnector(propList);
}
} // namespace libepubgen
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */