Local overrides for css and img display

This commit is contained in:
jpirnay
2026-03-20 18:17:34 +01:00
parent a01bb2902e
commit 09e65c30a3
8 changed files with 185 additions and 25 deletions
+11
View File
@@ -302,6 +302,8 @@ bool JsonSettingsIO::saveRecentBooks(const RecentBooksStore& store, const char*
obj["title"] = book.title; obj["title"] = book.title;
obj["author"] = book.author; obj["author"] = book.author;
obj["coverBmpPath"] = book.coverBmpPath; obj["coverBmpPath"] = book.coverBmpPath;
obj["embeddedStyleOverride"] = book.embeddedStyleOverride;
obj["imageRenderingOverride"] = book.imageRenderingOverride;
} }
String json; String json;
@@ -319,6 +321,13 @@ bool JsonSettingsIO::loadRecentBooks(RecentBooksStore& store, const char* json)
store.recentBooks.clear(); store.recentBooks.clear();
JsonArray arr = doc["books"].as<JsonArray>(); JsonArray arr = doc["books"].as<JsonArray>();
auto clampInt8 = [](int value, int minValue, int maxValue, int8_t fallback) -> int8_t {
if (value < minValue || value > maxValue) {
return fallback;
}
return static_cast<int8_t>(value);
};
for (JsonObject obj : arr) { for (JsonObject obj : arr) {
if (store.getCount() >= 10) break; if (store.getCount() >= 10) break;
RecentBook book; RecentBook book;
@@ -326,6 +335,8 @@ bool JsonSettingsIO::loadRecentBooks(RecentBooksStore& store, const char* json)
book.title = obj["title"] | std::string(""); book.title = obj["title"] | std::string("");
book.author = obj["author"] | std::string(""); book.author = obj["author"] | std::string("");
book.coverBmpPath = obj["coverBmpPath"] | std::string(""); book.coverBmpPath = obj["coverBmpPath"] | std::string("");
book.embeddedStyleOverride = clampInt8(obj["embeddedStyleOverride"] | -1, -1, 1, -1);
book.imageRenderingOverride = clampInt8(obj["imageRenderingOverride"] | -1, -1, 2, -1);
store.recentBooks.push_back(book); store.recentBooks.push_back(book);
} }
+29 -1
View File
@@ -22,15 +22,21 @@ RecentBooksStore RecentBooksStore::instance;
void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author, void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath) { const std::string& coverBmpPath) {
int8_t embeddedStyleOverride = -1;
int8_t imageRenderingOverride = -1;
// Remove existing entry if present // Remove existing entry if present
auto it = auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; }); std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it != recentBooks.end()) { if (it != recentBooks.end()) {
embeddedStyleOverride = it->embeddedStyleOverride;
imageRenderingOverride = it->imageRenderingOverride;
recentBooks.erase(it); recentBooks.erase(it);
} }
// Add to front // Add to front
recentBooks.insert(recentBooks.begin(), {path, title, author, coverBmpPath}); recentBooks.insert(recentBooks.begin(),
{path, title, author, coverBmpPath, embeddedStyleOverride, imageRenderingOverride});
// Trim to max size // Trim to max size
if (recentBooks.size() > MAX_RECENT_BOOKS) { if (recentBooks.size() > MAX_RECENT_BOOKS) {
@@ -53,6 +59,28 @@ void RecentBooksStore::updateBook(const std::string& path, const std::string& ti
} }
} }
RecentBook RecentBooksStore::getBookByPath(const std::string& path) const {
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it != recentBooks.end()) {
return *it;
}
return RecentBook{};
}
bool RecentBooksStore::setReaderOverrides(const std::string& path, const int8_t embeddedStyleOverride,
const int8_t imageRenderingOverride) {
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it == recentBooks.end()) {
return false;
}
it->embeddedStyleOverride = embeddedStyleOverride;
it->imageRenderingOverride = imageRenderingOverride;
return saveToFile();
}
bool RecentBooksStore::saveToFile() const { bool RecentBooksStore::saveToFile() const {
Storage.mkdir("/.crosspoint"); Storage.mkdir("/.crosspoint");
return JsonSettingsIO::saveRecentBooks(*this, RECENT_BOOKS_FILE_JSON); return JsonSettingsIO::saveRecentBooks(*this, RECENT_BOOKS_FILE_JSON);
+8
View File
@@ -1,12 +1,18 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
struct RecentBook { struct RecentBook {
std::string path; std::string path;
std::string title; std::string title;
std::string author; std::string author;
std::string coverBmpPath; std::string coverBmpPath;
// -1 = use global setting, otherwise explicit per-book override.
int8_t embeddedStyleOverride = -1;
// -1 = use global setting, otherwise CrossPointSettings::IMAGE_RENDERING value.
int8_t imageRenderingOverride = -1;
bool operator==(const RecentBook& other) const { return path == other.path; } bool operator==(const RecentBook& other) const { return path == other.path; }
}; };
@@ -47,6 +53,8 @@ class RecentBooksStore {
bool loadFromFile(); bool loadFromFile();
RecentBook getDataFromBook(std::string path) const; RecentBook getDataFromBook(std::string path) const;
RecentBook getBookByPath(const std::string& path) const;
bool setReaderOverrides(const std::string& path, int8_t embeddedStyleOverride, int8_t imageRenderingOverride);
private: private:
bool loadFromBinaryFile(); bool loadFromBinaryFile();
+2
View File
@@ -21,6 +21,8 @@ struct MenuResult {
int action = -1; int action = -1;
uint8_t orientation = 0; uint8_t orientation = 0;
uint8_t pageTurnOption = 0; uint8_t pageTurnOption = 0;
int8_t embeddedStyleOverride = -1;
int8_t imageRenderingOverride = -1;
}; };
struct ChapterResult { struct ChapterResult {
+64 -20
View File
@@ -85,6 +85,9 @@ void EpubReaderActivity::onEnter() {
APP_STATE.openEpubPath = epub->getPath(); APP_STATE.openEpubPath = epub->getPath();
APP_STATE.saveToFile(); APP_STATE.saveToFile();
RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), epub->getThumbBmpPath()); RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), epub->getThumbBmpPath());
const RecentBook currentBook = RECENT_BOOKS.getBookByPath(epub->getPath());
bookEmbeddedStyleOverride = currentBook.embeddedStyleOverride;
bookImageRenderingOverride = currentBook.imageRenderingOverride;
// Trigger first update // Trigger first update
requestUpdate(); requestUpdate();
@@ -145,18 +148,20 @@ void EpubReaderActivity::loop() {
bookProgress = epub->calculateProgress(currentSpineIndex, chapterProgress) * 100.0f; bookProgress = epub->calculateProgress(currentSpineIndex, chapterProgress) * 100.0f;
} }
const int bookProgressPercent = clampPercent(static_cast<int>(bookProgress + 0.5f)); const int bookProgressPercent = clampPercent(static_cast<int>(bookProgress + 0.5f));
startActivityForResult(std::make_unique<EpubReaderMenuActivity>( startActivityForResult(
renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent, std::make_unique<EpubReaderMenuActivity>(
SETTINGS.orientation, !currentPageFootnotes.empty()), renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent, SETTINGS.orientation,
[this](const ActivityResult& result) { !currentPageFootnotes.empty(), bookEmbeddedStyleOverride, bookImageRenderingOverride),
// Always apply orientation change even if the menu was cancelled [this](const ActivityResult& result) {
const auto& menu = std::get<MenuResult>(result.data); // Always apply orientation change even if the menu was cancelled
applyOrientation(menu.orientation); const auto& menu = std::get<MenuResult>(result.data);
toggleAutoPageTurn(menu.pageTurnOption); applyOrientation(menu.orientation);
if (!result.isCancelled) { toggleAutoPageTurn(menu.pageTurnOption);
onReaderMenuConfirm(static_cast<EpubReaderMenuActivity::MenuAction>(menu.action)); applyBookReaderOverrides(menu.embeddedStyleOverride, menu.imageRenderingOverride);
} if (!result.isCancelled) {
}); onReaderMenuConfirm(static_cast<EpubReaderMenuActivity::MenuAction>(menu.action));
}
});
} }
// Long press BACK (1s+) goes to file selection // Long press BACK (1s+) goes to file selection
@@ -456,6 +461,43 @@ void EpubReaderActivity::toggleAutoPageTurn(const uint8_t selectedPageTurnOption
} }
} }
void EpubReaderActivity::applyBookReaderOverrides(const int8_t embeddedStyleOverride,
const int8_t imageRenderingOverride) {
if (!epub) {
return;
}
if (bookEmbeddedStyleOverride == embeddedStyleOverride && bookImageRenderingOverride == imageRenderingOverride) {
return;
}
bookEmbeddedStyleOverride = embeddedStyleOverride;
bookImageRenderingOverride = imageRenderingOverride;
RECENT_BOOKS.setReaderOverrides(epub->getPath(), bookEmbeddedStyleOverride, bookImageRenderingOverride);
RenderLock lock(*this);
if (section) {
cachedSpineIndex = currentSpineIndex;
cachedChapterTotalPageCount = section->pageCount;
nextPageNumber = section->currentPage;
}
section.reset();
}
bool EpubReaderActivity::getEffectiveEmbeddedStyle() const {
if (bookEmbeddedStyleOverride >= 0) {
return bookEmbeddedStyleOverride != 0;
}
return SETTINGS.embeddedStyle != 0;
}
uint8_t EpubReaderActivity::getEffectiveImageRendering() const {
if (bookImageRenderingOverride >= 0) {
return static_cast<uint8_t>(bookImageRenderingOverride);
}
return SETTINGS.imageRendering;
}
void EpubReaderActivity::pageTurn(bool isForwardTurn) { void EpubReaderActivity::pageTurn(bool isForwardTurn) {
if (isForwardTurn) { if (isForwardTurn) {
if (section->currentPage < section->pageCount - 1) { if (section->currentPage < section->pageCount - 1) {
@@ -534,22 +576,23 @@ void EpubReaderActivity::render(RenderLock&& lock) {
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom; const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
if (!section) { if (!section) {
const bool embeddedStyle = getEffectiveEmbeddedStyle();
const uint8_t imageRendering = getEffectiveImageRendering();
const auto filepath = epub->getSpineItem(currentSpineIndex).href; const auto filepath = epub->getSpineItem(currentSpineIndex).href;
LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex); LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex);
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer)); section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering)) {
SETTINGS.imageRendering)) {
LOG_DBG("ERS", "Cache not found, building..."); LOG_DBG("ERS", "Cache not found, building...");
const auto popupFn = [this]() { GUI.drawPopup(renderer, tr(STR_INDEXING)); }; const auto popupFn = [this]() { GUI.drawPopup(renderer, tr(STR_INDEXING)); };
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering,
SETTINGS.imageRendering, popupFn)) { popupFn)) {
LOG_ERR("ERS", "Failed to persist page data to SD"); LOG_ERR("ERS", "Failed to persist page data to SD");
section.reset(); section.reset();
return; return;
@@ -659,19 +702,20 @@ void EpubReaderActivity::silentIndexNextChapterIfNeeded(const uint16_t viewportW
return; return;
} }
const bool embeddedStyle = getEffectiveEmbeddedStyle();
const uint8_t imageRendering = getEffectiveImageRendering();
Section nextSection(epub, nextSpineIndex, renderer); Section nextSection(epub, nextSpineIndex, renderer);
if (nextSection.loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), if (nextSection.loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering)) {
SETTINGS.imageRendering)) {
return; return;
} }
LOG_DBG("ERS", "Silently indexing next chapter: %d", nextSpineIndex); LOG_DBG("ERS", "Silently indexing next chapter: %d", nextSpineIndex);
if (!nextSection.createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), if (!nextSection.createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering)) {
SETTINGS.imageRendering)) {
LOG_ERR("ERS", "Failed silent indexing for chapter: %d", nextSpineIndex); LOG_ERR("ERS", "Failed silent indexing for chapter: %d", nextSpineIndex);
} }
} }
@@ -27,6 +27,9 @@ class EpubReaderActivity final : public Activity {
bool pendingScreenshot = false; bool pendingScreenshot = false;
bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit
bool automaticPageTurnActive = false; bool automaticPageTurnActive = false;
// -1 means use global SETTINGS value.
int8_t bookEmbeddedStyleOverride = -1;
int8_t bookImageRenderingOverride = -1;
// Footnote support // Footnote support
std::vector<FootnoteEntry> currentPageFootnotes; std::vector<FootnoteEntry> currentPageFootnotes;
@@ -48,6 +51,9 @@ class EpubReaderActivity final : public Activity {
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action); void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
void applyOrientation(uint8_t orientation); void applyOrientation(uint8_t orientation);
void toggleAutoPageTurn(uint8_t selectedPageTurnOption); void toggleAutoPageTurn(uint8_t selectedPageTurnOption);
void applyBookReaderOverrides(int8_t embeddedStyleOverride, int8_t imageRenderingOverride);
bool getEffectiveEmbeddedStyle() const;
uint8_t getEffectiveImageRendering() const;
void pageTurn(bool isForwardTurn); void pageTurn(bool isForwardTurn);
// Footnote navigation // Footnote navigation
@@ -10,11 +10,14 @@
EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::string& title, const int currentPage, const int totalPages, const std::string& title, const int currentPage, const int totalPages,
const int bookProgressPercent, const uint8_t currentOrientation, const int bookProgressPercent, const uint8_t currentOrientation,
const bool hasFootnotes) const bool hasFootnotes, const int8_t initialEmbeddedStyleOverride,
const int8_t initialImageRenderingOverride)
: Activity("EpubReaderMenu", renderer, mappedInput), : Activity("EpubReaderMenu", renderer, mappedInput),
menuItems(buildMenuItems(hasFootnotes)), menuItems(buildMenuItems(hasFootnotes)),
title(title), title(title),
pendingOrientation(currentOrientation), pendingOrientation(currentOrientation),
pendingEmbeddedStyleOverride(initialEmbeddedStyleOverride),
pendingImageRenderingOverride(initialImageRenderingOverride),
currentPage(currentPage), currentPage(currentPage),
totalPages(totalPages), totalPages(totalPages),
bookProgressPercent(bookProgressPercent) {} bookProgressPercent(bookProgressPercent) {}
@@ -26,6 +29,8 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
if (hasFootnotes) { if (hasFootnotes) {
items.push_back({MenuAction::FOOTNOTES, StrId::STR_FOOTNOTES}); items.push_back({MenuAction::FOOTNOTES, StrId::STR_FOOTNOTES});
} }
items.push_back({MenuAction::EMBEDDED_STYLE, StrId::STR_EMBEDDED_STYLE});
items.push_back({MenuAction::IMAGE_RENDERING, StrId::STR_IMAGES});
items.push_back({MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION}); items.push_back({MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION});
items.push_back({MenuAction::AUTO_PAGE_TURN, StrId::STR_AUTO_TURN_PAGES_PER_MIN}); items.push_back({MenuAction::AUTO_PAGE_TURN, StrId::STR_AUTO_TURN_PAGES_PER_MIN});
items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT}); items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT});
@@ -71,13 +76,41 @@ void EpubReaderMenuActivity::loop() {
return; return;
} }
setResult(MenuResult{static_cast<int>(selectedAction), pendingOrientation, selectedPageTurnOption}); if (selectedAction == MenuAction::EMBEDDED_STYLE) {
// Cycle per-book override: default -> ON -> OFF -> default.
if (pendingEmbeddedStyleOverride < 0) {
pendingEmbeddedStyleOverride = 1;
} else if (pendingEmbeddedStyleOverride > 0) {
pendingEmbeddedStyleOverride = 0;
} else {
pendingEmbeddedStyleOverride = -1;
}
requestUpdate();
return;
}
if (selectedAction == MenuAction::IMAGE_RENDERING) {
// Cycle per-book override: default -> display -> placeholder -> suppress -> default.
if (pendingImageRenderingOverride < 0) {
pendingImageRenderingOverride = 0;
} else if (pendingImageRenderingOverride >= 2) {
pendingImageRenderingOverride = -1;
} else {
pendingImageRenderingOverride++;
}
requestUpdate();
return;
}
setResult(MenuResult{static_cast<int>(selectedAction), pendingOrientation, selectedPageTurnOption,
pendingEmbeddedStyleOverride, pendingImageRenderingOverride});
finish(); finish();
return; return;
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
ActivityResult result; ActivityResult result;
result.isCancelled = true; result.isCancelled = true;
result.data = MenuResult{-1, pendingOrientation, selectedPageTurnOption}; result.data = MenuResult{-1, pendingOrientation, selectedPageTurnOption, pendingEmbeddedStyleOverride,
pendingImageRenderingOverride};
setResult(std::move(result)); setResult(std::move(result));
finish(); finish();
return; return;
@@ -147,6 +180,26 @@ void EpubReaderMenuActivity::render(RenderLock&&) {
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value); const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected); renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
} }
if (menuItems[i].action == MenuAction::EMBEDDED_STYLE) {
const char* value = tr(STR_DEFAULT_VALUE);
if (pendingEmbeddedStyleOverride == 1) {
value = tr(STR_STATE_ON);
} else if (pendingEmbeddedStyleOverride == 0) {
value = tr(STR_STATE_OFF);
}
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
}
if (menuItems[i].action == MenuAction::IMAGE_RENDERING) {
const char* value = tr(STR_DEFAULT_VALUE);
if (pendingImageRenderingOverride >= 0 && pendingImageRenderingOverride < imageRenderingLabels.size()) {
value = I18N.get(imageRenderingLabels[pendingImageRenderingOverride]);
}
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
}
} }
// Footer / Hints // Footer / Hints
@@ -14,6 +14,8 @@ class EpubReaderMenuActivity final : public Activity {
enum class MenuAction { enum class MenuAction {
SELECT_CHAPTER, SELECT_CHAPTER,
FOOTNOTES, FOOTNOTES,
EMBEDDED_STYLE,
IMAGE_RENDERING,
GO_TO_PERCENT, GO_TO_PERCENT,
AUTO_PAGE_TURN, AUTO_PAGE_TURN,
ROTATE_SCREEN, ROTATE_SCREEN,
@@ -26,7 +28,9 @@ class EpubReaderMenuActivity final : public Activity {
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
const int currentPage, const int totalPages, const int bookProgressPercent, const int currentPage, const int totalPages, const int bookProgressPercent,
const uint8_t currentOrientation, const bool hasFootnotes); const uint8_t currentOrientation, const bool hasFootnotes,
const int8_t initialEmbeddedStyleOverride,
const int8_t initialImageRenderingOverride);
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
@@ -50,8 +54,12 @@ class EpubReaderMenuActivity final : public Activity {
std::string title = "Reader Menu"; std::string title = "Reader Menu";
uint8_t pendingOrientation = 0; uint8_t pendingOrientation = 0;
uint8_t selectedPageTurnOption = 0; uint8_t selectedPageTurnOption = 0;
int8_t pendingEmbeddedStyleOverride = -1;
int8_t pendingImageRenderingOverride = -1;
const std::vector<StrId> orientationLabels = {StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, const std::vector<StrId> orientationLabels = {StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED,
StrId::STR_LANDSCAPE_CCW}; StrId::STR_LANDSCAPE_CCW};
const std::vector<StrId> imageRenderingLabels = {StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER,
StrId::STR_IMAGES_SUPPRESS};
const std::vector<const char*> pageTurnLabels = {I18N.get(StrId::STR_STATE_OFF), "1", "3", "6", "12"}; const std::vector<const char*> pageTurnLabels = {I18N.get(StrId::STR_STATE_OFF), "1", "3", "6", "12"};
int currentPage = 0; int currentPage = 0;
int totalPages = 0; int totalPages = 0;