feat: parse and display all available EPUB metadata fields
Add parsing for dc:publisher, dc:date, dc:subject, dc:rights, dc:contributor, dc:identifier (prefers ISBN scheme), and calibre:rating. All new fields serialized in BookMetadataCache (version bumped to 7) and displayed in BookInfoActivity with rating shown as N/5 scale. Made-with: Cursor
This commit is contained in:
@@ -81,6 +81,13 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata) {
|
|||||||
bookMetadata.series = opfParser.series;
|
bookMetadata.series = opfParser.series;
|
||||||
bookMetadata.seriesIndex = opfParser.seriesIndex;
|
bookMetadata.seriesIndex = opfParser.seriesIndex;
|
||||||
bookMetadata.description = opfParser.description;
|
bookMetadata.description = opfParser.description;
|
||||||
|
bookMetadata.publisher = opfParser.publisher;
|
||||||
|
bookMetadata.date = opfParser.date;
|
||||||
|
bookMetadata.subjects = opfParser.subjects;
|
||||||
|
bookMetadata.rights = opfParser.rights;
|
||||||
|
bookMetadata.contributor = opfParser.contributor;
|
||||||
|
bookMetadata.identifier = opfParser.identifier;
|
||||||
|
bookMetadata.rating = opfParser.rating;
|
||||||
|
|
||||||
// Guide-based cover fallback: if no cover found via metadata/properties,
|
// Guide-based cover fallback: if no cover found via metadata/properties,
|
||||||
// try extracting the image reference from the guide's cover page XHTML
|
// try extracting the image reference from the guide's cover page XHTML
|
||||||
@@ -547,6 +554,48 @@ const std::string& Epub::getDescription() const {
|
|||||||
return bookMetadataCache->coreMetadata.description;
|
return bookMetadataCache->coreMetadata.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getPublisher() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.publisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getDate() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getSubjects() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.subjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getRights() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.rights;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getContributor() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.contributor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getIdentifier() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Epub::getRating() const {
|
||||||
|
static std::string blank;
|
||||||
|
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return blank;
|
||||||
|
return bookMetadataCache->coreMetadata.rating;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Epub::getCoverBmpPath(bool cropped) const {
|
std::string Epub::getCoverBmpPath(bool cropped) const {
|
||||||
const auto coverFileName = std::string("cover") + (cropped ? "_crop" : "");
|
const auto coverFileName = std::string("cover") + (cropped ? "_crop" : "");
|
||||||
return cachePath + "/" + coverFileName + ".bmp";
|
return cachePath + "/" + coverFileName + ".bmp";
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ class Epub {
|
|||||||
const std::string& getSeries() const;
|
const std::string& getSeries() const;
|
||||||
const std::string& getSeriesIndex() const;
|
const std::string& getSeriesIndex() const;
|
||||||
const std::string& getDescription() const;
|
const std::string& getDescription() const;
|
||||||
|
const std::string& getPublisher() const;
|
||||||
|
const std::string& getDate() const;
|
||||||
|
const std::string& getSubjects() const;
|
||||||
|
const std::string& getRights() const;
|
||||||
|
const std::string& getContributor() const;
|
||||||
|
const std::string& getIdentifier() const;
|
||||||
|
const std::string& getRating() const;
|
||||||
std::string getCoverBmpPath(bool cropped = false) const;
|
std::string getCoverBmpPath(bool cropped = false) const;
|
||||||
bool generateCoverBmp(bool cropped = false) const;
|
bool generateCoverBmp(bool cropped = false) const;
|
||||||
std::string getThumbBmpPath() const;
|
std::string getThumbBmpPath() const;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "FsHelpers.h"
|
#include "FsHelpers.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t BOOK_CACHE_VERSION = 6;
|
constexpr uint8_t BOOK_CACHE_VERSION = 7;
|
||||||
constexpr char bookBinFile[] = "/book.bin";
|
constexpr char bookBinFile[] = "/book.bin";
|
||||||
constexpr char tmpSpineBinFile[] = "/spine.bin.tmp";
|
constexpr char tmpSpineBinFile[] = "/spine.bin.tmp";
|
||||||
constexpr char tmpTocBinFile[] = "/toc.bin.tmp";
|
constexpr char tmpTocBinFile[] = "/toc.bin.tmp";
|
||||||
@@ -118,7 +118,9 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
|
|||||||
const uint32_t metadataSize = metadata.title.size() + metadata.author.size() + metadata.language.size() +
|
const uint32_t metadataSize = metadata.title.size() + metadata.author.size() + metadata.language.size() +
|
||||||
metadata.coverItemHref.size() + metadata.textReferenceHref.size() +
|
metadata.coverItemHref.size() + metadata.textReferenceHref.size() +
|
||||||
metadata.series.size() + metadata.seriesIndex.size() + metadata.description.size() +
|
metadata.series.size() + metadata.seriesIndex.size() + metadata.description.size() +
|
||||||
sizeof(uint32_t) * 8;
|
metadata.publisher.size() + metadata.date.size() + metadata.subjects.size() +
|
||||||
|
metadata.rights.size() + metadata.contributor.size() + metadata.identifier.size() +
|
||||||
|
metadata.rating.size() + sizeof(uint32_t) * 15;
|
||||||
const uint32_t lutSize = sizeof(uint32_t) * spineCount + sizeof(uint32_t) * tocCount;
|
const uint32_t lutSize = sizeof(uint32_t) * spineCount + sizeof(uint32_t) * tocCount;
|
||||||
const uint32_t lutOffset = headerASize + metadataSize;
|
const uint32_t lutOffset = headerASize + metadataSize;
|
||||||
|
|
||||||
@@ -136,6 +138,13 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
|
|||||||
serialization::writeString(bookFile, metadata.series);
|
serialization::writeString(bookFile, metadata.series);
|
||||||
serialization::writeString(bookFile, metadata.seriesIndex);
|
serialization::writeString(bookFile, metadata.seriesIndex);
|
||||||
serialization::writeString(bookFile, metadata.description);
|
serialization::writeString(bookFile, metadata.description);
|
||||||
|
serialization::writeString(bookFile, metadata.publisher);
|
||||||
|
serialization::writeString(bookFile, metadata.date);
|
||||||
|
serialization::writeString(bookFile, metadata.subjects);
|
||||||
|
serialization::writeString(bookFile, metadata.rights);
|
||||||
|
serialization::writeString(bookFile, metadata.contributor);
|
||||||
|
serialization::writeString(bookFile, metadata.identifier);
|
||||||
|
serialization::writeString(bookFile, metadata.rating);
|
||||||
|
|
||||||
// Loop through spine entries, writing LUT positions
|
// Loop through spine entries, writing LUT positions
|
||||||
spineFile.seek(0);
|
spineFile.seek(0);
|
||||||
@@ -392,7 +401,14 @@ bool BookMetadataCache::load() {
|
|||||||
!serialization::readString(bookFile, coreMetadata.textReferenceHref) ||
|
!serialization::readString(bookFile, coreMetadata.textReferenceHref) ||
|
||||||
!serialization::readString(bookFile, coreMetadata.series) ||
|
!serialization::readString(bookFile, coreMetadata.series) ||
|
||||||
!serialization::readString(bookFile, coreMetadata.seriesIndex) ||
|
!serialization::readString(bookFile, coreMetadata.seriesIndex) ||
|
||||||
!serialization::readString(bookFile, coreMetadata.description)) {
|
!serialization::readString(bookFile, coreMetadata.description) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.publisher) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.date) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.subjects) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.rights) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.contributor) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.identifier) ||
|
||||||
|
!serialization::readString(bookFile, coreMetadata.rating)) {
|
||||||
LOG_ERR("BMC", "Failed to read metadata strings from cache");
|
LOG_ERR("BMC", "Failed to read metadata strings from cache");
|
||||||
bookFile.close();
|
bookFile.close();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ class BookMetadataCache {
|
|||||||
std::string series;
|
std::string series;
|
||||||
std::string seriesIndex;
|
std::string seriesIndex;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
std::string publisher;
|
||||||
|
std::string date;
|
||||||
|
std::string subjects;
|
||||||
|
std::string rights;
|
||||||
|
std::string contributor;
|
||||||
|
std::string identifier;
|
||||||
|
std::string rating;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpineEntry {
|
struct SpineEntry {
|
||||||
|
|||||||
@@ -158,6 +158,59 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:publisher") == 0) {
|
||||||
|
if (self->publisher.empty()) {
|
||||||
|
self->state = IN_BOOK_PUBLISHER;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:date") == 0) {
|
||||||
|
if (self->date.empty()) {
|
||||||
|
self->state = IN_BOOK_DATE;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:subject") == 0) {
|
||||||
|
if (!self->subjects.empty()) {
|
||||||
|
self->subjects += ", ";
|
||||||
|
}
|
||||||
|
self->state = IN_BOOK_SUBJECT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:rights") == 0) {
|
||||||
|
if (self->rights.empty()) {
|
||||||
|
self->state = IN_BOOK_RIGHTS;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:contributor") == 0) {
|
||||||
|
if (!self->contributor.empty()) {
|
||||||
|
self->contributor += ", ";
|
||||||
|
}
|
||||||
|
self->state = IN_BOOK_CONTRIBUTOR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_METADATA && strcmp(name, "dc:identifier") == 0) {
|
||||||
|
self->identifierIsIsbn = false;
|
||||||
|
for (int i = 0; atts[i]; i += 2) {
|
||||||
|
if (strcmp(atts[i], "opf:scheme") == 0 && strcasecmp(atts[i + 1], "ISBN") == 0) {
|
||||||
|
self->identifierIsIsbn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self->identifier.empty() || self->identifierIsIsbn) {
|
||||||
|
if (self->identifierIsIsbn) {
|
||||||
|
self->identifier.clear();
|
||||||
|
}
|
||||||
|
self->state = IN_BOOK_IDENTIFIER;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) {
|
if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) {
|
||||||
self->state = IN_MANIFEST;
|
self->state = IN_MANIFEST;
|
||||||
if (!Storage.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
|
if (!Storage.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
|
||||||
@@ -221,6 +274,8 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
self->series = trim(std::string(metaContent, std::min(strlen(metaContent), MAX_DESCRIPTION_LENGTH)));
|
self->series = trim(std::string(metaContent, std::min(strlen(metaContent), MAX_DESCRIPTION_LENGTH)));
|
||||||
} else if (strcmp(metaName, "calibre:series_index") == 0 && self->seriesIndex.empty()) {
|
} else if (strcmp(metaName, "calibre:series_index") == 0 && self->seriesIndex.empty()) {
|
||||||
self->seriesIndex = trim(std::string(metaContent, std::min(strlen(metaContent), MAX_DESCRIPTION_LENGTH)));
|
self->seriesIndex = trim(std::string(metaContent, std::min(strlen(metaContent), MAX_DESCRIPTION_LENGTH)));
|
||||||
|
} else if (strcmp(metaName, "calibre:rating") == 0 && self->rating.empty()) {
|
||||||
|
self->rating = trim(std::string(metaContent, std::min(strlen(metaContent), static_cast<size_t>(8))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,6 +494,42 @@ void XMLCALL ContentOpfParser::characterData(void* userData, const XML_Char* s,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_PUBLISHER) {
|
||||||
|
self->publisher.append(s, std::min(static_cast<size_t>(len), MAX_DESCRIPTION_LENGTH - self->publisher.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_DATE) {
|
||||||
|
self->date.append(s, std::min(static_cast<size_t>(len), MAX_DESCRIPTION_LENGTH - self->date.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_SUBJECT) {
|
||||||
|
const size_t remaining = MAX_DESCRIPTION_LENGTH - self->subjects.size();
|
||||||
|
if (remaining > 0) {
|
||||||
|
self->subjects.append(s, std::min(static_cast<size_t>(len), remaining));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_RIGHTS) {
|
||||||
|
self->rights.append(s, std::min(static_cast<size_t>(len), MAX_DESCRIPTION_LENGTH - self->rights.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_CONTRIBUTOR) {
|
||||||
|
const size_t remaining = MAX_DESCRIPTION_LENGTH - self->contributor.size();
|
||||||
|
if (remaining > 0) {
|
||||||
|
self->contributor.append(s, std::min(static_cast<size_t>(len), remaining));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_IDENTIFIER) {
|
||||||
|
self->identifier.append(s, std::min(static_cast<size_t>(len), MAX_DESCRIPTION_LENGTH - self->identifier.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name) {
|
void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name) {
|
||||||
@@ -496,6 +587,40 @@ void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_PUBLISHER && strcmp(name, "dc:publisher") == 0) {
|
||||||
|
self->publisher = trim(self->publisher);
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_DATE && strcmp(name, "dc:date") == 0) {
|
||||||
|
self->date = trim(self->date);
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_SUBJECT && strcmp(name, "dc:subject") == 0) {
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_RIGHTS && strcmp(name, "dc:rights") == 0) {
|
||||||
|
self->rights = trim(self->rights);
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_CONTRIBUTOR && strcmp(name, "dc:contributor") == 0) {
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_BOOK_IDENTIFIER && strcmp(name, "dc:identifier") == 0) {
|
||||||
|
self->identifier = trim(self->identifier);
|
||||||
|
self->state = IN_METADATA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->state == IN_METADATA && (strcmp(name, "metadata") == 0 || strcmp(name, "opf:metadata") == 0)) {
|
if (self->state == IN_METADATA && (strcmp(name, "metadata") == 0 || strcmp(name, "opf:metadata") == 0)) {
|
||||||
self->state = IN_PACKAGE;
|
self->state = IN_PACKAGE;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ class ContentOpfParser final : public Print {
|
|||||||
IN_BOOK_DESCRIPTION,
|
IN_BOOK_DESCRIPTION,
|
||||||
IN_BOOK_SERIES,
|
IN_BOOK_SERIES,
|
||||||
IN_BOOK_SERIES_INDEX,
|
IN_BOOK_SERIES_INDEX,
|
||||||
|
IN_BOOK_PUBLISHER,
|
||||||
|
IN_BOOK_DATE,
|
||||||
|
IN_BOOK_SUBJECT,
|
||||||
|
IN_BOOK_RIGHTS,
|
||||||
|
IN_BOOK_CONTRIBUTOR,
|
||||||
|
IN_BOOK_IDENTIFIER,
|
||||||
IN_MANIFEST,
|
IN_MANIFEST,
|
||||||
IN_SPINE,
|
IN_SPINE,
|
||||||
IN_GUIDE,
|
IN_GUIDE,
|
||||||
@@ -33,6 +39,7 @@ class ContentOpfParser final : public Print {
|
|||||||
BookMetadataCache* cache;
|
BookMetadataCache* cache;
|
||||||
FsFile tempItemStore;
|
FsFile tempItemStore;
|
||||||
std::string coverItemId;
|
std::string coverItemId;
|
||||||
|
bool identifierIsIsbn = false;
|
||||||
|
|
||||||
// Index for fast idref→href lookup (used only for large EPUBs)
|
// Index for fast idref→href lookup (used only for large EPUBs)
|
||||||
struct ItemIndexEntry {
|
struct ItemIndexEntry {
|
||||||
@@ -66,6 +73,13 @@ class ContentOpfParser final : public Print {
|
|||||||
std::string series;
|
std::string series;
|
||||||
std::string seriesIndex;
|
std::string seriesIndex;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
std::string publisher;
|
||||||
|
std::string date;
|
||||||
|
std::string subjects;
|
||||||
|
std::string rights;
|
||||||
|
std::string contributor;
|
||||||
|
std::string identifier;
|
||||||
|
std::string rating;
|
||||||
std::string tocNcxPath;
|
std::string tocNcxPath;
|
||||||
std::string tocNavPath; // EPUB 3 nav document path
|
std::string tocNavPath; // EPUB 3 nav document path
|
||||||
std::string coverItemHref;
|
std::string coverItemHref;
|
||||||
|
|||||||
@@ -452,6 +452,13 @@ enum class StrId : uint16_t {
|
|||||||
STR_SERIES,
|
STR_SERIES,
|
||||||
STR_FILE_SIZE,
|
STR_FILE_SIZE,
|
||||||
STR_DESCRIPTION,
|
STR_DESCRIPTION,
|
||||||
|
STR_PUBLISHER,
|
||||||
|
STR_DATE,
|
||||||
|
STR_SUBJECTS,
|
||||||
|
STR_RATING,
|
||||||
|
STR_ISBN,
|
||||||
|
STR_RIGHTS,
|
||||||
|
STR_CONTRIBUTOR,
|
||||||
STR_MANAGE,
|
STR_MANAGE,
|
||||||
STR_INFO,
|
STR_INFO,
|
||||||
STR_ARCHIVE_BOOK,
|
STR_ARCHIVE_BOOK,
|
||||||
|
|||||||
@@ -399,6 +399,13 @@ STR_AUTHOR: "Author"
|
|||||||
STR_SERIES: "Series"
|
STR_SERIES: "Series"
|
||||||
STR_FILE_SIZE: "File Size"
|
STR_FILE_SIZE: "File Size"
|
||||||
STR_DESCRIPTION: "Description"
|
STR_DESCRIPTION: "Description"
|
||||||
|
STR_PUBLISHER: "Publisher"
|
||||||
|
STR_DATE: "Date"
|
||||||
|
STR_SUBJECTS: "Subjects"
|
||||||
|
STR_RATING: "Rating"
|
||||||
|
STR_ISBN: "ISBN"
|
||||||
|
STR_RIGHTS: "Rights"
|
||||||
|
STR_CONTRIBUTOR: "Contributor"
|
||||||
STR_MANAGE: "Manage"
|
STR_MANAGE: "Manage"
|
||||||
STR_INFO: "Info"
|
STR_INFO: "Info"
|
||||||
STR_ARCHIVE_BOOK: "Archive Book"
|
STR_ARCHIVE_BOOK: "Archive Book"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ void BookInfoActivity::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string title, author, series, seriesIndex, description, language;
|
std::string title, author, series, seriesIndex, description, language;
|
||||||
|
std::string publisher, date, subjects, rights, contributor, identifier, rating;
|
||||||
|
|
||||||
if (FsHelpers::hasEpubExtension(fileName)) {
|
if (FsHelpers::hasEpubExtension(fileName)) {
|
||||||
Epub epub(filePath, "/.crosspoint");
|
Epub epub(filePath, "/.crosspoint");
|
||||||
@@ -78,6 +79,13 @@ void BookInfoActivity::onEnter() {
|
|||||||
seriesIndex = epub.getSeriesIndex();
|
seriesIndex = epub.getSeriesIndex();
|
||||||
description = normalizeWhitespace(epub.getDescription());
|
description = normalizeWhitespace(epub.getDescription());
|
||||||
language = epub.getLanguage();
|
language = epub.getLanguage();
|
||||||
|
publisher = epub.getPublisher();
|
||||||
|
date = epub.getDate();
|
||||||
|
subjects = epub.getSubjects();
|
||||||
|
rights = epub.getRights();
|
||||||
|
contributor = epub.getContributor();
|
||||||
|
identifier = epub.getIdentifier();
|
||||||
|
rating = epub.getRating();
|
||||||
|
|
||||||
const int coverH = renderer.getScreenHeight() * 2 / 5;
|
const int coverH = renderer.getScreenHeight() * 2 / 5;
|
||||||
if (epub.generateThumbBmp(coverH)) {
|
if (epub.generateThumbBmp(coverH)) {
|
||||||
@@ -129,7 +137,8 @@ void BookInfoActivity::onEnter() {
|
|||||||
title = fileName;
|
title = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildLayout(title, author, series, seriesIndex, description, language, fileSize);
|
buildLayout(title, author, series, seriesIndex, description, language, fileSize, publisher, date, subjects, rights,
|
||||||
|
contributor, identifier, rating);
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,9 +146,12 @@ void BookInfoActivity::onExit() { Activity::onExit(); }
|
|||||||
|
|
||||||
void BookInfoActivity::buildLayout(const std::string& title, const std::string& author, const std::string& series,
|
void BookInfoActivity::buildLayout(const std::string& title, const std::string& author, const std::string& series,
|
||||||
const std::string& seriesIndex, const std::string& description,
|
const std::string& seriesIndex, const std::string& description,
|
||||||
const std::string& language, size_t fileSize) {
|
const std::string& language, size_t fileSize, const std::string& publisher,
|
||||||
|
const std::string& date, const std::string& subjects, const std::string& rights,
|
||||||
|
const std::string& contributor, const std::string& identifier,
|
||||||
|
const std::string& rating) {
|
||||||
const int contentW = renderer.getScreenWidth() - MARGIN * 2;
|
const int contentW = renderer.getScreenWidth() - MARGIN * 2;
|
||||||
fields.reserve(6);
|
fields.reserve(13);
|
||||||
|
|
||||||
auto addField = [&](const char* label, const std::string& text, bool bold, EpdFontFamily::Style style) {
|
auto addField = [&](const char* label, const std::string& text, bool bold, EpdFontFamily::Style style) {
|
||||||
if (text.empty()) return;
|
if (text.empty()) return;
|
||||||
@@ -161,12 +173,28 @@ void BookInfoActivity::buildLayout(const std::string& title, const std::string&
|
|||||||
addField(tr(STR_SERIES), seriesStr, false, EpdFontFamily::REGULAR);
|
addField(tr(STR_SERIES), seriesStr, false, EpdFontFamily::REGULAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addField(tr(STR_PUBLISHER), publisher, false, EpdFontFamily::REGULAR);
|
||||||
|
addField(tr(STR_DATE), date, false, EpdFontFamily::REGULAR);
|
||||||
|
addField(tr(STR_SUBJECTS), subjects, false, EpdFontFamily::REGULAR);
|
||||||
|
|
||||||
|
if (!rating.empty()) {
|
||||||
|
int ratingVal = atoi(rating.c_str());
|
||||||
|
if (ratingVal > 0 && ratingVal <= 10) {
|
||||||
|
char ratingBuf[8];
|
||||||
|
snprintf(ratingBuf, sizeof(ratingBuf), "%d / 5", (ratingVal + 1) / 2);
|
||||||
|
addField(tr(STR_RATING), std::string(ratingBuf), false, EpdFontFamily::REGULAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addField(tr(STR_LANGUAGE), language, false, EpdFontFamily::REGULAR);
|
addField(tr(STR_LANGUAGE), language, false, EpdFontFamily::REGULAR);
|
||||||
|
addField(tr(STR_ISBN), identifier, false, EpdFontFamily::REGULAR);
|
||||||
|
addField(tr(STR_CONTRIBUTOR), contributor, false, EpdFontFamily::REGULAR);
|
||||||
|
|
||||||
if (fileSize > 0) {
|
if (fileSize > 0) {
|
||||||
addField(tr(STR_FILE_SIZE), formatFileSize(fileSize), false, EpdFontFamily::REGULAR);
|
addField(tr(STR_FILE_SIZE), formatFileSize(fileSize), false, EpdFontFamily::REGULAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addField(tr(STR_RIGHTS), rights, false, EpdFontFamily::REGULAR);
|
||||||
addField(tr(STR_DESCRIPTION), description, false, EpdFontFamily::REGULAR);
|
addField(tr(STR_DESCRIPTION), description, false, EpdFontFamily::REGULAR);
|
||||||
|
|
||||||
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
|
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ class BookInfoActivity final : public Activity {
|
|||||||
|
|
||||||
void buildLayout(const std::string& title, const std::string& author, const std::string& series,
|
void buildLayout(const std::string& title, const std::string& author, const std::string& series,
|
||||||
const std::string& seriesIndex, const std::string& description, const std::string& language,
|
const std::string& seriesIndex, const std::string& description, const std::string& language,
|
||||||
size_t fileSize);
|
size_t fileSize, const std::string& publisher, const std::string& date,
|
||||||
|
const std::string& subjects, const std::string& rights, const std::string& contributor,
|
||||||
|
const std::string& identifier, const std::string& rating);
|
||||||
static std::string formatFileSize(size_t bytes);
|
static std::string formatFileSize(size_t bytes);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user