Add TXT file reader support (#240)

## Summary

* **What is the goal of this PR?** 

Add support for reading plain text (.txt) files, enabling users to
browse, read, and track progress in TXT documents alongside existing
EPUB and XTC formats.

* **What changes are included?**

- New Txt library for loading and parsing plain text files
- New TxtReaderActivity with streaming page rendering using 8KB chunks
to handle large files without memory issues on ESP32-C3
- Page index caching system (index.bin) for instant re-open after sleep
or app restart
- Progress bar UI during initial file indexing (matching EPUB style)
- Word wrapping with proper UTF-8 support
- Cover image support for TXT files:
- Primary: image with same filename as TXT (e.g., book.jpg for book.txt)
  - Fallback: cover.bmp/jpg/jpeg in the same folder
  - JPG to BMP conversion using existing converter
  - Sleep screen cover mode now works with TXT files
- File browser now shows .txt files

## Additional Context

* Add any other information that might be helpful for the reviewer

* Memory constraints: The streaming approach was necessary because
ESP32-C3 only has 320KB RAM. A 700KB TXT file cannot be loaded entirely
into memory, so we read 8KB chunks and build a page offset index
instead.

* Cache invalidation: The page index cache automatically invalidates
when file size, viewport width, or lines per page changes (e.g., font
size or orientation change).

* Performance: First open requires indexing (with progress bar),
subsequent opens load from cache instantly.

* Cover image format: PNG is detected but not supported for conversion
(no PNG decoder available). Only BMP and JPG/JPEG work.
This commit is contained in:
Eunchurn Park
2026-01-14 19:36:40 +09:00
committed by GitHub
parent 14643d0225
commit 49f97b69ca
8 changed files with 1056 additions and 1 deletions

33
lib/Txt/Txt.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <SDCardManager.h>
#include <memory>
#include <string>
class Txt {
std::string filepath;
std::string cacheBasePath;
std::string cachePath;
bool loaded = false;
size_t fileSize = 0;
public:
explicit Txt(std::string path, std::string cacheBasePath);
bool load();
[[nodiscard]] const std::string& getPath() const { return filepath; }
[[nodiscard]] const std::string& getCachePath() const { return cachePath; }
[[nodiscard]] std::string getTitle() const;
[[nodiscard]] size_t getFileSize() const { return fileSize; }
void setupCacheDir() const;
// Cover image support - looks for cover.bmp/jpg/jpeg/png in same folder as txt file
[[nodiscard]] std::string getCoverBmpPath() const;
[[nodiscard]] bool generateCoverBmp() const;
[[nodiscard]] std::string findCoverImage() const;
// Read content from file
[[nodiscard]] bool readContent(uint8_t* buffer, size_t offset, size_t length) const;
};