Compare commits

..

39 Commits

Author SHA1 Message Date
Dave Allie 3ce11f14ce chore: Cut release 0.15.0 2026-01-22 02:20:22 +11:00
KasyanDiGris 47ef92e8fd fix: OPDS browser OOM (#403)
## Summary

- Rewrite OpdsParser to stream parsing instead of full content
- Fix OOM due to big http xml response

Closes #385 

---

### 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**_
2026-01-22 01:43:51 +11:00
Juan Biondi e3d6e32609 docs: Add detailed webserver documentation (#446)
## More detailed documentation

* **What is the goal of this PR?** 
Add more information about the exposed webserver.
* **What changes are included?**
Detailed documentation for the webserver endpoints
(`./docs/webserver-endpoints.md`)
Adding a table of content so it is easier to navigate directly to the
section you're interested on (Almost all `.md` files or at least all
those relevant)

## Additional Context

Not sure if this would get accepted but I thought it might be useful for
those trying to create separate apps that would sync files to the
device. It was at least to me trying to upload files using python as
stated
[here](https://github.com/crosspoint-reader/crosspoint-reader/discussions/434#discussioncomment-15545349)

---

### 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**_
2026-01-22 00:29:39 +11:00
Logan Garbarini d399afb53d feat: invalidate cache on web uploads and opds downloads and add Clear Cache action (#393)
## Summary

When uploading or downloading an updated ebook from SD/WebUI/OPDS with
same the filename the `.crosspoint` cache is not cleared. This can lead
to issues with the Table of Contents and hangs when switching between
chapters.

I encountered this issue in two places:
- When I need to do further ePub cleaning using Calibre after I load an
ePub and find that some of its formatting should be cleaned up. When I
reprocess the same book and want to place it back in the same location I
need a way to invalidate the cache.
- When syncing RSS feed generated epubs. I generate news ePubs with
filenames like `news-outlet.epub` and so every day when I fetch new news
the crosspoint cache needs to be cleared to load that file.

This change offers the following features:
- On web uploads, if the file already exists, the cache for that file is
cleared
- On OPDS downloads, if the file already exists, the cache for that file
is cleared
- There's now an action for `Clear Cache` in the Settings page which can
clear the cache for all books


Addresses
https://github.com/crosspoint-reader/crosspoint-reader/issues/281

---

### 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: Dave Allie <dave@daveallie.com>
2026-01-22 00:06:07 +11:00
Arthur Tazhitdinov 838993259d fix: hard reset via RTS pin after flashing firmware (#437)
## Summary

* Disables going to sleep after uploading new firmware
* Makes developer experience easier

---

### 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: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-21 23:35:23 +11:00
Jonas Diemer cc74039cab fix: Skip negative screen coordinates only after we read the bitmap row. (#431)
Otherwise, we don't crop properly.

Fixes #430 

### AI Usage

Did you use AI tools to help write this code? _**< NO >**_
2026-01-21 23:27:41 +11:00
Jonas Diemer 87d6c032a5 Reclaim space if we don't show battery Percentage (#352)
## Summary

Give space to the chapter title if we don't show battery percentage.

---

### 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: Dave Allie <dave@daveallie.com>
2026-01-21 12:09:48 +00:00
Dave Allie c9b5462370 feat: Include superscripts and subscripts in fonts (#463)
## Summary

* Include superscripts and subscripts in fonts

## Additional Context

* Original change came from
https://github.com/crosspoint-reader/crosspoint-reader/pull/248

---

### 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: cor <cor@pruijs.dev>
2026-01-21 22:42:41 +11:00
Kenneth e548bfc0e1 My Library: Tab bar w/ Recent Books + File Browser (#250)
# Summary

This PR introduces a reusable Tab Bar component and combines the Recent
Books and File Browser into a unified tabbed page called "My Library"
accessible from the Home screen.

## Features
### New Tab Bar Component
A flexible, reusable tab bar component added to `ScreenComponents` that
can be used throughout the application.

### New Scroll Indicator Component
A page position indicator for lists that span multiple pages.
**Features:**
- Up/down arrow indicators
- Current page fraction display (e.g., "1/3")
- Only renders when content spans multiple pages

### My Library Activity
A new unified view combining Recent Books and File Browser into a single
tabbed page.

**Tabs:**
- **Recent** - Shows recently opened books
- **Files** - Browse SD card directory structure

**Navigation:**
- Up/Down or Left/Right: Navigate through list items
- Left/Right (when first item selected): Switch between tabs
- Confirm: Open selected book or enter directory
- Back: Go up directory (Files tab) or return home
- Long press Back: Jump to root directory (Files tab)

**UI Elements:**
- Tab bar with selection indicator
- Scroll/page indicator on right side
- Side button hints (up/down arrows)
- Dynamic bottom button labels ("BACK" in subdirectories, "HOME" at
root)

## Tab Bar Usage
The tab bar component is designed to be reusable across different
activities. Here's how to use it:

### Basic Example
```cpp
#include "ScreenComponents.h"
void MyActivity::render() const {
  renderer.clearScreen();
  
  // Define tabs with labels and selection state
  std::vector<TabInfo> tabs = {
    {"Tab One", currentTab == 0},   // Selected when currentTab is 0
    {"Tab Two", currentTab == 1},   // Selected when currentTab is 1
    {"Tab Three", currentTab == 2}  // Selected when currentTab is 2
  };
  
  // Draw tab bar at Y position 15, returns height of the tab bar
  int tabBarHeight = ScreenComponents::drawTabBar(renderer, 15, tabs);
  
  // Position your content below the tab bar
  int contentStartY = 15 + tabBarHeight + 10; // Add some padding
  
  // Draw content based on selected tab
  if (currentTab == 0) {
    renderTabOneContent(contentStartY);
  } else if (currentTab == 1) {
    renderTabTwoContent(contentStartY);
  } else {
    renderTabThreeContent(contentStartY);
  }
  
  renderer.displayBuffer();
}
```
Video Demo: https://share.cleanshot.com/P6NBncFS

<img width="250"
src="https://github.com/user-attachments/assets/07de4418-968e-4a88-9b42-ac5f53d8a832"
/>
<img width="250"
src="https://github.com/user-attachments/assets/e40201ed-dcc8-4568-b008-cd2bf13ebb2a"
/>
<img width="250"
src="https://github.com/user-attachments/assets/73db269f-e629-4696-b8ca-0b8443451a05"
/>

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-21 11:38:38 +00:00
Daniel Poulter 73c30748d8 feat: adding categories to settings screen (#331)
## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module, Implements the new feature for
  file uploading.)

As we get more settings, I think it makes sense to do categories for
them. This just allows users to find the settings easier and navigate to
them.

* **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).

Co-authored-by: dpoulter <daniel@yoco.com>
Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-21 11:13:51 +00:00
GenesiaW 6d68466891 fix: truncate chapter names that are too long (#422)
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
- Implements a fix to truncate chapter names that exceeds display width
 
* **What changes are included?**
- Implements a fix to truncate chapter names that exceeds display width
## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).
- Prior to the fix, if the book contains multiple chapters with names
longer than the display width, there is a noticeable delay when
scrolling through the list of chapters.

Serial output of the issue:
```
[25673] [ACT] Entering activity: EpubReaderChapterSelection
[25693] [GFX] !! Outside range (485, 65) -> (65, -6)
[25693] [GFX] !! Outside range (486, 65) -> (65, -7)
[25693] [GFX] !! Outside range (487, 65) -> (65, -8)
[25693] [GFX] !! Outside range (488, 65) -> (65, -9)
[25693] [GFX] !! Outside range (485, 66) -> (66, -6)
[25693] [GFX] !! Outside range (486, 66) -> (66, -7)
[25694] [GFX] !! Outside range (487, 66) -> (66, -8)
[25694] [GFX] !! Outside range (484, 67) -> (67, -5)
[25694] [GFX] !! Outside range (485, 67) -> (67, -6)
[25694] [GFX] !! Outside range (486, 67) -> (67, -7)
[25694] [GFX] !! Outside range (483, 68) -> (68, -4)
[25694] [GFX] !! Outside range (484, 68) -> (68, -5)
[25694] [GFX] !! Outside range (485, 68) -> (68, -6)
``` 


---

### 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: Dave Allie <dave@daveallie.com>
2026-01-19 13:01:51 +00:00
Arthur Tazhitdinov 8824c87490 feat: dict based Hyphenation (#305)
## Summary

* Adds (optional) Hyphenation for English, French, German, Russian
languages

## Additional Context

* Included hyphenation dictionaries add approximately 280kb to the flash
usage (German alone takes 200kb)
* Trie encoded dictionaries are adopted from hypher project
(https://github.com/typst/hypher)
* Soft hyphens (and other explicit hyphens) take precedence over
dict-based hyphenation. Overall, the hyphenation rules are quite
aggressive, as I believe it makes more sense on our smaller screen.

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-19 12:56:26 +00:00
Maeve Andrews 5fef99c641 fix: render U+FFFD replacement character instead of ? (#366)
The current behavior of rendering `?` for an unknown Unicode character
can be hard to distinguish from a typo. Use the standard Unicode
"replacement character" instead, that's what it's designed for:

https://en.wikipedia.org/wiki/Specials_(Unicode_block)

I'm making this PR as a draft because I'm not sure I did everything that
was needed to change the character set covered by the fonts. Running
that script is in its own commit. If this is proper, I'll rebase/squash
into one commit and un-draft.

Co-authored-by: Maeve Andrews <maeve@git.mail.maeveandrews.com>
2026-01-19 22:58:43 +11:00
Luke Stein 7a792a5384 fix: Invert colors on home screen cover overlay when recent book is selected (#390)
## Summary

* Fixes #388 

## Additional Context

* Tested on my own device
  * See images at #388 for what home screen looked like before.
* With this PR the home screen shows the following (selected and
unselected recent book × cover image rendered or not)


![Picsew_20260115153419](https://github.com/user-attachments/assets/44193f9d-76b7-4c77-b890-72b0dbae01c4)


---

### 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-01-19 22:57:39 +11:00
Justin Mitchell f69cddf2cc Adds KOReader Sync support (#232)
## Summary

- Adds KOReader progress sync integration, allowing CrossPoint to sync
reading positions with other
KOReader-compatible devices
- Stores credentials securely with XOR obfuscation
- Uses KOReader's partial MD5 document hashing for cross-device book
matching
  - Syncs position via percentage with estimated XPath for compatibility

# Features
- Settings: KOReader Username, Password, and Authenticate options
- Sync from chapters menu: "Sync Progress" option appears when
credentials are configured
- Bidirectional sync: Can apply remote progress or upload local progress

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-19 11:55:35 +00:00
Nathan James 7185e5d287 feat: Change keyboard "caps" to "shift" & Wrap Keyboard (#377)
## Summary

* This PR solves issue
https://github.com/crosspoint-reader/crosspoint-reader/issues/357 in the
first commit
* I then added an additional commit which means when you reach the end
of the keyboard, if you go 'beyond', you wrap back to the other side.
* This replaces existing behaviour, so if you would rather this be
removed, let me know and I'll just do the `caps` -> `shift` change

## Additional Context

### Screenshots for the new shift display

I thought it might not fit and need column size changes, but ended up
fitting fine, see screenshots showing this below:

<img width="573" height="366" alt="image"
src="https://github.com/user-attachments/assets/b8f6a4ec-94f5-4f5e-b9a6-06cc5f250ddb"
/>

<img width="570" height="308" alt="image"
src="https://github.com/user-attachments/assets/7d775518-4784-4120-a20a-a9dc67af8565"
/>


### Gif showing the wrap-around of the text



![IMG_7648](https://github.com/user-attachments/assets/7eec9066-e1cc-49a1-8b6b-a61556038d31)

---

### AI Usage

Did you use AI tools to help write this code? **PARTIALLY** - used to
double check the text wrapping had no edge-cases. (It did also suggest
rewriting the function, but I decided that was too big of a change for a
working part of the codebase, for now!)
2026-01-19 22:50:34 +11:00
Eunchurn Park 12940cc546 fix: XTC 1-bit thumb BMP polarity inversion (#373)
## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
* **What changes are included?**

- Fix inverted colors in Continue Reading cover image for 1-bit XTC
files

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).

- Fix `grayValue = pixelBit ? 0 : 255` → `grayValue = pixelBit ? 255 :
0` in `lib/Xtc/Xtc.cpp`
- The thumb BMP generation had inverted polarity compared to cover BMP
generation
- bit=0 should be black, bit=1 should be white (matching the BMP palette
order)
- Update misleading comment about XTC polarity

---

### 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**_
2026-01-19 22:41:48 +11:00
Luke Stein 21277e03eb docs: Update User Guide to reflect release 0.14.0 (#376) 2026-01-15 23:27:17 +11:00
Luke Stein 4eef2b5793 feat: Add MAC address display to WiFi Networks screen (#381)
## Summary

* Implements #380, allowing the user to see the device's MAC address in
order to register on wifi networks

## Additional Context

* Although @markatlnk suggested showing on the settings screen, I
implemented display at the bottom of the WiFi Networks selection screen
(accessed via "File Transfer" > "Join a Network") since I think it makes
more sense there.
* Tested on my own device


![IMG_2873](https://github.com/user-attachments/assets/b82a20dc-41a0-4b21-81f1-20876aa2c6b0)


---

### 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-01-15 23:26:39 +11:00
Nathan James 5a55fa1c6e fix: also apply longPressChapterSkip setting to xtc reader (#378)
## Summary

* This builds upon the helpful PR
https://github.com/crosspoint-reader/crosspoint-reader/pull/341, and
adds support for the setting to also apply to the XTC reader, which I
believed has just been missed and was not intentionally left out.
* XTC does not have chapter support yet, but it does skip 10 pages when
long-pressed, and so I think this is useful.

---

### AI Usage

Did you use AI tools to help write this code? No
2026-01-15 23:25:18 +11:00
Maeve Andrews c98ba142e8 fix: draw button hints correctly if orientation is not portrait (#363)
~~Quick~~ fix for
https://github.com/daveallie/crosspoint-reader/issues/362

(this just applies to the chapter selection menu:)

~~If the orientation is portrait, hints as we know them make sense to
draw. If the orientation is inverted, we'd have to change the order of
the labels (along with everything's position), and if it's one of the
landscape choices, we'd have to render the text and buttons vertically.
All those other cases will be more complicated.~~

~~Punt on this for now by only rendering if portrait.~~

Update: this now draws the hints at the physical button position no
matter what the orientation is, by temporarily changing orientation to
portrait.

---------

Co-authored-by: Maeve Andrews <maeve@git.mail.maeveandrews.com>
2026-01-15 23:23:36 +11:00
Jonas Diemer c1c94c0112 Feature: Show img alt text (#168)
Let's start small by showing the ALT text of IMG. This is rudimentary,
but avoids those otherwise completely blank chapters.

I feel we will need this even when we can render images if that
rendering takes >1s - I would then prefer rendering optional and showing
the ALT text first.
2026-01-15 23:21:46 +11:00
Dave Allie eb84bcee7c chore: Pin links2004/WebSockets version 2026-01-15 23:15:30 +11:00
efenner d45f355e87 feat: Add EPUB table omitted placeholder (#372)
## Summary

* **What is the goal of this PR?**: Fix the bug I reported in
https://github.com/daveallie/crosspoint-reader/issues/292
* **What changes are included?**: Instead of silently dropping table
content in EPUBs., replace with an italicized '[Table omitted]' message
where tables appear.

## 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: Evan Fenner <evan@evanfenner.com>
Co-authored-by: Warp <agent@warp.dev>
2026-01-15 23:14:59 +11:00
Dave Allie 56ec3dfb6d chore: Cut release 0.14.0 2026-01-15 01:01:47 +11:00
Maeve Andrews e517945aaa Add a bullet to the beginning of any <li> (#368)
Currently there is no visual indication whatsoever if something is in a
list. An `<li>` is essentially just another paragraph.

As a partial remedy for this, add a bullet character to the beginning of
`<li>` text blocks so that the user can see that they're list items.
This is incomplete in that an `<ol>` should also have a counter so that
its list items can get numbers instead of bullets (right now I don't
think we track if we're in a `<ul>` or an `<ol>` at all), but it's
strictly better than the current situation.

Co-authored-by: Maeve Andrews <maeve@git.mail.maeveandrews.com>
2026-01-14 23:23:03 +11:00
Maeve Andrews 489220832f Only indent paragraphs for justify/left-align (#367)
Currently, when Extra Paragraph Spacing is off, an em-space is added to
the beginning of each ParsedText even for blocks like headers that are
centered. This whitespace makes the centering slightly off. Change the
calculation here to only add the em-space for left/justified text.

Co-authored-by: Maeve Andrews <maeve@git.mail.maeveandrews.com>
2026-01-14 23:21:48 +11:00
Dave Allie 3ee10b31ab Update OTA updater URL 2026-01-14 23:14:00 +11:00
swwilshub a946c83a07 Turbocharge WiFi uploads with WebSocket + watchdog stability (#364)
## Summary

* **What is the goal of this PR?** Fix WiFi file transfer stability
issues (especially crashes during uploads) and improve upload speed via
WebSocket binary protocol. File transfers now don't really crash as
much, if they do it recovers and speed has gone form 50KB/s to 300+KB/s.

* **What changes are included?**
- **WebSocket upload support** - Adds WebSocket binary protocol for file
uploads, achieving faster speeds 335 KB/s vs HTTP multipart)
- **Watchdog stability fixes** - Adds `esp_task_wdt_reset()` calls
throughout upload path to prevent watchdog timeouts during:
    - File creation (FAT allocation can be slow)
    - SD card write operations
    - HTTP header parsing
    - WebSocket chunk processing
- **4KB write buffering** - Batches SD card writes to reduce I/O
overhead
- **WiFi health monitoring** - Detects WiFi disconnection in STA mode
and exits gracefully
- **Improved handleClient loop** - 500 iterations with periodic watchdog
resets and button checks for responsiveness
- **Progress bar improvements** - Fixed jumping/inaccurate progress by
capping local progress at 95% until server confirms completion
- **Exit button responsiveness** - Button now checked inside the
handleClient loop every 64 iterations
- **Reduced exit delays** - Decreased shutdown delays from ~850ms to
~140ms

**Files changed:**
- `platformio.ini` - Added WebSockets library dependency
- `CrossPointWebServer.cpp/h` - WebSocket server, upload buffering,
watchdog resets
- `CrossPointWebServerActivity.cpp` - WiFi monitoring, improved loop,
button handling
- `FilesPage.html` - WebSocket upload JavaScript with HTTP fallback

## Additional Context

- WebSocket uses 4KB chunks with backpressure management to prevent
ESP32 buffer overflow
- Falls back to HTTP automatically if WebSocket connection fails
- The main bottleneck now is SD card write speed (~44% of transfer
time), not WiFi
- STA mode was more prone to crashes than AP mode due to external
network factors; WiFi health monitoring helps detect and handle
disconnections gracefully

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_ Claude did it
ALL, I have no idea what I am doing, but my books transfer fast now.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 23:11:28 +11:00
Justin Mitchell 847786e342 Fixes issue with Calibre web expecting SSL (#347)
http urls now work with Calibre web

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-14 11:54:14 +00:00
Luke Stein c2fb8ce55d Update User Guide to reflect release 0.13.1 (#337)
Please note I have not tested the Calibre features and am not yet in a
position to offer detailed documentation of how they work.
2026-01-14 22:48:43 +11:00
Armando Cerna ed05554d74 feat: Add setting to toggle long-press chapter skip (#341)
## Summary

Adds a new "Long-press Chapter Skip" toggle in Settings to control
whether holding the side buttons skips chapters.

I kept accidentally triggering chapter skips while reading, which caused
me to lose my place in the middle of long chapters.

## 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 **_
2026-01-14 22:47:24 +11:00
Jonas Diemer 9a9dc044ce ifdef around optional fonts to reduce flash size/time. (#339)
## Summary

Adds define to omit optional fonts from the build. This reduces time to
flash from >31s to <13s. Useful for development that doesn't require
fonts. Addresses #193

Invoke it like this during development:
`PLATFORMIO_BUILD_FLAGS="-D OMIT_FONTS" pio run --target upload && pio
device monitor`

Changing the define causes `pio` to do a full rebuild (but it will be
quick if you keep the define).

---

### 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
2026-01-14 22:40:40 +11:00
Jonas Diemer 1c027ce2cd Skip BOM character (sometimes used in front of em-dashes) (#340)
## Summary

Skip BOM character (sometimes used in front of em-dashes) - they are not
part of the glyph set and would render `?` otherwise.

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_
2026-01-14 22:38:30 +11:00
Eunchurn Park 49f97b69ca Add TXT file reader support (#240)
## Summary

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

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

* **What changes are included?**

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

## Additional Context

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

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

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

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

* Cover image format: PNG is detected but not supported for conversion
(no PNG decoder available). Only BMP and JPG/JPEG work.
2026-01-14 21:36:40 +11:00
Dave Allie 14643d0225 Move string helpers out of HomeActivity into StringUtils 2026-01-14 21:24:45 +11:00
Eunchurn Park fecd1849b9 Add cover image display in *Continue Reading* card with framebuffer caching (#200)
## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,

Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.

* **What changes are included?**

- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).

- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
2026-01-14 21:24:02 +11:00
Will Morrow 2040e088e7 Ensure new custom sleep image every time (#300)
When picking a random sleep image from a set of custom images, compare
the randomly chosen index against a cached value in settings. If the
value matches, use the next image (rolling over if it's the last image).
Cache the chosen image index to settings in either case.

## Summary

Implements a tweak on the custom sleep image feature that ensures that
the user gets a new image every time the device goes to sleep.
This change adds a new setting (perhaps there's a better place to cache
this?) that stores the most recently used file index. During picking the
random image index, we compare this against the random index and choose
the next one (modulo the number of image files) if it matches, ensuring
we get a new image.

## Additional Context

As mentioned, I used settings to cache this value since it is a
persisted store, perhaps that's overkill. Open to suggestions on if
there's a better way.
2026-01-14 21:05:08 +11:00
Andrew Brandt 65d23910a3 ci: add PR format check workflow (#328)
**Description**:

Add a new workflow to check the PR formatting to ensure consistency on
PR titles. We can also use this for semantic release versioning later,
if we so desire.

**Related Issue(s)**:

Implements first portion of #327

---------

Signed-off-by: Andrew Brandt <brandt.andrew89@gmail.com>
2026-01-14 21:04:02 +11:00
174 changed files with 66207 additions and 19025 deletions
+26
View File
@@ -0,0 +1,26 @@
name: "PR Formatting"
on:
pull_request_target:
types:
- opened
- reopened
- edited
permissions:
statuses: write
jobs:
title-check:
name: Title Check
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Check PR Title
uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2
View File
@@ -4,3 +4,5 @@
.vscode
lib/EpdFont/fontsrc
*.generated.h
build
**/__pycache__/
+52 -8
View File
@@ -2,6 +2,27 @@
Welcome to the **CrossPoint** firmware. This guide outlines the hardware controls, navigation, and reading features of the device.
- [CrossPoint User Guide](#crosspoint-user-guide)
- [1. Hardware Overview](#1-hardware-overview)
- [Button Layout](#button-layout)
- [2. Power \& Startup](#2-power--startup)
- [Power On / Off](#power-on--off)
- [First Launch](#first-launch)
- [3. Screens](#3-screens)
- [3.1 Home Screen](#31-home-screen)
- [3.2 Book Selection](#32-book-selection)
- [3.3 Reading Mode](#33-reading-mode)
- [3.4 File Upload Screen](#34-file-upload-screen)
- [3.5 Settings](#35-settings)
- [3.6 Sleep Screen](#36-sleep-screen)
- [4. Reading Mode](#4-reading-mode)
- [Page Turning](#page-turning)
- [Chapter Navigation](#chapter-navigation)
- [System Navigation](#system-navigation)
- [5. Chapter Selection Screen](#5-chapter-selection-screen)
- [6. Current Limitations \& Roadmap](#6-current-limitations--roadmap)
## 1. Hardware Overview
The device utilises the standard buttons on the Xtink X4 (in the same layout as the manufacturer firmware, by default):
@@ -20,9 +41,10 @@ Button layout can be customized in **[Settings](#35-settings)**.
### Power On / Off
To turn the device on or off, **press and hold the Power button for half a second**. In **[Settings](#35-settings)** you can configure the power button to trigger on a short press instead of a long one.
To turn the device on or off, **press and hold the Power button for approximately half a second**.
In **[Settings](#35-settings)** you can configure the power button to turn the device off with a short press instead of a long one.
To reboot the device (for example if it's frozen, or after a firmware update), press and release the Reset button, and then hold the Power button for a few seconds.
To reboot the device (for example if it's frozen, or after a firmware update), press and release the Reset button, and then quickly press and hold the Power button for a few seconds.
### First Launch
@@ -63,18 +85,29 @@ See the [webserver docs](./docs/webserver.md) for more information on how to con
The Settings screen allows you to configure the device's behavior. There are a few settings you can adjust:
- **Sleep Screen**: Which sleep screen to display when the device sleeps:
- "Dark" (default) - The default dark sleep screen
- "Dark" (default) - The default dark Crosspoint logo sleep screen
- "Light" - The same default sleep screen, on a white background
- "Custom" - Custom images from the SD card, see [Sleep Screen](#36-sleep-screen) below for more information
- "Custom" - Custom images from the SD card; see [Sleep Screen](#36-sleep-screen) below for more information
- "Cover" - The book cover image (Note: this is experimental and may not work as expected)
- "Blank" - A blank screen
- "None" - A blank screen
- **Sleep Screen Cover Mode**: How to display the book cover when "Cover" sleep screen is selected:
- "Fit" (default) - Scale the image down to fit centered on the screen, padding with white borders as necessary
- "Crop" - Scale the image down and crop as necessary to try to to fill the screen (Note: this is experimental and may not work as expected)
- **Status Bar**: Configure the status bar displayed while reading:
- "None" - No status bar
- "No Progress" - Show status bar without reading progress
- "Full" - Show status bar with reading progress
- **Hide Battery %**: Configure where to suppress the battery pecentage display in the status bar; the battery icon will still be shown:
- "Never" - Always show battery percentage (default)
- "In Reader" - Show battery percentage everywhere except in reading mode
- "Always" - Always hide battery percentage
- **Extra Paragraph Spacing**: If enabled, vertical space will be added between paragraphs in the book. If disabled, paragraphs will not have vertical space between them, but will have first-line indentation.
- **Short Power Button Click**: Whether to trigger the power button on a short press or a long press.
- **Reading Orientation**: Set the screen orientation for reading:
- **Text Anti-Aliasing**: Whether to show smooth grey edges (anti-aliasing) on text in reading mode. Note this slows down page turns slightly.
- **Short Power Button Click**: Controls the effect of a short click of the power button:
- "Ignore" - Require a long press to turn off the device
- "Sleep" - A short press powers the device off
- "Page Turn" - A short press in reading mode turns to the next page; a long press turns the device off
- **Reading Orientation**: Set the screen orientation for reading EPUB files:
- "Portrait" (default) - Standard portrait orientation
- "Landscape CW" - Landscape, rotated clockwise
- "Inverted" - Portrait, upside down
@@ -83,16 +116,22 @@ The Settings screen allows you to configure the device's behavior. There are a f
- Back, Confirm, Left, Right (default)
- Left, Right, Back, Confirm
- Left, Back, Confirm, Right
- **Side Button Layout**: Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading.
- **Side Button Layout (reader)**: Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading.
- **Long-press Chapter Skip**: Set whether long-pressing page turn buttons skip to the next/previous chapter.
- "Chapter Skip" (default) - Long-pressing skips to next/previous chapter
- "Page Scroll" - Long-pressing scrolls a page up/down
- Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading.
- **Reader Font Family**: Choose the font used for reading:
- "Bookerly" (default) - Amazon's reading font
- "Noto Sans" - Google's sans-serif font
- "Open Dyslexic" - Font designed for readers with dyslexia
- **Reader Font Size**: Adjust the text size for reading; options are "Small", "Medium", "Large", or "X Large".
- **Reader Line Spacing**: Adjust the spacing between lines; options are "Tight", "Normal", or "Wide".
- **Reader Screen Margin**: Controls the screen margins in reader mode between 5 and 40 pixels in 5 pixel increments.
- **Reader Paragraph Alignment**: Set the alignment of paragraphs; options are "Justified" (default), "Left", "Center", or "Right".
- **Time to Sleep**: Set the duration of inactivity before the device automatically goes to sleep.
- **Refresh Frequency**: Set how often the screen does a full refresh while reading to reduce ghosting.
- **Calibre Settings**: Set up integration for accessing a Calibre web library or connecting to Calibre as a wireless device.
- **Check for updates**: Check for firmware updates over WiFi.
### 3.6 Sleep Screen
@@ -124,10 +163,15 @@ Once you have opened a book, the button layout changes to facilitate reading.
The role of the volume (side) buttons can be swapped in **[Settings](#35-settings)**.
If the **Short Power Button Click** setting is set to "Page Turn", you can also turn to the next page by briefly pressing the Power button.
### Chapter Navigation
* **Next Chapter:** Press and **hold** the **Right** (or **Volume Down**) button briefly, then release.
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release.
This feature can be disabled in **[Settings](#35-settings)** to help avoid changing chapters by mistake.
### System Navigation
* **Return to Book Selection:** Press **Back** to close the book and return to the **[Book Selection](#32-book-selection)** screen.
* **Return to Home:** Press and **hold** the **Back** button to close the book and return to the **[Home](#31-home-screen)** screen.
+66
View File
@@ -0,0 +1,66 @@
# Hypher Binary Tries
CrossPoint embeds the exact binary automata produced by
[Typst's `hypher`](https://github.com/typst/hypher).
## File layout
Each `.bin` blob is a single self-contained automaton:
```
uint32_t root_addr_be; // big-endian offset of the root node
uint8_t levels[]; // shared "levels" tape (dist/score pairs)
uint8_t nodes[]; // node records packed back-to-back
```
The size of the `levels` tape is implicit. Individual nodes reference slices
inside that tape via 12-bit offsets, so no additional pointers are required.
### Node encoding
Every node starts with a single control byte:
- Bit 7 set when the node stores scores (`levels`).
- Bits 5-6 stride of the target deltas (1, 2, or 3 bytes, big-endian).
- Bits 0-4 transition count (values ≥ 31 spill into an extra byte).
If the `levels` flag is set, two more bytes follow. Together they encode a
12-bit offset into the global `levels` tape and a 4-bit length. Each byte in the
levels tape packs a distance/score pair as `dist * 10 + score`, where `dist`
counts how many UTF-8 bytes we advanced since the previous digit.
After the optional levels header come the transition labels (one byte per edge)
followed by the signed target deltas. Targets are stored as relative offsets
from the current node address. Deltas up to ±128 fit in a single byte, larger
distances grow to 2 or 3 bytes. The runtime walks the transitions with a simple
linear scan and materializes the absolute address by adding the decoded delta
to the current nodes base.
## Embedding blobs into the firmware
The helper script `scripts/generate_hyphenation_trie.py` acts as a thin
wrapper: it reads the hypher-generated `.bin` files, formats them as `constexpr`
byte arrays, and emits headers under
`lib/Epub/Epub/hyphenation/generated/`. Each header defines the raw data plus a
`SerializedHyphenationPatterns` descriptor so the reader can keep the automaton
in flash.
To refresh the firmware assets after updating the `.bin` files, run:
```
./scripts/generate_hyphenation_trie.py \
--input lib/Epub/Epub/hyphenation/tries/en.bin \
--output lib/Epub/Epub/hyphenation/generated/hyph-en.trie.h
./scripts/generate_hyphenation_trie.py \
--input lib/Epub/Epub/hyphenation/tries/fr.bin \
--output lib/Epub/Epub/hyphenation/generated/hyph-fr.trie.h
./scripts/generate_hyphenation_trie.py \
--input lib/Epub/Epub/hyphenation/tries/de.bin \
--output lib/Epub/Epub/hyphenation/generated/hyph-de.trie.h
./scripts/generate_hyphenation_trie.py \
--input lib/Epub/Epub/hyphenation/tries/ru.bin \
--output lib/Epub/Epub/hyphenation/generated/hyph-ru.trie.h
```
+57
View File
@@ -0,0 +1,57 @@
# Troubleshooting
This document show most common issues and possible solutions while using the device features.
- [Troubleshooting](#troubleshooting)
- [Cannot See the Device on the Network](#cannot-see-the-device-on-the-network)
- [Connection Drops or Times Out](#connection-drops-or-times-out)
- [Upload Fails](#upload-fails)
- [Saved Password Not Working](#saved-password-not-working)
### Cannot See the Device on the Network
**Problem:** Browser shows "Cannot connect" or "Site can't be reached"
**Solutions:**
1. Verify both devices are on the **same WiFi network**
- Check your computer/phone WiFi settings
- Confirm the CrossPoint Reader shows "Connected" status
2. Double-check the IP address
- Make sure you typed it correctly
- Include `http://` at the beginning
3. Try disabling VPN if you're using one
4. Some networks have "client isolation" enabled - check with your network administrator
### Connection Drops or Times Out
**Problem:** WiFi connection is unstable
**Solutions:**
1. Move closer to the WiFi router
2. Check signal strength on the device (should be at least `||` or better)
3. Avoid interference from other devices
4. Try a different WiFi network if available
### Upload Fails
**Problem:** File upload doesn't complete or shows an error
**Solutions:**
1. Ensure the file is a valid `.epub` file
2. Check that the SD card has enough free space
3. Try uploading a smaller file first to test
4. Refresh the browser page and try again
### Saved Password Not Working
**Problem:** Device fails to connect with saved credentials
**Solutions:**
1. When connection fails, you'll be prompted to "Forget Network"
2. Select **Yes** to remove the saved password
3. Reconnect and enter the password again
4. Choose to save the new password
+331
View File
@@ -0,0 +1,331 @@
# Webserver Endpoints
This document describes all HTTP and WebSocket endpoints available on the CrossPoint Reader webserver.
- [Webserver Endpoints](#webserver-endpoints)
- [Overview](#overview)
- [HTTP Endpoints](#http-endpoints)
- [GET `/` - Home Page](#get----home-page)
- [GET `/files` - File Browser Page](#get-files---file-browser-page)
- [GET `/api/status` - Device Status](#get-apistatus---device-status)
- [GET `/api/files` - List Files](#get-apifiles---list-files)
- [POST `/upload` - Upload File](#post-upload---upload-file)
- [POST `/mkdir` - Create Folder](#post-mkdir---create-folder)
- [POST `/delete` - Delete File or Folder](#post-delete---delete-file-or-folder)
- [WebSocket Endpoint](#websocket-endpoint)
- [Port 81 - Fast Binary Upload](#port-81---fast-binary-upload)
- [Network Modes](#network-modes)
- [Station Mode (STA)](#station-mode-sta)
- [Access Point Mode (AP)](#access-point-mode-ap)
- [Notes](#notes)
## Overview
The CrossPoint Reader exposes a webserver for file management and device monitoring:
- **HTTP Server**: Port 80
- **WebSocket Server**: Port 81 (for fast binary uploads)
---
## HTTP Endpoints
### GET `/` - Home Page
Serves the home page HTML interface.
**Request:**
```bash
curl http://crosspoint.local/
```
**Response:** HTML page (200 OK)
---
### GET `/files` - File Browser Page
Serves the file browser HTML interface.
**Request:**
```bash
curl http://crosspoint.local/files
```
**Response:** HTML page (200 OK)
---
### GET `/api/status` - Device Status
Returns JSON with device status information.
**Request:**
```bash
curl http://crosspoint.local/api/status
```
**Response (200 OK):**
```json
{
"version": "1.0.0",
"ip": "192.168.1.100",
"mode": "STA",
"rssi": -45,
"freeHeap": 123456,
"uptime": 3600
}
```
| Field | Type | Description |
| ---------- | ------ | --------------------------------------------------------- |
| `version` | string | CrossPoint firmware version |
| `ip` | string | Device IP address |
| `mode` | string | `"STA"` (connected to WiFi) or `"AP"` (access point mode) |
| `rssi` | number | WiFi signal strength in dBm (0 in AP mode) |
| `freeHeap` | number | Free heap memory in bytes |
| `uptime` | number | Seconds since device boot |
---
### GET `/api/files` - List Files
Returns a JSON array of files and folders in the specified directory.
**Request:**
```bash
# List root directory
curl http://crosspoint.local/api/files
# List specific directory
curl "http://crosspoint.local/api/files?path=/Books"
```
**Query Parameters:**
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | ---------------------- |
| `path` | No | `/` | Directory path to list |
**Response (200 OK):**
```json
[
{"name": "MyBook.epub", "size": 1234567, "isDirectory": false, "isEpub": true},
{"name": "Notes", "size": 0, "isDirectory": true, "isEpub": false},
{"name": "document.pdf", "size": 54321, "isDirectory": false, "isEpub": false}
]
```
| Field | Type | Description |
| ------------- | ------- | ---------------------------------------- |
| `name` | string | File or folder name |
| `size` | number | Size in bytes (0 for directories) |
| `isDirectory` | boolean | `true` if the item is a folder |
| `isEpub` | boolean | `true` if the file has `.epub` extension |
**Notes:**
- Hidden files (starting with `.`) are automatically filtered out
- System folders (`System Volume Information`, `XTCache`) are hidden
---
### POST `/upload` - Upload File
Uploads a file to the SD card via multipart form data.
**Request:**
```bash
# Upload to root directory
curl -X POST -F "file=@mybook.epub" http://crosspoint.local/upload
# Upload to specific directory
curl -X POST -F "file=@mybook.epub" "http://crosspoint.local/upload?path=/Books"
```
**Query Parameters:**
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | ------------------------------- |
| `path` | No | `/` | Target directory for the upload |
**Response (200 OK):**
```
File uploaded successfully: mybook.epub
```
**Error Responses:**
| Status | Body | Cause |
| ------ | ----------------------------------------------- | --------------------------- |
| 400 | `Failed to create file on SD card` | Cannot create file |
| 400 | `Failed to write to SD card - disk may be full` | Write error during upload |
| 400 | `Failed to write final data to SD card` | Error flushing final buffer |
| 400 | `Upload aborted` | Client aborted the upload |
| 400 | `Unknown error during upload` | Unspecified error |
**Notes:**
- Existing files with the same name will be overwritten
- Uses a 4KB buffer for efficient SD card writes
---
### POST `/mkdir` - Create Folder
Creates a new folder on the SD card.
**Request:**
```bash
curl -X POST -d "name=NewFolder&path=/" http://crosspoint.local/mkdir
```
**Form Parameters:**
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | ---------------------------- |
| `name` | Yes | - | Name of the folder to create |
| `path` | No | `/` | Parent directory path |
**Response (200 OK):**
```
Folder created: NewFolder
```
**Error Responses:**
| Status | Body | Cause |
| ------ | ----------------------------- | ----------------------------- |
| 400 | `Missing folder name` | `name` parameter not provided |
| 400 | `Folder name cannot be empty` | Empty folder name |
| 400 | `Folder already exists` | Folder with same name exists |
| 500 | `Failed to create folder` | SD card error |
---
### POST `/delete` - Delete File or Folder
Deletes a file or folder from the SD card.
**Request:**
```bash
# Delete a file
curl -X POST -d "path=/Books/mybook.epub&type=file" http://crosspoint.local/delete
# Delete an empty folder
curl -X POST -d "path=/OldFolder&type=folder" http://crosspoint.local/delete
```
**Form Parameters:**
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | -------------------------------- |
| `path` | Yes | - | Path to the item to delete |
| `type` | No | `file` | Type of item: `file` or `folder` |
**Response (200 OK):**
```
Deleted successfully
```
**Error Responses:**
| Status | Body | Cause |
| ------ | --------------------------------------------- | ----------------------------- |
| 400 | `Missing path` | `path` parameter not provided |
| 400 | `Cannot delete root directory` | Attempted to delete `/` |
| 400 | `Folder is not empty. Delete contents first.` | Non-empty folder |
| 403 | `Cannot delete system files` | Hidden file (starts with `.`) |
| 403 | `Cannot delete protected items` | Protected system folder |
| 404 | `Item not found` | Path does not exist |
| 500 | `Failed to delete item` | SD card error |
**Protected Items:**
- Files/folders starting with `.`
- `System Volume Information`
- `XTCache`
---
## WebSocket Endpoint
### Port 81 - Fast Binary Upload
A WebSocket endpoint for high-speed binary file uploads. More efficient than HTTP multipart for large files.
**Connection:**
```
ws://crosspoint.local:81/
```
**Protocol:**
1. **Client** sends TEXT message: `START:<filename>:<size>:<path>`
2. **Server** responds with TEXT: `READY`
3. **Client** sends BINARY messages with file data chunks
4. **Server** sends TEXT progress updates: `PROGRESS:<received>:<total>`
5. **Server** sends TEXT when complete: `DONE` or `ERROR:<message>`
**Example Session:**
```
Client -> "START:mybook.epub:1234567:/Books"
Server -> "READY"
Client -> [binary chunk 1]
Client -> [binary chunk 2]
Server -> "PROGRESS:65536:1234567"
Client -> [binary chunk 3]
...
Server -> "PROGRESS:1234567:1234567"
Server -> "DONE"
```
**Error Messages:**
| Message | Cause |
| --------------------------------- | ---------------------------------- |
| `ERROR:Failed to create file` | Cannot create file on SD card |
| `ERROR:Invalid START format` | Malformed START message |
| `ERROR:No upload in progress` | Binary data received without START |
| `ERROR:Write failed - disk full?` | SD card write error |
**Example with `websocat`:**
```bash
# Interactive session
websocat ws://crosspoint.local:81
# Then type:
START:mybook.epub:1234567:/Books
# Wait for READY, then send binary data
```
**Notes:**
- Progress updates are sent every 64KB or at completion
- Disconnection during upload will delete the incomplete file
- Existing files with the same name will be overwritten
---
## Network Modes
The device can operate in two network modes:
### Station Mode (STA)
- Device connects to an existing WiFi network
- IP address assigned by router/DHCP
- `mode` field in `/api/status` returns `"STA"`
- `rssi` field shows signal strength
### Access Point Mode (AP)
- Device creates its own WiFi hotspot
- Default IP is typically `192.168.4.1`
- `mode` field in `/api/status` returns `"AP"`
- `rssi` field returns `0`
---
## Notes
- These examples use `crosspoint.local`. If your network does not support mDNS or the address does not resolve, replace it with the specific **IP Address** displayed on your device screen (e.g., `http://192.168.1.102/`).
- All paths on the SD card start with `/`
- Trailing slashes are automatically stripped (except for root `/`)
- The webserver uses chunked transfer encoding for file listings
+2 -83
View File
@@ -172,89 +172,7 @@ This is useful for organizing your ebooks by genre, author, or series.
## Command Line File Management
For power users, you can manage files directly from your terminal using `curl` while the device is in File Upload mode.
### Uploading a File
To upload a file to the root directory, use the following command:
```bash
curl -F "file=@book.epub" "http://crosspoint.local/upload?path=/"
```
* **`-F "file=@filename"`**: Points to the local file on your computer.
* **`path=/`**: The destination folder on the device SD card.
### Deleting a File
To delete a specific file, provide the full path on the SD card:
```bash
curl -F "path=/folder/file.epub" "http://crosspoint.local/delete"
```
### Advanced Flags
For more reliable transfers of large EPUB files, consider adding these flags:
* `-#`: Shows a simple progress bar.
* `--connect-timeout 30`: Limits how long curl waits to establish a connection (in seconds).
* `--max-time 300`: Sets a maximum duration for the entire transfer (5 minutes).
> [!NOTE]
> These examples use `crosspoint.local`. If your network does not support mDNS or the address does not resolve, replace it with the specific **IP Address** displayed on your device screen (e.g., `http://192.168.1.102/`).
---
## Troubleshooting
### Cannot See the Device on the Network
**Problem:** Browser shows "Cannot connect" or "Site can't be reached"
**Solutions:**
1. Verify both devices are on the **same WiFi network**
- Check your computer/phone WiFi settings
- Confirm the CrossPoint Reader shows "Connected" status
2. Double-check the IP address
- Make sure you typed it correctly
- Include `http://` at the beginning
3. Try disabling VPN if you're using one
4. Some networks have "client isolation" enabled - check with your network administrator
### Connection Drops or Times Out
**Problem:** WiFi connection is unstable
**Solutions:**
1. Move closer to the WiFi router
2. Check signal strength on the device (should be at least `||` or better)
3. Avoid interference from other devices
4. Try a different WiFi network if available
### Upload Fails
**Problem:** File upload doesn't complete or shows an error
**Solutions:**
1. Ensure the file is a valid `.epub` file
2. Check that the SD card has enough free space
3. Try uploading a smaller file first to test
4. Refresh the browser page and try again
### Saved Password Not Working
**Problem:** Device fails to connect with saved credentials
**Solutions:**
1. When connection fails, you'll be prompted to "Forget Network"
2. Select **Yes** to remove the saved password
3. Reconnect and enter the password again
4. Choose to save the new password
---
For power users, you can manage files directly from your terminal using `curl` while the device is in File Upload mode a detailed documentation can be found [here](./webserver-endpoints.md).
## Security Notes
@@ -303,4 +221,5 @@ Your uploaded files will be immediately available in the file browser!
## Related Documentation
- [User Guide](../USER_GUIDE.md) - General device operation
- [Troubleshooting](./troubleshooting.md) - Troubleshooting
- [README](../README.md) - Project overview and features
+1 -2
View File
@@ -22,8 +22,7 @@ void EpdFont::getTextBounds(const char* string, const int startX, const int star
const EpdGlyph* glyph = getGlyph(cp);
if (!glyph) {
// TODO: Replace with fallback glyph property?
glyph = getGlyph('?');
glyph = getGlyph(REPLACEMENT_GLYPH);
}
if (!glyph) {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+191 -112
View File
@@ -7,7 +7,7 @@
#pragma once
#include "EpdFontData.h"
static const uint8_t bookerly_14_italicBitmaps[64856] = {
static const uint8_t bookerly_14_italicBitmaps[65749] = {
0x00, 0x05, 0x00, 0x0F, 0x80, 0x0B, 0xD0, 0x03, 0xF0, 0x00, 0xF8, 0x00, 0x7D, 0x00, 0x2F, 0x00,
0x0F, 0x80, 0x03, 0xD0, 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x1E, 0x00, 0x0B, 0x40, 0x02, 0xC0, 0x00,
0xF0, 0x00, 0x3C, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x3F, 0x40, 0x0F,
@@ -3681,7 +3681,51 @@ static const uint8_t bookerly_14_italicBitmaps[64856] = {
0x00, 0x00, 0x00, 0x0E, 0x00, 0xD0, 0x2C, 0x03, 0x80, 0x7E, 0x0B, 0xC0, 0xFC, 0x1F, 0x42, 0xF4,
0x3F, 0x07, 0xE0, 0xFD, 0x0F, 0xC2, 0xF8, 0x3F, 0x03, 0xF0, 0xBD, 0x0F, 0xC1, 0xF8, 0x2F, 0x43,
0xE0, 0x7D, 0x0B, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x3D, 0x0B, 0xC0, 0xF4, 0x0F, 0x01, 0xE0, 0x3D,
0x00, 0x40, 0x10, 0x02, 0x00, 0x10, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xD0, 0x07, 0xFF, 0xFF, 0xFD,
0x00, 0x40, 0x10, 0x02, 0x00, 0x10, 0x00, 0x00, 0x2F, 0x80, 0x0B, 0xFF, 0xC0, 0xBD, 0x2F, 0x43,
0xD0, 0x3E, 0x2E, 0x00, 0xB8, 0xF4, 0x03, 0xE7, 0xC0, 0x0F, 0x6E, 0x00, 0x3C, 0xB8, 0x01, 0xF2,
0xE0, 0x0F, 0x4B, 0xC0, 0xBC, 0x1F, 0x9B, 0xC0, 0x2F, 0xFC, 0x00, 0x19, 0x40, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xB8, 0x00, 0x3C, 0xB4, 0x00, 0xF0, 0xF0,
0x03, 0xD0, 0xF0, 0x0F, 0x41, 0xF0, 0x3F, 0xAB, 0xF9, 0x3F, 0xFF, 0xFD, 0x00, 0x03, 0xC0, 0x00,
0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xA4, 0x03, 0xFF, 0xE0, 0x1F, 0xAA,
0x00, 0xB4, 0x00, 0x03, 0xD0, 0x00, 0x1F, 0xF8, 0x00, 0x06, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03,
0xC0, 0x00, 0x1F, 0x00, 0x01, 0xF4, 0x05, 0xBF, 0x80, 0xBF, 0xF4, 0x00, 0x54, 0x00, 0x00, 0x00,
0x01, 0xB8, 0x00, 0x1F, 0xF9, 0x00, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x0B, 0xC0, 0x00, 0x0F, 0xFE,
0x00, 0x2F, 0xAF, 0xD0, 0x3D, 0x02, 0xF0, 0x3C, 0x01, 0xF0, 0x3C, 0x01, 0xF0, 0x3D, 0x03, 0xD0,
0x2F, 0x5F, 0xC0, 0x0B, 0xFE, 0x00, 0x01, 0x50, 0x00, 0x2A, 0xAA, 0x47, 0xFF, 0xFD, 0xB9, 0x5B,
0xCF, 0x00, 0xF0, 0x50, 0x3D, 0x00, 0x0B, 0x80, 0x01, 0xF0, 0x00, 0x3D, 0x00, 0x0B, 0x80, 0x01,
0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0x78, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2F, 0x80, 0x07,
0xEF, 0xC0, 0x7C, 0x0F, 0x42, 0xD0, 0x3D, 0x0B, 0x81, 0xF0, 0x0F, 0xAF, 0x00, 0x2F, 0xF0, 0x03,
0xD7, 0xE0, 0x3D, 0x07, 0xC1, 0xF0, 0x0F, 0x47, 0xC0, 0x3C, 0x0F, 0x87, 0xE0, 0x2F, 0xFE, 0x00,
0x05, 0x40, 0x00, 0x00, 0x1F, 0x90, 0x00, 0xFF, 0xF8, 0x03, 0xE0, 0x7C, 0x07, 0xC0, 0x3D, 0x07,
0x80, 0x2D, 0x0B, 0xC0, 0x3D, 0x07, 0xD0, 0x7C, 0x02, 0xFF, 0xFC, 0x00, 0x19, 0xF4, 0x00, 0x03,
0xE0, 0x00, 0x1F, 0xC0, 0x01, 0xFE, 0x00, 0x3F, 0xF4, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFD, 0x2F, 0x81, 0xF7, 0xFE, 0x03, 0xE8, 0x78, 0x0F, 0xC2, 0xD0, 0x7C, 0x0F, 0x02, 0xE0, 0x3C,
0x0F, 0x41, 0xE0, 0x3C, 0x0B, 0xB4, 0xF0, 0x3F, 0x81, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x02,
0xFF, 0x40, 0x3F, 0xBF, 0x03, 0xE0, 0x3D, 0x1F, 0x00, 0xF8, 0xF4, 0x02, 0xE3, 0xC0, 0x0F, 0x9F,
0x00, 0x3D, 0xB8, 0x01, 0xF2, 0xE0, 0x0B, 0xCB, 0x80, 0x3D, 0x1F, 0x03, 0xE0, 0x3F, 0xFE, 0x00,
0x7F, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x0F, 0xFD, 0x05, 0xBC, 0x00, 0x7C,
0x00, 0xB8, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF0, 0x01, 0xF0, 0x02, 0xE0, 0x03, 0xD0, 0x2F, 0xFC,
0x7F, 0xF8, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x40, 0x7F, 0xBF, 0x03, 0xE0, 0x3D, 0x0E, 0x00, 0xF4,
0x00, 0x07, 0xC0, 0x00, 0x3D, 0x00, 0x07, 0xD0, 0x00, 0x7D, 0x00, 0x0B, 0xC0, 0x00, 0xF8, 0x00,
0x0F, 0x80, 0x00, 0xFF, 0xFF, 0xD1, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x07, 0xEB,
0xE0, 0xB0, 0x2E, 0x00, 0x02, 0xE0, 0x00, 0x7C, 0x00, 0x7F, 0x00, 0x3F, 0xF4, 0x01, 0x0B, 0xC0,
0x00, 0x7C, 0x00, 0x0B, 0xC0, 0x02, 0xF4, 0x6B, 0xFD, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x02, 0xFC, 0x00, 0x07, 0xF8, 0x00, 0x1E, 0xB8, 0x00, 0x78,
0xF4, 0x01, 0xF0, 0xF0, 0x07, 0xC0, 0xF0, 0x1F, 0x01, 0xE0, 0x3F, 0xFF, 0xFD, 0x2F, 0xFF, 0xFC,
0x00, 0x03, 0xC0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0x40, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xD0, 0x2E,
0x00, 0x00, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x0B, 0xFD, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xF0, 0x00,
0x03, 0xC0, 0x00, 0x2F, 0x00, 0x02, 0xF0, 0x1B, 0xFF, 0x00, 0x7F, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xFD, 0x00, 0x3F, 0x90, 0x01, 0xF8, 0x00, 0x03, 0xD0, 0x00, 0x0F,
0x94, 0x00, 0x1F, 0xFF, 0x80, 0x2E, 0x57, 0xE0, 0x3C, 0x01, 0xF0, 0x3C, 0x01, 0xF0, 0x3C, 0x02,
0xE0, 0x3E, 0x03, 0xD0, 0x1F, 0xFF, 0x80, 0x07, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xD7,
0xFF, 0xFD, 0xB0, 0x0B, 0x8E, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x40, 0x02, 0xE0, 0x00, 0x7C,
0x00, 0x0F, 0x40, 0x02, 0xF0, 0x00, 0x3D, 0x00, 0x07, 0xC0, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0xFF, 0x40, 0x3E, 0x6F, 0x02, 0xE0, 0x3D, 0x0B, 0x40, 0xF0, 0x1F, 0x0B,
0x80, 0x2F, 0xF4, 0x00, 0xFF, 0xC0, 0x1F, 0x0F, 0xC0, 0xF0, 0x0F, 0x47, 0x80, 0x3D, 0x1F, 0x01,
0xF0, 0x3F, 0xAF, 0x40, 0x2F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7F, 0xF0,
0x01, 0xFA, 0xFC, 0x03, 0xD0, 0x3C, 0x07, 0xC0, 0x3D, 0x0B, 0x80, 0x3D, 0x07, 0xC0, 0x3D, 0x03,
0xF9, 0xBC, 0x00, 0xFF, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xD0, 0x00, 0x2F, 0x40, 0x1B, 0xFD,
0x00, 0x2F, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xD0, 0x07, 0xFF, 0xFF, 0xFD,
0x00, 0x07, 0xE5, 0x57, 0xD0, 0x00, 0x7D, 0x00, 0x3C, 0x00, 0x0B, 0xC0, 0x03, 0xC0, 0x00, 0xFC,
0x00, 0x10, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0x95, 0x64, 0x00,
0x02, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xAA, 0xA0, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x7D, 0x00,
@@ -4061,7 +4105,19 @@ static const uint8_t bookerly_14_italicBitmaps[64856] = {
0xFC, 0x00, 0x06, 0xAA, 0xBF, 0xAA, 0xA6, 0xFF, 0xFF, 0xFF, 0xFE, 0xBF, 0xFF, 0xFF, 0xFF, 0x80,
0x0A, 0x43, 0xFC, 0x3F, 0xC2, 0xF8, 0x00, 0x00, 0x0A, 0x00, 0x01, 0xA0, 0x00, 0x29, 0x0F, 0xF0,
0x01, 0xFD, 0x00, 0x2F, 0xC3, 0xFC, 0x00, 0xBF, 0x80, 0x0F, 0xF0, 0xFD, 0x00, 0x0F, 0xC0, 0x01,
0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x02, 0xFF,
0xE0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x5B, 0xE0, 0x00, 0x00, 0x00, 0x02, 0xC0, 0x02, 0xE0, 0x00,
0x00, 0x00, 0x2E, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x02, 0xFC, 0x69, 0x02, 0xE0, 0x00, 0x00, 0x2F,
0xFF, 0xFF, 0x03, 0xE0, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0x00, 0x2F, 0xFF, 0xFF, 0xFC,
0x2F, 0xE0, 0x02, 0xFF, 0xFF, 0xFF, 0xF0, 0xBF, 0xE0, 0x2F, 0xFF, 0xFF, 0xFF, 0x42, 0xFF, 0xE2,
0xFF, 0xFF, 0xFE, 0x40, 0x0F, 0xFF, 0xE3, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x03, 0xFF, 0xFF,
0x00, 0x2F, 0xFF, 0xF0, 0x03, 0xFF, 0xFC, 0x0B, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xF0, 0xBF, 0xFF,
0xF0, 0x00, 0x03, 0xFF, 0xD1, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00,
0x03, 0xFF, 0xEF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x03, 0xF0,
0x3F, 0x00, 0x00, 0x00, 0x00, 0x03, 0xD2, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
};
static const EpdGlyph bookerly_14_italicGlyphs[] = {
@@ -4716,83 +4772,102 @@ static const EpdGlyph bookerly_14_italicGlyphs[] = {
{ 0, 0, 0, 0, 0, 0, 58775 }, //
{ 0, 0, 0, 0, 0, 0, 58775 }, //
{ 0, 0, 0, 0, 0, 0, 58775 }, //
{ 18, 20, 18, 0, 20, 90, 58775 }, //
{ 19, 22, 18, -1, 21, 105, 58865 }, //
{ 35, 22, 36, 0, 21, 193, 58970 }, //
{ 20, 22, 18, -1, 21, 110, 59163 }, //
{ 17, 12, 29, 6, 16, 51, 59273 }, //
{ 13, 20, 29, 8, 20, 65, 59324 }, //
{ 17, 12, 29, 6, 16, 51, 59389 }, //
{ 13, 20, 29, 8, 20, 65, 59440 }, //
{ 18, 12, 29, 5, 16, 54, 59505 }, //
{ 13, 20, 29, 8, 20, 65, 59559 }, //
{ 13, 20, 29, 8, 20, 65, 59624 }, //
{ 13, 20, 29, 8, 20, 65, 59689 }, //
{ 13, 20, 29, 8, 20, 65, 59754 }, //
{ 17, 14, 29, 6, 17, 60, 59819 }, //
{ 17, 14, 29, 6, 17, 60, 59879 }, //
{ 13, 18, 29, 8, 19, 59, 59939 }, //
{ 17, 14, 29, 6, 17, 60, 59998 }, //
{ 13, 18, 29, 8, 19, 59, 60058 }, //
{ 19, 14, 29, 5, 17, 67, 60117 }, //
{ 19, 22, 29, 5, 21, 105, 60184 }, //
{ 14, 24, 18, 2, 23, 84, 60289 }, //
{ 13, 21, 29, 8, 21, 69, 60373 }, //
{ 23, 24, 29, 3, 22, 138, 60442 }, //
{ 19, 21, 19, 0, 21, 100, 60580 }, //
{ 19, 22, 29, 5, 21, 105, 60680 }, //
{ 19, 21, 29, 5, 21, 100, 60785 }, //
{ 19, 26, 29, 5, 23, 124, 60885 }, //
{ 19, 16, 29, 5, 18, 76, 61009 }, //
{ 19, 21, 29, 5, 21, 100, 61085 }, //
{ 19, 26, 29, 5, 23, 124, 61185 }, //
{ 19, 16, 29, 5, 18, 76, 61309 }, //
{ 22, 26, 23, 1, 21, 143, 61385 }, //
{ 18, 26, 18, 0, 21, 117, 61528 }, //
{ 13, 3, 18, 3, 11, 10, 61645 }, //
{ 17, 23, 14, -1, 21, 98, 61655 }, //
{ 11, 23, 14, 2, 21, 64, 61753 }, //
{ 14, 14, 18, 2, 17, 49, 61817 }, //
{ 10, 10, 18, 4, 15, 25, 61866 }, //
{ 6, 4, 18, 6, 10, 6, 61891 }, //
{ 18, 28, 18, 0, 24, 126, 61897 }, //
{ 14, 11, 18, 2, 15, 39, 62023 }, //
{ 18, 10, 18, 0, 14, 45, 62062 }, //
{ 19, 21, 29, 5, 21, 100, 62107 }, //
{ 21, 21, 29, 3, 21, 111, 62207 }, //
{ 4, 30, 15, 5, 23, 30, 62318 }, //
{ 9, 30, 15, 3, 23, 68, 62348 }, //
{ 19, 21, 29, 5, 21, 100, 62416 }, //
{ 19, 21, 29, 5, 21, 100, 62516 }, //
{ 17, 21, 29, 6, 21, 90, 62616 }, //
{ 17, 22, 29, 6, 21, 94, 62706 }, //
{ 16, 30, 18, 1, 23, 120, 62800 }, //
{ 19, 17, 29, 5, 16, 81, 62920 }, //
{ 19, 17, 29, 5, 16, 81, 63001 }, //
{ 6, 16, 9, 1, 18, 24, 63082 }, //
{ 19, 17, 29, 5, 16, 81, 63106 }, //
{ 17, 5, 29, 6, 11, 22, 63187 }, //
{ 17, 15, 29, 6, 17, 64, 63209 }, //
{ 14, 10, 18, 2, 15, 35, 63273 }, //
{ 17, 10, 29, 6, 13, 43, 63308 }, //
{ 13, 13, 18, 3, 16, 43, 63351 }, //
{ 17, 13, 29, 6, 15, 56, 63394 }, //
{ 14, 18, 18, 2, 18, 63, 63450 }, //
{ 14, 18, 18, 2, 18, 63, 63513 }, //
{ 23, 14, 29, 3, 17, 81, 63576 }, //
{ 23, 14, 29, 3, 17, 81, 63657 }, //
{ 19, 16, 29, 5, 18, 76, 63738 }, //
{ 19, 26, 29, 5, 23, 124, 63814 }, //
{ 19, 26, 29, 5, 23, 124, 63938 }, //
{ 19, 21, 29, 5, 21, 100, 64062 }, //
{ 19, 21, 29, 5, 21, 100, 64162 }, //
{ 21, 22, 29, 4, 21, 116, 64262 }, //
{ 21, 22, 29, 4, 21, 116, 64378 }, //
{ 21, 22, 29, 4, 21, 116, 64494 }, //
{ 21, 22, 29, 4, 21, 116, 64610 }, //
{ 17, 21, 29, 6, 21, 90, 64726 }, //
{ 6, 5, 18, 6, 10, 8, 64816 }, //
{ 25, 5, 29, 2, 10, 32, 64824 }, //
{ 11, 14, 13, 1, 24, 39, 58775 }, //
{ 12, 14, 13, 0, 24, 42, 58814 }, //
{ 11, 14, 13, 1, 24, 39, 58856 }, //
{ 12, 14, 13, 1, 24, 42, 58895 }, //
{ 10, 14, 13, 3, 24, 35, 58937 }, //
{ 11, 14, 13, 1, 24, 39, 58972 }, //
{ 12, 14, 13, 0, 24, 42, 59011 }, //
{ 11, 11, 16, 3, 21, 31, 59053 }, //
{ 11, 15, 13, 1, 10, 42, 59084 }, //
{ 8, 14, 13, 2, 10, 28, 59126 }, //
{ 11, 14, 13, 1, 10, 39, 59154 }, //
{ 10, 15, 13, 1, 10, 38, 59193 }, //
{ 12, 14, 13, 0, 10, 42, 59231 }, //
{ 11, 14, 13, 1, 9, 39, 59273 }, //
{ 12, 15, 13, 1, 10, 45, 59312 }, //
{ 10, 14, 13, 3, 9, 35, 59357 }, //
{ 11, 15, 13, 1, 10, 42, 59392 }, //
{ 12, 15, 13, 0, 10, 45, 59434 }, //
{ 18, 20, 18, 0, 20, 90, 59479 }, //
{ 19, 22, 18, -1, 21, 105, 59569 }, //
{ 35, 22, 36, 0, 21, 193, 59674 }, //
{ 20, 22, 18, -1, 21, 110, 59867 }, //
{ 17, 12, 29, 6, 16, 51, 59977 }, //
{ 13, 20, 29, 8, 20, 65, 60028 }, //
{ 17, 12, 29, 6, 16, 51, 60093 }, //
{ 13, 20, 29, 8, 20, 65, 60144 }, //
{ 18, 12, 29, 5, 16, 54, 60209 }, //
{ 13, 20, 29, 8, 20, 65, 60263 }, //
{ 13, 20, 29, 8, 20, 65, 60328 }, //
{ 13, 20, 29, 8, 20, 65, 60393 }, //
{ 13, 20, 29, 8, 20, 65, 60458 }, //
{ 17, 14, 29, 6, 17, 60, 60523 }, //
{ 17, 14, 29, 6, 17, 60, 60583 }, //
{ 13, 18, 29, 8, 19, 59, 60643 }, //
{ 17, 14, 29, 6, 17, 60, 60702 }, //
{ 13, 18, 29, 8, 19, 59, 60762 }, //
{ 19, 14, 29, 5, 17, 67, 60821 }, //
{ 19, 22, 29, 5, 21, 105, 60888 }, //
{ 14, 24, 18, 2, 23, 84, 60993 }, //
{ 13, 21, 29, 8, 21, 69, 61077 }, //
{ 23, 24, 29, 3, 22, 138, 61146 }, //
{ 19, 21, 19, 0, 21, 100, 61284 }, //
{ 19, 22, 29, 5, 21, 105, 61384 }, //
{ 19, 21, 29, 5, 21, 100, 61489 }, //
{ 19, 26, 29, 5, 23, 124, 61589 }, //
{ 19, 16, 29, 5, 18, 76, 61713 }, //
{ 19, 21, 29, 5, 21, 100, 61789 }, //
{ 19, 26, 29, 5, 23, 124, 61889 }, //
{ 19, 16, 29, 5, 18, 76, 62013 }, //
{ 22, 26, 23, 1, 21, 143, 62089 }, //
{ 18, 26, 18, 0, 21, 117, 62232 }, //
{ 13, 3, 18, 3, 11, 10, 62349 }, //
{ 17, 23, 14, -1, 21, 98, 62359 }, //
{ 11, 23, 14, 2, 21, 64, 62457 }, //
{ 14, 14, 18, 2, 17, 49, 62521 }, //
{ 10, 10, 18, 4, 15, 25, 62570 }, //
{ 6, 4, 18, 6, 10, 6, 62595 }, //
{ 18, 28, 18, 0, 24, 126, 62601 }, //
{ 14, 11, 18, 2, 15, 39, 62727 }, //
{ 18, 10, 18, 0, 14, 45, 62766 }, //
{ 19, 21, 29, 5, 21, 100, 62811 }, //
{ 21, 21, 29, 3, 21, 111, 62911 }, //
{ 4, 30, 15, 5, 23, 30, 63022 }, //
{ 9, 30, 15, 3, 23, 68, 63052 }, //
{ 19, 21, 29, 5, 21, 100, 63120 }, //
{ 19, 21, 29, 5, 21, 100, 63220 }, //
{ 17, 21, 29, 6, 21, 90, 63320 }, //
{ 17, 22, 29, 6, 21, 94, 63410 }, //
{ 16, 30, 18, 1, 23, 120, 63504 }, //
{ 19, 17, 29, 5, 16, 81, 63624 }, //
{ 19, 17, 29, 5, 16, 81, 63705 }, //
{ 6, 16, 9, 1, 18, 24, 63786 }, //
{ 19, 17, 29, 5, 16, 81, 63810 }, //
{ 17, 5, 29, 6, 11, 22, 63891 }, //
{ 17, 15, 29, 6, 17, 64, 63913 }, //
{ 14, 10, 18, 2, 15, 35, 63977 }, //
{ 17, 10, 29, 6, 13, 43, 64012 }, //
{ 13, 13, 18, 3, 16, 43, 64055 }, // ≠
{ 17, 13, 29, 6, 15, 56, 64098 }, // ≡
{ 14, 18, 18, 2, 18, 63, 64154 }, // ≤
{ 14, 18, 18, 2, 18, 63, 64217 }, // ≥
{ 23, 14, 29, 3, 17, 81, 64280 }, // ≪
{ 23, 14, 29, 3, 17, 81, 64361 }, // ≫
{ 19, 16, 29, 5, 18, 76, 64442 }, // ⊃
{ 19, 26, 29, 5, 23, 124, 64518 }, // ⊄
{ 19, 26, 29, 5, 23, 124, 64642 }, // ⊅
{ 19, 21, 29, 5, 21, 100, 64766 }, // ⊆
{ 19, 21, 29, 5, 21, 100, 64866 }, // ⊇
{ 21, 22, 29, 4, 21, 116, 64966 }, // ⊕
{ 21, 22, 29, 4, 21, 116, 65082 }, // ⊖
{ 21, 22, 29, 4, 21, 116, 65198 }, // ⊗
{ 21, 22, 29, 4, 21, 116, 65314 }, // ⊘
{ 17, 21, 29, 6, 21, 90, 65430 }, // ⊥
{ 6, 5, 18, 6, 10, 8, 65520 }, // ⋅
{ 25, 5, 29, 2, 10, 32, 65528 }, // ⋯
{ 27, 28, 29, 1, 24, 189, 65560 }, //
};
static const EpdUnicodeInterval bookerly_14_italicIntervals[] = {
@@ -4825,44 +4900,48 @@ static const EpdUnicodeInterval bookerly_14_italicIntervals[] = {
{ 0x2053, 0x2053, 0x283 },
{ 0x2057, 0x2057, 0x284 },
{ 0x205F, 0x2064, 0x285 },
{ 0x20A3, 0x20A4, 0x28B },
{ 0x20A7, 0x20A7, 0x28D },
{ 0x20AC, 0x20AC, 0x28E },
{ 0x2190, 0x2195, 0x28F },
{ 0x21A8, 0x21A8, 0x295 },
{ 0x21B2, 0x21B3, 0x296 },
{ 0x21B5, 0x21B5, 0x298 },
{ 0x21D0, 0x21D4, 0x299 },
{ 0x2200, 0x2200, 0x29E },
{ 0x2202, 0x2203, 0x29F },
{ 0x2205, 0x220D, 0x2A1 },
{ 0x220F, 0x220F, 0x2AA },
{ 0x2211, 0x2212, 0x2AB },
{ 0x2215, 0x221A, 0x2AD },
{ 0x221D, 0x2220, 0x2B3 },
{ 0x2223, 0x2223, 0x2B7 },
{ 0x2225, 0x2225, 0x2B8 },
{ 0x2227, 0x222B, 0x2B9 },
{ 0x2234, 0x2237, 0x2BE },
{ 0x223C, 0x223C, 0x2C2 },
{ 0x2245, 0x2245, 0x2C3 },
{ 0x2248, 0x2248, 0x2C4 },
{ 0x224D, 0x224D, 0x2C5 },
{ 0x2260, 0x2261, 0x2C6 },
{ 0x2264, 0x2265, 0x2C8 },
{ 0x226A, 0x226B, 0x2CA },
{ 0x2283, 0x2287, 0x2CC },
{ 0x2295, 0x2298, 0x2D1 },
{ 0x22A5, 0x22A5, 0x2D5 },
{ 0x22C5, 0x22C5, 0x2D6 },
{ 0x22EF, 0x22EF, 0x2D7 },
{ 0x2070, 0x2070, 0x28B },
{ 0x2074, 0x2079, 0x28C },
{ 0x207F, 0x2089, 0x292 },
{ 0x20A3, 0x20A4, 0x29D },
{ 0x20A7, 0x20A7, 0x29F },
{ 0x20AC, 0x20AC, 0x2A0 },
{ 0x2190, 0x2195, 0x2A1 },
{ 0x21A8, 0x21A8, 0x2A7 },
{ 0x21B2, 0x21B3, 0x2A8 },
{ 0x21B5, 0x21B5, 0x2AA },
{ 0x21D0, 0x21D4, 0x2AB },
{ 0x2200, 0x2200, 0x2B0 },
{ 0x2202, 0x2203, 0x2B1 },
{ 0x2205, 0x220D, 0x2B3 },
{ 0x220F, 0x220F, 0x2BC },
{ 0x2211, 0x2212, 0x2BD },
{ 0x2215, 0x221A, 0x2BF },
{ 0x221D, 0x2220, 0x2C5 },
{ 0x2223, 0x2223, 0x2C9 },
{ 0x2225, 0x2225, 0x2CA },
{ 0x2227, 0x222B, 0x2CB },
{ 0x2234, 0x2237, 0x2D0 },
{ 0x223C, 0x223C, 0x2D4 },
{ 0x2245, 0x2245, 0x2D5 },
{ 0x2248, 0x2248, 0x2D6 },
{ 0x224D, 0x224D, 0x2D7 },
{ 0x2260, 0x2261, 0x2D8 },
{ 0x2264, 0x2265, 0x2DA },
{ 0x226A, 0x226B, 0x2DC },
{ 0x2283, 0x2287, 0x2DE },
{ 0x2295, 0x2298, 0x2E3 },
{ 0x22A5, 0x22A5, 0x2E7 },
{ 0x22C5, 0x22C5, 0x2E8 },
{ 0x22EF, 0x22EF, 0x2E9 },
{ 0xFFFD, 0xFFFD, 0x2EA },
};
static const EpdFontData bookerly_14_italic = {
bookerly_14_italicBitmaps,
bookerly_14_italicGlyphs,
bookerly_14_italicIntervals,
60,
64,
38,
31,
-8,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More