## 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**_
Adds the missing Turkish translation for STR_SHOW_HIDDEN_FILES in
lib/I18n/translations/turkish.yaml.
- Added: STR_SHOW_HIDDEN_FILES: "Gizli Dosyaları Göster"
- Scope: Turkish only (single-key micro-fix)
This follows up the missing-translations callout in #1483.
Co-authored-by: Barış Albayrak <barisa@pop-os.lan>
## Summary
* Adding Spanish translation for string `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
- Add CSS `display: none` support to the EPUB rendering pipeline (fixes
#1431)
- Parse `display` property in stylesheets and inline styles, with full
cascade resolution (element, class, element.class, inline)
- Skip hidden elements and all their descendants in
`ChapterHtmlSlimParser`
- Separate display:none check for `<img>` tags (image code path is
independent of the general element handler)
- Flush pending text blocks before placing images to fix layout ordering
(text preceding an image now correctly renders above it)
- Bump CSS cache version to 4 to invalidate stale caches
- Add test EPUB (`test_display_none.epub`) covering class selectors,
element selectors, combined selectors, inline styles, nested hidden
content, hidden images, style priority/override, and realistic use cases
## Summary
Fixes#1263
I spent half of my day(-off) reverse engineering the stock english
firmware V3.1.1, it's more or less like solving a sudoku with some known
pieces (like debug strings, known static addresses, known compiled
function, etc) and then the task is to guess the rest.
Long story short, this is the sleep routine that they use:
<img width="674" height="604" alt="image"
src="https://github.com/user-attachments/assets/6d53ce44-7bae-40c7-b4fb-24f898dbcc05"
/>
From the code above:
- They pull down GPIO13 (value = 0xd) before sleep
- They verify that power button is released by doing a delay loop of
50ms, similar to what we're doing
- `esp_sleep_config_gpio_isolate` is called but I'm not 100% sure why
- Pull up power button, note that it's likely redundant because power
button should already pulled up by `InputManager`
- `param1` and `param2` means enabling front/side buttons for wake up,
but it doesn't used in the code in reality. But I think it's physically
impossible, see the explanation below
- `param3` means "wake up from power button"
- `esp_sleep_start` is used; there is a logic to handle if it fails to
sleep, then retry recursively (no idea why!)
My observation is that they use GPIO13 so that it will be on HIGH state
when the chip is powered on, without any user space code to keep it on
that state. And once going to deep sleep, it goes into FLOATING by
default. That may explain why it need to be in LOW state before going to
sleep. (Nice trick btw)
Looking again at the circuit diagram provided
[here](https://github.com/sunwoods/Xteink-X4/blob/main/readme-img/sch.jpg)
(note: it's not official):
<img width="705" height="384" alt="image"
src="https://github.com/user-attachments/assets/b98d59fd-47ca-4d3d-a24a-94bf999e957b"
/>
It kinda make sense as the GPIO13 and VBUS (USB VCC) have the same role,
they are part of a simple "battery protection" cirtuit
Now, we may wonder, how the device wake up when there is no battery at
all?
<img width="440" height="323" alt="image"
src="https://github.com/user-attachments/assets/2981c411-239b-49a7-b9f7-9a75b6c1b6d3"
/>
It seems like power button is not just a simple switch between GPIO3 and
ground, but it also linked the POWER_CTRL, which leads to nowhere on the
diagram, but I suppose it connects the battery back for a short amount
of time, just enough for the MCU to wake up, and GPIO13 goes HIGH again.
It may also explain why power button becomes non-responsive for ~1
second after power on, as it's being pulled up by the current from
battery (remind: high = not pressed, low = pressed)
To test the theory above, I simply **comment out** the
`esp_deep_sleep_enable_gpio_wakeup`:
- On battery, power button works as nothing happen
- On USB, it doesn't wake up, I need to press RST
---
Important things about my analysis:
1. I had to name every function on the code above **manually**, but I'm
99% confident about it. The only function that I'm not sure is
`esp_wifi_bt_power_domain_off` ; Edit: it was indeed mislabeled, see
https://github.com/crosspoint-reader/crosspoint-reader/pull/1298#discussion_r2879670852
2. Some logic inside the stock firmware looks very strange, there is
almost no mention to "arduino" in the hardware, suggesting that they may
just call esp-idf functions directly, bypassing the arduino abstraction.
---
### 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: Zach Nelson <zach@zdnelson.com>
## Summary
**What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
Fix prewarm perf when a page contains many styles.
The prewarm page buffer was a single slot, so each `prewarmCache` call
for a new font style freed the previous style's glyphs. On pages with
multiple styles (regular + bold + italic), only the last style was
prewarmed. The others fell through to the hot-group compaction path at
~2-3ms per glyph.
This was most visible on rich formatting (e.g. this [Czech prayer
book](https://stahuj.kancional.cz/e-kniha/kancional.epub) with bold
headings, italic liturgical text, and regular body), where page renders
took 3-5 seconds instead of ~700ms.
Fix: use up to 4 page buffer slots (one per font style) so all styles
stay prewarmed simultaneously.
Fixes#1450.
---
### 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 diagnose and
brainstorm solutions.
## Summary
* **What is the goal of this PR?** All praise goes to @didacta for his
PR #537. Just picked up the reviewer comments to contain the changes as
suggested (there was no response for more than 6 weeks, so I wanted to
reanimate this feature).
Just one addition: should recognize usb cable plug ins / retractions and
update the icon immediately
* **What changes are included?**
## Additional Context
see #537
---
### 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?**
Removing no longer used i18n keys/string, to reduce (~28k) used flash
space.
To correct to swedish translations for `STR_FONT_SIZE` and
`STR_KOREADER_SYNC`.
* **What changes are included?**
`lib\I18n\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? _**NO**_
## Summary
* **What is the goal of this PR?** Add setting to display hidden files /
directories in filebrowser / web file browser
* **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
* **What is the goal of this PR?** Fix leak on decode error path in JPEG
converter
* **What changes are included?**
Unif resource cleanup
## 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? _**PARTIALLY**_
Identification of the issue by AI
## Summary
* **What is the goal of this PR?** Implements Kazakh language
* **What changes are included?**
## 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? YES
---------
Co-authored-by: Okzhetpes <okhzetpes.20@gmail.com>
## Summary
* **What is the goal of this PR?** Fixing / extending the hyphenation
logic to deal with words containing an apostophe as raised in #1186
* **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? _**PARTIALLY**_ (as the
user provided a thorough analysis that I followed)
## Purpose
This PR includes some preparatory changes that are needed for an
upcoming performant CJK font feature. The changes have no impact on
render time and heap allocation for latin text. **Despite this, I think
these changes stand on their own as a better font
compression/decompression implementation.**
## Summary
- Font decompressor rewrite: Replaced the 4-slot LRU group cache with a
two-tier system — a page buffer (glyphs prewarmed before rendering
begins) and a hot-group fallback (last decompressed group retained for
non-prewarmed
glyphs).
- Byte-aligned compressed bitmap format: Glyph bitmaps within compressed
groups are now stored row-padded rather than tightly packed before
DEFLATE compression, improving compression ratios by making identical
pixel rows produce
identical byte patterns. Glyphs are compacted back to packed format on
demand at render time. Reduces flash size by 155 KB.
- Page prewarm system: Added `Page::collectText` and
`Page::getDominantStyle` to extract per-style glyph requirements before
rendering, and `GfxRenderer::prewarmFontCache` to pre-decompress only
the groups needed for the dominant style
— eliminating mid-render decompression for the common case.
- UTF-8 robustness fixes: `utf8NextCodepoint` now validates continuation
bytes and returns a replacement glyph on malformed input;
`ChapterHtmlSlimParser` correctly preserves incomplete multi-byte
sequences across word-buffer flush
boundaries rather than splitting them.
---
### 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**_ Architecture and
design was done by me, refined a bit by Claude. Code mostly by Claude,
but not entirely.
## Summary
* **What is the goal of this PR?**
- Improve and add the latest missing Spanish translations
* **What changes are included?**
- Replaced `Der.` with `Dcha.`, as it's a more common abbreviation
- Replaced `Selec.` with `Selecc.` (same reason as in the previous case)
- Added missing strings `STR_IMAGES`, `STR_IMAGES_DISPLAY`,
`STR_IMAGES_PLACEHOLDER` and `STR_IMAGES_SUPPRESS`
- Rewording certain translations to make them clearer
---
### 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?**
### Problem
Inter-word gap widths were computed as two separately-snapped integers:
```cpp
gap = getSpaceWidth(fontId, style); // fp4::toPixel(spaceAdvance)
gap += getSpaceKernAdjust(fontId, leftCp, rightCp, style); // fp4::toPixel(kern1 + kern2)
```
Because `fp4::toPixel(a) + fp4::toPixel(b)` can differ from
`fp4::toPixel(a + b)` by +/-1 pixel when the fractional parts straddle a
rounding boundary, each inter-word space could be one pixel wider or
narrower than the correct value. This affected line-break width
decisions and word-position accumulation across the whole paragraph
layout pipeline.
### Fix
Replaces `getSpaceKernAdjust()` with `getSpaceAdvance(fontId, leftCp,
rightCp, style)`, which combines the space glyph advance and both
flanking kern values (`kern(leftCp, ' ')` + `kern(' ', rightCp)`) into a
single fixed-point sum before the snap:
```cpp
return fp4::toPixel(spaceAdvanceFP + kern(leftCp, ' ') + kern(' ', rightCp));
```
This is the same single-snap pattern already used by `getTextAdvanceX`
for word widths.
### Changes
- **`GfxRenderer`**: Replaces `getSpaceKernAdjust()` with
`getSpaceAdvance()`. `getSpaceWidth()` is retained for the
single-space-word case in `measureWordWidth` where no adjacent-word kern
context is available.
- **`ParsedText`**: All four call sites (`computeLineBreaks`,
`computeHyphenatedLineBreaks`, and both loops in `extractLine`) updated
to use `getSpaceAdvance()`. The now-redundant `spaceWidth`
pre-computation and parameter are removed from all three internal layout
functions.
---
### 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 for
correctness**_
## Summary
Fixes a crash (load access fault) when opening EPUB chapters whose first
text block exceeds 750 words.
## Changes
* **Crash fix (`addLineToPage`)**: Added a null guard for `currentPage`.
If `makePages()` hasn't run yet (which can happen when the first block
triggers the "text block too long" split path), the page is now created
on demand.
* **Layout fix (`characterData`)**: The early-split path previously used
`viewportWidth`, ignoring CSS margins and padding. It now computes
`effectiveWidth` using `totalHorizontalInset()`, consistent with
`makePages()`.
## Additional Context
* Crash signature: `MCAUSE=0x5` (load access fault), `A0=0x0` (`Page*`
null), `MTVAL=0x4 / 0x8` (offsets into `Page::elements`).
* Confirmed in two user reports reported in #1328
* Tested on PR #1357 (not on `master`).
---
### 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 >**_