Recognize and use printed pages info
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <JpegToBmpConverter.h>
|
||||
#include <Logging.h>
|
||||
#include <PngToBmpConverter.h>
|
||||
#include <Serialization.h>
|
||||
#include <ZipFile.h>
|
||||
|
||||
#include <cctype>
|
||||
@@ -13,11 +14,37 @@
|
||||
|
||||
#include "Epub/parsers/ContainerParser.h"
|
||||
#include "Epub/parsers/ContentOpfParser.h"
|
||||
#include "Epub/parsers/PageMapParser.h"
|
||||
#include "Epub/parsers/TocNavParser.h"
|
||||
#include "Epub/parsers/TocNcxParser.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Serialise a list of printed-page entries (href, anchor, label) to pagelist.bin in the
|
||||
// book cache. Templated on the parser's entry type so both NCX <pageList> and EPUB 3
|
||||
// <nav epub:type="page-list"> share the same writer.
|
||||
template <typename Entry>
|
||||
void writePageListBin(const std::string& cachePath, const std::vector<Entry>& pageList) {
|
||||
if (pageList.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto pageListPath = cachePath + "/pagelist.bin";
|
||||
FsFile pageListFile;
|
||||
if (!Storage.openFileForWrite("EBP", pageListPath, pageListFile)) {
|
||||
LOG_ERR("EBP", "Could not write pagelist.bin");
|
||||
return;
|
||||
}
|
||||
serialization::writePod(pageListFile, static_cast<uint16_t>(pageList.size()));
|
||||
for (const auto& entry : pageList) {
|
||||
serialization::writeString(pageListFile, entry.href);
|
||||
serialization::writeString(pageListFile, entry.anchor);
|
||||
serialization::writeString(pageListFile, entry.label);
|
||||
}
|
||||
pageListFile.flush();
|
||||
pageListFile.close();
|
||||
LOG_DBG("EBP", "Wrote pagelist.bin with %u entries", static_cast<unsigned>(pageList.size()));
|
||||
}
|
||||
|
||||
enum class CoverImageFormat { Unknown, Jpeg, Png };
|
||||
|
||||
CoverImageFormat detectCoverImageFormat(FsFile& imageFile) {
|
||||
@@ -311,6 +338,10 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCac
|
||||
tocNavItem = opfParser.tocNavPath;
|
||||
}
|
||||
|
||||
if (!opfParser.pageMapPath.empty()) {
|
||||
pageMapItem = opfParser.pageMapPath;
|
||||
}
|
||||
|
||||
if (!opfParser.cssFiles.empty()) {
|
||||
cssFiles = opfParser.cssFiles;
|
||||
}
|
||||
@@ -373,6 +404,12 @@ bool Epub::parseTocNcxFile() const {
|
||||
tempNcxFile.close();
|
||||
Storage.remove(tmpNcxPath.c_str());
|
||||
|
||||
// Persist the printed-page list (NCX <pageList>) to a small cache file so the
|
||||
// section builder can stamp printed-page labels onto rendered pages without
|
||||
// re-parsing the NCX. Format: u16 count, then per entry: writeString(href),
|
||||
// writeString(anchor), writeString(label).
|
||||
writePageListBin(getCachePath(), ncxParser.getPageList());
|
||||
|
||||
LOG_DBG("EBP", "Parsed TOC items");
|
||||
return true;
|
||||
}
|
||||
@@ -430,10 +467,74 @@ bool Epub::parseTocNavFile() const {
|
||||
tempNavFile.close();
|
||||
Storage.remove(tmpNavPath.c_str());
|
||||
|
||||
// Persist EPUB 3 <nav epub:type="page-list"> entries to pagelist.bin (same format
|
||||
// as the NCX writer); the section builder consumes either source uniformly.
|
||||
writePageListBin(getCachePath(), navParser.getPageList());
|
||||
|
||||
LOG_DBG("EBP", "Parsed TOC nav items");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Epub::parsePageMapFile() const {
|
||||
// EPUB 2.01 page-map.xml: a separate top-level manifest item with media-type
|
||||
// "application/oebps-page-map+xml". Structure is a flat list of <page name="..." href="..."/>.
|
||||
if (pageMapItem.empty()) {
|
||||
LOG_DBG("EBP", "No page-map file specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("EBP", "Parsing page-map file: %s", pageMapItem.c_str());
|
||||
|
||||
const auto tmpPageMapPath = getCachePath() + "/page-map.xml";
|
||||
FsFile tempPageMapFile;
|
||||
if (!Storage.openFileForWrite("EBP", tmpPageMapPath, tempPageMapFile)) {
|
||||
return false;
|
||||
}
|
||||
readItemContentsToStream(pageMapItem, tempPageMapFile, 1024);
|
||||
tempPageMapFile.close();
|
||||
if (!Storage.openFileForRead("EBP", tmpPageMapPath, tempPageMapFile)) {
|
||||
return false;
|
||||
}
|
||||
const auto pageMapSize = tempPageMapFile.size();
|
||||
|
||||
// page-map hrefs are relative to the page-map file itself (typically content.opf's dir).
|
||||
const std::string pageMapBasePath = pageMapItem.substr(0, pageMapItem.find_last_of('/') + 1);
|
||||
PageMapParser pageMapParser(pageMapBasePath, pageMapSize);
|
||||
|
||||
if (!pageMapParser.setup()) {
|
||||
LOG_ERR("EBP", "Could not setup page-map parser");
|
||||
tempPageMapFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto pageMapBuffer = static_cast<uint8_t*>(malloc(1024));
|
||||
if (!pageMapBuffer) {
|
||||
LOG_ERR("EBP", "Could not allocate memory for page-map parser");
|
||||
tempPageMapFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
while (tempPageMapFile.available()) {
|
||||
const auto readSize = tempPageMapFile.read(pageMapBuffer, 1024);
|
||||
if (readSize == 0) break;
|
||||
const auto processedSize = pageMapParser.write(pageMapBuffer, readSize);
|
||||
if (processedSize != readSize) {
|
||||
LOG_ERR("EBP", "Could not process all page-map data");
|
||||
free(pageMapBuffer);
|
||||
tempPageMapFile.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
free(pageMapBuffer);
|
||||
tempPageMapFile.close();
|
||||
Storage.remove(tmpPageMapPath.c_str());
|
||||
|
||||
writePageListBin(getCachePath(), pageMapParser.getPageList());
|
||||
LOG_DBG("EBP", "Parsed page-map entries");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Epub::parseCssFiles() const {
|
||||
// Maximum CSS file size we'll attempt to parse (uncompressed)
|
||||
// Larger files risk memory exhaustion on ESP32
|
||||
@@ -607,6 +708,17 @@ bool Epub::load(const bool buildIfMissing, const bool skipLoadingCss) {
|
||||
// Continue anyway - book will work without TOC
|
||||
}
|
||||
|
||||
// EPUB 2.01 page-map.xml — only parse if neither NCX <pageList> nor nav page-list
|
||||
// wrote a pagelist.bin already (so an explicit NCX/nav printed-page list always wins).
|
||||
if (!pageMapItem.empty()) {
|
||||
const auto pageListPath = getCachePath() + "/pagelist.bin";
|
||||
if (!Storage.exists(pageListPath.c_str())) {
|
||||
parsePageMapFile();
|
||||
} else {
|
||||
LOG_DBG("EBP", "page-map.xml present but pagelist.bin already written by NCX/nav; skipping");
|
||||
}
|
||||
}
|
||||
|
||||
if (!bookMetadataCache->endTocPass()) {
|
||||
LOG_ERR("EBP", "Could not end writing toc pass");
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user