Merge branch 'master' of https://github.com/crosspoint-reader/crosspoint-reader into feat-page-overlay
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "RecentBooksStore.h"
|
||||
|
||||
#include <Epub.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <HalStorage.h>
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
@@ -9,8 +10,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
|
||||
constexpr char RECENT_BOOKS_FILE_BIN[] = "/.crosspoint/recent.bin";
|
||||
@@ -71,19 +70,17 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
|
||||
// If epub, try to load the metadata for title/author and cover.
|
||||
// Use buildIfMissing=false to avoid heavy epub loading on boot; getTitle()/getAuthor() may be
|
||||
// blank until the book is opened, and entries with missing title are omitted from recent list.
|
||||
if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) {
|
||||
if (FsHelpers::hasEpubExtension(lastBookFileName)) {
|
||||
Epub epub(path, "/.crosspoint");
|
||||
epub.load(false, true);
|
||||
return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()};
|
||||
} else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") ||
|
||||
StringUtils::checkFileExtension(lastBookFileName, ".xtc")) {
|
||||
} else if (FsHelpers::hasXtcExtension(lastBookFileName)) {
|
||||
// Handle XTC file
|
||||
Xtc xtc(path, "/.crosspoint");
|
||||
if (xtc.load()) {
|
||||
return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()};
|
||||
}
|
||||
} else if (StringUtils::checkFileExtension(lastBookFileName, ".txt") ||
|
||||
StringUtils::checkFileExtension(lastBookFileName, ".md")) {
|
||||
} else if (FsHelpers::hasTxtExtension(lastBookFileName) || FsHelpers::hasMarkdownExtension(lastBookFileName)) {
|
||||
return RecentBook{path, lastBookFileName, "", ""};
|
||||
}
|
||||
return RecentBook{path, "", "", ""};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "SleepActivity.h"
|
||||
|
||||
#include <Epub.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
@@ -18,7 +19,6 @@
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "images/Logo120.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -211,7 +211,7 @@ void SleepActivity::renderCustomSleepScreen() const {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filename.substr(filename.length() - 4) != ".bmp") {
|
||||
if (!FsHelpers::hasBmpExtension(filename)) {
|
||||
LOG_DBG("SLP", "Skipping non-.bmp file name: %s", name);
|
||||
file.close();
|
||||
continue;
|
||||
@@ -378,8 +378,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
||||
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
||||
|
||||
// Check if the current book is XTC, TXT, or EPUB
|
||||
if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") ||
|
||||
StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) {
|
||||
if (FsHelpers::hasXtcExtension(APP_STATE.openEpubPath)) {
|
||||
// Handle XTC file
|
||||
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
|
||||
if (!lastXtc.load()) {
|
||||
@@ -393,7 +392,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
||||
}
|
||||
|
||||
coverBmpPath = lastXtc.getCoverBmpPath();
|
||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".txt")) {
|
||||
} else if (FsHelpers::hasTxtExtension(APP_STATE.openEpubPath)) {
|
||||
// Handle TXT file - looks for cover image in the same folder
|
||||
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
|
||||
if (!lastTxt.load()) {
|
||||
@@ -407,7 +406,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
||||
}
|
||||
|
||||
coverBmpPath = lastTxt.getCoverBmpPath();
|
||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) {
|
||||
} else if (FsHelpers::hasEpubExtension(APP_STATE.openEpubPath)) {
|
||||
// Handle EPUB file
|
||||
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
|
||||
// Skip loading css since we only need metadata here
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "FileBrowserActivity.h"
|
||||
|
||||
#include <Epub.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
@@ -11,7 +12,6 @@
|
||||
#include "MappedInputManager.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
constexpr unsigned long GO_HOME_MS = 1000;
|
||||
@@ -91,10 +91,10 @@ void FileBrowserActivity::loadFiles() {
|
||||
if (file.isDirectory()) {
|
||||
files.emplace_back(std::string(name) + "/");
|
||||
} else {
|
||||
auto filename = std::string(name);
|
||||
if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") ||
|
||||
StringUtils::checkFileExtension(filename, ".xtc") || StringUtils::checkFileExtension(filename, ".txt") ||
|
||||
StringUtils::checkFileExtension(filename, ".md") || StringUtils::checkFileExtension(filename, ".bmp")) {
|
||||
std::string_view filename{name};
|
||||
if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename) ||
|
||||
FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename) ||
|
||||
FsHelpers::hasBmpExtension(filename)) {
|
||||
files.emplace_back(filename);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ void FileBrowserActivity::onExit() {
|
||||
|
||||
void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
|
||||
// Only clear cache for .epub files
|
||||
if (StringUtils::checkFileExtension(fullPath, ".epub")) {
|
||||
if (FsHelpers::hasEpubExtension(fullPath)) {
|
||||
Epub(fullPath, "/.crosspoint").clearCache();
|
||||
LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str());
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <Epub.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
int HomeActivity::getMenuItemCount() const {
|
||||
int count = 4; // File Browser, Recents, File transfer, Settings
|
||||
@@ -61,7 +61,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
|
||||
std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight);
|
||||
if (!Storage.exists(coverPath.c_str())) {
|
||||
// If epub, try to load the metadata for title/author and cover
|
||||
if (StringUtils::checkFileExtension(book.path, ".epub")) {
|
||||
if (FsHelpers::hasEpubExtension(book.path)) {
|
||||
Epub epub(book.path, "/.crosspoint");
|
||||
// Skip loading css since we only need metadata here
|
||||
epub.load(false, true);
|
||||
@@ -79,8 +79,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
|
||||
}
|
||||
coverRendered = false;
|
||||
requestUpdate();
|
||||
} else if (StringUtils::checkFileExtension(book.path, ".xtch") ||
|
||||
StringUtils::checkFileExtension(book.path, ".xtc")) {
|
||||
} else if (FsHelpers::hasXtcExtension(book.path)) {
|
||||
// Handle XTC file
|
||||
Xtc xtc(book.path, "/.crosspoint");
|
||||
if (xtc.load()) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
constexpr unsigned long GO_HOME_MS = 1000;
|
||||
|
||||
@@ -597,6 +597,16 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
section->currentPage = nextPageNumber;
|
||||
}
|
||||
|
||||
if (!pendingAnchor.empty()) {
|
||||
if (const auto page = section->getPageForAnchor(pendingAnchor)) {
|
||||
section->currentPage = *page;
|
||||
LOG_DBG("ERS", "Resolved anchor '%s' to page %d", pendingAnchor.c_str(), *page);
|
||||
} else {
|
||||
LOG_DBG("ERS", "Anchor '%s' not found in section %d", pendingAnchor.c_str(), currentSpineIndex);
|
||||
}
|
||||
pendingAnchor.clear();
|
||||
}
|
||||
|
||||
// handles changes in reader settings and reset to approximate position based on cached progress
|
||||
if (cachedChapterTotalPageCount > 0) {
|
||||
// only goes to relative position if spine index matches cached value
|
||||
@@ -792,12 +802,18 @@ void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool s
|
||||
LOG_DBG("ERS", "Saved position [%d]: spine %d, page %d", footnoteDepth, currentSpineIndex, section->currentPage);
|
||||
}
|
||||
|
||||
// Extract fragment anchor (e.g. "#note1" or "chapter2.xhtml#note1")
|
||||
std::string anchor;
|
||||
const auto hashPos = hrefStr.find('#');
|
||||
if (hashPos != std::string::npos && hashPos + 1 < hrefStr.size()) {
|
||||
anchor = hrefStr.substr(hashPos + 1);
|
||||
}
|
||||
|
||||
// Check for same-file anchor reference (#anchor only)
|
||||
bool sameFile = !hrefStr.empty() && hrefStr[0] == '#';
|
||||
|
||||
int targetSpineIndex;
|
||||
if (sameFile) {
|
||||
// Same file — navigate to page 0 of current spine item
|
||||
targetSpineIndex = currentSpineIndex;
|
||||
} else {
|
||||
targetSpineIndex = epub->resolveHrefToSpineIndex(hrefStr);
|
||||
@@ -811,6 +827,7 @@ void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool s
|
||||
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
pendingAnchor = std::move(anchor);
|
||||
currentSpineIndex = targetSpineIndex;
|
||||
nextPageNumber = 0;
|
||||
section.reset();
|
||||
|
||||
@@ -11,6 +11,9 @@ class EpubReaderActivity final : public Activity {
|
||||
std::unique_ptr<Section> section = nullptr;
|
||||
int currentSpineIndex = 0;
|
||||
int nextPageNumber = 0;
|
||||
// Set when navigating to a footnote href with a fragment (e.g. #note1).
|
||||
// Cleared on the next render after the new section loads and resolves it to a page.
|
||||
std::string pendingAnchor;
|
||||
int pagesUntilFullRefresh = 0;
|
||||
int cachedSpineIndex = 0;
|
||||
int cachedChapterTotalPageCount = 0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ReaderActivity.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HalStorage.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
@@ -11,7 +12,6 @@
|
||||
#include "XtcReaderActivity.h"
|
||||
#include "activities/util/BmpViewerActivity.h"
|
||||
#include "activities/util/FullScreenMessageActivity.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
|
||||
const auto lastSlash = filePath.find_last_of('/');
|
||||
@@ -21,16 +21,14 @@ std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
|
||||
return filePath.substr(0, lastSlash);
|
||||
}
|
||||
|
||||
bool ReaderActivity::isXtcFile(const std::string& path) {
|
||||
return StringUtils::checkFileExtension(path, ".xtc") || StringUtils::checkFileExtension(path, ".xtch");
|
||||
}
|
||||
bool ReaderActivity::isXtcFile(const std::string& path) { return FsHelpers::hasXtcExtension(path); }
|
||||
|
||||
bool ReaderActivity::isTxtFile(const std::string& path) {
|
||||
return StringUtils::checkFileExtension(path, ".txt") ||
|
||||
StringUtils::checkFileExtension(path, ".md"); // Treat .md as txt files (until we have a markdown reader)
|
||||
return FsHelpers::hasTxtExtension(path) ||
|
||||
FsHelpers::hasMarkdownExtension(path); // Treat .md as txt files (until we have a markdown reader)
|
||||
}
|
||||
|
||||
bool ReaderActivity::isBmpFile(const std::string& path) { return StringUtils::checkFileExtension(path, ".bmp"); }
|
||||
bool ReaderActivity::isBmpFile(const std::string& path) { return FsHelpers::hasBmpExtension(path); }
|
||||
|
||||
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
|
||||
if (!Storage.exists(path.c_str())) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "UITheme.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <Logging.h>
|
||||
|
||||
@@ -10,7 +11,6 @@
|
||||
#include "components/themes/BaseTheme.h"
|
||||
#include "components/themes/lyra/Lyra3CoversTheme.h"
|
||||
#include "components/themes/lyra/LyraTheme.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
@@ -74,18 +74,17 @@ std::string UITheme::getCoverThumbPath(std::string coverBmpPath, int coverHeight
|
||||
return coverBmpPath;
|
||||
}
|
||||
|
||||
UIIcon UITheme::getFileIcon(std::string filename) {
|
||||
UIIcon UITheme::getFileIcon(const std::string& filename) {
|
||||
if (filename.back() == '/') {
|
||||
return Folder;
|
||||
}
|
||||
if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") ||
|
||||
StringUtils::checkFileExtension(filename, ".xtc")) {
|
||||
if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename)) {
|
||||
return Book;
|
||||
}
|
||||
if (StringUtils::checkFileExtension(filename, ".txt") || StringUtils::checkFileExtension(filename, ".md")) {
|
||||
if (FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename)) {
|
||||
return Text;
|
||||
}
|
||||
if (StringUtils::checkFileExtension(filename, ".bmp")) {
|
||||
if (FsHelpers::hasBmpExtension(filename)) {
|
||||
return Image;
|
||||
}
|
||||
return File;
|
||||
|
||||
@@ -21,7 +21,7 @@ class UITheme {
|
||||
static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints,
|
||||
bool hasSubtitle);
|
||||
static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight);
|
||||
static UIIcon getFileIcon(std::string filename);
|
||||
static UIIcon getFileIcon(const std::string& filename);
|
||||
static int getStatusBarHeight();
|
||||
static int getProgressBarHeight();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <HalGPIO.h>
|
||||
#include <HalPowerManager.h>
|
||||
#include <HalStorage.h>
|
||||
#include <HalSystem.h>
|
||||
#include <I18n.h>
|
||||
#include <Logging.h>
|
||||
#include <SPI.h>
|
||||
@@ -227,6 +228,7 @@ void setupDisplayAndFonts() {
|
||||
void setup() {
|
||||
t1 = millis();
|
||||
|
||||
HalSystem::begin();
|
||||
gpio.begin();
|
||||
powerManager.begin();
|
||||
|
||||
@@ -249,6 +251,9 @@ void setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
HalSystem::checkPanic();
|
||||
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
||||
|
||||
SETTINGS.loadFromFile();
|
||||
I18N.loadSettings();
|
||||
KOREADER_STORE.loadFromFile();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "html/FilesPageHtml.generated.h"
|
||||
#include "html/HomePageHtml.generated.h"
|
||||
#include "html/SettingsPageHtml.generated.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
// Folders/files to hide from the web interface file browser
|
||||
@@ -44,7 +43,7 @@ unsigned long wsLastCompleteAt = 0;
|
||||
// Helper function to clear epub cache after upload
|
||||
void clearEpubCacheIfNeeded(const String& filePath) {
|
||||
// Only clear cache for .epub files
|
||||
if (StringUtils::checkFileExtension(filePath, ".epub")) {
|
||||
if (FsHelpers::hasEpubExtension(filePath)) {
|
||||
Epub(filePath.c_str(), "/.crosspoint").clearCache();
|
||||
LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str());
|
||||
}
|
||||
@@ -391,11 +390,7 @@ void CrossPointWebServer::scanFiles(const char* path, const std::function<void(F
|
||||
root.close();
|
||||
}
|
||||
|
||||
bool CrossPointWebServer::isEpubFile(const String& filename) const {
|
||||
String lower = filename;
|
||||
lower.toLowerCase();
|
||||
return lower.endsWith(".epub");
|
||||
}
|
||||
bool CrossPointWebServer::isEpubFile(const String& filename) const { return FsHelpers::hasEpubExtension(filename); }
|
||||
|
||||
void CrossPointWebServer::handleFileList() const {
|
||||
sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml));
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <Logging.h>
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
namespace {
|
||||
const char* HIDDEN_ITEMS[] = {"System Volume Information", "XTCache"};
|
||||
constexpr size_t HIDDEN_ITEMS_COUNT = sizeof(HIDDEN_ITEMS) / sizeof(HIDDEN_ITEMS[0]);
|
||||
@@ -801,28 +799,26 @@ bool WebDAVHandler::getOverwrite(WebServer& s) const {
|
||||
}
|
||||
|
||||
void WebDAVHandler::clearEpubCacheIfNeeded(const String& path) const {
|
||||
if (StringUtils::checkFileExtension(path, ".epub")) {
|
||||
if (FsHelpers::hasEpubExtension(path)) {
|
||||
Epub(path.c_str(), "/.crosspoint").clearCache();
|
||||
LOG_DBG("DAV", "Cleared epub cache for: %s", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
String WebDAVHandler::getMimeType(const String& path) const {
|
||||
if (StringUtils::checkFileExtension(path, ".epub")) return "application/epub+zip";
|
||||
if (StringUtils::checkFileExtension(path, ".pdf")) return "application/pdf";
|
||||
if (StringUtils::checkFileExtension(path, ".txt")) return "text/plain";
|
||||
if (StringUtils::checkFileExtension(path, ".html") || StringUtils::checkFileExtension(path, ".htm"))
|
||||
return "text/html";
|
||||
if (StringUtils::checkFileExtension(path, ".css")) return "text/css";
|
||||
if (StringUtils::checkFileExtension(path, ".js")) return "application/javascript";
|
||||
if (StringUtils::checkFileExtension(path, ".json")) return "application/json";
|
||||
if (StringUtils::checkFileExtension(path, ".xml")) return "application/xml";
|
||||
if (StringUtils::checkFileExtension(path, ".jpg") || StringUtils::checkFileExtension(path, ".jpeg"))
|
||||
return "image/jpeg";
|
||||
if (StringUtils::checkFileExtension(path, ".png")) return "image/png";
|
||||
if (StringUtils::checkFileExtension(path, ".gif")) return "image/gif";
|
||||
if (StringUtils::checkFileExtension(path, ".svg")) return "image/svg+xml";
|
||||
if (StringUtils::checkFileExtension(path, ".zip")) return "application/zip";
|
||||
if (StringUtils::checkFileExtension(path, ".gz")) return "application/gzip";
|
||||
if (FsHelpers::hasEpubExtension(path)) return "application/epub+zip";
|
||||
if (FsHelpers::checkFileExtension(path, ".pdf")) return "application/pdf";
|
||||
if (FsHelpers::hasTxtExtension(path)) return "text/plain";
|
||||
if (FsHelpers::checkFileExtension(path, ".html") || FsHelpers::checkFileExtension(path, ".htm")) return "text/html";
|
||||
if (FsHelpers::checkFileExtension(path, ".css")) return "text/css";
|
||||
if (FsHelpers::checkFileExtension(path, ".js")) return "application/javascript";
|
||||
if (FsHelpers::checkFileExtension(path, ".json")) return "application/json";
|
||||
if (FsHelpers::checkFileExtension(path, ".xml")) return "application/xml";
|
||||
if (FsHelpers::hasJpgExtension(path)) return "image/jpeg";
|
||||
if (FsHelpers::hasPngExtension(path)) return "image/png";
|
||||
if (FsHelpers::hasGifExtension(path)) return "image/gif";
|
||||
if (FsHelpers::checkFileExtension(path, ".svg")) return "image/svg+xml";
|
||||
if (FsHelpers::checkFileExtension(path, ".zip")) return "application/zip";
|
||||
if (FsHelpers::checkFileExtension(path, ".gz")) return "application/gzip";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <Utf8.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace StringUtils {
|
||||
|
||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
||||
@@ -45,30 +43,4 @@ std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
||||
return result.empty() ? "book" : result;
|
||||
}
|
||||
|
||||
bool checkFileExtension(const std::string& fileName, const char* extension) {
|
||||
if (fileName.length() < strlen(extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string fileExt = fileName.substr(fileName.length() - strlen(extension));
|
||||
for (size_t i = 0; i < fileExt.length(); i++) {
|
||||
if (tolower(fileExt[i]) != tolower(extension[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool checkFileExtension(const String& fileName, const char* extension) {
|
||||
if (fileName.length() < strlen(extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String localFile(fileName);
|
||||
String localExtension(extension);
|
||||
localFile.toLowerCase();
|
||||
localExtension.toLowerCase();
|
||||
return localFile.endsWith(localExtension);
|
||||
}
|
||||
|
||||
} // namespace StringUtils
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <WString.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace StringUtils {
|
||||
@@ -13,10 +11,4 @@ namespace StringUtils {
|
||||
*/
|
||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100);
|
||||
|
||||
/**
|
||||
* Check if the given filename ends with the specified extension (case-insensitive).
|
||||
*/
|
||||
bool checkFileExtension(const std::string& fileName, const char* extension);
|
||||
bool checkFileExtension(const String& fileName, const char* extension);
|
||||
|
||||
} // namespace StringUtils
|
||||
|
||||
Reference in New Issue
Block a user