## Summary This release adds SD card fonts — the most-requested feature since launch — brings the X3 to first-class status, redesigns the on-screen keyboard, overhauls OPDS, and ships SD-card firmware updates. 144 changes from 53 contributors, 32 of whom are new to the project. **🔠 SD Card Fonts** Custom fonts are here. A complete font subsystem lets you install and use fonts beyond the three built-in families. A new `.cpfont` binary format packs multiple styles (regular, bold, italic, bold-italic) into a single file per size, with on-demand glyph loading from the SD card. A two-pass prewarm renderer bulk-reads glyphs per page, achieving near-flash performance for Latin text and viable CJK rendering. Fonts can be downloaded over WiFi directly from the device, uploaded via the web interface, or copied manually to the SD card. The build pipeline ships a 17-family font library (serif, sans, mono, accessibility) with CI distribution via a dedicated crosspoint-fonts repository. As a bonus, CJK characters no longer get spurious hyphens at line breaks, and an advance-table cache eliminates 30+ second stalls during CJK section indexing. **📱 X3 Comes of Age** The X3 graduates from initial bring-up to a proper target. Grayscale antialiasing is sharper, EPUB images render correctly, OTA updates work, and sleep screen dimensions are dialed in. The headline addition: gyroscope-based tilt page turning via the QMI8658 IMU — tilt the device to turn pages hands-free. SD-card firmware update support and X3 bootloader compatibility mean users can update without a USB connection. **⌨️ Redesigned On-Screen Keyboard** The keyboard has been completely redesigned with improved layout, better key feedback, and a fix for the space key barely moving the cursor. Text entry across WiFi setup, OPDS search, and KOSync login is noticeably smoother. **👁️ Focus Reading** A new reading mode bolds the initial characters of each word (similar to Bionic Reading) to create artificial fixation points, helping improve reading speed and focus. The bolding ratio is 45%, with a minimum of 1 character and a maximum of 9, applied dynamically during indexing. **📚 OPDS Overhaul** OPDS gains in-catalog search with next/prev page navigation, support for multiple servers, correct handling of relative paths and query parameters (fixing CopyParty compatibility), and KOReader-compatible download filenames. **🔤 Text Rendering Refinements** Combining marks (diacritics) now use font metrics for positioning instead of heuristics, proportional numeral spacing is supported, and differential rounding eliminates uneven inter-glyph gaps. Hyphenation now recognizes ISO 639-2 language codes, nested block-level CSS styles are tracked correctly, and horizontal CSS insets are capped at 2em to prevent runaway margins. Bookerly has been replaced with Noto Serif for licensing reasons. **🎨 New Theme: RoundedRaff** A new rounded theme joins the theme picker, with fixes for sleep cover crop grid artifacts. **🔋 Battery & Power** Battery percentage smoothing on the X4 eliminates jittery readings. A short press on the power button can be set to trigger a manual screen refresh — handy for clearing ghosting. **📶 WiFi & Networking** WiFi connections now self-heal from transient drops without manual intervention, and a dBm signal strength indicator appears during web server sessions. WiFi networks can be edited directly from the web UI. **🔄 KOSync** Reading position sync is significantly more accurate. The old character-offset approach frequently landed on the wrong paragraph after syncing between devices — the new xpath-based mapping syncs at the paragraph level, matching KOReader's own behavior. A separate fix switches the HTTP layer to `esp_http_client`, and the reader now releases ~65KB of EPUB heap before the TLS handshake — together these eliminate the out-of-memory crashes that plagued KOSync on large books. **🛡️ Stability** Two memory leaks patched, a wild pointer crash in JPEGDEC MCU_SKIP handling fixed, boot loops with large XTC files eliminated, legacy XTC headers supported, the OTA updater now streams GitHub release JSON instead of buffering it in RAM, and a JPEG downscaler y-axis scale factor bug is corrected. **🌐 Languages** Slovenian is new. Russian, Ukrainian, Swedish, Italian, and Spanish translations received significant updates. --- Also in this release: **SD-card firmware updates without USB**, **file extensions in the file browser**, **full path bar navigation**, **end-of-book navigation improvements**, **XTC status bar**, **smarter "Cover + Custom" sleep screens**, **set sleep cover from the BMP viewer**, **orientation-aware popups**, **page turn buttons that follow orientation**, **long-press delete for directories**, **context-aware screenshot filenames with book title**, **crash reason displayed on boot**, **empty line rendering in the TXT reader**, **wallpaper recency buffer to prevent clustering**, **font family deletion from the device**, **next/prev labels in the BMP viewer**, **non-breaking space justification fix**, **README guidance for USB-locked third-party Xteink units**, and a long tail of web UI polish, i18n memory optimizations, and code quality improvements. ## What's Changed ### Features * feat: add SD card font support with on-device download and web management by @adriancaruana, @znelson, @itsthisjustin, @jpirnay, and @mcrosson * feat: Initial support for the x3 by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/875 * feat: X3 grayscale antialiasing improvements by @juicecultus in https://github.com/crosspoint-reader/crosspoint-reader/pull/1607 * feat: X3 gyroscope-based tilt page turning via QMI8658 IMU by @juicecultus in https://github.com/crosspoint-reader/crosspoint-reader/pull/1636 * feat(update): SD-card firmware update + X3 bootloader compatibility by @eunchurn in https://github.com/crosspoint-reader/crosspoint-reader/pull/1786 * feat: self-heal from transient WiFi loss, add dBm indicator during WebServerActivity by @jeremydk in https://github.com/crosspoint-reader/crosspoint-reader/pull/1780 * feat: edit wifi networks in webui by @osteotek in https://github.com/crosspoint-reader/crosspoint-reader/pull/1743 * feat: add OPDS search support & next/prev page navigation by @rxmmah in https://github.com/crosspoint-reader/crosspoint-reader/pull/1462 * feat: Support for multiple OPDS servers by @osteotek in https://github.com/crosspoint-reader/crosspoint-reader/pull/1209 * feat: Adjust Navigation at End of Book by @nscheung in https://github.com/crosspoint-reader/crosspoint-reader/pull/1425 * feat: Display file extensions in File Browser by @CaptainFrito in https://github.com/crosspoint-reader/crosspoint-reader/pull/1019 * feat: show full path bar in file browser by @zgredex in https://github.com/crosspoint-reader/crosspoint-reader/pull/1411 * feat: enable manual screen refresh on power button short press by @bdeshi in https://github.com/crosspoint-reader/crosspoint-reader/pull/1626 * feat: Rework "Cover + Custom" sleep screens to show covers only when currently reading by @iandchasse in https://github.com/crosspoint-reader/crosspoint-reader/pull/1256 * feat: Set sleep cover from BMP viewer by @el in https://github.com/crosspoint-reader/crosspoint-reader/pull/1104 * feat: show crash reason on boot by @ngxson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1453 * feat: Support for proportional numeral spacing by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1414 * feat: add orientation-aware popups for reader activities by @mrtnvgr in https://github.com/crosspoint-reader/crosspoint-reader/pull/1428 * feat: smooth battery percentage for x4 by @jonvex in https://github.com/crosspoint-reader/crosspoint-reader/pull/1635 * feat: context-aware screenshot filenames with book title by @jonstieglitz in https://github.com/crosspoint-reader/crosspoint-reader/pull/1589 * feat(theme): add roundedraff theme and fix sleep cover crop grid artifacts by @bunsoootchi in https://github.com/crosspoint-reader/crosspoint-reader/pull/918 * feat: Page turn button orientation change by @mchuck in https://github.com/crosspoint-reader/crosspoint-reader/pull/1069 * feat: Status bar for XTC files by @leecming82 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1849 * feat: enhance long press action to delete both files and directories by @WuTofu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1803 * feat: Added Slovenian translation by @thehijacker in https://github.com/crosspoint-reader/crosspoint-reader/pull/1551 * feat: focus reading by @vjapolitzer in https://github.com/crosspoint-reader/crosspoint-reader/pull/1670 * feat: add next / prev labels to bmp viewer by @Telemaniaka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1852 * feat: add font family deletion functionality by @WuTofu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1919 * feat: separate into "Download All" and "Update All" in font manager by @WuTofu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1955 * feat: verify CRC32 checksum for font files by @WuTofu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1904 * feat: increase default weight of Bitter font for improved rendering by @uxjulia in https://github.com/crosspoint-reader/crosspoint-reader/pull/1922 * feat: allow unnamed intervals by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1903 ### Fixes * fix: epub images not rendering correctly on x3 by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1572 * fix: OTA update on x3 and progress bar on x4 and x3 by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1805 * fix: boot looping when opening large XTC files by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1648 * fix: Wild pointer crash in JPEGDEC MCU_SKIP handling by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1627 * fix: two small memory leaks by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1628 * fix: use esp_http_client for KOSync to prevent TLS OOM on ESP32-C3 by @trilwu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1381 * fix: Read GH release JSON as stream in OTA updater by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1810 * fix: support legacy XTC file headers where pageTableOffset=48 by @uxjulia in https://github.com/crosspoint-reader/crosspoint-reader/pull/1816 * fix: Use font metrics for combining mark positioning by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1310 * fix: Use differential rounding for consistent inter-glyph spacing by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1413 * fix: Support hyphenation for EPUBs using ISO 639-2 language codes by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1461 * fix: Track block style stack for nested styles by @daveallie in https://github.com/crosspoint-reader/crosspoint-reader/pull/1582 * fix: cap per-side horizontal CSS inset at 2em by @rhoopr in https://github.com/crosspoint-reader/crosspoint-reader/pull/1694 * fix: increase loadable epub size by @CSCMe in https://github.com/crosspoint-reader/crosspoint-reader/pull/1638 * fix: Switch to xpath map for paragraph level syncing in KOSync by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1686 * fix: free Epub RAM and simplify KOSync navigation via ActivityManager by @wylanswets in https://github.com/crosspoint-reader/crosspoint-reader/pull/1860 * fix: improve KOSync bidirectional position matching accuracy by @wylanswets in https://github.com/crosspoint-reader/crosspoint-reader/pull/1897 * fix: Fix failing very first wifi connection attempt by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1521 * fix: avoid skipping chapter after screenshot by @Mraulio in https://github.com/crosspoint-reader/crosspoint-reader/pull/1625 * fix: back navigation from BMPViewer by @Telemaniaka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1597 * fix: Fix ghosting on exit of BMPViewer by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1432 * fix: make footnotes consider orientation for gutters by @Telemaniaka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1665 * fix: footnote link text by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1666 * fix: Erroneous navigation with long filenames in footnote links by @CSCMe in https://github.com/crosspoint-reader/crosspoint-reader/pull/1723 * fix: prevent wallpaper clustering with 16-entry recency buffer by @zgredex in https://github.com/crosspoint-reader/crosspoint-reader/pull/1606 * fix: webserver /delete API backward compatibility by @DianaNites in https://github.com/crosspoint-reader/crosspoint-reader/pull/1475 * fix: relative opds paths and query param with copyparty by @philips in https://github.com/crosspoint-reader/crosspoint-reader/pull/1535 * fix: use same file name as KOReader for OPDS downloads by @spfenwick in https://github.com/crosspoint-reader/crosspoint-reader/pull/1286 * fix: pressing space barely moves input cursor (#1729) by @pablohc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1733 * fix: keyboard feedback #1644 by @pablohc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1697 * fix: pluralize folder/file counts correctly in file list summary by @fain182 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1701 * fix: rendering bug of scrollbar in RoundedRaff theme by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1814 * fix: two roundedraff bugs by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1851 * fix: overlap in download font list layout by @pablohc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1900 * fix: remove duplicate 'Download Fonts' menu entry and improve navigation by @zgredex in https://github.com/crosspoint-reader/crosspoint-reader/pull/1893 * fix: Add common ligatures to SD font conversion ranges by @znelson * fix: capture instantiateVariableFont return value by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1911 * fix: Roundraff theme home menu offset with no recent books by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1845 * fix: Missing navigation button labels in Roundedraff theme by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1905 * fix: gracefully resolve fonts missing variants by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1921 * fix: distribute justifyExtra to non-breaking space tokens by @prawnwhoyawns in https://github.com/crosspoint-reader/crosspoint-reader/pull/1783 * fix: remove percent rendering from activities by @mcrosson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1901 * fix: Restore performance in fontconvert_sdcard.py by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1924 * fix: Prepare SD card font caches from txt reader by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1973 * fix: make script help paths lightweight by @sabraman in https://github.com/crosspoint-reader/crosspoint-reader/pull/1937 * fix: Replaced Bookerly with Noto Serif for licensing reasons by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1736 * fix: incorrect y-axis scale factor in jpeg nearest-neighbor downscaler by @WuTofu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1807 * fix: display empty lines in txt reader by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1841 * fix: short-press power action triggered after screenshot combo release by @pablohc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1853 * fix: correct Russian auto-turn translations by @a-ignatev in https://github.com/crosspoint-reader/crosspoint-reader/pull/1566 * fix: Update Ukrainian translations for footnotes (issue 1409) by @mirus-ua in https://github.com/crosspoint-reader/crosspoint-reader/pull/1585 * fix: missing swedish translations by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1667 * fix: Add swedish keyboard translations by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1726 * fix: swedish translations by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1762 * fix: swedish translation by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1829 * fix: swedish translation by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1888 * fix: Polish translation by @th0m4sek in https://github.com/crosspoint-reader/crosspoint-reader/pull/1909 * fix: Ukrainian-translation by @KymAndriy in https://github.com/crosspoint-reader/crosspoint-reader/pull/1946 * fix: Ukrainian translation by @KymAndriy in https://github.com/crosspoint-reader/crosspoint-reader/pull/1939 * fix: python requirements files by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1768 * fix: missing requirement by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1896 * fix: Use LOG_ macros in loc functions by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1794 ### Internal * refactor: redesign on-screen keyboard by @pablohc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1644 * refactor: replace picojpeg with JPEGDEC for cover art conversion by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1517 * refactor: Refactor drawArc / fillArc for faster execution by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1540 * perf: replace i18n pointer tables with offset tables, strip unused strings by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1408 * refactor: Store only unique localization strings in offset buffers by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1802 * refactor: Move language setting into JSON settings by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1796 * refactor: Use C++20 'requires' in ActivityResult constructor by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1420 * refactor: Use default member initializers for JpegContext and PngContext by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1435 * refactor: logPrintf and predefined log level strings by @CSCMe in https://github.com/crosspoint-reader/crosspoint-reader/pull/1546 * refactor: RAII scoped open/close for ZipFile by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1433 * refactor: Deduplicated BMP header writing in Xtc by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1439 * refactor: Added shared XML parser teardown helper by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1438 * refactor: Removed redundant FsFile close() calls by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1434 * refactor: Deduplicate battery drawing code and fix Lyra charging indicator by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1437 * refactor: Deduplicate Roundraff battery drawing by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1847 * refactor: Simplify sort in GfxRenderer::fillPolygon by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1817 * refactor: Avoid vector for page turn rates list by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1818 * refactor: Use std::size instead of sizeof/sizeof by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1819 * refactor: Use fixed-size integers for BookMetadataCache data by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1844 * refactor: Simplify isReaderActivity bookkeeping by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1838 * refactor: Simplify XtcReaderActivity with detectPageTurn by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1837 * refactor: change ukrainian translation to adaptation and add missing lines by @KymAndriy in https://github.com/crosspoint-reader/crosspoint-reader/pull/1828 * chore: drop JPEGDEC patch in favour of upstream fix by @martinbrook in https://github.com/crosspoint-reader/crosspoint-reader/pull/1465 * chore: clang-format.fix.ps1 script: Add .venv to list of path exclusions by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1515 * chore: Updating sleep screen dimensions for X3 by @jensechu in https://github.com/crosspoint-reader/crosspoint-reader/pull/1688 * chore: Clarify X3 RTC in SCOPE.md by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1687 * chore: Improved Italian translations by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1685 * chore: change ukrainian translation to adaptation by @KymAndriy in https://github.com/crosspoint-reader/crosspoint-reader/pull/1684 * chore: Update spanish.yaml by @mvidelatraduc in https://github.com/crosspoint-reader/crosspoint-reader/pull/1717 * chore: One Italian translation tweak by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1718 * chore: git pre-commit hook for format fix by @osteotek in https://github.com/crosspoint-reader/crosspoint-reader/pull/1730 * chore: Update SDK to fork in CrossPoint org by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1836 * chore: Added RAM to firmware_size_history.py script by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1830 * chore: Updated docs to reflect DESTRUCTOR_CLOSES_FILE=1 by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1878 * feat: cap compressed group size at 64 KB by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1913 * fix: build-script bug fixes for fontconvert{,_sdcard}.py by @jpirnay in https://github.com/crosspoint-reader/crosspoint-reader/pull/1910 * feat: include short SHA in CROSSPOINT_VERSION by @osteotek in https://github.com/crosspoint-reader/crosspoint-reader/pull/1728 * feat: show long branch names by @steka in https://github.com/crosspoint-reader/crosspoint-reader/pull/1727 * feat: enable pio build cache by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1769 * style: put page name first in browser titles by @fain182 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1703 * style: unify page headers across web UI by @fain182 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1702 * style: move file type badges into Type column by @fain182 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1793 * style: align action buttons vertically with page title by @fain182 in https://github.com/crosspoint-reader/crosspoint-reader/pull/1795 * docs: Update README with firmware flashing instructions by @ryneches in https://github.com/crosspoint-reader/crosspoint-reader/pull/1654 * docs: fix typos by @kianmeng in https://github.com/crosspoint-reader/crosspoint-reader/pull/1705 * docs: update README.md to reflect the current state of crosspoint by @Uri-Tauber in https://github.com/crosspoint-reader/crosspoint-reader/pull/1812 * docs: Add documentation for USB-locked Xteink devices by @itsthisjustin in https://github.com/crosspoint-reader/crosspoint-reader/pull/1990 * docs: expand first use of OPDS acronym and provide a wikipedia link by @sizezero in https://github.com/crosspoint-reader/crosspoint-reader/pull/1824 * docs: fix KOReader sync guide link by @sabraman in https://github.com/crosspoint-reader/crosspoint-reader/pull/1930 * docs: fix hyphenation updater script name by @sabraman in https://github.com/crosspoint-reader/crosspoint-reader/pull/1931 * fix: sd font download urls in docs by @mcrosson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1945 * fix: sd font folder paths in documentation by @mcrosson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1944 * chore: Add verbose mode to build-sd-fonts.py by @znelson in https://github.com/crosspoint-reader/crosspoint-reader/pull/1923 ## New Contributors * @a-ignatev made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1566 * @CSCMe made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1546 * @thehijacker made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1551 * @Telemaniaka made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1597 * @Mraulio made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1625 * @rxmmah made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1462 * @bdeshi made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1626 * @DianaNites made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1475 * @ryneches made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1654 * @zgredex made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1411 * @jonvex made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1635 * @KymAndriy made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1684 * @jensechu made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1688 * @kianmeng made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1705 * @philips made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1535 * @fain182 made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1701 * @mvidelatraduc made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1717 * @bunsoootchi made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/918 * @rhoopr made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1694 * @spfenwick made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1286 * @trilwu made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1381 * @jonstieglitz made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1589 * @uxjulia made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1816 * @mchuck made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1069 * @sizezero made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1824 * @leecming82 made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1849 * @jeremydk made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1780 * @WuTofu made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1803 * @wylanswets made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1860 * @sabraman made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1930 * @prawnwhoyawns made their first contribution in https://github.com/crosspoint-reader/crosspoint-reader/pull/1783 * @mcrosson made their first contribution as co-author on SD card font support **Full Changelog**: https://github.com/crosspoint-reader/crosspoint-reader/compare/1.2.0...release/1.3.0 --------- Co-authored-by: Justin Mitchell <justin@jmitch.com> Co-authored-by: Chun Ming Lee <95391408+leecming82@users.noreply.github.com> Co-authored-by: Uri Tauber <uritaube@gmail.com>
CrossPoint Reader
CrossPoint is open-source e-reader firmware - community-built, fully hackable, free forever. It's maintained by a growing community of developers and readers who believe your device should do what you want - not what a manufacturer decided for you.
Now running on: ESP32C3-based Xteink X4 and X3.
What can CrossPoint do?
-
Reader engine: EPUB 2/3 rendering with embedded-style option, image handling, hyphenation, kerning, chapter navigation, footnotes, go-to-percent, auto page turn, orientation control, focus reading, KOReader progress sync and more.
-
Various formats: native handling for
.epub,.xtc/.xtch,.txt, and.bmp. -
Screenshots.
-
Custom fonts: install your favorite fonts on the SD card.
-
Tilt page turn (X3 only).
-
Library workflow: folder browser, hidden-file toggle, long-press delete, recent books, SD-cache management.
-
Wireless workflows:
- File transfer web UI
- EPUB Optimizer
- Web settings UI/API (edit many device settings from browser)
- WebSocket fast uploads
- WebDAV handler
- AP mode (hotspot) and STA mode (join existing WiFi), both with QR helpers
- Calibre wireless connect flow
- OPDS browser with saved servers (up to 8), search, pagination, and direct download
- OTA update checks and installs from GitHub releases
-
Customization: multiple themes (Classic, Lyra, Lyra Extended, RoundedRaff), sleep screen modes, front/side button remapping, status bar controls, power-button behavior, refresh cadence, and more.
-
Localization: 22 UI languages and counting.
Coming soon:
-
RTL support — Arabic, Hebrew, and Farsi.
-
Bookmarks.
-
Dictionary lookup — inline word lookup without leaving the reader.
-
More themes.
-
Much more! stay tuned.
USB-locked devices (Xteink Unlocker)
Some Xteink units purchased from third-party stores (e.g. AliExpress) ship with USB flashing locked from the factory. If your device is locked, you will need to use the Xteink Unlocker tool available at https://crosspointreader.com/#unlock-tool before you can flash CrossPoint.
You do not need this tool if you bought your device directly from xteink.com. Those units are not locked.
Not sure if your device is locked? Power it on, connect the USB-C cable, and try flashing via the web flasher first (see Install firmware below). If the browser's serial device picker does not show your device, try a different USB port or browser before assuming the device is locked. Only reach for the unlocker if the device still doesn't appear.
⚠️ WARNING: READ THIS BEFORE USING THE UNLOCKER ⚠️
The only officially supported firmwares in the unlock tool are CrossPoint and CrossInk.
Flashing any other firmware on a USB-locked device may permanently brick the device or leave it permanently stuck on that firmware with no recovery path. Once USB flashing is re-locked, your only way back is via OTA, and if the firmware you flashed doesn't support OTA, there is no way out.
The Papyrix fork has removed OTA update support from its code. If you flash Papyrix onto a USB-locked unit, you will have zero update or recovery path and will be stuck on it forever. Do not flash Papyrix (or any other unsupported firmware) on a locked device.
Install firmware
Web installer (recommended)
- Connect your device to your computer via USB-C and wake/unlock the device
- Go to https://crosspointreader.com/#flash-tools, select device (X3 or X4), and choose an official CrossPoint release.
Web installer (specific version)
- Connect your device to your computer via USB-C and wake/unlock the device
- Download a
firmware.binfrom Releases, local build, or continuous integration artifact. - Go to https://crosspointreader.com/#flash-tools, select device (X3 or X4), click "Custom .bin" and upload a
firmware.bin.
Revert to Official Firmware
To revert to the official firmware, you can also flash the latest official firmware using https://crosspointreader.com/#flash-tools.
Command line
- Install
esptool:
pip install esptool
- Download
firmware.binfrom the releases page. - Connect your device via USB-C.
- Find the device port. On Linux, run
dmesgafter connecting. On macOS:
log stream --predicate 'subsystem == "com.apple.iokit"' --info
- Flash:
esptool.py --chip esp32c3 --port /dev/ttyACM0 --baud 921600 write_flash 0x10000 /path/to/firmware.bin
Adjust /dev/ttyACM0 to match your system.
Manual
See Development quick start below.
Documentation
Development quick start
Prerequisites
- pioarduino or VS Code + pioarduino plugin
- Python 3.8+
clang-format21- USB-C cable supporting data transfer
Setup
git clone --recursive https://github.com/crosspoint-reader/crosspoint-reader
cd crosspoint-reader
# if cloned without --recursive:
git submodule update --init --recursive
Build / flash / monitor
pio run --target upload
Contributor pre-PR checks
./bin/clang-format-fix
pio check -e default
pio run -e default
Debugging
After flashing the new features, it’s recommended to capture detailed logs from the serial port.
First, make sure all required Python packages are installed:
python3 -m pip install pyserial colorama matplotlib
After that run the script:
# For Linux
# This was tested on Debian and should work on most Linux systems.
python3 scripts/debugging_monitor.py
# For macOS
python3 scripts/debugging_monitor.py /dev/cu.usbmodem2101
Minor adjustments may be required for Windows.
Internals
CrossPoint Reader is pretty aggressive about caching data down to the SD card to minimise RAM usage. The ESP32-C3 only has ~380KB of usable RAM, so we have to be careful. A lot of the decisions made in the design of the firmware were based on this constraint.
Data caching
The first time chapters of a book are loaded, they are cached to the SD card. Subsequent loads are served from the
cache. This cache directory exists at .crosspoint on the SD card. The structure is as follows:
.crosspoint/
├── epub_<hash>/ # one directory per book, named by content hash
│ ├── progress.bin # reading position (chapter, page, etc.)
│ ├── cover.bmp # generated cover image
│ ├── book.bin # metadata: title, author, spine, TOC
│ └── sections/ # per-chapter layout cache
│ ├── 0.bin
│ ├── 1.bin
│ └── ...
Removing /.crosspoint clears all cached metadata and forces a full regeneration on next open. Note: the cache isn't cleared automatically when you delete a book, and moving a file to a new path resets its reading progress.
For more details on the internal file structures, see the file formats document.
Contributing
Contributions are welcome. If you're new to the codebase, start with the contributing docs. For things to work on, check the ideas discussion board — leave a comment before starting so we don't duplicate effort.
Everyone here is a volunteer, so please be respectful and patient. For governance and community expectations, see GOVERNANCE.md.
Community forks
One of the best things about open source is that anyone can take the code in a different direction. If you need something outside CrossPoint's scope, check out the community forks:
-
CrossInk — Typography and reading tracking: Bionic Reading (bolds word stems to create fixation points), guide dots between words, improved paragraph indents, and replaces the default fonts with ChareInk/Lexend/Bitter.
-
papyrix-reader — Adds FB2 and MD format support. Actively maintained with Arabic script support. Custom themes via SD card.
-
crosspet — A Vietnamese fork that adds a Tamagotchi-style virtual chicken that grows based on your reading milestones (pages read, streaks, care). Also: Flashcards, Weather, Pomodoro timer, and mini-games.
-
crosspoint-reader (jpirnay) — Faster integration of functionality. Tracks upstream PRs and integrates the good ones ahead of the official merge.
-
crosspoint-reader-cjk — Purpose-built for Chinese, Japanese, and Korean reading.
-
inx — Completely reimagines the user interface with tabbed navigation.
-
PlusPoint — custom JS apps support.(Unmaintained) -
crosspoint-reader-papers3 — Crosspoint port for M5Stack Paper S3.
Note: Many of these features will make their way into CrossPoint over time. We maintain a slower pace to ensure rock-solid stability and squash bugs before they reach your device.
Want to build your own device? Be sure to check out the de-link project.
CrossPoint Reader is not affiliated with Xteink or any device manufacturer.
Huge shoutout to diy-esp32-epub-reader, which inspired this project.
