## Summary
Following up on #1685. One tweaked string confirmed by @alan0ford.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?**
* Increase the allowed link length for footnotes
* **What changes are included?**
* Un-magic-numbers parts of the footnote parser
* Increases max href length
## Additional Context
* Additional RAM usage was not noticable to me, worst case 16 * 128 * 2
= 4KB instead of previously 16 * 88 * 2 ~ 2.8KB, see #1031
* Motivation: My book navigated me to the start of the footnote chapter,
which required manual navigating for every footnote due to ungodly file
name choices which pushed the required href length to 72, which caused
truncating, which caused Crosspoint to not find the anchor (Sacred and
Terrible Air, available via reddit)
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
Make this more transparent in Spanish:
`STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"`
A weakness of the current translation is that the verb used is "to
read", but it's not clear that it is the user who will be reading a
certain amount of pages per minute. I've replaced it with the verb for
"to turn (pages)" which is the (analogous) physical action happening
regardless of who executes it, and the abbreviated adverb to clarify
that this is an automatic process. Lastly, a clarification of what the
number means in a simple proportion: "X pages in a minute". The Spanish
is very good overall, this one string is very hard to do in such a short
space. Even more so given that the number cannot be accompanied by any
units, so it must be clear from the text alone what it means.
## Additional Context
N/A
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
Addresses reviewer feedback from #1644:
- **Localize keyboard hint strings** — 13 hardcoded English strings
replaced with \`tr()\` macro (\`STR_KB_HINT_*\`), making them
translatable across all 22 languages (fallback to English when not yet
translated)
- **Deduplicate \`Lyra3CoversMetrics\`** — now derives from
\`LyraMetrics\` via lambda copy, overriding only \`homeCoverTileHeight\`
and \`homeRecentBooksCount\` (eliminates ~30 duplicated metric fields)
- **Unify keyboard drawing in \`BaseTheme\`** — \`drawTextField\` and
\`drawKeyboardKey\` overrides removed from \`LyraTheme\`; variability
controlled via \`keyboardKeyCornerRadius\` metric (0=Base, 6=Lyra).
Unified text field padding to 6, adopted Lyra's secondary label draw
order (main first, then secondary)
- **Add URL-optimized keyboard layout** — \`urlLayout\` with \`:\` and
\`/\` replacing \`=\` and \`,\` for easier URL input without switching
to SYM mode
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES **_
Switch KOReader sync progress mapping from chapter matching to
XPath-based mapping.
- resolves KOReader positions using real XHTML ancestry paths
- supports paragraph-based upload mapping with text offsets
where needed
- passes the current paragraph index into sync so uploads map
back to KOReader more accurately
No HTTP client changes are included. No reader-state or resume-flow
changes are included.
---------
Co-authored-by: jpirnay <jens@pirnay.com>
## Summary
Fix typos found via `codespell -S
*.txt,*.yaml,generate_kerning_ligature_epub.py -L
currenty,flate,ser,localy,logicaly,ans,clen,portugues,notin,curren`
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it helps set the right
context for reviewers.
Did you use AI tools to help write this code? _**NO**_
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
**What is the goal of this PR?**
Reading a book with frequent numbers, I noticed that the spacing between
numeral glyphs was strangely large. This was because Bookerly and Noto
Sans default to tabular figures, where every digit gets an identical
advance width. This is designed for column alignment in spreadsheets,
but in rendering prose it produces visually wide gaps between digits.
This change adds a `--pnum` flag to fontconvert.py that applies the
font's OpenType `pnum` (proportional numerals) feature during
conversion. When active, the converter:
- Parses the GSUB table for pnum SingleSubst lookups
- Resolves substitute glyph indices via fonttools' glyph order
- Loads the proportional alternate glyphs instead of the tabular
defaults
- Includes substitute glyph names in kern pair extraction, so kerning
data that references proportional alternates is captured
Bookerly's proportional alternates also carry digit-digit and
digit-punctuation kerning that the tabular glyphs lack (e.g., at 16pt
7->4 at -1.69px, 7->. at -2.31px, 7->1 at +1.00px).
Noto Sans gains proportional advances but no new kerning (its
proportional glyphs have no kern class data in the font).
OpenDyslexic is unaffected. Its `cmap` already points to proportional
glyphs, so `--pnum` is a no-op. `--pnum` is intentionally omitted from
OpenDyslexic in the build script for deliberately uniform digit spacing
as an accessibility choice.
UI fonts (Ubuntu, notosans_8) also omit `--pnum` to preserve tabular
alignment for page numbers, battery percentages, etc.
| Before | After |
| -- | -- |
| <img
src="https://github.com/user-attachments/files/26042238/screenshot-31673.bmp"
width="300" /> | <img
src="https://github.com/user-attachments/files/26042241/screenshot-124075.bmp"
width="300" /> |
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**YES**_
## Summary
Improved Italian translations provided by @alan0ford, closes#1578.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
Opening XTC files with a high page count (e.g. *The Magic Mountain* at
4,187 pages) causes an immediate `abort()` crash and reboot loop. The
device becomes unusable until the book is removed from the SD card.
**Crash log:**
```
abort() was called at 0x4214a5fb on core 0
```
### Root cause
During `XtcParser::open()`, the parser calls
`m_pageTable.resize(pageCount)` to load the entire page table into RAM.
Each `PageInfo` entry is 16 bytes, so:
- 4,187 pages x 16 bytes = **66,992 bytes (~65KB)** as a single
contiguous heap allocation
On the ESP32-C3 with ~380KB total RAM (no PSRAM), this allocation fails
after firmware, fonts, and the activity system are already loaded.
Because the firmware is compiled with `-fno-exceptions`, the failed
`new` inside `std::vector::resize()` calls `abort()` instead of
throwing.
This affects any XTC file with roughly 3,000+ pages, depending on heap
state at the time of loading.
## Solution
Replace the bulk page table allocation with on-demand reads from the SD
card. Instead of loading all page table entries into a vector at file
open, we now:
1. Read only the **first** page table entry at open time (to get default
page dimensions)
2. Read a **single** 16-byte entry from the SD card each time a page is
loaded
This reduces page table memory usage from `pageCount * 16` bytes to
**zero bytes**, regardless of how many pages the file contains.
### Changes
| File | What changed |
|------|-------------|
| `XtcParser.h` | Removed `std::vector<PageInfo> m_pageTable`. Added
`readPageTableEntry()` for on-demand reads. |
| `XtcParser.cpp` | Replaced `readPageTable()` with
`readFirstPageInfo()`. Updated `getPageInfo()`, `loadPage()`, and
`loadPageStreaming()` to seek and read individual entries from the file.
|
## Trade-offs
### Performance
Each page turn now requires one additional SD card seek + 16-byte read
to look up the page table entry before reading the page data itself.
- SD card sequential read latency: ~0.1-0.5ms for a 16-byte read
- E-ink full display refresh: ~1,000-2,000ms
I personally can't see any performance difference while reading and the
trade off of not boot looping seems to make this well worth it.
### Memory
| Metric | Before | After |
|--------|--------|-------|
| Page table RAM (4,187 pages) | ~65KB | 0 bytes |
| Page table RAM (1,000 pages) | ~16KB | 0 bytes |
| Page table RAM (max 65,535 pages) | ~1MB (impossible) | 0 bytes |
## Summary
* **What is the goal of this PR?**
Make adaptation instead of pure translation.
* **What changes are included?**
Fix issues where text could not fit in line.
Fix didn't cover `KOReader` settings
**Additional Reviever**
@mirus-ua
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
---------
Co-authored-by: Kym_Adnriy <kym_andr@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
**What is the goal of this PR?**
Added `destroyXmlParser()` helper to replace the repeated 4-line parser
cleanup block (stop, clear callbacks, free, null) that was copyied
across 6 XML parser files.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
* **What is the goal of this PR?** Replace the o(r^2) routines with a
o(r) scanline logic - will make fillArc roughly 50% faster and drawArc
roughly 5x faster. Still probably unnoticeable.
* **What changes are included?**
## Additional Context
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
## Summary
Battery percentage is calculated from the voltage, which is not totally
stable. We smooth the battery percentage using a moving average.
## Additional Context
issue discussion:
https://github.com/crosspoint-reader/crosspoint-reader/issues/1444
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code?
PARTIALLY
## Summary
- Removes the vendored `picojpeg` library and rewrites
`JpegToBmpConverter` to use the already-present `JPEGDEC` (bitbank2)
dependency
- Eliminates the redundancy of having two JPEG decoders in the firmware
- All BMP output (headers, fixed-point scaling, Atkinson/Floyd-Steinberg
dithering) is identical to before — cached cover BMPs are unaffected
## Size impact
| | Before | After | Delta |
|---|---|---|---|
| Flash | 5,754,089 bytes (87.8%) | 5,744,777 bytes (87.7%) | **−9,312
bytes** |
| RAM | 95,212 bytes (29.1%) | 92,852 bytes (28.3%) | **−2,360 bytes** |
## Implementation notes
- `bmpDrawCallback` receives MCU-sized blocks from JPEGDEC (up to 16
rows × MCU-width), accumulates them into a pre-allocated `mcuBuf`, and
applies the same scaling + dithering logic once each MCU row is complete
- File I/O uses a file-scope static `FsFile*` (safe in single-threaded
embedded context) via JPEGDEC's open/read/seek callbacks — same pattern
as `JpegToFramebufferConverter`
- Added a 52 KB free-heap guard before allocating the JPEGDEC object
(~17 KB)
- `lib/picojpeg/` deleted (2,087 lines of C removed)
## Test plan
- [ ] Build compiles without warnings
- [ ] Cover art BMP cache regenerates correctly for EPUB books
- [ ] Home screen thumbnails (1-bit BMP path) render correctly
- [ ] Custom-size thumbnails (`jpegFileToBmpStreamWithSize`) render
correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
Previouls where ALL whitespace (and square brackes) removed from the
footnote link text, however some link texts are multiworded, like "`turn
to 252`" which were truncated into "`turnto252`", (an example from the
first book "Flight from the Dark" of the Lone Wolf book series by Joe
Dever, see [link](https://www.projectaon.org/en/Main/FlightFromTheDark))
* This change will only remove whitespaces from the beginning and end of
the string
so "` [ 12 ] `" will become "`12`" just like before, and "` turn to 252
`" will become "`turn to 252`".
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* Add missing swedish translations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
**What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
`DESTRUCTOR_CLOSES_FILE=1` is set in platformio.ini, which makes SdFat's
FsBaseFile destructor call close() automatically when a file goes out of
scope.
Three categories of file close calls remain untouched:
1. Close before Storage.remove() on the same path: ScreenshotUtil.cpp
closes the file before deleting it on write error. The remove might fail
if the file is still open.
2. Close before reopening the same variable: Epub.cpp writes a temp
NCX/nav file, closes it, then reopens it for reading. The
RecentBooksStore.cpp close before saveToFile() is the same pattern, it
rewrites the same file.
3. Close on member variables: BookMetadataCache.cpp (bookFile,
spineFile, tocFile), Section.cpp (file), XtcParser.cpp (m_file),
ZipFile.cpp (file). These persist beyond any single function scope, so
the destructor timing doesn't match the intended close point.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
**What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
Combining diacritical marks (U+0300–U+036F) were positioned using a
heuristic that centered them at the midpoint of the base glyph's
**advance width**. This worked acceptably for Bookerly but produced
visibly off-center marks for Noto Sans due to a fundamental difference
in how the two fonts design their combining mark metrics.
Builds on the work of #1037.
## The problem
The two built-in body fonts encode combining mark `left` offsets with
very different conventions:
| Mark | Bookerly `left` | Noto Sans `left` |
|---|---|---|
| U+0301 (acute) | -2 | -10 |
| U+0300 (grave) | -5 | -15 |
| U+0302 (circumflex) | -5 | -5 |
| U+0323 (dot below) | -2 | -11 |
Noto Sans uses large negative `left` values because its marks are
designed for placement at the post-advance cursor position, with `left`
pulling the bitmap back over the base glyph. Bookerly uses small offsets
because its marks sit closer to the glyph origin. The old `advance/2`
centering split the difference poorly — it happened to land close to
correct for Bookerly but placed Noto Sans marks roughly 6px left of
center on a typical lowercase letter.
There was also a bug in the vertical gap heuristic. It unconditionally
computed a `raiseBy` value to prevent above-baseline marks from
colliding with tall base glyphs, but it applied the same logic to
**below-baseline** marks like cedilla (U+0327), dot below (U+0323), and
ogonek (U+0328). For those marks, the math produced a large positive
raise (e.g., 24px for dot-below on 'a'), launching them above the
x-height instead of keeping them below the baseline.
## The fix
**Horizontal positioning**: Instead of centering at `advance/2`, align
the mark bitmap's visual midpoint directly over the base glyph bitmap's
visual midpoint. This uses the base glyph's actual `left` and `width`
rather than its advance width, producing correct results regardless of
how the font encodes its mark offsets.
**Vertical positioning**: The raise heuristic now checks `markTop -
markHeight > 0` and skips below-baseline marks entirely, leaving them at
their font-designed position.
**Consolidation**: The shared math is extracted into two `constexpr`
helpers (`combiningMark::centerOver` and
`combiningMark::raiseAboveBase`) in `EpdFontData.h`, eliminating the
previously triplicated inline calculations across `drawText`,
`drawTextRotated90CW`, and `getTextBounds`. The `MIN_COMBINING_GAP_PX`
constant is also centralized as `combiningMark::MIN_GAP_PX`.
| Before | After |
| -- | -- |
| <img
src="https://github.com/user-attachments/files/25752257/before-noto.bmp"
width="250" /> | <img
src="https://github.com/user-attachments/files/25752258/after-noto.bmp"
width="250" /> |
| <img
src="https://github.com/user-attachments/files/25752259/before-bookerly.bmp"
width="250" /> | <img
src="https://github.com/user-attachments/files/25752260/after-bookerly.bmp"
width="250" /> |
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**YES to analyze
differences between Noto Sans and Bookerly font metrics**_
---------
Co-authored-by: Uri Tauber <142022451+Uri-Tauber@users.noreply.github.com>
## Summary
If the system reboots from crash, display the reason and tell user to
include `crash_report.txt` file.
To test this, simply add an `assert(false)` somewhere inside the code.

---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? **NO**
## Summary
* **What is the goal of this PR?** Slightly increase the OOM limit when
loading epubs
* **What changes are included?** Switched vectors for parsing epubs to
deques, allowing use of more memory
## Additional Context
* Increases loadable epub size from 2000+ chapter/ToC entries loadable
to 5000+ chapter/ToC entries
* #1574, but without the complicated stuff
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
### Testing
| Commit | Book | Time |
|----------|------|-------|
| 83cd96bc2f | 1000.epub | ~30 sec |
| 83cd96bc2f | 5000.epub | crash |
| PR | 1000.epub | ~29 sec |
| PR | 5000.epub | ~2 min 20 sec |
=> No actual loading time regressions
[tested_epubs.zip](https://github.com/user-attachments/files/26645243/tested_epubs.zip)
## Summary
Adds an option to allow manual screen refresh on power button short
press.
## Additional Context
there's an option to refresh the screen after a set number of pages. but
sometimes a manual refresh is needed to clear up stale ink pixels. this
works everywhere both in and out of reading mode.
resolves#550
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it helps set the right
context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_, to
understand the project structure.
## Summary
**What is the goal of this PR?**
Adds OPDS search support, allowing users to search a catalog directly
from the book browser when the server exposes an OpenSearch template.
**What changes are included?**
- `OpdsParser`: parses the OpenSearch template URL from feed-level
`<link rel="search">` elements and exposes it via `getSearchTemplate()`
- `OpdsBookBrowserActivity`: fetches and stores the search template
after each feed load; shows a Search hint on the Left button when a
template is available; launches the existing `KeyboardEntryActivity` for
query input; URL-encodes the query and fetches the result feed
- Absolute search result URLs are handled correctly in `fetchFeed`
(skips prepending the server base URL)
- A `consumeConfirm` guard prevents the Confirm release that submits the
keyboard from immediately triggering a book download on the first
browsing frame after search results load
## Additional Context
- Search is silently unavailable if the server does not advertise an
OpenSearch template — no UI change in that case
- Tested against a Calibre-Web OPDS endpoint which exposes `<link
rel="search" type="application/opensearchdescription+xml">`
- The inline URL encoder in `performSearch` was necessary as
`StringUtils` has no such utility; worth considering extracting to
`StringUtils` in a follow-up
- No new dependencies introduced
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
---------
Co-authored-by: kira <rammah@tuta.io>
Co-authored-by: Justin Mitchell <justin@jmitch.com>
## Summary
This fixes navigating back from the BMP Viewer to the FileBrowser which
was broken when moving to the new ActivityManager
This is fixed by making FileBrowserActivity able to take a full file
path on enter and splitting the basePath and fileName from it and
navigating to the correct place.
fixes:
https://github.com/crosspoint-reader/crosspoint-reader/issues/1553
duplicates:
https://github.com/crosspoint-reader/crosspoint-reader/pull/910 to some
extend but mine has the file cursor at the correct file instead of the
first one in the folder
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
---------
Co-authored-by: Jan Ivanov <jan.ivanov@sirma.com>
Adde
## Summary
* **What is the goal of this PR?**
Adding Slovenian translation
* **What changes are included?**
Just new slovenian.yaml file with translated strings
## Additional Context
I kindly ask to include it in next release.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
---------
Co-authored-by: Andrej Kralj <andrej.kralj@gmail.com>
Co-authored-by: Uri Tauber <142022451+Uri-Tauber@users.noreply.github.com>
## Summary
**What is the goal of this PR?**
Replaced manual 1-bit BMP header logic in `Xtc::generateCoverBmp()` and
`Xtc::generateThumbBmp()` with calls to the existing `createBmpHeader()`
utility. Added a `BmpRowOrder` enum and param to support the top-down
row order of XTC cover images.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
* **What is the goal of this PR?**
Fix two issues pointed out by `CodeRabbit` in #1433:
1. Free the output buffer in `readFileToMemory` on early error paths
(prevents memory leaks).
2. Check `out.write()` return value in the STORED path (same as in the
DEFLATED path).
I can confirm both issues were real (not hallucinations). Both fixes are
minimal — no behavior change on success.
---
### AI Usage
Did you use AI tools to help write this code? _**< NO >**_
## Summary
**What is the goal of this PR?**
Added `ScopedOpenClose` RAII guard to eliminate repetitive
`wasOpen`/`close()` boilerplate across all ZipFile methods.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
Improves text antialiasing quality on the Xteink X3 (SSD1677) display to
bring it closer to X4 rendering quality. Addresses white lines through
letter strokes and ghosting artifacts during page turns and screen
transitions.
## Changes
### Display Driver (open-x4-sdk)
- Dedicated X3 grayscale LUTs with tuned VDL drive strengths for dark
gray (2 time units) and light gray (3 time units), with active GND hold
on non-gray transitions to prevent floating source crosstalk
- Tight scan timing: TP2/TP3 reduced to 1 (total gate-on 7 units vs 17),
minimizing parasitic charge leakage that caused white lines through
letter strokes
- Fast diff BB reinforcement: Added mild VDH reinforcing pulse to
lut_x3_bb_full so black pixels are actively driven during differential
refreshes, clearing gray residue/ghosting
- displayGrayBuffer() updated to use the dedicated gray LUT bank instead
of full refresh LUTs for X3
### Rendering Pipeline
- Re-enabled light gray rendering for X3 text and images, now safe with
dedicated gray LUTs providing proper 4-level gray
- Removed isLightGrayRestricted() gating that was limiting X3 to 3-level
gray
- Runtime display dimensions in DirectPixelWriter and ScreenshotUtil,
replaced hardcoded constants with runtime getters to support X3 792x528
resolution
### Note
The open-x4-sdk submodule references a commit on
juicecultus/community-sdk. A corresponding PR to
open-x4-epaper/community-sdk should be merged first so the submodule ref
resolves on upstream.
## Testing
Tested on physical X3 hardware. White lines through letters
significantly reduced, in-book ghosting improved via BB reinforcement,
antialiasing visually closer to X4 quality.
## AI Disclosure
Yes, AI was used to assist with the development of these changes.
---------
## Summary
* More consistent string formatting in logPrintf
* Moved [ and ] from log level strings into new format string
* Behaviour change: Early exit if user string format fails
## Additional Context
* Should not have performance implications, debug monitor etc work as
before
* Clamp may be unnecessary due to information snprintf currently never
being able to exceed max buffer length, but not having it would bug me
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY, to reason
about correctness**_
## Summary
**What is the goal of this PR?**
A tweak to the fixed-point x-advance and kerning calculations to ensure
that the spacing between any two glyphs is always calculated
consistently.
I noticed that sometimes I'd see common character pairs like "oo" more
than once on a page, and the distance between the two snapped to
different pixels depending on the running accumulated error for the line
of text.
This change uses a differential rounding approach where each glyph's
x-advance plus the kerning relative to the next glyph are combined in
fixed-point precision, then snapped to a pixel to draw the next glyph.
This results in a consistent inter-glyph spacing any time the same two
glyphs show up adjacent to each other, regardless of the accumulated
error across the line.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
Replace hardcoded DISPLAY_WIDTH, DISPLAY_HEIGHT, and DISPLAY_WIDTH_BYTES
constants with runtime values from display object to support multiple
device models (X3 and X4) with different screen dimensions.
## Summary
* **What is the goal of this PR?**
Solve the issue
https://github.com/crosspoint-reader/crosspoint-reader/issues/1409
* **What changes are included?**
Updated translation for footnotes
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?**
Fix inaccurate Russian UI text for the reader auto-turn feature and make
the affected Russian labels consistent with how adjacent UI strings are
formatted.
* **What changes are included?**
Updated Russian translations in `lib/I18n/translations/russian.yaml`:
- changed `Auto Turn` text from wording that implied screen rotation to
wording that means automatic page turning
- adjusted a few Russian prefix/separator strings to include spacing
where the UI concatenates labels with dynamic values
## Screenshots
| Before| After |
|--------|--------|
| <img width="480" height="800" alt="image"
src="https://github.com/user-attachments/assets/db416dd2-6174-4a46-bd3a-6f52d1cc01cb"
/>| <img width="480" height="800" alt="image"
src="https://github.com/user-attachments/assets/dd9055e0-d4f1-459d-bd40-60fc4970d458"
/>|
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
**What is the goal of this PR?**
Replace verbose constructor initializer lists with in-class default
member initializers in JpegContext and PngContext
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
EPUBs that use ISO 639-2 three-letter language codes in their
`dc:language` metadata (e.g. `<dc:language>eng</dc:language>`) got no
hyphenation. The hyphenator registry only matched ISO 639-1 two-letter
codes (`"en"`, `"fr"`, etc.), so `"eng"` produced a null hyphenator and
every word in the book was treated as unhyphenatable.
Added a normalization step in `hyphenatorForLanguage` that maps ISO
639-2 codes (both bibliographic and terminological variants) to their
two-letter equivalents before the registry lookup.
Discovered via *Project Hail Mary* (Random House), which uses
`<dc:language>eng</dc:language>`.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
Adds Xteink X3 hardware support to CrossPoint Reader. The X3 uses the
same SSD1677 e-ink controller as the X4 but with a different panel
(792x528 vs 800x480), different button layout, and an I2C fuel gauge
(BQ27220) instead of ADC-based battery reading.
All X3-specific behavior is gated by runtime device detection — X4
behavior is unchanged.
Depends on community-sdk X3 support: open-x4-epaper/community-sdk#19
(merged).
## Changes
### HAL Layer
**HalGPIO** (`lib/hal/HalGPIO.cpp/.h`)
- I2C-based device fingerprinting at boot: probes for BQ27220 fuel
gauge, DS3231 RTC, and QMI8658 IMU to distinguish X3 from X4
- Detection result cached in NVS for fast subsequent boots
- Exposes `deviceIsX3()` / `deviceIsX4()` helpers used throughout the
codebase
- X3 button mapping (7 GPIOs vs X4's layout)
- USB connection detection and wake classification for X3
**HalDisplay** (`lib/hal/HalDisplay.cpp/.h`)
- Calls `einkDisplay.setDisplayX3()` before init when X3 is detected
- Requests display resync after power button / flash wake events
- Runtime display dimension accessors (`getDisplayWidth()`,
`getDisplayHeight()`, `getBufferSize()`)
- Exposed as global `display` instance for use by image converters
**HalPowerManager** (`lib/hal/HalPowerManager.cpp/.h`)
- X3 battery reading via I2C fuel gauge (BQ27220 at 0x55, SOC register)
- X3 power button uses GPIO hold for deep sleep
### Display & Rendering
**GfxRenderer** (`lib/GfxRenderer/GfxRenderer.cpp/.h`)
- Buffer size and display dimensions are now runtime values (not
compile-time constants) to support both panel sizes
- X3 anti-aliasing tuning: only the darker grayscale level is applied to
avoid washed-out text on the X3 panel. X4 retains both levels via
`deviceIsX4()` gate
**Image Converters** (`lib/JpegToBmpConverter`, `lib/PngToBmpConverter`)
- Cover image prescale target uses runtime display dimensions from HAL
instead of hardcoded 800x480
### UI Themes
**BaseTheme / LyraTheme** (`src/components/themes/`)
- X3 button position mapping for the different physical layout
- Adjusted UI element positioning for 792x528 viewport
### Boot & Init
**main.cpp**
- X3 hardware detection logging
- Adjusted init sequence for X3 (no `HalSystem::begin()` dependency on
X3 path)
**HomeActivity**
- Uses runtime `renderer.getBufferSize()` instead of static
`GfxRenderer::getBufferSize()`
FYI I did not add support for the gyro page turner. That can be it's own
PR.
Full Hungarian localization for the firmware with all UI elements and
system messages translated.
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
* **What changes are included?**
Added hungarian.yaml language file
Translated all UI elements and system messages into Hungarian
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
I am a native Hungarian speaker. The initial translation was assisted by
AI, but I reviewed and corrected all translation errors to ensure a
natural and accurate Hungarian localization.
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
## Summary
* Add Lithuanian transilation
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? YES
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Replace per-pixel getRenderMode() + rotateCoordinates() + bounds checks
with a DirectPixelWriter struct that pre-computes orientation and render
mode state once per row. Use bitwise ops instead of division/modulo for
cache pixel packing. Skip PNG cache allocation when buffer exceeds 48KB
(framebuffer size) since PNG decode is fast enough that caching provides
minimal benefit, and the large buffer competes with the 44KB PNG decoder
for heap.
## Additional Context
Measured improvements on ESP32-C3 @ 160MHz:
- JPEG decode: 5-7% faster (1:1 scale)
- PNG decode: 15-20% faster (1:1 scale)
- Cache renders: 3-6% faster across both formats
- Eliminates "Failed to allocate cache buffer" errors for large PNGs
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< PARTIALLY >**_
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
Added new Dutch translations
* **What changes are included?**
New Dutch translations
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_
Added missing strings for the French translation.
## Summary
Adding the missing French translated strings before the next PR.
No modification done to the preexisting strings, only added new ones.
## Additional Context
N/A
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
No AI usage.
## Summary
* **What is the goal of this PR?** Updating German language file
* **What changes are included?**
## Additional Context
- None
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< NO >**_
## Summary
Polish localization for `STR_SHOW_HIDDEN_FILES`
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
Adds the missing Catalan translation for STR_SHOW_HIDDEN_FILES in
lib/I18n/translations/catalan.yaml.
* **What changes are included?**
Only modified catalan.yaml file
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? NO
## Summary
* **What is the goal of this PR?**
fix swedish translation
* **What changes are included?**
lib\I18n\translations\swedish.yaml
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_