Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fdf23f1d2 | ||
|
|
2fb417ee90 | ||
|
|
c8f6160fbc | ||
|
|
8e4484cd22 | ||
|
|
0332e1103a | ||
|
|
5790d6f5dc | ||
|
|
062d69dc2a | ||
|
|
5e9626eb2a | ||
|
|
00e83af4e8 | ||
|
|
39080c0e51 | ||
|
|
9e59a5106b | ||
|
|
a922e553ed | ||
|
|
04ad4e5aa4 | ||
|
|
6e9ba1006a | ||
|
|
40f9ed485c | ||
|
|
b82e044ac3 | ||
|
|
026733a4fe | ||
|
|
57b075ec97 | ||
|
|
648c688642 | ||
|
|
06065dfd8b | ||
|
|
93226c9fbb | ||
|
|
941643cf97 | ||
|
|
9bba41ed96 | ||
|
|
34cf5f0636 | ||
|
|
f2ca65d752 |
@@ -25,7 +25,7 @@ This project is **not affiliated with Xteink**; it's built as a community projec
|
|||||||
|
|
||||||
## Features & Usage
|
## Features & Usage
|
||||||
|
|
||||||
- [x] EPUB parsing and rendering
|
- [x] EPUB parsing and rendering (EPUB 2 and EPUB 3)
|
||||||
- [ ] Image support within EPUB
|
- [ ] Image support within EPUB
|
||||||
- [x] Saved reading position
|
- [x] Saved reading position
|
||||||
- [x] File explorer with file picker
|
- [x] File explorer with file picker
|
||||||
@@ -33,11 +33,13 @@ This project is **not affiliated with Xteink**; it's built as a community projec
|
|||||||
- [x] Support nested folders
|
- [x] Support nested folders
|
||||||
- [ ] EPUB picker with cover art
|
- [ ] EPUB picker with cover art
|
||||||
- [x] Custom sleep screen
|
- [x] Custom sleep screen
|
||||||
- [ ] Cover sleep screen
|
- [x] Cover sleep screen
|
||||||
- [x] Wifi book upload
|
- [x] Wifi book upload
|
||||||
- [ ] Wifi OTA updates
|
- [x] Wifi OTA updates
|
||||||
- [ ] Configurable font, layout, and display options
|
- [x] Configurable font, layout, and display options
|
||||||
- [ ] Screen rotation
|
- [ ] User provided fonts
|
||||||
|
- [ ] Full UTF support
|
||||||
|
- [x] Screen rotation
|
||||||
|
|
||||||
See [the user guide](./USER_GUIDE.md) for instructions on operating CrossPoint.
|
See [the user guide](./USER_GUIDE.md) for instructions on operating CrossPoint.
|
||||||
|
|
||||||
|
|||||||
+39
-17
@@ -5,7 +5,7 @@ the device.
|
|||||||
|
|
||||||
## 1. Hardware Overview
|
## 1. Hardware Overview
|
||||||
|
|
||||||
The device utilises the standard buttons on the Xtink X4 in the same layout:
|
The device utilises the standard buttons on the Xtink X4 (in the same layout as the manufacturer firmware, by default):
|
||||||
|
|
||||||
### Button Layout
|
### Button Layout
|
||||||
| Location | Buttons |
|
| Location | Buttons |
|
||||||
@@ -13,20 +13,23 @@ The device utilises the standard buttons on the Xtink X4 in the same layout:
|
|||||||
| **Bottom Edge** | **Back**, **Confirm**, **Left**, **Right** |
|
| **Bottom Edge** | **Back**, **Confirm**, **Left**, **Right** |
|
||||||
| **Right Side** | **Power**, **Volume Up**, **Volume Down** |
|
| **Right Side** | **Power**, **Volume Up**, **Volume Down** |
|
||||||
|
|
||||||
|
Button layout can be customized in **[Settings](#35-settings)**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Power & Startup
|
## 2. Power & Startup
|
||||||
|
|
||||||
### Power On / Off
|
### Power On / Off
|
||||||
|
|
||||||
To turn the device on or off, **press and hold the Power button for half a second**. In **Settings** you can configure
|
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.
|
the power button to trigger on a short press instead of a long one.
|
||||||
|
|
||||||
### First Launch
|
### First Launch
|
||||||
|
|
||||||
Upon turning the device on for the first time, you will be placed on the **Home** screen.
|
Upon turning the device on for the first time, you will be placed on the **[Home](#31-home-screen)** screen.
|
||||||
|
|
||||||
> **Note:** On subsequent restarts, the firmware will automatically reopen the last book you were reading.
|
> [!NOTE]
|
||||||
|
> On subsequent restarts, the firmware will automatically reopen the last book you were reading.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,10 +37,10 @@ Upon turning the device on for the first time, you will be placed on the **Home*
|
|||||||
|
|
||||||
### 3.1 Home Screen
|
### 3.1 Home Screen
|
||||||
|
|
||||||
The Home Screen is the main entry point to the firmware. From here you can navigate to the **Book Selection** screen,
|
The Home Screen is the main entry point to the firmware. From here you can navigate to **[Reading Mode](#4-reading-mode)** with the most recently read book, **[Book Selection](#32-book-selection)**,
|
||||||
**Settings** screen, or **File Upload** screen.
|
**[Settings](#35-settings)**, or the **[File Upload](#34-file-upload-screen)** screen.
|
||||||
|
|
||||||
### 3.2 Book Selection (Read)
|
### 3.2 Book Selection
|
||||||
|
|
||||||
The Book Selection acts as a folder and file browser.
|
The Book Selection acts as a folder and file browser.
|
||||||
|
|
||||||
@@ -45,13 +48,13 @@ The Book Selection acts as a folder and file browser.
|
|||||||
and down through folders and books.
|
and down through folders and books.
|
||||||
* **Open Selection:** Press **Confirm** to open a folder or read a selected book.
|
* **Open Selection:** Press **Confirm** to open a folder or read a selected book.
|
||||||
|
|
||||||
### 3.3 Reading Screen
|
### 3.3 Reading Mode
|
||||||
|
|
||||||
See [4. Reading Mode](#4-reading-mode) below for more information.
|
See [Reading Mode](#4-reading-mode) below for more information.
|
||||||
|
|
||||||
### 3.4 File Upload Screen
|
### 3.4 File Upload Screen
|
||||||
|
|
||||||
The File Upload screen allows you to upload new e-books to the device. When you enter the screen you'll be prompted with
|
The File Upload screen allows you to upload new e-books to the device. When you enter the screen, you'll be prompted with
|
||||||
a WiFi selection dialog and then your X4 will start hosting a web server.
|
a WiFi selection dialog and then your X4 will start hosting a web server.
|
||||||
|
|
||||||
See the [webserver docs](./docs/webserver.md) for more information on how to connect to the web server and upload files.
|
See the [webserver docs](./docs/webserver.md) for more information on how to connect to the web server and upload files.
|
||||||
@@ -62,12 +65,32 @@ The Settings screen allows you to configure the device's behavior. There are a f
|
|||||||
- **Sleep Screen**: Which sleep screen to display when the device sleeps, options are:
|
- **Sleep Screen**: Which sleep screen to display when the device sleeps, options are:
|
||||||
- "Dark" (default) - The default dark sleep screen
|
- "Dark" (default) - The default dark sleep screen
|
||||||
- "Light" - The same default sleep screen, on a white background
|
- "Light" - The same default sleep screen, on a white background
|
||||||
- "Custom" - Custom images from the SD card, see [3.6 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)
|
- "Cover" - The book cover image (Note: this is experimental and may not work as expected)
|
||||||
|
- **Status Bar**: Configure the status bar displayed while reading, options are:
|
||||||
|
- "None" - No status bar
|
||||||
|
- "No Progress" - Show status bar without reading progress
|
||||||
|
- "Full" - Show status bar with reading progress
|
||||||
- **Extra Paragraph Spacing**: If enabled, vertical space will be added between paragraphs in the book, if disabled,
|
- **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 word indentation.
|
paragraphs will not have vertical space between them, but will have first word indentation.
|
||||||
- **Short Power Button Click**: Whether to trigger the power button on a short press or a long press.
|
- **Short Power Button Click**: Whether to trigger the power button on a short press or a long press.
|
||||||
- **Front Button Layout**: Swap the order of the bottom edge buttons from Back/Confirm/Left/Right to Left/Right/Back/Confirm.
|
- **Reading Orientation**: Set the screen orientation for reading, options are:
|
||||||
|
- "Portrait" (default) - Standard portrait orientation
|
||||||
|
- "Landscape CW" - Landscape, rotated clockwise
|
||||||
|
- "Inverted" - Portrait, upside down
|
||||||
|
- "Landscape CCW" - Landscape, rotated counter-clockwise
|
||||||
|
- **Front Button Layout**: Configure the order of the bottom edge buttons, options are:
|
||||||
|
- "Bck, Cnfrm, Lft, Rght" (default) - Back, Confirm, Left, Right
|
||||||
|
- "Lft, Rght, Bck, Cnfrm" - Left, Right, Back, Confirm
|
||||||
|
- "Lft, Bck, Cnfrm, Rght" - Left, Back, Confirm, Right
|
||||||
|
- **Side Button Layout**: Swap the order of the 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, options are:
|
||||||
|
- "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".
|
||||||
|
- **Check for updates**: Check for firmware updates over WiFi.
|
||||||
|
|
||||||
### 3.6 Sleep Screen
|
### 3.6 Sleep Screen
|
||||||
|
|
||||||
@@ -75,7 +98,7 @@ You can customize the sleep screen by placing custom images in specific location
|
|||||||
|
|
||||||
- **Single Image:** Place a file named `sleep.bmp` in the root directory.
|
- **Single Image:** Place a file named `sleep.bmp` in the root directory.
|
||||||
- **Multiple Images:** Create a `sleep` directory in the root of the SD card and place any number of `.bmp` images
|
- **Multiple Images:** Create a `sleep` directory in the root of the SD card and place any number of `.bmp` images
|
||||||
inside. If images are found in this directory, they will take priority over the `sleep.png` file, and one will be
|
inside. If images are found in this directory, they will take priority over the `sleep.bmp` file, and one will be
|
||||||
randomly selected each time the device sleeps.
|
randomly selected each time the device sleeps.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
@@ -103,8 +126,9 @@ Once you have opened a book, the button layout changes to facilitate reading.
|
|||||||
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release.
|
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release.
|
||||||
|
|
||||||
### System Navigation
|
### System Navigation
|
||||||
* **Return to Home:** Press **Back** to close the book and return to the Book Selection screen.
|
* **Return to Book Selection:** Press **Back** to close the book and return to the **[Book Selection](#32-book-selection)** screen.
|
||||||
* **Chapter Menu:** Press **Confirm** to open the Table of Contents/Chapter Selection screen.
|
* **Return to Home:** Press and hold **Back** to close the book and return to the **[Home](#31-home-screen)** screen.
|
||||||
|
* **Chapter Menu:** Press **Confirm** to open the **[Table of Contents/Chapter Selection](#5-chapter-selection-screen)**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -124,5 +148,3 @@ Please note that this firmware is currently in active development. The following
|
|||||||
are planned for future updates:
|
are planned for future updates:
|
||||||
|
|
||||||
* **Images:** Embedded images in e-books will not render.
|
* **Images:** Embedded images in e-books will not render.
|
||||||
* **Text Formatting:** There are currently no settings to adjust font type, size, line spacing, or margins.
|
|
||||||
* **Rotation**: Different rotation options are not supported.
|
|
||||||
|
|||||||
+214
-2
@@ -2,8 +2,220 @@
|
|||||||
|
|
||||||
## `book.bin`
|
## `book.bin`
|
||||||
|
|
||||||

|
### Version 3
|
||||||
|
|
||||||
|
ImHex Pattern:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
import std.mem;
|
||||||
|
import std.string;
|
||||||
|
import std.core;
|
||||||
|
|
||||||
|
// === Configuration ===
|
||||||
|
#define EXPECTED_VERSION 3
|
||||||
|
#define MAX_STRING_LENGTH 65535
|
||||||
|
|
||||||
|
// === String Structure ===
|
||||||
|
|
||||||
|
struct String {
|
||||||
|
u32 length [[hidden, comment("String byte length")]];
|
||||||
|
if (length > MAX_STRING_LENGTH) {
|
||||||
|
std::warning(std::format("Unusually large string length: {} bytes", length));
|
||||||
|
}
|
||||||
|
char data[length] [[comment("UTF-8 string data")]];
|
||||||
|
} [[sealed, format("format_string"), comment("Length-prefixed UTF-8 string")]];
|
||||||
|
|
||||||
|
fn format_string(String s) {
|
||||||
|
return s.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Metadata Structure ===
|
||||||
|
|
||||||
|
struct Metadata {
|
||||||
|
String title [[comment("Book title")]];
|
||||||
|
String author [[comment("Book author")]];
|
||||||
|
String coverItemHref [[comment("Path to cover image")]];
|
||||||
|
String textReferenceHref [[comment("Path to guided first text reference")]];
|
||||||
|
} [[comment("Book metadata information")]];
|
||||||
|
|
||||||
|
// === Spine Entry Structure ===
|
||||||
|
|
||||||
|
struct SpineEntry {
|
||||||
|
String href [[comment("Resource path")]];
|
||||||
|
u32 cumulativeSize [[comment("Cumulative size in bytes"), color("FF6B6B")]];
|
||||||
|
s16 tocIndex [[comment("Index into TOC (-1 if none)"), color("4ECDC4")]];
|
||||||
|
} [[comment("Spine entry defining reading order")]];
|
||||||
|
|
||||||
|
// === TOC Entry Structure ===
|
||||||
|
|
||||||
|
struct TocEntry {
|
||||||
|
String title [[comment("Chapter/section title")]];
|
||||||
|
String href [[comment("Resource path")]];
|
||||||
|
String anchor [[comment("Fragment identifier")]];
|
||||||
|
u8 level [[comment("Nesting level (0-255)"), color("95E1D3")]];
|
||||||
|
s16 spineIndex [[comment("Index into spine (-1 if none)"), color("F38181")]];
|
||||||
|
} [[comment("Table of contents entry")]];
|
||||||
|
|
||||||
|
// === Book Bin Structure ===
|
||||||
|
|
||||||
|
struct BookBin {
|
||||||
|
// Header
|
||||||
|
u8 version [[comment("Format version"), color("FFD93D")]];
|
||||||
|
|
||||||
|
// Version validation
|
||||||
|
if (version != EXPECTED_VERSION) {
|
||||||
|
std::error(std::format("Unsupported version: {} (expected {})", version, EXPECTED_VERSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 lutOffset [[comment("Offset to lookup tables"), color("6BCB77")]];
|
||||||
|
u16 spineCount [[comment("Number of spine entries"), color("4D96FF")]];
|
||||||
|
u16 tocCount [[comment("Number of TOC entries"), color("FF6B9D")]];
|
||||||
|
|
||||||
|
// Metadata section
|
||||||
|
Metadata metadata [[comment("Book metadata")]];
|
||||||
|
|
||||||
|
// Validate LUT offset alignment
|
||||||
|
u32 currentOffset = $;
|
||||||
|
if (currentOffset != lutOffset) {
|
||||||
|
std::warning(std::format("LUT offset mismatch: expected 0x{:X}, got 0x{:X}", lutOffset, currentOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup Tables
|
||||||
|
u32 spineLut[spineCount] [[comment("Spine entry offsets"), color("4D96FF")]];
|
||||||
|
u32 tocLut[tocCount] [[comment("TOC entry offsets"), color("FF6B9D")]];
|
||||||
|
|
||||||
|
// Data Entries
|
||||||
|
SpineEntry spines[spineCount] [[comment("Spine entries (reading order)")]];
|
||||||
|
TocEntry toc[tocCount] [[comment("Table of contents entries")]];
|
||||||
|
};
|
||||||
|
|
||||||
|
// === File Parsing ===
|
||||||
|
|
||||||
|
BookBin book @ 0x00;
|
||||||
|
|
||||||
|
// Validate we've consumed the entire file
|
||||||
|
u32 fileSize = std::mem::size();
|
||||||
|
u32 parsedSize = $;
|
||||||
|
|
||||||
|
if (parsedSize != fileSize) {
|
||||||
|
std::warning(std::format("Unparsed data detected: {} bytes remaining at offset 0x{:X}", fileSize - parsedSize, parsedSize));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `section.bin`
|
## `section.bin`
|
||||||
|
|
||||||

|
### Version 8
|
||||||
|
|
||||||
|
ImHex Pattern:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
import std.mem;
|
||||||
|
import std.string;
|
||||||
|
import std.core;
|
||||||
|
|
||||||
|
// === Configuration ===
|
||||||
|
#define EXPECTED_VERSION 8
|
||||||
|
#define MAX_STRING_LENGTH 65535
|
||||||
|
|
||||||
|
// === String Structure ===
|
||||||
|
|
||||||
|
struct String {
|
||||||
|
u32 length [[hidden, comment("String byte length")]];
|
||||||
|
if (length > MAX_STRING_LENGTH) {
|
||||||
|
std::warning(std::format("Unusually large string length: {} bytes", length));
|
||||||
|
}
|
||||||
|
char data[length] [[comment("UTF-8 string data")]];
|
||||||
|
} [[sealed, format("format_string"), comment("Length-prefixed UTF-8 string")]];
|
||||||
|
|
||||||
|
fn format_string(String s) {
|
||||||
|
return s.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Page Structure ===
|
||||||
|
|
||||||
|
enum StorageType : u8 {
|
||||||
|
PageLine = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WordStyle : u8 {
|
||||||
|
REGULAR = 0,
|
||||||
|
BOLD = 1,
|
||||||
|
ITALIC = 2,
|
||||||
|
BOLD_ITALIC = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BlockStyle : u8 {
|
||||||
|
JUSTIFIED = 0,
|
||||||
|
LEFT_ALIGN = 1,
|
||||||
|
CENTER_ALIGN = 2,
|
||||||
|
RIGHT_ALIGN = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PageLine {
|
||||||
|
s16 xPos;
|
||||||
|
s16 yPos;
|
||||||
|
u16 wordCount;
|
||||||
|
String words[wordCount];
|
||||||
|
u16 wordXPos[wordCount];
|
||||||
|
WordStyle wordStyle[wordCount];
|
||||||
|
BlockStyle blockStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PageElement {
|
||||||
|
u8 pageElementType;
|
||||||
|
if (pageElementType == 1) {
|
||||||
|
PageLine pageLine [[inline]];
|
||||||
|
} else {
|
||||||
|
std::error(std::format("Unknown page element type: {}", pageElementType));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Page {
|
||||||
|
u16 elementCount;
|
||||||
|
PageElement elements[elementCount] [[inline]];
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Section Bin Structure ===
|
||||||
|
|
||||||
|
struct SectionBin {
|
||||||
|
// Header
|
||||||
|
u8 version [[comment("Format version"), color("FFD93D")]];
|
||||||
|
|
||||||
|
// Version validation
|
||||||
|
if (version != EXPECTED_VERSION) {
|
||||||
|
std::error(std::format("Unsupported version: {} (expected {})", version, EXPECTED_VERSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache busting parameters
|
||||||
|
s32 fontId;
|
||||||
|
float lineCompression;
|
||||||
|
bool extraParagraphSpacing;
|
||||||
|
u16 viewportWidth;
|
||||||
|
u16 vieportHeight;
|
||||||
|
u16 pageCount;
|
||||||
|
u32 lutOffset;
|
||||||
|
|
||||||
|
Page page[pageCount];
|
||||||
|
|
||||||
|
// Validate LUT offset alignment
|
||||||
|
u32 currentOffset = $;
|
||||||
|
if (currentOffset != lutOffset) {
|
||||||
|
std::warning(std::format("LUT offset mismatch: expected 0x{:X}, got 0x{:X}", lutOffset, currentOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup Tables
|
||||||
|
u32 lut[pageCount];
|
||||||
|
};
|
||||||
|
|
||||||
|
// === File Parsing ===
|
||||||
|
|
||||||
|
SectionBin book @ 0x00;
|
||||||
|
|
||||||
|
// Validate we've consumed the entire file
|
||||||
|
u32 fileSize = std::mem::size();
|
||||||
|
u32 parsedSize = $;
|
||||||
|
|
||||||
|
if (parsedSize != fileSize) {
|
||||||
|
std::warning(std::format("Unparsed data detected: {} bytes remaining at offset 0x{:X}", fileSize - parsedSize, parsedSize));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 539 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 296 KiB |
@@ -1,6 +1,6 @@
|
|||||||
#include "EpdFontFamily.h"
|
#include "EpdFontFamily.h"
|
||||||
|
|
||||||
const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
const EpdFont* EpdFontFamily::getFont(const Style style) const {
|
||||||
if (style == BOLD && bold) {
|
if (style == BOLD && bold) {
|
||||||
return bold;
|
return bold;
|
||||||
}
|
}
|
||||||
@@ -22,16 +22,16 @@ const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
|||||||
return regular;
|
return regular;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdFontFamily::getTextDimensions(const char* string, int* w, int* h, const EpdFontStyle style) const {
|
void EpdFontFamily::getTextDimensions(const char* string, int* w, int* h, const Style style) const {
|
||||||
getFont(style)->getTextDimensions(string, w, h);
|
getFont(style)->getTextDimensions(string, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EpdFontFamily::hasPrintableChars(const char* string, const EpdFontStyle style) const {
|
bool EpdFontFamily::hasPrintableChars(const char* string, const Style style) const {
|
||||||
return getFont(style)->hasPrintableChars(string);
|
return getFont(style)->hasPrintableChars(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EpdFontData* EpdFontFamily::getData(const EpdFontStyle style) const { return getFont(style)->data; }
|
const EpdFontData* EpdFontFamily::getData(const Style style) const { return getFont(style)->data; }
|
||||||
|
|
||||||
const EpdGlyph* EpdFontFamily::getGlyph(const uint32_t cp, const EpdFontStyle style) const {
|
const EpdGlyph* EpdFontFamily::getGlyph(const uint32_t cp, const Style style) const {
|
||||||
return getFont(style)->getGlyph(cp);
|
return getFont(style)->getGlyph(cp);
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-14
@@ -1,24 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "EpdFont.h"
|
#include "EpdFont.h"
|
||||||
|
|
||||||
enum EpdFontStyle { REGULAR, BOLD, ITALIC, BOLD_ITALIC };
|
|
||||||
|
|
||||||
class EpdFontFamily {
|
class EpdFontFamily {
|
||||||
|
public:
|
||||||
|
enum Style : uint8_t { REGULAR = 0, BOLD = 1, ITALIC = 2, BOLD_ITALIC = 3 };
|
||||||
|
|
||||||
|
explicit EpdFontFamily(const EpdFont* regular, const EpdFont* bold = nullptr, const EpdFont* italic = nullptr,
|
||||||
|
const EpdFont* boldItalic = nullptr)
|
||||||
|
: regular(regular), bold(bold), italic(italic), boldItalic(boldItalic) {}
|
||||||
|
~EpdFontFamily() = default;
|
||||||
|
void getTextDimensions(const char* string, int* w, int* h, Style style = REGULAR) const;
|
||||||
|
bool hasPrintableChars(const char* string, Style style = REGULAR) const;
|
||||||
|
const EpdFontData* getData(Style style = REGULAR) const;
|
||||||
|
const EpdGlyph* getGlyph(uint32_t cp, Style style = REGULAR) const;
|
||||||
|
|
||||||
|
private:
|
||||||
const EpdFont* regular;
|
const EpdFont* regular;
|
||||||
const EpdFont* bold;
|
const EpdFont* bold;
|
||||||
const EpdFont* italic;
|
const EpdFont* italic;
|
||||||
const EpdFont* boldItalic;
|
const EpdFont* boldItalic;
|
||||||
|
|
||||||
const EpdFont* getFont(EpdFontStyle style) const;
|
const EpdFont* getFont(Style style) const;
|
||||||
|
|
||||||
public:
|
|
||||||
explicit EpdFontFamily(const EpdFont* regular, const EpdFont* bold = nullptr, const EpdFont* italic = nullptr,
|
|
||||||
const EpdFont* boldItalic = nullptr)
|
|
||||||
: regular(regular), bold(bold), italic(italic), boldItalic(boldItalic) {}
|
|
||||||
~EpdFontFamily() = default;
|
|
||||||
void getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style = REGULAR) const;
|
|
||||||
bool hasPrintableChars(const char* string, EpdFontStyle style = REGULAR) const;
|
|
||||||
|
|
||||||
const EpdFontData* getData(EpdFontStyle style = REGULAR) const;
|
|
||||||
const EpdGlyph* getGlyph(uint32_t cp, EpdFontStyle style = REGULAR) const;
|
|
||||||
};
|
};
|
||||||
|
|||||||
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
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
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
@@ -1,21 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <builtinFonts/aleo_12_bold.h>
|
#include <builtinFonts/bookerly_12_bold.h>
|
||||||
#include <builtinFonts/aleo_12_bolditalic.h>
|
#include <builtinFonts/bookerly_12_bolditalic.h>
|
||||||
#include <builtinFonts/aleo_12_italic.h>
|
#include <builtinFonts/bookerly_12_italic.h>
|
||||||
#include <builtinFonts/aleo_12_regular.h>
|
#include <builtinFonts/bookerly_12_regular.h>
|
||||||
#include <builtinFonts/aleo_14_bold.h>
|
#include <builtinFonts/bookerly_14_bold.h>
|
||||||
#include <builtinFonts/aleo_14_bolditalic.h>
|
#include <builtinFonts/bookerly_14_bolditalic.h>
|
||||||
#include <builtinFonts/aleo_14_italic.h>
|
#include <builtinFonts/bookerly_14_italic.h>
|
||||||
#include <builtinFonts/aleo_14_regular.h>
|
#include <builtinFonts/bookerly_14_regular.h>
|
||||||
#include <builtinFonts/aleo_16_bold.h>
|
#include <builtinFonts/bookerly_16_bold.h>
|
||||||
#include <builtinFonts/aleo_16_bolditalic.h>
|
#include <builtinFonts/bookerly_16_bolditalic.h>
|
||||||
#include <builtinFonts/aleo_16_italic.h>
|
#include <builtinFonts/bookerly_16_italic.h>
|
||||||
#include <builtinFonts/aleo_16_regular.h>
|
#include <builtinFonts/bookerly_16_regular.h>
|
||||||
#include <builtinFonts/aleo_18_bold.h>
|
#include <builtinFonts/bookerly_18_bold.h>
|
||||||
#include <builtinFonts/aleo_18_bolditalic.h>
|
#include <builtinFonts/bookerly_18_bolditalic.h>
|
||||||
#include <builtinFonts/aleo_18_italic.h>
|
#include <builtinFonts/bookerly_18_italic.h>
|
||||||
#include <builtinFonts/aleo_18_regular.h>
|
#include <builtinFonts/bookerly_18_regular.h>
|
||||||
|
#include <builtinFonts/notosans_8_regular.h>
|
||||||
#include <builtinFonts/notosans_12_bold.h>
|
#include <builtinFonts/notosans_12_bold.h>
|
||||||
#include <builtinFonts/notosans_12_bolditalic.h>
|
#include <builtinFonts/notosans_12_bolditalic.h>
|
||||||
#include <builtinFonts/notosans_12_italic.h>
|
#include <builtinFonts/notosans_12_italic.h>
|
||||||
@@ -48,7 +49,6 @@
|
|||||||
#include <builtinFonts/opendyslexic_8_bolditalic.h>
|
#include <builtinFonts/opendyslexic_8_bolditalic.h>
|
||||||
#include <builtinFonts/opendyslexic_8_italic.h>
|
#include <builtinFonts/opendyslexic_8_italic.h>
|
||||||
#include <builtinFonts/opendyslexic_8_regular.h>
|
#include <builtinFonts/opendyslexic_8_regular.h>
|
||||||
#include <builtinFonts/spacegrotesk.h>
|
|
||||||
#include <builtinFonts/ubuntu_10_bold.h>
|
#include <builtinFonts/ubuntu_10_bold.h>
|
||||||
#include <builtinFonts/ubuntu_10_regular.h>
|
#include <builtinFonts/ubuntu_10_regular.h>
|
||||||
#include <builtinFonts/ubuntu_12_bold.h>
|
#include <builtinFonts/ubuntu_12_bold.h>
|
||||||
|
|||||||
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
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
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,93 +0,0 @@
|
|||||||
Copyright 2018 The Aleo Project Authors (https://github.com/AlessioLaiso/aleo)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
https://openfontlicense.org
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,93 +0,0 @@
|
|||||||
Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
https://openfontlicense.org
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
Binary file not shown.
@@ -1,826 +0,0 @@
|
|||||||
/**
|
|
||||||
* generated by fontconvert.py
|
|
||||||
* name: spacegrotesk
|
|
||||||
* size: 8
|
|
||||||
* mode: 1-bit
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include "EpdFontData.h"
|
|
||||||
|
|
||||||
static const uint8_t spacegroteskBitmaps[5331] = {
|
|
||||||
0x66, 0x66, 0x66, 0x66, 0x60, 0x67, 0x20, 0x00, 0xF9, 0xF1, 0xA6, 0xC0, 0x00, 0x33, 0x0C, 0xC3,
|
|
||||||
0x31, 0xFF, 0xFF, 0xCC, 0xC3, 0x33, 0xFF, 0xFF, 0xCC, 0xC3, 0x30, 0xCC, 0x0C, 0x03, 0x03, 0xF1,
|
|
||||||
0xFE, 0x6D, 0x9B, 0x77, 0xC8, 0xF8, 0x1F, 0x93, 0x7E, 0xCD, 0xB3, 0x7F, 0x8F, 0xE0, 0xC0, 0x30,
|
|
||||||
0x78, 0x37, 0xE3, 0x33, 0x39, 0x9B, 0x87, 0xD8, 0x1D, 0x80, 0x19, 0x01, 0xFE, 0x1D, 0xB8, 0xCC,
|
|
||||||
0xCC, 0x66, 0xC3, 0xE0, 0x04, 0x00, 0x3E, 0x1F, 0x86, 0x03, 0x84, 0x63, 0x1F, 0xF7, 0xFD, 0x8C,
|
|
||||||
0xC3, 0x38, 0xC7, 0xF0, 0xFC, 0x06, 0x73, 0x60, 0x19, 0xEE, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C,
|
|
||||||
0x30, 0xC3, 0x0E, 0x1E, 0x3C, 0xC3, 0xC3, 0x86, 0x1C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x61,
|
|
||||||
0xBC, 0xE0, 0x08, 0x6A, 0x3E, 0xFF, 0x7F, 0x3E, 0x6A, 0x08, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18,
|
|
||||||
0x18, 0x18, 0x06, 0x73, 0x60, 0xFF, 0xE0, 0x06, 0x72, 0x0C, 0x18, 0x30, 0xE1, 0x83, 0x06, 0x18,
|
|
||||||
0x30, 0x61, 0x83, 0x06, 0x18, 0x30, 0x40, 0x3E, 0x3F, 0xB0, 0xF8, 0x3C, 0x1E, 0x0F, 0x07, 0x83,
|
|
||||||
0xC1, 0xE0, 0xF9, 0xCF, 0xE1, 0x80, 0x1C, 0xF7, 0xDB, 0xEF, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x3F,
|
|
||||||
0x1F, 0xE6, 0x19, 0x87, 0x41, 0xC0, 0x60, 0xF8, 0xF8, 0x70, 0x18, 0x07, 0xF9, 0xFF, 0x7F, 0x9F,
|
|
||||||
0xF0, 0x38, 0x3C, 0x1C, 0x07, 0xC1, 0xF8, 0x06, 0xC1, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x07,
|
|
||||||
0x03, 0xC0, 0xF0, 0x6C, 0x3B, 0x0C, 0xC6, 0x33, 0x8C, 0xFF, 0xFF, 0xF0, 0x30, 0x0C, 0x7F, 0x9F,
|
|
||||||
0xE6, 0x01, 0x80, 0x7F, 0x1F, 0xE7, 0x18, 0x07, 0x61, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x7E,
|
|
||||||
0x7F, 0xB0, 0xD8, 0x3F, 0xE7, 0xFB, 0x0D, 0x83, 0xC1, 0xE1, 0xF9, 0xCF, 0xC1, 0x80, 0xFF, 0xFF,
|
|
||||||
0xC0, 0x60, 0x60, 0x30, 0x30, 0x38, 0x18, 0x1C, 0x0C, 0x0E, 0x06, 0x00, 0x3F, 0x1F, 0xE6, 0x1D,
|
|
||||||
0x83, 0x61, 0xCF, 0xE7, 0xF9, 0x87, 0xE0, 0xF8, 0x37, 0x3C, 0xFE, 0x0C, 0x00, 0x3F, 0x1F, 0xE6,
|
|
||||||
0x19, 0x87, 0xE1, 0xD8, 0x77, 0xFC, 0xFF, 0x41, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x06, 0x72,
|
|
||||||
0x00, 0x06, 0x72, 0x06, 0x72, 0x00, 0x06, 0x73, 0x60, 0x01, 0x0F, 0x3E, 0xF0, 0xF0, 0x7C, 0x1F,
|
|
||||||
0x03, 0xFF, 0xFF, 0x7F, 0xFF, 0x00, 0xC0, 0xF0, 0x7E, 0x0F, 0x07, 0x3E, 0xF8, 0xC0, 0x3F, 0x3F,
|
|
||||||
0xD8, 0x78, 0x34, 0x18, 0x3C, 0x3C, 0x18, 0x0C, 0x00, 0x07, 0x03, 0x80, 0x80, 0x0F, 0xE0, 0x3F,
|
|
||||||
0xF0, 0xE0, 0x73, 0x18, 0x6E, 0x7F, 0x79, 0xDE, 0xF3, 0x1D, 0xEE, 0x1B, 0xDC, 0x37, 0x98, 0xEF,
|
|
||||||
0x3B, 0xFB, 0x3F, 0xE7, 0x10, 0x07, 0x80, 0x07, 0xFC, 0x01, 0xF0, 0x0E, 0x03, 0xC0, 0x7C, 0x0D,
|
|
||||||
0x83, 0xB0, 0x66, 0x0C, 0x63, 0xFC, 0x7F, 0x8C, 0x39, 0x83, 0x60, 0x60, 0xFF, 0x9F, 0xF8, 0xC3,
|
|
||||||
0x18, 0x63, 0x0C, 0x7F, 0x8F, 0xE1, 0x86, 0x30, 0xC6, 0x19, 0xFF, 0x7F, 0xC0, 0x3E, 0x3F, 0xB8,
|
|
||||||
0x78, 0x3C, 0x1E, 0x03, 0x01, 0x83, 0xC1, 0xE0, 0xFD, 0xEF, 0xE1, 0xC0, 0xFF, 0x1F, 0xF0, 0xC7,
|
|
||||||
0x18, 0x63, 0x0E, 0x61, 0xCC, 0x39, 0x87, 0x30, 0xC6, 0x19, 0xFF, 0x7F, 0xC0, 0xFF, 0xFF, 0xC0,
|
|
||||||
0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x3E, 0x3F, 0xB8, 0x78, 0x3C, 0x1E, 0x03, 0x3F, 0x9F, 0xC1, 0xE0,
|
|
||||||
0xFD, 0xEF, 0xF1, 0x80, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xFF, 0xFF, 0x83, 0xC1, 0xE0, 0xF0,
|
|
||||||
0x78, 0x30, 0xFF, 0xFF, 0xFF, 0x0F, 0x83, 0xE0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x33, 0x0C, 0xC3,
|
|
||||||
0x38, 0xC7, 0x71, 0xF8, 0x18, 0x00, 0xC3, 0xE3, 0xB3, 0x9B, 0x8F, 0x87, 0x83, 0xC1, 0xF0, 0xDC,
|
|
||||||
0x67, 0x31, 0xD8, 0x70, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF,
|
|
||||||
0xE0, 0x77, 0x83, 0xBC, 0x3D, 0xE1, 0xEF, 0x8F, 0x6C, 0x7B, 0x66, 0xDB, 0x36, 0xCD, 0xB6, 0x7D,
|
|
||||||
0xB3, 0xCD, 0x9E, 0x60, 0xE1, 0xF8, 0xFC, 0x7F, 0x3D, 0x9E, 0xCF, 0x37, 0x9B, 0xCF, 0xE3, 0xF1,
|
|
||||||
0xF8, 0xF0, 0x3E, 0x1F, 0xEE, 0x1B, 0x06, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xB0, 0x6F, 0x79,
|
|
||||||
0xFC, 0x1C, 0x00, 0xFE, 0x7F, 0xB0, 0xF8, 0x3C, 0x1E, 0x1B, 0xFD, 0xF8, 0xC0, 0x60, 0x30, 0x18,
|
|
||||||
0x00, 0x3E, 0x1F, 0xEE, 0x1B, 0x06, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xB0, 0x6F, 0x79, 0xFC,
|
|
||||||
0x1C, 0x03, 0x80, 0xE0, 0xFE, 0x7F, 0xB0, 0x78, 0x3C, 0x1E, 0x1F, 0xFD, 0xFF, 0xC3, 0xE1, 0xF0,
|
|
||||||
0xF8, 0x70, 0x3F, 0x1F, 0xE6, 0x19, 0x87, 0x70, 0x8F, 0x81, 0xF9, 0x07, 0xE0, 0xD8, 0x37, 0x38,
|
|
||||||
0xFE, 0x0C, 0x00, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
|
|
||||||
0xC0, 0x30, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xFD, 0xEF, 0xE1,
|
|
||||||
0xC0, 0xC0, 0xF8, 0x36, 0x1D, 0x86, 0x61, 0x8C, 0x63, 0x30, 0xCC, 0x1B, 0x07, 0xC1, 0xE0, 0x78,
|
|
||||||
0xC3, 0x8F, 0x9E, 0x36, 0x78, 0xD9, 0xB3, 0x66, 0xCD, 0x9B, 0x76, 0xED, 0x9B, 0x36, 0x3C, 0xF8,
|
|
||||||
0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0xE0, 0xCC, 0x38, 0xC6, 0x1D, 0x81, 0xF0, 0x3C, 0x03, 0x80, 0xF8,
|
|
||||||
0x3B, 0x06, 0x31, 0xC7, 0x70, 0x60, 0xC0, 0xF8, 0x76, 0x18, 0xCE, 0x3B, 0x07, 0x81, 0xE0, 0x30,
|
|
||||||
0x0C, 0x03, 0x00, 0xC0, 0x30, 0x7F, 0xBF, 0xC0, 0xE0, 0x60, 0x60, 0x70, 0x70, 0x70, 0x30, 0x30,
|
|
||||||
0x3F, 0xFF, 0xF0, 0x77, 0xF1, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1E, 0xF8, 0x60, 0x60,
|
|
||||||
0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x0C, 0x0C, 0x0C, 0x06, 0x06, 0x06, 0x02, 0xF7, 0xC6,
|
|
||||||
0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x7F, 0xF0, 0x1C, 0x1E, 0x1D, 0x8C, 0xEC, 0x30, 0xFF,
|
|
||||||
0x7F, 0x80, 0x00, 0x67, 0x30, 0x1C, 0x0F, 0xC6, 0x31, 0x8C, 0x3F, 0x1F, 0xCE, 0x31, 0x9E, 0x7F,
|
|
||||||
0xC6, 0x00, 0xC0, 0x60, 0x30, 0x1B, 0x8F, 0xF7, 0x1F, 0x07, 0x83, 0xC1, 0xE0, 0xFC, 0xFF, 0xE0,
|
|
||||||
0xC0, 0x0E, 0x0F, 0xC7, 0x39, 0x86, 0x60, 0x18, 0x06, 0x19, 0xCE, 0x3F, 0x03, 0x00, 0x00, 0xC0,
|
|
||||||
0x30, 0x0C, 0x73, 0x3F, 0xDC, 0x76, 0x1D, 0x83, 0x60, 0xD8, 0x77, 0x3C, 0xFF, 0x0C, 0x00, 0x0E,
|
|
||||||
0x1F, 0x9C, 0xEC, 0x37, 0xFB, 0xFD, 0x86, 0xE7, 0x3F, 0x06, 0x00, 0x1E, 0x3C, 0x63, 0xFF, 0xE3,
|
|
||||||
0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x1C, 0x8F, 0xF7, 0x1D, 0x87, 0x60, 0xD8, 0x36, 0x1D, 0xEF,
|
|
||||||
0x3F, 0xC0, 0x30, 0x0C, 0xFF, 0x3F, 0x80, 0xC0, 0xC0, 0xC0, 0xDC, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xDB, 0x0D, 0xB6, 0xDB, 0x60, 0x31, 0x8C, 0x03, 0x18, 0xC6, 0x31, 0x8C, 0x63,
|
|
||||||
0x1B, 0xCC, 0xC0, 0xC0, 0xC0, 0xC2, 0xCE, 0xDC, 0xF8, 0xF0, 0xF0, 0xD8, 0xCE, 0xC7, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x18, 0xC7, 0xFF, 0xB9, 0xCD, 0x8C, 0x6C, 0x63, 0x63, 0x1B, 0x18, 0xD8, 0xC6, 0xC6, 0x30,
|
|
||||||
0x1C, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x0E, 0x0F, 0xC7, 0x39, 0x87, 0x60, 0xD8,
|
|
||||||
0x36, 0x19, 0xCE, 0x3F, 0x03, 0x00, 0x1C, 0x7F, 0xB8, 0xF8, 0x3C, 0x1E, 0x0F, 0x07, 0xE7, 0xFF,
|
|
||||||
0x66, 0x30, 0x18, 0x0C, 0x00, 0x1C, 0x8F, 0xF7, 0x1D, 0x87, 0x60, 0xD8, 0x36, 0x1D, 0xCF, 0x3F,
|
|
||||||
0xC3, 0x30, 0x0C, 0x03, 0x00, 0x80, 0x1F, 0xF9, 0x8C, 0x63, 0x18, 0xC0, 0x1C, 0x7E, 0x63, 0x62,
|
|
||||||
0x7E, 0x1F, 0x63, 0x63, 0x3F, 0x0C, 0x00, 0x30, 0x63, 0xFF, 0xE3, 0x06, 0x0C, 0x18, 0x30, 0x70,
|
|
||||||
0xF0, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x41, 0x71, 0xD8, 0xCC, 0x67,
|
|
||||||
0x71, 0xB0, 0xD8, 0x38, 0x1C, 0x00, 0x42, 0x13, 0x38, 0xD9, 0xEE, 0xCF, 0x66, 0xDB, 0x36, 0xD8,
|
|
||||||
0xF3, 0xC7, 0x9E, 0x38, 0xE0, 0x40, 0x98, 0x63, 0x30, 0xF8, 0x1E, 0x07, 0x83, 0xF1, 0xCC, 0x61,
|
|
||||||
0x80, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x13, 0x03, 0x7F, 0x7E, 0xFD, 0xFC,
|
|
||||||
0x38, 0xE3, 0x8E, 0x38, 0x7E, 0xFE, 0x1E, 0x3C, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0xF1, 0xE0, 0x60,
|
|
||||||
0xC1, 0x83, 0x06, 0x0F, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF1, 0xF0, 0x60, 0xC1, 0x83, 0x06,
|
|
||||||
0x0C, 0x1E, 0x3C, 0x60, 0xC1, 0x83, 0x06, 0x7C, 0xF0, 0x10, 0x7B, 0xFF, 0x4C, 0x67, 0x60, 0x66,
|
|
||||||
0x66, 0x66, 0x66, 0x60, 0x08, 0x0C, 0x07, 0x0F, 0xCF, 0xF6, 0xDB, 0x61, 0xB0, 0xDB, 0x7F, 0x9F,
|
|
||||||
0x83, 0x01, 0x80, 0x1F, 0x0F, 0xE3, 0x1C, 0xC3, 0x30, 0xDF, 0x07, 0xC0, 0xC0, 0x30, 0x0C, 0x07,
|
|
||||||
0xF9, 0xFF, 0x00, 0x1F, 0xF7, 0xF9, 0x86, 0x60, 0xD8, 0x36, 0x19, 0xCE, 0x7F, 0xD3, 0x20, 0xC0,
|
|
||||||
0xCC, 0x39, 0xC6, 0x19, 0xC3, 0xB0, 0x3C, 0x1F, 0xE1, 0xFC, 0x7F, 0x87, 0xF0, 0x30, 0x06, 0x00,
|
|
||||||
0xFF, 0xFF, 0x0F, 0xFF, 0xF0, 0x3E, 0x7E, 0x63, 0x62, 0x7C, 0x7E, 0x7B, 0x3F, 0x47, 0xC3, 0x67,
|
|
||||||
0x7E, 0x18, 0x6C, 0xD9, 0xB0, 0x3F, 0x1F, 0xEC, 0xDF, 0x7B, 0xF3, 0xFC, 0xBF, 0x0F, 0xCF, 0xFE,
|
|
||||||
0xF7, 0xB6, 0x39, 0xFC, 0x0C, 0x00, 0x78, 0xD8, 0xF3, 0xEC, 0xCF, 0xCC, 0x00, 0x0C, 0x63, 0xBD,
|
|
||||||
0xEE, 0x73, 0x87, 0x70, 0x77, 0x87, 0x38, 0x23, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xE0, 0x3E, 0x7F,
|
|
||||||
0x5D, 0x5D, 0x55, 0x77, 0x3E, 0x00, 0x7C, 0xF8, 0x7B, 0xFC, 0xF3, 0x78, 0x40, 0x08, 0x18, 0x18,
|
|
||||||
0xFF, 0xFF, 0x18, 0x18, 0x18, 0x7F, 0xFF, 0x01, 0xEC, 0xC3, 0x7B, 0x8F, 0xC0, 0xFC, 0x73, 0x8F,
|
|
||||||
0xCD, 0xF3, 0x00, 0x36, 0x60, 0x00, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0xCE, 0xFF, 0xE8,
|
|
||||||
0x30, 0x18, 0x0C, 0x00, 0x3F, 0xBF, 0xF3, 0x79, 0xBC, 0xDB, 0xED, 0xF6, 0x1B, 0x0D, 0x86, 0xC3,
|
|
||||||
0x61, 0xB0, 0x1F, 0xA0, 0x21, 0x8E, 0xF7, 0x00, 0x77, 0xF3, 0x33, 0x3C, 0xFF, 0x1E, 0x3C, 0x6F,
|
|
||||||
0x8E, 0x00, 0xCC, 0x3B, 0xC7, 0xB8, 0x67, 0x39, 0xDD, 0xEE, 0xE2, 0x30, 0x70, 0x21, 0xC1, 0x8F,
|
|
||||||
0x0C, 0x0C, 0x60, 0x33, 0x80, 0xCC, 0x00, 0x67, 0x03, 0x1C, 0x18, 0xD0, 0xE7, 0xE3, 0x1F, 0xD8,
|
|
||||||
0x04, 0x00, 0x00, 0x70, 0x21, 0xC1, 0x8F, 0x0C, 0x0C, 0x60, 0x33, 0x80, 0xCC, 0x00, 0x6F, 0x83,
|
|
||||||
0x33, 0x18, 0x18, 0xE1, 0xE3, 0x0C, 0x18, 0x3F, 0x00, 0x00, 0xFC, 0x18, 0x38, 0x60, 0xE1, 0xC1,
|
|
||||||
0xE3, 0x0C, 0xCC, 0x0F, 0xB0, 0x0C, 0xC7, 0x03, 0x9E, 0x0E, 0x6C, 0x19, 0xFC, 0x63, 0xF9, 0x80,
|
|
||||||
0x60, 0x00, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x00, 0xC0, 0xE0, 0xE0, 0xE0, 0xE1, 0xF0, 0xD8, 0x6F,
|
|
||||||
0xE1, 0xE0, 0x1C, 0x01, 0x80, 0x10, 0x07, 0x01, 0xE0, 0x3E, 0x06, 0xC1, 0xD8, 0x33, 0x06, 0x31,
|
|
||||||
0xFE, 0x3F, 0xC6, 0x1C, 0xC1, 0xB0, 0x30, 0x06, 0x01, 0xC0, 0x30, 0x07, 0x01, 0xE0, 0x3E, 0x06,
|
|
||||||
0xC1, 0xD8, 0x33, 0x06, 0x31, 0xFE, 0x3F, 0xC6, 0x1C, 0xC1, 0xB0, 0x30, 0x0E, 0x03, 0xE0, 0xCC,
|
|
||||||
0x07, 0x01, 0xE0, 0x3E, 0x06, 0xC1, 0xD8, 0x33, 0x06, 0x31, 0xFE, 0x3F, 0xC6, 0x1C, 0xC1, 0xB0,
|
|
||||||
0x30, 0x1D, 0x07, 0xE0, 0x18, 0x07, 0x01, 0xE0, 0x3E, 0x06, 0xC1, 0xD8, 0x33, 0x06, 0x31, 0xFE,
|
|
||||||
0x3F, 0xC6, 0x1C, 0xC1, 0xB0, 0x30, 0x1B, 0x07, 0x60, 0x4C, 0x07, 0x01, 0xE0, 0x3E, 0x06, 0xC1,
|
|
||||||
0xD8, 0x33, 0x06, 0x31, 0xFE, 0x3F, 0xC6, 0x1C, 0xC1, 0xB0, 0x30, 0x0E, 0x03, 0xC0, 0x78, 0x06,
|
|
||||||
0x00, 0xE0, 0x3C, 0x07, 0xC0, 0xD8, 0x3B, 0x06, 0x60, 0xC6, 0x3F, 0xC7, 0xF8, 0xC3, 0x98, 0x36,
|
|
||||||
0x06, 0x07, 0xF8, 0x3F, 0xE0, 0xF0, 0x07, 0xC0, 0x1B, 0x00, 0x6F, 0xE3, 0x3F, 0x8F, 0xC0, 0x7F,
|
|
||||||
0x01, 0x8C, 0x06, 0x3F, 0xB0, 0xFE, 0x3E, 0x3F, 0xB8, 0x78, 0x3C, 0x1E, 0x03, 0x01, 0x83, 0xC1,
|
|
||||||
0xE0, 0xFD, 0xEF, 0xE1, 0xC0, 0x70, 0x78, 0x38, 0x30, 0x18, 0x18, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0,
|
|
||||||
0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x0C, 0x18, 0x18, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE,
|
|
||||||
0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x18, 0x3E, 0x66, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x66, 0x7E, 0x24, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0,
|
|
||||||
0xC0, 0xC0, 0xFF, 0xFF, 0xCD, 0xB6, 0xDB, 0x6D, 0xB6, 0xD8, 0x7B, 0x6D, 0xB6, 0xDB, 0x6D, 0xB0,
|
|
||||||
0x39, 0xFC, 0xCC, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00, 0xCF, 0xF4, 0x8C, 0x30,
|
|
||||||
0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00, 0xFF, 0x1F, 0xF0, 0xC7, 0x18, 0x63, 0x0E, 0xF9,
|
|
||||||
0xFF, 0x39, 0x87, 0x30, 0xC6, 0x19, 0xFF, 0x7F, 0xC0, 0x3A, 0x1F, 0x09, 0x1C, 0x3F, 0x1F, 0x8F,
|
|
||||||
0xE7, 0xB3, 0xD9, 0xE6, 0xF3, 0x79, 0xFC, 0x7E, 0x3F, 0x1E, 0x18, 0x06, 0x00, 0xC0, 0xF8, 0x7F,
|
|
||||||
0xB8, 0x6C, 0x1B, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70, 0x0C, 0x03,
|
|
||||||
0x00, 0x80, 0xF8, 0x7F, 0xB8, 0x6C, 0x1B, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7,
|
|
||||||
0xF0, 0x70, 0x1C, 0x0F, 0x83, 0x20, 0xF8, 0x7F, 0xB8, 0x6C, 0x1B, 0x07, 0xC1, 0xF0, 0x7C, 0x1F,
|
|
||||||
0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70, 0x3A, 0x0F, 0x82, 0x40, 0xF8, 0x7F, 0xB8, 0x6C, 0x1B, 0x07,
|
|
||||||
0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70, 0x36, 0x0D, 0xC3, 0x20, 0xF8, 0x7F,
|
|
||||||
0xB8, 0x6C, 0x1B, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70, 0x00, 0x67,
|
|
||||||
0x7E, 0x3C, 0x3C, 0x7E, 0x67, 0x42, 0x03, 0x0F, 0xC7, 0xFB, 0x9E, 0xC7, 0xB3, 0x7C, 0xDF, 0x27,
|
|
||||||
0xD9, 0xF6, 0x6F, 0x1B, 0xDE, 0x7F, 0x1F, 0x06, 0x00, 0x18, 0x0C, 0x02, 0x18, 0x3C, 0x1E, 0x0F,
|
|
||||||
0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x0C, 0x06, 0x02, 0x18, 0x3C,
|
|
||||||
0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x1C, 0x1F, 0x0C,
|
|
||||||
0x98, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x36,
|
|
||||||
0x1B, 0x8C, 0x98, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC,
|
|
||||||
0x38, 0x06, 0x03, 0x80, 0xC3, 0x03, 0xE1, 0xD8, 0x63, 0x38, 0xEC, 0x1E, 0x07, 0x80, 0xC0, 0x30,
|
|
||||||
0x0C, 0x03, 0x00, 0xC0, 0xC0, 0x60, 0x3F, 0x9F, 0xEC, 0x3E, 0x0F, 0x07, 0x87, 0xFF, 0x7F, 0x30,
|
|
||||||
0x18, 0x00, 0x3E, 0x3F, 0xB8, 0xD8, 0x6C, 0x66, 0x63, 0x39, 0x9E, 0xC3, 0xF8, 0xFE, 0x7B, 0xF0,
|
|
||||||
0x60, 0x18, 0x06, 0x00, 0xC0, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F, 0x38, 0xC6, 0x79, 0xFF,
|
|
||||||
0x18, 0x00, 0x06, 0x03, 0x00, 0xC0, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F, 0x38, 0xC6, 0x79,
|
|
||||||
0xFF, 0x18, 0x00, 0x1C, 0x0F, 0x83, 0x70, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F, 0x38, 0xC6,
|
|
||||||
0x79, 0xFF, 0x18, 0x00, 0x10, 0x0F, 0xC2, 0xE0, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F, 0x38,
|
|
||||||
0xC6, 0x79, 0xFF, 0x18, 0x00, 0x36, 0x0D, 0xC3, 0x60, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F,
|
|
||||||
0x38, 0xC6, 0x79, 0xFF, 0x18, 0x00, 0x08, 0x07, 0x81, 0x60, 0x70, 0x1C, 0x0F, 0xC6, 0x31, 0x8C,
|
|
||||||
0x3F, 0x1F, 0xCE, 0x31, 0x9E, 0x7F, 0xC6, 0x00, 0x1C, 0x70, 0x7F, 0xF9, 0x8E, 0x33, 0x1C, 0x33,
|
|
||||||
0xFF, 0xEF, 0xFF, 0xF8, 0xE1, 0x33, 0xE6, 0x7E, 0xFC, 0x30, 0x60, 0x0E, 0x0F, 0xC7, 0x39, 0x86,
|
|
||||||
0x60, 0x18, 0x06, 0x19, 0xCE, 0x3F, 0x03, 0x00, 0xE0, 0x78, 0x0C, 0x00, 0x18, 0x0E, 0x03, 0x01,
|
|
||||||
0xC3, 0xF3, 0x9D, 0x86, 0xFF, 0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0x06, 0x07, 0x03, 0x01, 0xC3,
|
|
||||||
0xF3, 0x9D, 0x86, 0xFF, 0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0x0C, 0x0F, 0x0C, 0xC1, 0xC3, 0xF3,
|
|
||||||
0x9D, 0x86, 0xFF, 0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0x12, 0x1F, 0x8C, 0xC1, 0xC3, 0xF3, 0x9D,
|
|
||||||
0x86, 0xFF, 0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0xDD, 0x86, 0xDB, 0x6D, 0xB0, 0x6E, 0xC0, 0xCC,
|
|
||||||
0xCC, 0xCC, 0xCC, 0x31, 0xEC, 0xC0, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x4B, 0xFC, 0xC0, 0x30,
|
|
||||||
0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x02, 0x07, 0x80, 0xE0, 0x3C, 0x0D, 0x8F, 0xE7, 0x39, 0x87, 0x60,
|
|
||||||
0xD8, 0x36, 0x19, 0xCE, 0x3F, 0x03, 0x00, 0x10, 0x3E, 0x6C, 0x1C, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0x18, 0x03, 0x00, 0xC0, 0x38, 0x3F, 0x1C, 0xE6, 0x1D, 0x83, 0x60, 0xD8, 0x67,
|
|
||||||
0x38, 0xFC, 0x0C, 0x00, 0x06, 0x01, 0x80, 0xC0, 0x38, 0x3F, 0x1C, 0xE6, 0x1D, 0x83, 0x60, 0xD8,
|
|
||||||
0x67, 0x38, 0xFC, 0x0C, 0x00, 0x0C, 0x07, 0x83, 0x30, 0x38, 0x3F, 0x1C, 0xE6, 0x1D, 0x83, 0x60,
|
|
||||||
0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x08, 0x07, 0xC3, 0x70, 0x38, 0x3F, 0x1C, 0xE6, 0x1D, 0x83,
|
|
||||||
0x60, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x12, 0x0F, 0xC1, 0xB0, 0x38, 0x3F, 0x1C, 0xE6, 0x1D,
|
|
||||||
0x83, 0x60, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x18, 0x1C, 0x18, 0xFF, 0xFF, 0x00, 0x1C, 0x18,
|
|
||||||
0x00, 0x03, 0xE3, 0xF1, 0xCE, 0x67, 0xDB, 0x36, 0xCD, 0xE6, 0x7B, 0x8F, 0xC2, 0xC0, 0x00, 0x30,
|
|
||||||
0x38, 0x18, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x0C, 0x1C, 0x18, 0x01,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x18, 0x3C, 0x66, 0x01, 0xC3, 0xC3, 0xC3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x24, 0x7E, 0x66, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
|
|
||||||
0xE7, 0x7F, 0x10, 0x0C, 0x1C, 0x18, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x13,
|
|
||||||
0x03, 0x7F, 0x7E, 0xC0, 0x60, 0x30, 0x1B, 0x8F, 0xF7, 0x1F, 0x07, 0x83, 0xC1, 0xE0, 0xFC, 0xDF,
|
|
||||||
0xEC, 0xC6, 0x03, 0x01, 0x80, 0x24, 0x7E, 0x76, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7,
|
|
||||||
0x7F, 0x13, 0x03, 0x7F, 0x7E, 0x3F, 0x03, 0xC0, 0x38, 0x0F, 0x01, 0xF0, 0x36, 0x0E, 0xC1, 0x98,
|
|
||||||
0x31, 0x8F, 0xF1, 0xFE, 0x30, 0xE6, 0x0D, 0x81, 0x80, 0x3F, 0x0F, 0x81, 0xC0, 0xFC, 0x63, 0x18,
|
|
||||||
0xC3, 0xF1, 0xFC, 0xE3, 0x19, 0xE7, 0xFC, 0x60, 0x33, 0x03, 0xE0, 0x38, 0x07, 0x01, 0xE0, 0x3E,
|
|
||||||
0x06, 0xC1, 0xD8, 0x33, 0x06, 0x31, 0xFE, 0x3F, 0xC6, 0x1C, 0xC1, 0xB0, 0x30, 0x32, 0x0F, 0x81,
|
|
||||||
0xE0, 0x70, 0x3F, 0x18, 0xC6, 0x30, 0xFC, 0x7F, 0x38, 0xC6, 0x79, 0xFF, 0x18, 0x00, 0x0E, 0x03,
|
|
||||||
0xC0, 0x7C, 0x0D, 0x83, 0xB0, 0x66, 0x0C, 0x63, 0xFC, 0x7F, 0x8C, 0x39, 0x83, 0x60, 0x60, 0x0C,
|
|
||||||
0x01, 0xC0, 0x38, 0x1C, 0x0F, 0xC6, 0x31, 0x8C, 0x3F, 0x1F, 0xCE, 0x31, 0x9E, 0x7F, 0xC6, 0x60,
|
|
||||||
0x18, 0x07, 0x0C, 0x06, 0x02, 0x07, 0xC7, 0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x60, 0x30, 0x78, 0x3C,
|
|
||||||
0x1F, 0xBD, 0xFC, 0x38, 0x06, 0x01, 0x80, 0xC0, 0x38, 0x3F, 0x1C, 0xE6, 0x19, 0x80, 0x60, 0x18,
|
|
||||||
0x67, 0x38, 0xFC, 0x0C, 0x00, 0x1C, 0x1F, 0x0C, 0x87, 0xC7, 0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x60,
|
|
||||||
0x30, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x0C, 0x07, 0x83, 0x30, 0x38, 0x3F, 0x1C, 0xE6, 0x19,
|
|
||||||
0x80, 0x60, 0x18, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x1C, 0x0E, 0x02, 0x07, 0xC7, 0xF7, 0x0F, 0x07,
|
|
||||||
0x83, 0xC0, 0x60, 0x30, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x3F,
|
|
||||||
0x1C, 0xE6, 0x19, 0x80, 0x60, 0x18, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x36, 0x1F, 0x07, 0x07, 0xC7,
|
|
||||||
0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x60, 0x30, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x33, 0x07, 0xC0,
|
|
||||||
0xE0, 0x38, 0x3F, 0x1C, 0xE6, 0x19, 0x80, 0x60, 0x18, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x3B, 0x03,
|
|
||||||
0xC0, 0x30, 0x7F, 0x8F, 0xF8, 0x63, 0x8C, 0x31, 0x87, 0x30, 0xE6, 0x1C, 0xC3, 0x98, 0x63, 0x0C,
|
|
||||||
0xFF, 0xBF, 0xE0, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x39, 0xE3, 0xFC, 0x38, 0xE1, 0x87, 0x0C, 0x18,
|
|
||||||
0x60, 0xC3, 0x0E, 0x1C, 0xF0, 0x7F, 0x80, 0xC0, 0x00, 0xFF, 0x1F, 0xF0, 0xC7, 0x18, 0x63, 0x0E,
|
|
||||||
0xF9, 0xFF, 0x39, 0x87, 0x30, 0xC6, 0x19, 0xFF, 0x7F, 0xC0, 0x00, 0xC0, 0xFC, 0x1F, 0x8E, 0x63,
|
|
||||||
0xFC, 0xE3, 0x98, 0x73, 0x06, 0x60, 0xCC, 0x39, 0xCF, 0x1F, 0xE0, 0xC0, 0x7E, 0x3C, 0xFF, 0xFF,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x3F, 0x1F, 0x83, 0x87, 0xE7, 0x3B,
|
|
||||||
0x0D, 0xFE, 0xFF, 0x61, 0xB9, 0xCF, 0xC1, 0x80, 0x66, 0x7E, 0x1C, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0,
|
|
||||||
0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x33, 0x1F, 0x87, 0x81, 0xC3, 0xF3, 0x9D, 0x86, 0xFF,
|
|
||||||
0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0x0C, 0x06, 0x03, 0x01, 0xC3, 0xF3, 0x9D, 0x86, 0xFF, 0x7F, 0xB0,
|
|
||||||
0xDC, 0xE7, 0xE0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF,
|
|
||||||
0x06, 0x07, 0x07, 0x0E, 0x1F, 0x9C, 0xEC, 0x37, 0xFB, 0xFD, 0x86, 0xE7, 0x3F, 0x07, 0x01, 0xC0,
|
|
||||||
0xE0, 0x66, 0x3C, 0x18, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF,
|
|
||||||
0x33, 0x0F, 0x03, 0x81, 0xC3, 0xF3, 0x9D, 0x86, 0xFF, 0x7F, 0xB0, 0xDC, 0xE7, 0xE0, 0xC0, 0x1C,
|
|
||||||
0x1F, 0x0C, 0x87, 0xC7, 0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x67, 0xF3, 0xF8, 0x3C, 0x1F, 0xBD, 0xFE,
|
|
||||||
0x30, 0x0E, 0x07, 0xC1, 0xB0, 0x72, 0x3F, 0xDC, 0x76, 0x1D, 0x83, 0x60, 0xD8, 0x77, 0xBC, 0xFF,
|
|
||||||
0x00, 0xC0, 0x33, 0xFC, 0xFE, 0x32, 0x1F, 0x07, 0x07, 0xC7, 0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x67,
|
|
||||||
0xF3, 0xF8, 0x3C, 0x1F, 0xBD, 0xFE, 0x30, 0x19, 0x07, 0xC0, 0xE0, 0x72, 0x3F, 0xDC, 0x76, 0x1D,
|
|
||||||
0x83, 0x60, 0xD8, 0x77, 0xBC, 0xFF, 0x00, 0xC0, 0x33, 0xFC, 0xFE, 0x0C, 0x0E, 0x02, 0x07, 0xC7,
|
|
||||||
0xF7, 0x0F, 0x07, 0x83, 0xC0, 0x67, 0xF3, 0xF8, 0x3C, 0x1F, 0xBD, 0xFE, 0x30, 0x04, 0x03, 0x80,
|
|
||||||
0x40, 0x72, 0x3F, 0xDC, 0x76, 0x1D, 0x83, 0x60, 0xD8, 0x77, 0xBC, 0xFF, 0x00, 0xC0, 0x33, 0xFC,
|
|
||||||
0xFE, 0x3E, 0x3F, 0xB8, 0x78, 0x3C, 0x1E, 0x03, 0x3F, 0x9F, 0xC1, 0xE0, 0xFD, 0xEF, 0xF1, 0x80,
|
|
||||||
0xE0, 0x70, 0x18, 0x08, 0x00, 0x06, 0x03, 0x00, 0xE0, 0x38, 0x1C, 0x8F, 0xF7, 0x1D, 0x87, 0x60,
|
|
||||||
0xD8, 0x36, 0x1D, 0xEF, 0x3F, 0xC0, 0x30, 0x0C, 0xFF, 0x3F, 0x80, 0x1C, 0x1F, 0x0D, 0x98, 0x3C,
|
|
||||||
0x1E, 0x0F, 0x07, 0x83, 0xFF, 0xFF, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x06, 0x38, 0x1F, 0x0C, 0xC0,
|
|
||||||
0xC0, 0x30, 0x0C, 0x03, 0x70, 0xFE, 0x38, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0x60,
|
|
||||||
0xCC, 0x1B, 0xFF, 0xFF, 0xF6, 0x0C, 0xFF, 0x9F, 0xF3, 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0x60,
|
|
||||||
0x60, 0x7E, 0x3F, 0x0D, 0xC7, 0xF3, 0x8D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x30, 0x6F, 0xF1,
|
|
||||||
0x8C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00, 0x23, 0xFD, 0x80, 0x30, 0xC3, 0x0C,
|
|
||||||
0x30, 0xC3, 0x0C, 0xFD, 0xE3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, 0xFF, 0xF0,
|
|
||||||
0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00, 0xCF, 0xF3, 0x8C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C,
|
|
||||||
0x30, 0xC3, 0x00, 0xCF, 0xF7, 0x80, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0xDB, 0x6D, 0xB6, 0xDB,
|
|
||||||
0x6D, 0xB8, 0xDB, 0x0D, 0xB6, 0xDB, 0x6D, 0xB8, 0xDB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB0, 0x3F, 0xFF,
|
|
||||||
0xC0, 0xF9, 0xFF, 0x9F, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x20, 0xC0, 0x0C, 0x30, 0xC3, 0x0C,
|
|
||||||
0x39, 0xC1, 0xF8, 0x06, 0x00, 0xCD, 0x9F, 0x30, 0x2C, 0xD9, 0xB3, 0x66, 0xCD, 0x9B, 0x36, 0x60,
|
|
||||||
0xC1, 0x87, 0x0C, 0x07, 0x03, 0xE0, 0xCC, 0x3E, 0x0F, 0x80, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xCC,
|
|
||||||
0x33, 0x0C, 0xE3, 0x1D, 0xC7, 0xE0, 0x60, 0x31, 0xEC, 0xC0, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C,
|
|
||||||
0x30, 0xCF, 0x18, 0xC3, 0xE3, 0xB3, 0x9B, 0x8F, 0x87, 0x83, 0xC1, 0xF0, 0xDC, 0x67, 0x31, 0xD8,
|
|
||||||
0x70, 0x00, 0xC0, 0x70, 0x30, 0x18, 0x00, 0xC0, 0xC0, 0xC0, 0xC2, 0xCE, 0xDC, 0xF8, 0xF0, 0xF0,
|
|
||||||
0xD8, 0xCE, 0xC7, 0x00, 0x38, 0x38, 0x18, 0x10, 0x02, 0xCE, 0xDC, 0xF8, 0xF0, 0xF0, 0xD8, 0xCE,
|
|
||||||
0xC7, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF,
|
|
||||||
0x7B, 0x6D, 0xB6, 0xDB, 0x6D, 0xB0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
|
|
||||||
0xFF, 0xFF, 0x00, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0xD8, 0xD8, 0xD0, 0xD0,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xDE, 0xF5, 0xAC, 0x63, 0x18, 0xC6, 0x31, 0x80,
|
|
||||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC4, 0xCE, 0xC4, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xC3, 0x0C, 0x30, 0xC3,
|
|
||||||
0x0C, 0xB7, 0xCB, 0x0C, 0x30, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x0F, 0x07, 0x03, 0xC0, 0x30,
|
|
||||||
0x0C, 0x03, 0xFC, 0xFF, 0x30, 0xC3, 0x0C, 0x30, 0xF3, 0xFC, 0xF0, 0xC3, 0x0C, 0x0C, 0x06, 0x02,
|
|
||||||
0x1C, 0x3F, 0x1F, 0x8F, 0xE7, 0xB3, 0xD9, 0xE6, 0xF3, 0x79, 0xFC, 0x7E, 0x3F, 0x1E, 0x0C, 0x0C,
|
|
||||||
0x18, 0x1C, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE1, 0xF8, 0xFC, 0x7F, 0x3D, 0x9E,
|
|
||||||
0xCF, 0x37, 0x9B, 0xCF, 0xE3, 0xF1, 0xF8, 0xF0, 0x00, 0xE0, 0x70, 0x18, 0x08, 0x00, 0x1C, 0xFE,
|
|
||||||
0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x18, 0x18, 0x18, 0x18, 0x36, 0x1F, 0x07, 0x1C,
|
|
||||||
0x3F, 0x1F, 0x8F, 0xE7, 0xB3, 0xD9, 0xE6, 0xF3, 0x79, 0xFC, 0x7E, 0x3F, 0x1E, 0x66, 0x3E, 0x1C,
|
|
||||||
0x1C, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC0, 0xE0, 0x60, 0xC0, 0x1C, 0xFE, 0xE3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE1, 0xF8, 0xFC, 0x7F, 0x3D, 0x9E, 0xCF, 0x37, 0x9B, 0xCF,
|
|
||||||
0xE3, 0xF1, 0xF8, 0xF0, 0x18, 0x0C, 0x1E, 0x0E, 0x1C, 0xFE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
|
|
||||||
0xC3, 0x03, 0x03, 0x0F, 0x0E, 0x3E, 0x0F, 0x83, 0xE1, 0xFE, 0xE1, 0xB0, 0x6C, 0x1F, 0x07, 0xC1,
|
|
||||||
0xF0, 0x7C, 0x1B, 0x06, 0xF7, 0x9F, 0xC1, 0xC0, 0x3F, 0x0F, 0xC0, 0xE0, 0xFC, 0x73, 0x98, 0x76,
|
|
||||||
0x0D, 0x83, 0x61, 0x9C, 0xE3, 0xF0, 0x30, 0x32, 0x0F, 0x81, 0xC0, 0xF8, 0x7F, 0xB8, 0x6C, 0x1B,
|
|
||||||
0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70, 0x33, 0x07, 0xC1, 0xE0, 0x38,
|
|
||||||
0x3F, 0x1C, 0xE6, 0x1D, 0x83, 0x60, 0xD8, 0x67, 0x38, 0xFC, 0x0C, 0x00, 0x1F, 0x0F, 0x83, 0x40,
|
|
||||||
0xF8, 0x7F, 0xB8, 0x6C, 0x1B, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x06, 0xC1, 0xBD, 0xE7, 0xF0, 0x70,
|
|
||||||
0x0F, 0x06, 0xC1, 0xE0, 0x38, 0x3F, 0x1C, 0xE6, 0x1D, 0x83, 0x60, 0xD8, 0x67, 0x38, 0xFC, 0x0C,
|
|
||||||
0x00, 0x1F, 0xF3, 0xFF, 0xB9, 0x81, 0x8C, 0x0C, 0x60, 0x63, 0xFB, 0x1F, 0xD8, 0xC0, 0xC6, 0x07,
|
|
||||||
0x30, 0x1F, 0xFC, 0x7F, 0xF0, 0x0E, 0x1C, 0x1F, 0xBF, 0x1C, 0xF8, 0xCC, 0x38, 0x66, 0x0F, 0xF3,
|
|
||||||
0x07, 0xF9, 0x87, 0x0C, 0xE7, 0xCE, 0x3F, 0x7E, 0x06, 0x0C, 0x00, 0x0C, 0x0E, 0x06, 0x1F, 0xCF,
|
|
||||||
0xF6, 0x0F, 0x07, 0x83, 0xC3, 0xFF, 0xBF, 0xF8, 0x7C, 0x3E, 0x1F, 0x0E, 0x19, 0x8C, 0x3F, 0xF3,
|
|
||||||
0x18, 0xC6, 0x31, 0x80, 0xFE, 0x7F, 0xB0, 0x78, 0x3C, 0x1E, 0x1F, 0xFD, 0xFF, 0xC3, 0xE1, 0xF0,
|
|
||||||
0xF8, 0x70, 0x00, 0xC0, 0x70, 0x38, 0x18, 0x00, 0x1F, 0xF9, 0x8C, 0x63, 0x18, 0xC0, 0x31, 0x8C,
|
|
||||||
0x60, 0x66, 0x1E, 0x06, 0x1F, 0xCF, 0xF6, 0x0F, 0x07, 0x83, 0xC3, 0xFF, 0xBF, 0xF8, 0x7C, 0x3E,
|
|
||||||
0x1F, 0x0E, 0x6C, 0xF8, 0xE0, 0x67, 0xCE, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x00, 0x06, 0x03, 0x80,
|
|
||||||
0xC0, 0xFC, 0x7F, 0x98, 0x66, 0x1D, 0xC2, 0x3E, 0x07, 0xE4, 0x1F, 0x83, 0x60, 0xDC, 0xE3, 0xF8,
|
|
||||||
0x30, 0x0C, 0x0C, 0x18, 0x1C, 0x7E, 0x63, 0x62, 0x7E, 0x1F, 0x63, 0x63, 0x3F, 0x0C, 0x0E, 0x07,
|
|
||||||
0xC3, 0x30, 0xFC, 0x7F, 0x98, 0x66, 0x1D, 0xC2, 0x3E, 0x07, 0xE4, 0x1F, 0x83, 0x60, 0xDC, 0xE3,
|
|
||||||
0xF8, 0x30, 0x1C, 0x3E, 0x76, 0x1C, 0x7E, 0x63, 0x62, 0x7E, 0x1F, 0x63, 0x63, 0x3F, 0x0C, 0x3F,
|
|
||||||
0x1F, 0xE6, 0x19, 0x87, 0x70, 0x8F, 0x81, 0xF9, 0x07, 0xE0, 0xD8, 0x37, 0x38, 0xFE, 0x0E, 0x01,
|
|
||||||
0x81, 0xE0, 0x38, 0x1C, 0x7E, 0x63, 0x62, 0x7E, 0x1F, 0x63, 0x63, 0x3F, 0x0C, 0x0E, 0x1E, 0x1C,
|
|
||||||
0x33, 0x07, 0x80, 0xC0, 0xFC, 0x7F, 0x98, 0x66, 0x1D, 0xC2, 0x3E, 0x07, 0xE4, 0x1F, 0x83, 0x60,
|
|
||||||
0xDC, 0xE3, 0xF8, 0x30, 0x66, 0x3E, 0x1C, 0x1C, 0x7E, 0x63, 0x62, 0x7E, 0x1F, 0x63, 0x63, 0x3F,
|
|
||||||
0x0C, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30,
|
|
||||||
0x0C, 0x03, 0x81, 0xE0, 0x30, 0x00, 0x30, 0x63, 0xFF, 0xE3, 0x06, 0x0C, 0x18, 0x30, 0x70, 0xF1,
|
|
||||||
0x83, 0x8F, 0x0C, 0x33, 0x07, 0x80, 0xC3, 0xFF, 0xFF, 0xC3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
|
|
||||||
0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x06, 0x06, 0x1E, 0x1E, 0x7E, 0xFE, 0x18, 0x18, 0x18, 0x18,
|
|
||||||
0x18, 0x1C, 0x1E, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x0F, 0xC3, 0xF0, 0x30, 0x0C, 0x03, 0x00,
|
|
||||||
0xC0, 0x30, 0x00, 0x30, 0x63, 0xFF, 0xE3, 0x06, 0x7F, 0xFE, 0x30, 0x70, 0xF0, 0x3A, 0x1F, 0x09,
|
|
||||||
0x18, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x10,
|
|
||||||
0x7E, 0x6C, 0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x3E, 0x1F, 0x30, 0x78,
|
|
||||||
0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3F, 0x7B, 0xF8, 0x70, 0x7E, 0x7E, 0x01,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x32, 0x1F, 0x07, 0x18, 0x3C, 0x1E, 0x0F,
|
|
||||||
0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x66, 0x7E, 0x3C, 0x01, 0xC3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x1C, 0x0F, 0x07, 0x03, 0x8C, 0x1E, 0x0F, 0x07,
|
|
||||||
0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0xDE, 0xFE, 0x1C, 0x00, 0x08, 0x3C, 0x3C, 0x3C,
|
|
||||||
0x01, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0x1F, 0x1B, 0x0D, 0x18, 0x3C, 0x1E,
|
|
||||||
0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0xBD, 0xFC, 0x38, 0x16, 0x36, 0x3C, 0x01,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x10, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F,
|
|
||||||
0x07, 0x83, 0xC1, 0xE0, 0xFD, 0xEF, 0xE1, 0xE0, 0x30, 0x18, 0x01, 0x61, 0xB0, 0xD8, 0x6C, 0x36,
|
|
||||||
0x1B, 0x0D, 0xCE, 0x7F, 0x09, 0x80, 0xE0, 0x70, 0x03, 0x80, 0x1F, 0x00, 0xCC, 0x30, 0xE3, 0xE7,
|
|
||||||
0x8D, 0x9E, 0x36, 0x6C, 0xD9, 0xB3, 0x66, 0xDD, 0xBB, 0x66, 0xCD, 0x8F, 0x3E, 0x3C, 0x78, 0xF1,
|
|
||||||
0xE3, 0xC7, 0x80, 0x07, 0x00, 0x7C, 0x03, 0x70, 0x84, 0x26, 0x71, 0xB3, 0xDD, 0x9E, 0xCD, 0xB6,
|
|
||||||
0x6D, 0xB1, 0xE7, 0x8F, 0x3C, 0x71, 0xC0, 0x0E, 0x07, 0xC3, 0x33, 0x03, 0xE1, 0xD8, 0x63, 0x38,
|
|
||||||
0xEC, 0x1E, 0x07, 0x80, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x18, 0x3C, 0x66, 0x01, 0xC3, 0xC3,
|
|
||||||
0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7F, 0x13, 0x03, 0x7F, 0x7E, 0x33, 0x0F, 0xC1, 0x23, 0x03, 0xE1,
|
|
||||||
0xD8, 0x63, 0x38, 0xEC, 0x1E, 0x07, 0x80, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x0E, 0x06, 0x02,
|
|
||||||
0x0F, 0xF7, 0xF8, 0x1C, 0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x06, 0x06, 0x07, 0xFF, 0xFE, 0x18, 0x30,
|
|
||||||
0xC7, 0xEF, 0xE1, 0xC7, 0x1C, 0x71, 0xC3, 0xF7, 0xF0, 0x0C, 0x0E, 0x02, 0x0F, 0xF7, 0xF8, 0x1C,
|
|
||||||
0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x06, 0x06, 0x07, 0xFF, 0xFE, 0x30, 0x70, 0xC7, 0xEF, 0xE1, 0xC7,
|
|
||||||
0x1C, 0x71, 0xC3, 0xF7, 0xF0, 0x37, 0x0F, 0x03, 0x0F, 0xF7, 0xF8, 0x1C, 0x0C, 0x0C, 0x0E, 0x0E,
|
|
||||||
0x0E, 0x06, 0x06, 0x07, 0xFF, 0xFE, 0xCC, 0xF8, 0xE7, 0xEF, 0xE1, 0xC7, 0x1C, 0x71, 0xC3, 0xF7,
|
|
||||||
0xF0, 0x3C, 0xF3, 0x1C, 0xF0, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x67, 0x30, 0x36, 0x60, 0x38, 0x79,
|
|
||||||
0xB8, 0x20, 0xF9, 0x70, 0x7C, 0xF8, 0x4D, 0xF7, 0x80, 0x6D, 0x80, 0x6C, 0xD9, 0xB0, 0x73, 0xCE,
|
|
||||||
0x60, 0x23, 0x96, 0xE0, 0x6D, 0xB7, 0x80, 0x66, 0xF8, 0xE0, 0x79, 0xB3, 0xC0, 0x39, 0xF4, 0xC0,
|
|
||||||
0x69, 0xB0, 0x66, 0x60, 0x6D, 0x00, 0x6C, 0xD9, 0x00, 0x6D, 0xA0, 0x21, 0x8E, 0xF7, 0x00, 0x44,
|
|
||||||
0x66, 0x4D, 0xF3, 0x80, 0x7C, 0xF8, 0x7C, 0xFC, 0x7F, 0xF7, 0xFF, 0x00, 0x03, 0x06, 0x06, 0x0C,
|
|
||||||
0x18, 0x18, 0x30, 0x30, 0x60, 0x40, 0x00, 0x03, 0x06, 0x06, 0x06, 0x0C, 0x0C, 0x08, 0x18, 0x18,
|
|
||||||
0x30, 0x30, 0x20, 0x60, 0x60, 0x40, 0xFF, 0xE0, 0xFF, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xC0, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0x46, 0x60, 0x06, 0x73, 0x60, 0x06, 0x73, 0x60, 0x00, 0xD9,
|
|
||||||
0x63, 0xE7, 0xC0, 0x00, 0x00, 0xF9, 0xF1, 0xA6, 0xC0, 0x00, 0x00, 0xF9, 0xF1, 0xA6, 0xC0, 0x00,
|
|
||||||
0x18, 0x18, 0x18, 0x7E, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFE,
|
|
||||||
0xFF, 0x18, 0x18, 0xFE, 0xFF, 0x18, 0x18, 0x18, 0x6F, 0xFF, 0xF0, 0x00, 0x0D, 0xDD, 0xFB, 0x92,
|
|
||||||
0x20, 0x78, 0x30, 0xFC, 0x60, 0xCC, 0xE0, 0xCD, 0xC0, 0x7D, 0x80, 0x3B, 0x00, 0x06, 0x44, 0x0F,
|
|
||||||
0xFF, 0x1D, 0xBB, 0x19, 0x33, 0x31, 0xBB, 0x61, 0xFF, 0x00, 0x44, 0x6D, 0xA4, 0x6D, 0xE7, 0x96,
|
|
||||||
0x58, 0x06, 0x3D, 0xE7, 0x07, 0x07, 0x83, 0x83, 0xC3, 0xC7, 0x87, 0x1D, 0xEE, 0x20, 0x00, 0x60,
|
|
||||||
0x18, 0x07, 0x01, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x06, 0x01, 0x80, 0x60, 0x00, 0x00,
|
|
||||||
0x70, 0x6E, 0x1D, 0xC3, 0x00, 0xC0, 0x30, 0x0E, 0x03, 0x80, 0x60, 0x18, 0x06, 0x19, 0x87, 0xB0,
|
|
||||||
0xF0, 0x04, 0x0F, 0x05, 0x0F, 0x8F, 0xEF, 0xFE, 0xFF, 0x7F, 0xA8, 0xFC, 0x7E, 0xFF, 0x7F, 0xBF,
|
|
||||||
0xFB, 0xF9, 0xF0, 0xF0, 0x3F, 0x8F, 0xE3, 0x00, 0xC0, 0x30, 0x0F, 0xE3, 0xF8, 0xC0, 0x7C, 0x3F,
|
|
||||||
0x83, 0x00, 0xC0, 0x1F, 0x0F, 0xE3, 0x1C, 0xC3, 0x30, 0xDF, 0x03, 0x01, 0xF0, 0x30, 0x0C, 0x07,
|
|
||||||
0xF9, 0xFF, 0x38, 0x63, 0xC6, 0x3C, 0x63, 0xC6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0x63,
|
|
||||||
0x3E, 0x31, 0xE3, 0x1E, 0x3F, 0x83, 0xFC, 0x30, 0xCF, 0xFF, 0xFF, 0xF3, 0x1C, 0x3F, 0xC3, 0xF0,
|
|
||||||
0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xCE, 0x63, 0xCC, 0xFF, 0xFE,
|
|
||||||
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0x36, 0x6C, 0x3C, 0x78, 0x3C, 0x78, 0x1C, 0x38, 0x00, 0xC0,
|
|
||||||
0xFC, 0x1F, 0x8E, 0x63, 0xFC, 0xE3, 0x98, 0x73, 0x06, 0x60, 0xCC, 0x39, 0xCF, 0x1F, 0xE0, 0xC0,
|
|
||||||
0xFF, 0x80, 0x1F, 0x87, 0xF8, 0xC3, 0x98, 0x3F, 0xC7, 0xFC, 0x3F, 0x07, 0xF3, 0x70, 0x66, 0x1C,
|
|
||||||
0xF7, 0x0F, 0xE0, 0x60, 0x30, 0xC6, 0x38, 0xCE, 0x1B, 0x83, 0xE1, 0xFF, 0x3F, 0xE1, 0xF0, 0x37,
|
|
||||||
0x06, 0x70, 0xC7, 0x18, 0x70, 0x3F, 0x83, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0x3F, 0xC3,
|
|
||||||
0xF0, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x08, 0x06, 0x0F, 0x8F, 0xEE, 0xDE, 0x6F, 0x37, 0x98,
|
|
||||||
0xCF, 0xE7, 0xF3, 0x79, 0xBF, 0xFB, 0xFC, 0x70, 0x18, 0x08, 0x06, 0x0F, 0x8F, 0xEE, 0xDE, 0x6F,
|
|
||||||
0x37, 0x98, 0xCC, 0x66, 0xF3, 0x79, 0xBF, 0xFB, 0xF8, 0x70, 0x18, 0xFF, 0xFF, 0xE0, 0x63, 0xFF,
|
|
||||||
0xFF, 0x81, 0x8F, 0xE3, 0xF8, 0x06, 0x01, 0x80, 0x60, 0x18, 0x30, 0x18, 0x0F, 0x0F, 0x8F, 0x01,
|
|
||||||
0xE3, 0xE7, 0x63, 0x31, 0x99, 0x8F, 0xC7, 0xC0, 0x0C, 0x03, 0x03, 0xF1, 0xFE, 0xED, 0xB3, 0x7C,
|
|
||||||
0xCF, 0x33, 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xC0, 0xF0, 0x30, 0x3F, 0x87, 0xF8, 0xC3, 0x18, 0x73,
|
|
||||||
0x0C, 0xFF, 0xBF, 0xE1, 0x80, 0xFF, 0x06, 0x00, 0xC0, 0x18, 0x00, 0x1E, 0x07, 0x8F, 0xF3, 0xFE,
|
|
||||||
0x30, 0xCC, 0x33, 0x0C, 0xFE, 0x3F, 0x8C, 0x73, 0x0C, 0xC3, 0xFF, 0xFF, 0xE1, 0xE0, 0x78, 0x08,
|
|
||||||
0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0F, 0xFF, 0xBF, 0xFC, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
|
||||||
0x0C, 0x07, 0x83, 0xF1, 0xF6, 0x6C, 0xC3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x00,
|
|
||||||
0x80, 0x06, 0x00, 0x18, 0x00, 0x6F, 0xFF, 0xBF, 0xFC, 0x01, 0xC0, 0x1C, 0x00, 0xC0, 0x00, 0x00,
|
|
||||||
0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC1, 0x32, 0x6D, 0xCF, 0xE1, 0xF0, 0x38, 0x00,
|
|
||||||
0x00, 0x08, 0x80, 0xE6, 0x0E, 0x18, 0xE0, 0x6F, 0xFF, 0xBF, 0xFC, 0xC1, 0xC3, 0x1C, 0x0C, 0xC0,
|
|
||||||
0x00, 0x00, 0x0C, 0x07, 0x83, 0xF1, 0xF6, 0x6C, 0xC3, 0x00, 0xC1, 0x32, 0x6D, 0xCF, 0xE1, 0xF0,
|
|
||||||
0x38, 0x00, 0x00, 0x7C, 0x7F, 0x38, 0x1E, 0x0F, 0x86, 0xE3, 0x38, 0x0E, 0x03, 0x80, 0x00, 0x1F,
|
|
||||||
0x1F, 0xC0, 0xE0, 0xF0, 0xF8, 0xEC, 0xE6, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x1C, 0x27, 0x31,
|
|
||||||
0xD8, 0x7C, 0x1E, 0x3F, 0x3F, 0x80, 0x00, 0x01, 0xE1, 0xD9, 0xCD, 0xC7, 0xC3, 0xC1, 0xF8, 0xFE,
|
|
||||||
0x00, 0x38, 0x1E, 0x03, 0x87, 0xE7, 0xF7, 0x3B, 0x0F, 0x83, 0xC1, 0xE1, 0xB9, 0xCF, 0xC1, 0x80,
|
|
||||||
0x01, 0x03, 0xC3, 0xF9, 0xDE, 0x66, 0xDB, 0xB6, 0xCD, 0xB3, 0x7B, 0x8F, 0xC3, 0xC0, 0xC0, 0x0E,
|
|
||||||
0x07, 0x81, 0xE0, 0x6C, 0x33, 0x0C, 0xC3, 0x39, 0xC6, 0x61, 0x98, 0x67, 0xFF, 0xFF, 0xFF, 0x7F,
|
|
||||||
0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x82,
|
|
||||||
0x7F, 0xDF, 0xF6, 0x00, 0xC0, 0x38, 0x07, 0x00, 0xC0, 0x18, 0x0E, 0x03, 0x01, 0x80, 0xE0, 0x70,
|
|
||||||
0x18, 0x07, 0xFD, 0xFF, 0xFF, 0xFF, 0x06, 0x0C, 0x30, 0x60, 0xC3, 0x06, 0x0C, 0x30, 0x60, 0xC3,
|
|
||||||
0x06, 0x0C, 0x30, 0x60, 0xC0, 0x77, 0xBF, 0xE0, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x06, 0x01, 0x80,
|
|
||||||
0x60, 0x18, 0x0C, 0x73, 0x3C, 0xC3, 0x30, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x13, 0x1F, 0xE6, 0xCD,
|
|
||||||
0xB3, 0x7F, 0x8E, 0xE0, 0x1C, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xCF, 0x38,
|
|
||||||
0x3B, 0x7F, 0xCE, 0x3B, 0x7F, 0xCE, 0x00, 0x0C, 0x0C, 0xFF, 0xFF, 0x7F, 0xFF, 0x30, 0x30, 0x01,
|
|
||||||
0x0F, 0x3E, 0xF0, 0xF0, 0x7C, 0x1F, 0x03, 0x7F, 0xFF, 0xC0, 0xF0, 0x7E, 0x0F, 0x07, 0x3E, 0xF8,
|
|
||||||
0xC0, 0x7F, 0xFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const EpdGlyph spacegroteskGlyphs[] = {
|
|
||||||
{ 0, 0, 10, 0, 0, 0, 0 }, //
|
|
||||||
{ 0, 0, 4, 0, 0, 0, 0 }, //
|
|
||||||
{ 4, 13, 4, 0, 12, 7, 0 }, // !
|
|
||||||
{ 7, 6, 7, 0, 13, 6, 7 }, // "
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 13 }, // #
|
|
||||||
{ 10, 16, 10, 0, 14, 20, 28 }, // $
|
|
||||||
{ 13, 13, 13, 0, 12, 22, 48 }, // %
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 70 }, // &
|
|
||||||
{ 4, 6, 4, 0, 13, 3, 85 }, // '
|
|
||||||
{ 6, 17, 6, 1, 13, 13, 88 }, // (
|
|
||||||
{ 6, 17, 6, 0, 13, 13, 101 }, // )
|
|
||||||
{ 8, 8, 9, 0, 12, 8, 114 }, // *
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 122 }, // +
|
|
||||||
{ 4, 6, 4, 0, 3, 3, 130 }, // ,
|
|
||||||
{ 6, 2, 8, 1, 5, 2, 133 }, // -
|
|
||||||
{ 4, 4, 4, 0, 3, 2, 135 }, // .
|
|
||||||
{ 7, 16, 6, 0, 12, 14, 137 }, // /
|
|
||||||
{ 9, 13, 11, 1, 12, 15, 151 }, // 0
|
|
||||||
{ 6, 12, 7, 0, 12, 9, 166 }, // 1
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 175 }, // 2
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 190 }, // 3
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 207 }, // 4
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 222 }, // 5
|
|
||||||
{ 9, 13, 10, 1, 12, 15, 239 }, // 6
|
|
||||||
{ 9, 12, 9, 0, 12, 14, 254 }, // 7
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 268 }, // 8
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 285 }, // 9
|
|
||||||
{ 4, 10, 4, 0, 9, 5, 302 }, // :
|
|
||||||
{ 4, 12, 4, 0, 9, 6, 307 }, // ;
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 313 }, // <
|
|
||||||
{ 8, 5, 10, 1, 8, 5, 321 }, // =
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 326 }, // >
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 334 }, // ?
|
|
||||||
{ 15, 16, 17, 1, 12, 30, 349 }, // @
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 379 }, // A
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 396 }, // B
|
|
||||||
{ 9, 13, 11, 1, 12, 15, 413 }, // C
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 428 }, // D
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 445 }, // E
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 457 }, // F
|
|
||||||
{ 9, 13, 11, 1, 12, 15, 469 }, // G
|
|
||||||
{ 9, 12, 11, 1, 12, 14, 484 }, // H
|
|
||||||
{ 2, 12, 4, 1, 12, 3, 498 }, // I
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 501 }, // J
|
|
||||||
{ 9, 12, 10, 1, 12, 14, 518 }, // K
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 532 }, // L
|
|
||||||
{ 13, 12, 14, 1, 12, 20, 544 }, // M
|
|
||||||
{ 9, 12, 11, 1, 12, 14, 564 }, // N
|
|
||||||
{ 10, 13, 11, 1, 12, 17, 578 }, // O
|
|
||||||
{ 9, 12, 10, 1, 12, 14, 595 }, // P
|
|
||||||
{ 10, 15, 11, 1, 12, 19, 609 }, // Q
|
|
||||||
{ 9, 12, 11, 1, 12, 14, 628 }, // R
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 642 }, // S
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 659 }, // T
|
|
||||||
{ 9, 13, 11, 1, 12, 15, 674 }, // U
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 689 }, // V
|
|
||||||
{ 14, 12, 15, 0, 12, 21, 704 }, // W
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 725 }, // X
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 742 }, // Y
|
|
||||||
{ 9, 12, 10, 0, 12, 14, 757 }, // Z
|
|
||||||
{ 5, 17, 6, 1, 13, 11, 771 }, // [
|
|
||||||
{ 8, 16, 6, -1, 12, 16, 782 }, // <backslash>
|
|
||||||
{ 5, 17, 6, 0, 13, 11, 798 }, // ]
|
|
||||||
{ 9, 5, 10, 1, 12, 6, 809 }, // ^
|
|
||||||
{ 9, 3, 10, 1, 0, 4, 815 }, // _
|
|
||||||
{ 4, 3, 5, 0, 12, 2, 819 }, // `
|
|
||||||
{ 10, 10, 10, 0, 9, 13, 821 }, // a
|
|
||||||
{ 9, 13, 11, 1, 12, 15, 834 }, // b
|
|
||||||
{ 10, 10, 10, 0, 9, 13, 849 }, // c
|
|
||||||
{ 10, 13, 11, 0, 12, 17, 862 }, // d
|
|
||||||
{ 9, 10, 10, 0, 9, 12, 879 }, // e
|
|
||||||
{ 7, 12, 7, 0, 12, 11, 891 }, // f
|
|
||||||
{ 10, 13, 11, 0, 9, 17, 902 }, // g
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 919 }, // h
|
|
||||||
{ 3, 12, 4, 1, 12, 5, 931 }, // i
|
|
||||||
{ 5, 16, 4, -1, 12, 10, 936 }, // j
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 946 }, // k
|
|
||||||
{ 2, 12, 4, 1, 12, 3, 958 }, // l
|
|
||||||
{ 13, 9, 14, 1, 9, 15, 961 }, // m
|
|
||||||
{ 8, 9, 10, 1, 9, 9, 976 }, // n
|
|
||||||
{ 10, 10, 10, 0, 9, 13, 985 }, // o
|
|
||||||
{ 9, 13, 11, 1, 9, 15, 998 }, // p
|
|
||||||
{ 10, 13, 11, 0, 9, 17, 1013 }, // q
|
|
||||||
{ 5, 9, 6, 1, 9, 6, 1030 }, // r
|
|
||||||
{ 8, 10, 9, 0, 9, 10, 1036 }, // s
|
|
||||||
{ 7, 12, 8, 0, 12, 11, 1046 }, // t
|
|
||||||
{ 8, 10, 10, 1, 9, 10, 1057 }, // u
|
|
||||||
{ 9, 9, 9, 0, 9, 11, 1067 }, // v
|
|
||||||
{ 13, 9, 13, 0, 9, 15, 1078 }, // w
|
|
||||||
{ 10, 9, 10, 0, 9, 12, 1093 }, // x
|
|
||||||
{ 8, 13, 10, 1, 9, 13, 1105 }, // y
|
|
||||||
{ 7, 9, 9, 1, 9, 8, 1118 }, // z
|
|
||||||
{ 7, 17, 7, 0, 13, 15, 1126 }, // {
|
|
||||||
{ 2, 19, 4, 1, 14, 5, 1141 }, // |
|
|
||||||
{ 7, 17, 7, 0, 13, 15, 1146 }, // }
|
|
||||||
{ 8, 4, 10, 1, 8, 4, 1161 }, // ~
|
|
||||||
{ 0, 0, 4, 0, 0, 0, 1165 }, //
|
|
||||||
{ 4, 13, 4, 0, 9, 7, 1165 }, // ¡
|
|
||||||
{ 9, 13, 10, 1, 11, 15, 1172 }, // ¢
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 1187 }, // £
|
|
||||||
{ 10, 10, 10, 0, 11, 13, 1202 }, // ¤
|
|
||||||
{ 11, 12, 10, 0, 12, 17, 1215 }, // ¥
|
|
||||||
{ 2, 19, 4, 1, 14, 5, 1232 }, // ¦
|
|
||||||
{ 8, 13, 8, 0, 12, 13, 1237 }, // §
|
|
||||||
{ 7, 3, 7, 0, 12, 3, 1250 }, // ¨
|
|
||||||
{ 10, 13, 12, 1, 12, 17, 1253 }, // ©
|
|
||||||
{ 7, 7, 7, 0, 12, 7, 1270 }, // ª
|
|
||||||
{ 11, 8, 11, 0, 8, 11, 1277 }, // «
|
|
||||||
{ 8, 4, 10, 1, 7, 4, 1288 }, // ¬
|
|
||||||
{ 6, 2, 8, 1, 5, 2, 1292 }, //
|
|
||||||
{ 8, 8, 9, 0, 12, 8, 1294 }, // ®
|
|
||||||
{ 7, 2, 7, 0, 11, 2, 1302 }, // ¯
|
|
||||||
{ 6, 6, 6, 0, 12, 5, 1304 }, // °
|
|
||||||
{ 8, 10, 10, 1, 10, 10, 1309 }, // ±
|
|
||||||
{ 6, 7, 6, 0, 13, 6, 1319 }, // ²
|
|
||||||
{ 6, 7, 6, 0, 12, 6, 1325 }, // ³
|
|
||||||
{ 4, 3, 5, 0, 12, 2, 1331 }, // ´
|
|
||||||
{ 9, 13, 10, 1, 9, 15, 1333 }, // µ
|
|
||||||
{ 9, 12, 10, 0, 12, 14, 1348 }, // ¶
|
|
||||||
{ 3, 4, 3, 0, 7, 2, 1362 }, // ·
|
|
||||||
{ 5, 5, 5, 0, 1, 4, 1364 }, // ¸
|
|
||||||
{ 4, 6, 4, 0, 12, 3, 1368 }, // ¹
|
|
||||||
{ 7, 7, 7, 0, 12, 7, 1371 }, // º
|
|
||||||
{ 10, 8, 11, 1, 8, 10, 1378 }, // »
|
|
||||||
{ 14, 13, 14, 0, 12, 23, 1388 }, // ¼
|
|
||||||
{ 14, 13, 14, 0, 12, 23, 1411 }, // ½
|
|
||||||
{ 15, 13, 15, 0, 12, 25, 1434 }, // ¾
|
|
||||||
{ 9, 13, 10, 0, 9, 15, 1459 }, // ¿
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 1474 }, // À
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 1495 }, // Á
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 1516 }, // Â
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 1537 }, // Ã
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 1558 }, // Ä
|
|
||||||
{ 11, 16, 11, 0, 16, 22, 1579 }, // Å
|
|
||||||
{ 14, 12, 14, 0, 12, 21, 1601 }, // Æ
|
|
||||||
{ 9, 16, 11, 1, 12, 18, 1622 }, // Ç
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 1640 }, // È
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 1655 }, // É
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 1670 }, // Ê
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 1685 }, // Ë
|
|
||||||
{ 3, 15, 4, 0, 15, 6, 1700 }, // Ì
|
|
||||||
{ 3, 15, 4, 1, 15, 6, 1706 }, // Í
|
|
||||||
{ 6, 15, 4, -1, 15, 12, 1712 }, // Î
|
|
||||||
{ 6, 15, 4, -1, 15, 12, 1724 }, // Ï
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 1736 }, // Ð
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 1753 }, // Ñ
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 1770 }, // Ò
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 1790 }, // Ó
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 1810 }, // Ô
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 1830 }, // Õ
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 1850 }, // Ö
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 1870 }, // ×
|
|
||||||
{ 10, 15, 11, 1, 13, 19, 1878 }, // Ø
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 1897 }, // Ù
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 1915 }, // Ú
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 1933 }, // Û
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 1951 }, // Ü
|
|
||||||
{ 10, 15, 10, 0, 15, 19, 1969 }, // Ý
|
|
||||||
{ 9, 12, 10, 1, 12, 14, 1988 }, // Þ
|
|
||||||
{ 9, 13, 10, 1, 12, 15, 2002 }, // ß
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2017 }, // à
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2034 }, // á
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2051 }, // â
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2068 }, // ã
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2085 }, // ä
|
|
||||||
{ 10, 14, 10, 0, 13, 18, 2102 }, // å
|
|
||||||
{ 15, 10, 15, 0, 9, 19, 2120 }, // æ
|
|
||||||
{ 10, 13, 10, 0, 9, 17, 2139 }, // ç
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2156 }, // è
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2171 }, // é
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2186 }, // ê
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2201 }, // ë
|
|
||||||
{ 3, 12, 4, 0, 12, 5, 2216 }, // ì
|
|
||||||
{ 4, 12, 4, 1, 12, 6, 2221 }, // í
|
|
||||||
{ 6, 12, 4, -1, 12, 9, 2227 }, // î
|
|
||||||
{ 6, 12, 4, -1, 12, 9, 2236 }, // ï
|
|
||||||
{ 10, 14, 10, 0, 13, 18, 2245 }, // ð
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 2263 }, // ñ
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2275 }, // ò
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2292 }, // ó
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2309 }, // ô
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2326 }, // õ
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2343 }, // ö
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 2360 }, // ÷
|
|
||||||
{ 10, 12, 10, 0, 10, 15, 2368 }, // ø
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 2383 }, // ù
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 2396 }, // ú
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 2409 }, // û
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 2422 }, // ü
|
|
||||||
{ 8, 16, 10, 1, 12, 16, 2435 }, // ý
|
|
||||||
{ 9, 16, 11, 1, 12, 18, 2451 }, // þ
|
|
||||||
{ 8, 16, 10, 1, 12, 16, 2469 }, // ÿ
|
|
||||||
{ 11, 14, 11, 0, 14, 20, 2485 }, // Ā
|
|
||||||
{ 10, 12, 10, 0, 11, 15, 2505 }, // ā
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 2520 }, // Ă
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2541 }, // ă
|
|
||||||
{ 11, 15, 11, 0, 12, 21, 2558 }, // Ą
|
|
||||||
{ 10, 12, 10, 0, 9, 15, 2579 }, // ą
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2594 }, // Ć
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2612 }, // ć
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2629 }, // Ĉ
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2647 }, // ĉ
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2664 }, // Ċ
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2682 }, // ċ
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2699 }, // Č
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 2717 }, // č
|
|
||||||
{ 11, 15, 11, 0, 15, 21, 2734 }, // Ď
|
|
||||||
{ 13, 13, 12, 0, 12, 22, 2755 }, // ď
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 2777 }, // Đ
|
|
||||||
{ 11, 13, 11, 0, 12, 18, 2794 }, // đ
|
|
||||||
{ 8, 14, 9, 1, 14, 14, 2812 }, // Ē
|
|
||||||
{ 9, 12, 10, 0, 11, 14, 2826 }, // ē
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 2840 }, // Ĕ
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2855 }, // ĕ
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 2870 }, // Ė
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2885 }, // ė
|
|
||||||
{ 8, 15, 9, 1, 12, 15, 2900 }, // Ę
|
|
||||||
{ 9, 12, 10, 0, 9, 14, 2915 }, // ę
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 2929 }, // Ě
|
|
||||||
{ 9, 13, 10, 0, 12, 15, 2944 }, // ě
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2959 }, // Ĝ
|
|
||||||
{ 10, 16, 11, 0, 12, 20, 2977 }, // ĝ
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 2997 }, // Ğ
|
|
||||||
{ 10, 16, 11, 0, 12, 20, 3015 }, // ğ
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 3035 }, // Ġ
|
|
||||||
{ 10, 16, 11, 0, 12, 20, 3053 }, // ġ
|
|
||||||
{ 9, 17, 11, 1, 12, 20, 3073 }, // Ģ
|
|
||||||
{ 10, 17, 11, 0, 13, 22, 3093 }, // ģ
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 3115 }, // Ĥ
|
|
||||||
{ 10, 15, 10, -1, 15, 19, 3132 }, // ĥ
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 3151 }, // Ħ
|
|
||||||
{ 9, 12, 10, 0, 12, 14, 3168 }, // ħ
|
|
||||||
{ 6, 15, 4, -1, 15, 12, 3182 }, // Ĩ
|
|
||||||
{ 6, 12, 4, -1, 12, 9, 3194 }, // ĩ
|
|
||||||
{ 6, 14, 4, -1, 14, 11, 3203 }, // Ī
|
|
||||||
{ 6, 11, 4, -1, 11, 9, 3214 }, // ī
|
|
||||||
{ 6, 15, 4, -1, 15, 12, 3223 }, // Ĭ
|
|
||||||
{ 6, 12, 4, -1, 12, 9, 3235 }, // ĭ
|
|
||||||
{ 3, 15, 4, 1, 12, 6, 3244 }, // Į
|
|
||||||
{ 3, 15, 4, 1, 12, 6, 3250 }, // į
|
|
||||||
{ 3, 15, 4, 1, 15, 6, 3256 }, // İ
|
|
||||||
{ 2, 9, 4, 1, 9, 3, 3262 }, // ı
|
|
||||||
{ 12, 13, 12, 0, 12, 20, 3265 }, // IJ
|
|
||||||
{ 7, 16, 8, 1, 12, 14, 3285 }, // ij
|
|
||||||
{ 10, 16, 10, 0, 15, 20, 3299 }, // Ĵ
|
|
||||||
{ 6, 16, 4, -1, 12, 12, 3319 }, // ĵ
|
|
||||||
{ 9, 17, 10, 1, 12, 20, 3331 }, // Ķ
|
|
||||||
{ 8, 17, 9, 1, 12, 17, 3351 }, // ķ
|
|
||||||
{ 8, 9, 9, 1, 9, 9, 3368 }, // ĸ
|
|
||||||
{ 8, 15, 9, 1, 15, 15, 3377 }, // Ĺ
|
|
||||||
{ 3, 15, 4, 1, 15, 6, 3392 }, // ĺ
|
|
||||||
{ 8, 17, 9, 1, 12, 17, 3398 }, // Ļ
|
|
||||||
{ 2, 17, 4, 1, 12, 5, 3415 }, // ļ
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 3420 }, // Ľ
|
|
||||||
{ 5, 12, 5, 1, 12, 8, 3432 }, // ľ
|
|
||||||
{ 8, 12, 9, 1, 12, 12, 3440 }, // Ŀ
|
|
||||||
{ 6, 12, 7, 1, 12, 9, 3452 }, // ŀ
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 3461 }, // Ł
|
|
||||||
{ 6, 12, 6, 0, 12, 9, 3476 }, // ł
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 3485 }, // Ń
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 3502 }, // ń
|
|
||||||
{ 9, 17, 11, 1, 12, 20, 3514 }, // Ņ
|
|
||||||
{ 8, 14, 10, 1, 9, 14, 3534 }, // ņ
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 3548 }, // Ň
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 3565 }, // ň
|
|
||||||
{ 8, 13, 10, 1, 13, 13, 3577 }, // ʼn
|
|
||||||
{ 9, 16, 11, 1, 12, 18, 3590 }, // Ŋ
|
|
||||||
{ 8, 13, 10, 1, 9, 13, 3608 }, // ŋ
|
|
||||||
{ 10, 15, 11, 1, 14, 19, 3621 }, // Ō
|
|
||||||
{ 10, 12, 10, 0, 11, 15, 3640 }, // ō
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 3655 }, // Ŏ
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 3675 }, // ŏ
|
|
||||||
{ 10, 16, 11, 1, 15, 20, 3692 }, // Ő
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 3712 }, // ő
|
|
||||||
{ 13, 12, 14, 1, 12, 20, 3729 }, // Œ
|
|
||||||
{ 17, 10, 17, 0, 9, 22, 3749 }, // œ
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 3771 }, // Ŕ
|
|
||||||
{ 5, 12, 6, 1, 12, 8, 3788 }, // ŕ
|
|
||||||
{ 9, 17, 11, 1, 12, 20, 3796 }, // Ŗ
|
|
||||||
{ 5, 14, 6, 1, 9, 9, 3816 }, // ŗ
|
|
||||||
{ 9, 15, 11, 1, 15, 17, 3825 }, // Ř
|
|
||||||
{ 7, 12, 6, 0, 12, 11, 3842 }, // ř
|
|
||||||
{ 10, 16, 10, 0, 15, 20, 3853 }, // Ś
|
|
||||||
{ 8, 13, 9, 0, 12, 13, 3873 }, // ś
|
|
||||||
{ 10, 16, 10, 0, 15, 20, 3886 }, // Ŝ
|
|
||||||
{ 8, 13, 9, 0, 12, 13, 3906 }, // ŝ
|
|
||||||
{ 10, 16, 10, 0, 12, 20, 3919 }, // Ş
|
|
||||||
{ 8, 13, 9, 0, 9, 13, 3939 }, // ş
|
|
||||||
{ 10, 16, 10, 0, 15, 20, 3952 }, // Š
|
|
||||||
{ 8, 13, 9, 0, 12, 13, 3972 }, // š
|
|
||||||
{ 10, 16, 10, 0, 12, 20, 3985 }, // Ţ
|
|
||||||
{ 7, 16, 8, 0, 12, 14, 4005 }, // ţ
|
|
||||||
{ 10, 15, 10, 0, 15, 19, 4019 }, // Ť
|
|
||||||
{ 8, 13, 8, 0, 13, 13, 4038 }, // ť
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 4051 }, // Ŧ
|
|
||||||
{ 7, 12, 8, 0, 12, 11, 4066 }, // ŧ
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 4077 }, // Ũ
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 4095 }, // ũ
|
|
||||||
{ 9, 15, 11, 1, 14, 17, 4108 }, // Ū
|
|
||||||
{ 8, 12, 10, 1, 11, 12, 4125 }, // ū
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 4137 }, // Ŭ
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 4155 }, // ŭ
|
|
||||||
{ 9, 17, 11, 1, 16, 20, 4168 }, // Ů
|
|
||||||
{ 8, 14, 10, 1, 13, 14, 4188 }, // ů
|
|
||||||
{ 9, 16, 11, 1, 15, 18, 4202 }, // Ű
|
|
||||||
{ 8, 13, 10, 1, 12, 13, 4220 }, // ű
|
|
||||||
{ 9, 15, 11, 1, 12, 17, 4233 }, // Ų
|
|
||||||
{ 9, 12, 10, 1, 9, 14, 4250 }, // ų
|
|
||||||
{ 14, 15, 15, 0, 15, 27, 4264 }, // Ŵ
|
|
||||||
{ 13, 12, 13, 0, 12, 20, 4291 }, // ŵ
|
|
||||||
{ 10, 15, 10, 0, 15, 19, 4311 }, // Ŷ
|
|
||||||
{ 8, 16, 10, 1, 12, 16, 4330 }, // ŷ
|
|
||||||
{ 10, 15, 10, 0, 15, 19, 4346 }, // Ÿ
|
|
||||||
{ 9, 15, 10, 0, 15, 17, 4365 }, // Ź
|
|
||||||
{ 7, 12, 9, 1, 12, 11, 4382 }, // ź
|
|
||||||
{ 9, 15, 10, 0, 15, 17, 4393 }, // Ż
|
|
||||||
{ 7, 12, 9, 1, 12, 11, 4410 }, // ż
|
|
||||||
{ 9, 15, 10, 0, 15, 17, 4421 }, // Ž
|
|
||||||
{ 7, 12, 9, 1, 12, 11, 4438 }, // ž
|
|
||||||
{ 6, 12, 6, 0, 12, 9, 4449 }, // ſ
|
|
||||||
{ 4, 3, 0, 0, 12, 2, 4458 }, // ̀
|
|
||||||
{ 4, 3, 0, 0, 12, 2, 4460 }, // ́
|
|
||||||
{ 7, 3, 0, 0, 12, 3, 4462 }, // ̂
|
|
||||||
{ 7, 3, 0, 0, 12, 3, 4465 }, // ̃
|
|
||||||
{ 7, 2, 0, 0, 11, 2, 4468 }, // ̄
|
|
||||||
{ 6, 3, 0, 0, 12, 3, 4470 }, // ̆
|
|
||||||
{ 3, 3, 0, 0, 12, 2, 4473 }, // ̇
|
|
||||||
{ 7, 3, 0, 0, 12, 3, 4475 }, // ̈
|
|
||||||
{ 5, 4, 0, 0, 13, 3, 4478 }, // ̉
|
|
||||||
{ 5, 4, 0, 0, 13, 3, 4481 }, // ̊
|
|
||||||
{ 6, 3, 0, 0, 12, 3, 4484 }, // ̋
|
|
||||||
{ 7, 3, 0, 0, 12, 3, 4487 }, // ̌
|
|
||||||
{ 6, 3, 0, 0, 12, 3, 4490 }, // ̏
|
|
||||||
{ 6, 3, 0, 0, 12, 3, 4493 }, // ̑
|
|
||||||
{ 3, 4, 0, 0, 13, 2, 4496 }, // ̒
|
|
||||||
{ 4, 3, 0, 0, 10, 2, 4498 }, // ̛
|
|
||||||
{ 3, 3, 0, 0, -1, 2, 4500 }, // ̣
|
|
||||||
{ 7, 3, 0, 0, -1, 3, 4502 }, // ̤
|
|
||||||
{ 3, 4, 0, 0, -1, 2, 4505 }, // ̦
|
|
||||||
{ 5, 5, 0, 0, 1, 4, 4507 }, // ̧
|
|
||||||
{ 4, 4, 0, 0, 1, 2, 4511 }, // ̨
|
|
||||||
{ 6, 3, 0, 0, -1, 3, 4513 }, // ̮
|
|
||||||
{ 7, 2, 0, 0, -1, 2, 4516 }, // ̱
|
|
||||||
{ 7, 2, 0, 0, 11, 2, 4518 }, // ̵
|
|
||||||
{ 12, 2, 0, 0, 10, 3, 4520 }, // ̶
|
|
||||||
{ 8, 12, 0, 0, 10, 12, 4523 }, // ̷
|
|
||||||
{ 8, 15, 0, 0, 13, 15, 4535 }, // ̸
|
|
||||||
{ 0, 0, 10, 0, 0, 0, 4550 }, //
|
|
||||||
{ 0, 0, 4, 0, 0, 0, 4550 }, //
|
|
||||||
{ 0, 0, 3, 0, 0, 0, 4550 }, //
|
|
||||||
{ 0, 0, 1, 0, 0, 0, 4550 }, //
|
|
||||||
{ 0, 0, 0, 0, 0, 0, 4550 }, //
|
|
||||||
{ 6, 2, 8, 1, 5, 2, 4550 }, // ‐
|
|
||||||
{ 9, 2, 10, 1, 5, 3, 4552 }, // ‒
|
|
||||||
{ 8, 2, 10, 1, 5, 2, 4555 }, // –
|
|
||||||
{ 13, 2, 15, 1, 5, 4, 4557 }, // —
|
|
||||||
{ 13, 2, 15, 1, 5, 4, 4561 }, // ―
|
|
||||||
{ 4, 6, 4, 0, 13, 3, 4565 }, // ‘
|
|
||||||
{ 4, 6, 4, 0, 13, 3, 4568 }, // ’
|
|
||||||
{ 4, 6, 4, 0, 3, 3, 4571 }, // ‚
|
|
||||||
{ 7, 6, 7, 0, 13, 6, 4574 }, // “
|
|
||||||
{ 7, 6, 7, 0, 13, 6, 4580 }, // ”
|
|
||||||
{ 7, 6, 7, 0, 3, 6, 4586 }, // „
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 4592 }, // †
|
|
||||||
{ 8, 12, 10, 1, 12, 12, 4604 }, // ‡
|
|
||||||
{ 4, 5, 6, 1, 7, 3, 4616 }, // •
|
|
||||||
{ 11, 4, 11, 0, 3, 6, 4619 }, // …
|
|
||||||
{ 16, 13, 16, 0, 12, 26, 4625 }, // ‰
|
|
||||||
{ 3, 5, 3, 0, 12, 2, 4651 }, // ′
|
|
||||||
{ 6, 5, 6, 0, 12, 4, 4653 }, // ″
|
|
||||||
{ 7, 8, 8, 0, 8, 7, 4657 }, // ‹
|
|
||||||
{ 6, 8, 8, 1, 8, 6, 4664 }, // ›
|
|
||||||
{ 11, 13, 11, 0, 12, 18, 4670 }, // ⁄
|
|
||||||
{ 11, 13, 12, 0, 12, 18, 4688 }, // ⁒
|
|
||||||
{ 9, 16, 11, 1, 14, 18, 4706 }, // ₡
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 4724 }, // ₣
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 4739 }, // ₤
|
|
||||||
{ 12, 12, 13, 0, 12, 18, 4754 }, // ₦
|
|
||||||
{ 12, 12, 12, 0, 12, 18, 4772 }, // ₧
|
|
||||||
{ 16, 12, 16, 0, 12, 24, 4790 }, // ₩
|
|
||||||
{ 11, 14, 12, 0, 12, 20, 4814 }, // ₫
|
|
||||||
{ 11, 13, 11, 0, 12, 18, 4834 }, // €
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 4852 }, // ₭
|
|
||||||
{ 12, 12, 12, 0, 12, 18, 4869 }, // ₱
|
|
||||||
{ 9, 16, 11, 1, 14, 18, 4887 }, // ₲
|
|
||||||
{ 9, 16, 11, 1, 14, 18, 4905 }, // ₵
|
|
||||||
{ 10, 12, 11, 1, 12, 15, 4923 }, // ₹
|
|
||||||
{ 9, 12, 9, 0, 12, 14, 4938 }, // ₺
|
|
||||||
{ 10, 14, 12, 1, 14, 18, 4952 }, // ₼
|
|
||||||
{ 11, 12, 11, 0, 12, 17, 4970 }, // ₽
|
|
||||||
{ 10, 16, 12, 1, 14, 20, 4987 }, // ₿
|
|
||||||
{ 13, 10, 13, 0, 9, 17, 5007 }, // ←
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 5024 }, // ↑
|
|
||||||
{ 13, 10, 13, 0, 9, 17, 5039 }, // →
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 5056 }, // ↓
|
|
||||||
{ 13, 10, 13, 0, 9, 17, 5073 }, // ↔
|
|
||||||
{ 10, 13, 10, 0, 12, 17, 5090 }, // ↕
|
|
||||||
{ 9, 10, 10, 1, 9, 12, 5107 }, // ↖
|
|
||||||
{ 9, 10, 10, 0, 9, 12, 5119 }, // ↗
|
|
||||||
{ 9, 9, 10, 0, 9, 11, 5131 }, // ↘
|
|
||||||
{ 9, 9, 10, 1, 9, 11, 5142 }, // ↙
|
|
||||||
{ 9, 13, 10, 1, 12, 15, 5153 }, // ∂
|
|
||||||
{ 10, 12, 11, 0, 10, 15, 5168 }, // ∅
|
|
||||||
{ 10, 12, 10, 0, 12, 15, 5183 }, // ∆
|
|
||||||
{ 9, 16, 10, 1, 12, 18, 5198 }, // ∏
|
|
||||||
{ 10, 16, 10, 0, 12, 20, 5216 }, // ∑
|
|
||||||
{ 8, 2, 10, 1, 7, 2, 5236 }, // −
|
|
||||||
{ 7, 17, 6, 0, 14, 15, 5238 }, // ∕
|
|
||||||
{ 5, 5, 10, 3, 8, 4, 5253 }, // ∙
|
|
||||||
{ 10, 15, 10, 0, 15, 19, 5257 }, // √
|
|
||||||
{ 10, 6, 10, 0, 9, 8, 5276 }, // ∞
|
|
||||||
{ 6, 16, 10, 2, 12, 12, 5284 }, // ∫
|
|
||||||
{ 8, 7, 10, 1, 9, 7, 5296 }, // ≈
|
|
||||||
{ 8, 8, 10, 1, 10, 8, 5303 }, // ≠
|
|
||||||
{ 8, 10, 10, 1, 10, 10, 5311 }, // ≤
|
|
||||||
{ 8, 10, 10, 1, 10, 10, 5321 }, // ≥
|
|
||||||
};
|
|
||||||
|
|
||||||
static const EpdUnicodeInterval spacegroteskIntervals[] = {
|
|
||||||
{ 0xD, 0xD, 0x0 },
|
|
||||||
{ 0x20, 0x7E, 0x1 },
|
|
||||||
{ 0xA0, 0xFF, 0x60 },
|
|
||||||
{ 0x100, 0x17F, 0xC0 },
|
|
||||||
{ 0x300, 0x304, 0x140 },
|
|
||||||
{ 0x306, 0x30C, 0x145 },
|
|
||||||
{ 0x30F, 0x30F, 0x14C },
|
|
||||||
{ 0x311, 0x312, 0x14D },
|
|
||||||
{ 0x31B, 0x31B, 0x14F },
|
|
||||||
{ 0x323, 0x324, 0x150 },
|
|
||||||
{ 0x326, 0x328, 0x152 },
|
|
||||||
{ 0x32E, 0x32E, 0x155 },
|
|
||||||
{ 0x331, 0x331, 0x156 },
|
|
||||||
{ 0x335, 0x338, 0x157 },
|
|
||||||
{ 0x2007, 0x200B, 0x15B },
|
|
||||||
{ 0x2010, 0x2010, 0x160 },
|
|
||||||
{ 0x2012, 0x2015, 0x161 },
|
|
||||||
{ 0x2018, 0x201A, 0x165 },
|
|
||||||
{ 0x201C, 0x201E, 0x168 },
|
|
||||||
{ 0x2020, 0x2022, 0x16B },
|
|
||||||
{ 0x2026, 0x2026, 0x16E },
|
|
||||||
{ 0x2030, 0x2030, 0x16F },
|
|
||||||
{ 0x2032, 0x2033, 0x170 },
|
|
||||||
{ 0x2039, 0x203A, 0x172 },
|
|
||||||
{ 0x2044, 0x2044, 0x174 },
|
|
||||||
{ 0x2052, 0x2052, 0x175 },
|
|
||||||
{ 0x20A1, 0x20A1, 0x176 },
|
|
||||||
{ 0x20A3, 0x20A4, 0x177 },
|
|
||||||
{ 0x20A6, 0x20A7, 0x179 },
|
|
||||||
{ 0x20A9, 0x20A9, 0x17B },
|
|
||||||
{ 0x20AB, 0x20AD, 0x17C },
|
|
||||||
{ 0x20B1, 0x20B2, 0x17F },
|
|
||||||
{ 0x20B5, 0x20B5, 0x181 },
|
|
||||||
{ 0x20B9, 0x20BA, 0x182 },
|
|
||||||
{ 0x20BC, 0x20BD, 0x184 },
|
|
||||||
{ 0x20BF, 0x20BF, 0x186 },
|
|
||||||
{ 0x2190, 0x2199, 0x187 },
|
|
||||||
{ 0x2202, 0x2202, 0x191 },
|
|
||||||
{ 0x2205, 0x2206, 0x192 },
|
|
||||||
{ 0x220F, 0x220F, 0x194 },
|
|
||||||
{ 0x2211, 0x2212, 0x195 },
|
|
||||||
{ 0x2215, 0x2215, 0x197 },
|
|
||||||
{ 0x2219, 0x221A, 0x198 },
|
|
||||||
{ 0x221E, 0x221E, 0x19A },
|
|
||||||
{ 0x222B, 0x222B, 0x19B },
|
|
||||||
{ 0x2248, 0x2248, 0x19C },
|
|
||||||
{ 0x2260, 0x2260, 0x19D },
|
|
||||||
{ 0x2264, 0x2265, 0x19E },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const EpdFontData spacegrotesk = {
|
|
||||||
spacegroteskBitmaps,
|
|
||||||
spacegroteskGlyphs,
|
|
||||||
spacegroteskIntervals,
|
|
||||||
48,
|
|
||||||
21,
|
|
||||||
17,
|
|
||||||
-5,
|
|
||||||
false,
|
|
||||||
};
|
|
||||||
@@ -8,39 +8,39 @@ echo "// The contents of this file are generated by ./lib/EpdFont/scripts/build-
|
|||||||
echo "#pragma once"
|
echo "#pragma once"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "#define ALEO_12_FONT_ID ($(
|
echo "#define BOOKERLY_12_FONT_ID ($(
|
||||||
ruby -rdigest -e 'puts [
|
ruby -rdigest -e 'puts [
|
||||||
"./aleo_12_regular.h",
|
"./bookerly_12_regular.h",
|
||||||
"./aleo_12_bold.h",
|
"./bookerly_12_bold.h",
|
||||||
"./aleo_12_bolditalic.h",
|
"./bookerly_12_bolditalic.h",
|
||||||
"./aleo_12_italic.h",
|
"./bookerly_12_italic.h",
|
||||||
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
||||||
))"
|
))"
|
||||||
|
|
||||||
echo "#define ALEO_14_FONT_ID ($(
|
echo "#define BOOKERLY_14_FONT_ID ($(
|
||||||
ruby -rdigest -e 'puts [
|
ruby -rdigest -e 'puts [
|
||||||
"./aleo_14_regular.h",
|
"./bookerly_14_regular.h",
|
||||||
"./aleo_14_bold.h",
|
"./bookerly_14_bold.h",
|
||||||
"./aleo_14_bolditalic.h",
|
"./bookerly_14_bolditalic.h",
|
||||||
"./aleo_14_italic.h",
|
"./bookerly_14_italic.h",
|
||||||
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
||||||
))"
|
))"
|
||||||
|
|
||||||
echo "#define ALEO_16_FONT_ID ($(
|
echo "#define BOOKERLY_16_FONT_ID ($(
|
||||||
ruby -rdigest -e 'puts [
|
ruby -rdigest -e 'puts [
|
||||||
"./aleo_16_regular.h",
|
"./bookerly_16_regular.h",
|
||||||
"./aleo_16_bold.h",
|
"./bookerly_16_bold.h",
|
||||||
"./aleo_16_bolditalic.h",
|
"./bookerly_16_bolditalic.h",
|
||||||
"./aleo_16_italic.h",
|
"./bookerly_16_italic.h",
|
||||||
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
||||||
))"
|
))"
|
||||||
|
|
||||||
echo "#define ALEO_18_FONT_ID ($(
|
echo "#define BOOKERLY_18_FONT_ID ($(
|
||||||
ruby -rdigest -e 'puts [
|
ruby -rdigest -e 'puts [
|
||||||
"./aleo_18_regular.h",
|
"./bookerly_18_regular.h",
|
||||||
"./aleo_18_bold.h",
|
"./bookerly_18_bold.h",
|
||||||
"./aleo_18_bolditalic.h",
|
"./bookerly_18_bolditalic.h",
|
||||||
"./aleo_18_italic.h",
|
"./bookerly_18_italic.h",
|
||||||
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
||||||
))"
|
))"
|
||||||
|
|
||||||
@@ -132,6 +132,6 @@ ruby -rdigest -e 'puts [
|
|||||||
|
|
||||||
echo "#define SMALL_FONT_ID ($(
|
echo "#define SMALL_FONT_ID ($(
|
||||||
ruby -rdigest -e 'puts [
|
ruby -rdigest -e 'puts [
|
||||||
"./spacegrotesk.h",
|
"./notosans_8_regular.h",
|
||||||
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
|
||||||
))"
|
))"
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ set -e
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
READER_FONT_STYLES=("Regular" "Italic" "Bold" "BoldItalic")
|
READER_FONT_STYLES=("Regular" "Italic" "Bold" "BoldItalic")
|
||||||
ALEO_FONT_SIZES=(12 14 16 18)
|
BOOKERLY_FONT_SIZES=(12 14 16 18)
|
||||||
NOTOSANS_FONT_SIZES=(12 14 16 18)
|
NOTOSANS_FONT_SIZES=(12 14 16 18)
|
||||||
OPENDYSLEXIC_FONT_SIZES=(8 10 12 14)
|
OPENDYSLEXIC_FONT_SIZES=(8 10 12 14)
|
||||||
|
|
||||||
for size in ${ALEO_FONT_SIZES[@]}; do
|
for size in ${BOOKERLY_FONT_SIZES[@]}; do
|
||||||
for style in ${READER_FONT_STYLES[@]}; do
|
for style in ${READER_FONT_STYLES[@]}; do
|
||||||
font_name="aleo_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')"
|
font_name="bookerly_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')"
|
||||||
font_path="../builtinFonts/source/Aleo/Aleo-${style}.ttf"
|
font_path="../builtinFonts/source/Bookerly/Bookerly-${style}.ttf"
|
||||||
output_path="../builtinFonts/${font_name}.h"
|
output_path="../builtinFonts/${font_name}.h"
|
||||||
python fontconvert.py $font_name $size $font_path --2bit > $output_path
|
python fontconvert.py $font_name $size $font_path --2bit > $output_path
|
||||||
echo "Generated $output_path"
|
echo "Generated $output_path"
|
||||||
@@ -52,4 +52,4 @@ for size in ${UI_FONT_SIZES[@]}; do
|
|||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
python fontconvert.py spacegrotesk 8 ../builtinFonts/source/SpaceGrotesk/SpaceGrotesk-Regular.ttf > ../builtinFonts/spacegrotesk.h
|
python fontconvert.py notosans_8_regular 8 ../builtinFonts/source/NotoSans/NotoSans-Regular.ttf > ../builtinFonts/notosans_8_regular.h
|
||||||
|
|||||||
+79
-4
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "Epub/parsers/ContainerParser.h"
|
#include "Epub/parsers/ContainerParser.h"
|
||||||
#include "Epub/parsers/ContentOpfParser.h"
|
#include "Epub/parsers/ContentOpfParser.h"
|
||||||
|
#include "Epub/parsers/TocNavParser.h"
|
||||||
#include "Epub/parsers/TocNcxParser.h"
|
#include "Epub/parsers/TocNcxParser.h"
|
||||||
|
|
||||||
bool Epub::findContentOpfFile(std::string* contentOpfFile) const {
|
bool Epub::findContentOpfFile(std::string* contentOpfFile) const {
|
||||||
@@ -80,6 +81,10 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata) {
|
|||||||
tocNcxItem = opfParser.tocNcxPath;
|
tocNcxItem = opfParser.tocNcxPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!opfParser.tocNavPath.empty()) {
|
||||||
|
tocNavItem = opfParser.tocNavPath;
|
||||||
|
}
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Successfully parsed content.opf\n", millis());
|
Serial.printf("[%lu] [EBP] Successfully parsed content.opf\n", millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -141,6 +146,60 @@ bool Epub::parseTocNcxFile() const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Epub::parseTocNavFile() const {
|
||||||
|
// the nav file should have been specified in the content.opf file (EPUB 3)
|
||||||
|
if (tocNavItem.empty()) {
|
||||||
|
Serial.printf("[%lu] [EBP] No nav file specified\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [EBP] Parsing toc nav file: %s\n", millis(), tocNavItem.c_str());
|
||||||
|
|
||||||
|
const auto tmpNavPath = getCachePath() + "/toc.nav";
|
||||||
|
FsFile tempNavFile;
|
||||||
|
if (!SdMan.openFileForWrite("EBP", tmpNavPath, tempNavFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
readItemContentsToStream(tocNavItem, tempNavFile, 1024);
|
||||||
|
tempNavFile.close();
|
||||||
|
if (!SdMan.openFileForRead("EBP", tmpNavPath, tempNavFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto navSize = tempNavFile.size();
|
||||||
|
|
||||||
|
TocNavParser navParser(contentBasePath, navSize, bookMetadataCache.get());
|
||||||
|
|
||||||
|
if (!navParser.setup()) {
|
||||||
|
Serial.printf("[%lu] [EBP] Could not setup toc nav parser\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto navBuffer = static_cast<uint8_t*>(malloc(1024));
|
||||||
|
if (!navBuffer) {
|
||||||
|
Serial.printf("[%lu] [EBP] Could not allocate memory for toc nav parser\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (tempNavFile.available()) {
|
||||||
|
const auto readSize = tempNavFile.read(navBuffer, 1024);
|
||||||
|
const auto processedSize = navParser.write(navBuffer, readSize);
|
||||||
|
|
||||||
|
if (processedSize != readSize) {
|
||||||
|
Serial.printf("[%lu] [EBP] Could not process all toc nav data\n", millis());
|
||||||
|
free(navBuffer);
|
||||||
|
tempNavFile.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(navBuffer);
|
||||||
|
tempNavFile.close();
|
||||||
|
SdMan.remove(tmpNavPath.c_str());
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [EBP] Parsed TOC nav items\n", millis());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// load in the meta data for the epub file
|
// load in the meta data for the epub file
|
||||||
bool Epub::load(const bool buildIfMissing) {
|
bool Epub::load(const bool buildIfMissing) {
|
||||||
Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str());
|
Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str());
|
||||||
@@ -184,15 +243,31 @@ bool Epub::load(const bool buildIfMissing) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOC Pass
|
// TOC Pass - try EPUB 3 nav first, fall back to NCX
|
||||||
if (!bookMetadataCache->beginTocPass()) {
|
if (!bookMetadataCache->beginTocPass()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not begin writing toc pass\n", millis());
|
Serial.printf("[%lu] [EBP] Could not begin writing toc pass\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!parseTocNcxFile()) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not parse toc\n", millis());
|
bool tocParsed = false;
|
||||||
return false;
|
|
||||||
|
// Try EPUB 3 nav document first (preferred)
|
||||||
|
if (!tocNavItem.empty()) {
|
||||||
|
Serial.printf("[%lu] [EBP] Attempting to parse EPUB 3 nav document\n", millis());
|
||||||
|
tocParsed = parseTocNavFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall back to NCX if nav parsing failed or wasn't available
|
||||||
|
if (!tocParsed && !tocNcxItem.empty()) {
|
||||||
|
Serial.printf("[%lu] [EBP] Falling back to NCX TOC\n", millis());
|
||||||
|
tocParsed = parseTocNcxFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tocParsed) {
|
||||||
|
Serial.printf("[%lu] [EBP] Warning: Could not parse any TOC format\n", millis());
|
||||||
|
// Continue anyway - book will work without TOC
|
||||||
|
}
|
||||||
|
|
||||||
if (!bookMetadataCache->endTocPass()) {
|
if (!bookMetadataCache->endTocPass()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not end writing toc pass\n", millis());
|
Serial.printf("[%lu] [EBP] Could not end writing toc pass\n", millis());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+4
-1
@@ -12,8 +12,10 @@
|
|||||||
class ZipFile;
|
class ZipFile;
|
||||||
|
|
||||||
class Epub {
|
class Epub {
|
||||||
// the ncx file
|
// the ncx file (EPUB 2)
|
||||||
std::string tocNcxItem;
|
std::string tocNcxItem;
|
||||||
|
// the nav file (EPUB 3)
|
||||||
|
std::string tocNavItem;
|
||||||
// where is the EPUBfile?
|
// where is the EPUBfile?
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
// the base path for items in the EPUB file
|
// the base path for items in the EPUB file
|
||||||
@@ -26,6 +28,7 @@ class Epub {
|
|||||||
bool findContentOpfFile(std::string* contentOpfFile) const;
|
bool findContentOpfFile(std::string* contentOpfFile) const;
|
||||||
bool parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata);
|
bool parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata);
|
||||||
bool parseTocNcxFile() const;
|
bool parseTocNcxFile() const;
|
||||||
|
bool parseTocNavFile() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Epub(std::string filepath, const std::string& cacheDir) : filepath(std::move(filepath)) {
|
explicit Epub(std::string filepath, const std::string& cacheDir) : filepath(std::move(filepath)) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Page::serialize(FsFile& file) const {
|
bool Page::serialize(FsFile& file) const {
|
||||||
const uint32_t count = elements.size();
|
const uint16_t count = elements.size();
|
||||||
serialization::writePod(file, count);
|
serialization::writePod(file, count);
|
||||||
|
|
||||||
for (const auto& el : elements) {
|
for (const auto& el : elements) {
|
||||||
@@ -49,10 +49,10 @@ bool Page::serialize(FsFile& file) const {
|
|||||||
std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||||
auto page = std::unique_ptr<Page>(new Page());
|
auto page = std::unique_ptr<Page>(new Page());
|
||||||
|
|
||||||
uint32_t count;
|
uint16_t count;
|
||||||
serialization::readPod(file, count);
|
serialization::readPod(file, count);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
for (uint16_t i = 0; i < count; i++) {
|
||||||
uint8_t tag;
|
uint8_t tag;
|
||||||
serialization::readPod(file, tag);
|
serialization::readPod(file, tag);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
constexpr int MAX_COST = std::numeric_limits<int>::max();
|
constexpr int MAX_COST = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
void ParsedText::addWord(std::string word, const EpdFontStyle fontStyle) {
|
void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle) {
|
||||||
if (word.empty()) return;
|
if (word.empty()) return;
|
||||||
|
|
||||||
words.push_back(std::move(word));
|
words.push_back(std::move(word));
|
||||||
@@ -18,7 +18,7 @@ void ParsedText::addWord(std::string word, const EpdFontStyle fontStyle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Consumes data to minimize memory usage
|
// Consumes data to minimize memory usage
|
||||||
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const int viewportWidth,
|
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const uint16_t viewportWidth,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
||||||
const bool includeLastLine) {
|
const bool includeLastLine) {
|
||||||
if (words.empty()) {
|
if (words.empty()) {
|
||||||
@@ -188,7 +188,7 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
|
|||||||
// *** CRITICAL STEP: CONSUME DATA USING SPLICE ***
|
// *** CRITICAL STEP: CONSUME DATA USING SPLICE ***
|
||||||
std::list<std::string> lineWords;
|
std::list<std::string> lineWords;
|
||||||
lineWords.splice(lineWords.begin(), words, words.begin(), wordEndIt);
|
lineWords.splice(lineWords.begin(), words, words.begin(), wordEndIt);
|
||||||
std::list<EpdFontStyle> lineWordStyles;
|
std::list<EpdFontFamily::Style> lineWordStyles;
|
||||||
lineWordStyles.splice(lineWordStyles.begin(), wordStyles, wordStyles.begin(), wordStyleEndIt);
|
lineWordStyles.splice(lineWordStyles.begin(), wordStyles, wordStyles.begin(), wordStyleEndIt);
|
||||||
|
|
||||||
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
|
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class GfxRenderer;
|
|||||||
|
|
||||||
class ParsedText {
|
class ParsedText {
|
||||||
std::list<std::string> words;
|
std::list<std::string> words;
|
||||||
std::list<EpdFontStyle> wordStyles;
|
std::list<EpdFontFamily::Style> wordStyles;
|
||||||
TextBlock::BLOCK_STYLE style;
|
TextBlock::Style style;
|
||||||
bool extraParagraphSpacing;
|
bool extraParagraphSpacing;
|
||||||
|
|
||||||
std::vector<size_t> computeLineBreaks(int pageWidth, int spaceWidth, const std::vector<uint16_t>& wordWidths) const;
|
std::vector<size_t> computeLineBreaks(int pageWidth, int spaceWidth, const std::vector<uint16_t>& wordWidths) const;
|
||||||
@@ -25,16 +25,16 @@ class ParsedText {
|
|||||||
std::vector<uint16_t> calculateWordWidths(const GfxRenderer& renderer, int fontId);
|
std::vector<uint16_t> calculateWordWidths(const GfxRenderer& renderer, int fontId);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ParsedText(const TextBlock::BLOCK_STYLE style, const bool extraParagraphSpacing)
|
explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing)
|
||||||
: style(style), extraParagraphSpacing(extraParagraphSpacing) {}
|
: style(style), extraParagraphSpacing(extraParagraphSpacing) {}
|
||||||
~ParsedText() = default;
|
~ParsedText() = default;
|
||||||
|
|
||||||
void addWord(std::string word, EpdFontStyle fontStyle);
|
void addWord(std::string word, EpdFontFamily::Style fontStyle);
|
||||||
void setStyle(const TextBlock::BLOCK_STYLE style) { this->style = style; }
|
void setStyle(const TextBlock::Style style) { this->style = style; }
|
||||||
TextBlock::BLOCK_STYLE getStyle() const { return style; }
|
TextBlock::Style getStyle() const { return style; }
|
||||||
size_t size() const { return words.size(); }
|
size_t size() const { return words.size(); }
|
||||||
bool isEmpty() const { return words.empty(); }
|
bool isEmpty() const { return words.empty(); }
|
||||||
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, int viewportWidth,
|
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
||||||
bool includeLastLine = true);
|
bool includeLastLine = true);
|
||||||
};
|
};
|
||||||
|
|||||||
+22
-14
@@ -7,9 +7,9 @@
|
|||||||
#include "parsers/ChapterHtmlSlimParser.h"
|
#include "parsers/ChapterHtmlSlimParser.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SECTION_FILE_VERSION = 7;
|
constexpr uint8_t SECTION_FILE_VERSION = 9;
|
||||||
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) +
|
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
|
||||||
sizeof(int) + sizeof(int) + sizeof(uint32_t);
|
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
|
uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
|
||||||
@@ -30,19 +30,21 @@ uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Section::writeSectionFileHeader(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
void Section::writeSectionFileHeader(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||||
const int viewportWidth, const int viewportHeight) {
|
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||||
|
const uint16_t viewportHeight) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.printf("[%lu] [SCT] File not open for writing header\n", millis());
|
Serial.printf("[%lu] [SCT] File not open for writing header\n", millis());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) +
|
static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) +
|
||||||
sizeof(extraParagraphSpacing) + sizeof(viewportWidth) + sizeof(viewportHeight) +
|
sizeof(extraParagraphSpacing) + sizeof(paragraphAlignment) + sizeof(viewportWidth) +
|
||||||
sizeof(pageCount) + sizeof(uint32_t),
|
sizeof(viewportHeight) + sizeof(pageCount) + sizeof(uint32_t),
|
||||||
"Header size mismatch");
|
"Header size mismatch");
|
||||||
serialization::writePod(file, SECTION_FILE_VERSION);
|
serialization::writePod(file, SECTION_FILE_VERSION);
|
||||||
serialization::writePod(file, fontId);
|
serialization::writePod(file, fontId);
|
||||||
serialization::writePod(file, lineCompression);
|
serialization::writePod(file, lineCompression);
|
||||||
serialization::writePod(file, extraParagraphSpacing);
|
serialization::writePod(file, extraParagraphSpacing);
|
||||||
|
serialization::writePod(file, paragraphAlignment);
|
||||||
serialization::writePod(file, viewportWidth);
|
serialization::writePod(file, viewportWidth);
|
||||||
serialization::writePod(file, viewportHeight);
|
serialization::writePod(file, viewportHeight);
|
||||||
serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written)
|
serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written)
|
||||||
@@ -50,7 +52,8 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||||
const int viewportWidth, const int viewportHeight) {
|
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||||
|
const uint16_t viewportHeight) {
|
||||||
if (!SdMan.openFileForRead("SCT", filePath, file)) {
|
if (!SdMan.openFileForRead("SCT", filePath, file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -66,18 +69,21 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fileFontId, fileViewportWidth, fileViewportHeight;
|
int fileFontId;
|
||||||
|
uint16_t fileViewportWidth, fileViewportHeight;
|
||||||
float fileLineCompression;
|
float fileLineCompression;
|
||||||
bool fileExtraParagraphSpacing;
|
bool fileExtraParagraphSpacing;
|
||||||
|
uint8_t fileParagraphAlignment;
|
||||||
serialization::readPod(file, fileFontId);
|
serialization::readPod(file, fileFontId);
|
||||||
serialization::readPod(file, fileLineCompression);
|
serialization::readPod(file, fileLineCompression);
|
||||||
serialization::readPod(file, fileExtraParagraphSpacing);
|
serialization::readPod(file, fileExtraParagraphSpacing);
|
||||||
|
serialization::readPod(file, fileParagraphAlignment);
|
||||||
serialization::readPod(file, fileViewportWidth);
|
serialization::readPod(file, fileViewportWidth);
|
||||||
serialization::readPod(file, fileViewportHeight);
|
serialization::readPod(file, fileViewportHeight);
|
||||||
|
|
||||||
if (fontId != fileFontId || lineCompression != fileLineCompression ||
|
if (fontId != fileFontId || lineCompression != fileLineCompression ||
|
||||||
extraParagraphSpacing != fileExtraParagraphSpacing || viewportWidth != fileViewportWidth ||
|
extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment ||
|
||||||
viewportHeight != fileViewportHeight) {
|
viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight) {
|
||||||
file.close();
|
file.close();
|
||||||
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
|
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
|
||||||
clearCache();
|
clearCache();
|
||||||
@@ -108,8 +114,8 @@ bool Section::clearCache() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||||
const int viewportWidth, const int viewportHeight,
|
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||||
const std::function<void()>& progressSetupFn,
|
const uint16_t viewportHeight, const std::function<void()>& progressSetupFn,
|
||||||
const std::function<void(int)>& progressFn) {
|
const std::function<void(int)>& progressFn) {
|
||||||
constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
||||||
const auto localPath = epub->getSpineItem(spineIndex).href;
|
const auto localPath = epub->getSpineItem(spineIndex).href;
|
||||||
@@ -165,11 +171,13 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
|||||||
if (!SdMan.openFileForWrite("SCT", filePath, file)) {
|
if (!SdMan.openFileForWrite("SCT", filePath, file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight);
|
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
|
||||||
|
viewportHeight);
|
||||||
std::vector<uint32_t> lut = {};
|
std::vector<uint32_t> lut = {};
|
||||||
|
|
||||||
ChapterHtmlSlimParser visitor(
|
ChapterHtmlSlimParser visitor(
|
||||||
tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight,
|
tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
|
||||||
|
viewportHeight,
|
||||||
[this, &lut](std::unique_ptr<Page> page) { lut.emplace_back(this->onPageComplete(std::move(page))); },
|
[this, &lut](std::unique_ptr<Page> page) { lut.emplace_back(this->onPageComplete(std::move(page))); },
|
||||||
progressFn);
|
progressFn);
|
||||||
success = visitor.parseAndBuildPages();
|
success = visitor.parseAndBuildPages();
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ class Section {
|
|||||||
std::string filePath;
|
std::string filePath;
|
||||||
FsFile file;
|
FsFile file;
|
||||||
|
|
||||||
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||||
int viewportHeight);
|
uint16_t viewportWidth, uint16_t viewportHeight);
|
||||||
uint32_t onPageComplete(std::unique_ptr<Page> page);
|
uint32_t onPageComplete(std::unique_ptr<Page> page);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int pageCount = 0;
|
uint16_t pageCount = 0;
|
||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
|
|
||||||
explicit Section(const std::shared_ptr<Epub>& epub, const int spineIndex, GfxRenderer& renderer)
|
explicit Section(const std::shared_ptr<Epub>& epub, const int spineIndex, GfxRenderer& renderer)
|
||||||
@@ -28,11 +28,12 @@ class Section {
|
|||||||
renderer(renderer),
|
renderer(renderer),
|
||||||
filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
|
filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
|
||||||
~Section() = default;
|
~Section() = default;
|
||||||
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||||
int viewportHeight);
|
uint16_t viewportWidth, uint16_t viewportHeight);
|
||||||
bool clearCache() const;
|
bool clearCache() const;
|
||||||
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||||
int viewportHeight, const std::function<void()>& progressSetupFn = nullptr,
|
uint16_t viewportWidth, uint16_t viewportHeight,
|
||||||
|
const std::function<void()>& progressSetupFn = nullptr,
|
||||||
const std::function<void(int)>& progressFn = nullptr);
|
const std::function<void(int)>& progressFn = nullptr);
|
||||||
std::unique_ptr<Page> loadPageFromSectionFile();
|
std::unique_ptr<Page> loadPageFromSectionFile();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ bool TextBlock::serialize(FsFile& file) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Word data
|
// Word data
|
||||||
serialization::writePod(file, static_cast<uint32_t>(words.size()));
|
serialization::writePod(file, static_cast<uint16_t>(words.size()));
|
||||||
for (const auto& w : words) serialization::writeString(file, w);
|
for (const auto& w : words) serialization::writeString(file, w);
|
||||||
for (auto x : wordXpos) serialization::writePod(file, x);
|
for (auto x : wordXpos) serialization::writePod(file, x);
|
||||||
for (auto s : wordStyles) serialization::writePod(file, s);
|
for (auto s : wordStyles) serialization::writePod(file, s);
|
||||||
@@ -44,11 +44,11 @@ bool TextBlock::serialize(FsFile& file) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
|
std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
|
||||||
uint32_t wc;
|
uint16_t wc;
|
||||||
std::list<std::string> words;
|
std::list<std::string> words;
|
||||||
std::list<uint16_t> wordXpos;
|
std::list<uint16_t> wordXpos;
|
||||||
std::list<EpdFontStyle> wordStyles;
|
std::list<EpdFontFamily::Style> wordStyles;
|
||||||
BLOCK_STYLE style;
|
Style style;
|
||||||
|
|
||||||
// Word count
|
// Word count
|
||||||
serialization::readPod(file, wc);
|
serialization::readPod(file, wc);
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
#include "Block.h"
|
#include "Block.h"
|
||||||
|
|
||||||
// represents a block of words in the html document
|
// Represents a line of text on a page
|
||||||
class TextBlock final : public Block {
|
class TextBlock final : public Block {
|
||||||
public:
|
public:
|
||||||
enum BLOCK_STYLE : uint8_t {
|
enum Style : uint8_t {
|
||||||
JUSTIFIED = 0,
|
JUSTIFIED = 0,
|
||||||
LEFT_ALIGN = 1,
|
LEFT_ALIGN = 1,
|
||||||
CENTER_ALIGN = 2,
|
CENTER_ALIGN = 2,
|
||||||
@@ -21,16 +21,16 @@ class TextBlock final : public Block {
|
|||||||
private:
|
private:
|
||||||
std::list<std::string> words;
|
std::list<std::string> words;
|
||||||
std::list<uint16_t> wordXpos;
|
std::list<uint16_t> wordXpos;
|
||||||
std::list<EpdFontStyle> wordStyles;
|
std::list<EpdFontFamily::Style> wordStyles;
|
||||||
BLOCK_STYLE style;
|
Style style;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TextBlock(std::list<std::string> words, std::list<uint16_t> word_xpos, std::list<EpdFontStyle> word_styles,
|
explicit TextBlock(std::list<std::string> words, std::list<uint16_t> word_xpos,
|
||||||
const BLOCK_STYLE style)
|
std::list<EpdFontFamily::Style> word_styles, const Style style)
|
||||||
: words(std::move(words)), wordXpos(std::move(word_xpos)), wordStyles(std::move(word_styles)), style(style) {}
|
: words(std::move(words)), wordXpos(std::move(word_xpos)), wordStyles(std::move(word_styles)), style(style) {}
|
||||||
~TextBlock() override = default;
|
~TextBlock() override = default;
|
||||||
void setStyle(const BLOCK_STYLE style) { this->style = style; }
|
void setStyle(const Style style) { this->style = style; }
|
||||||
BLOCK_STYLE getStyle() const { return style; }
|
Style getStyle() const { return style; }
|
||||||
bool isEmpty() override { return words.empty(); }
|
bool isEmpty() override { return words.empty(); }
|
||||||
void layout(GfxRenderer& renderer) override {};
|
void layout(GfxRenderer& renderer) override {};
|
||||||
// given a renderer works out where to break the words into lines
|
// given a renderer works out where to break the words into lines
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ bool matches(const char* tag_name, const char* possible_tags[], const int possib
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start a new text block if needed
|
// start a new text block if needed
|
||||||
void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::BLOCK_STYLE style) {
|
void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) {
|
||||||
if (currentTextBlock) {
|
if (currentTextBlock) {
|
||||||
// already have a text block running and it is empty - just reuse it
|
// already have a text block running and it is empty - just reuse it
|
||||||
if (currentTextBlock->isEmpty()) {
|
if (currentTextBlock->isEmpty()) {
|
||||||
@@ -97,7 +97,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
|||||||
if (strcmp(name, "br") == 0) {
|
if (strcmp(name, "br") == 0) {
|
||||||
self->startNewTextBlock(self->currentTextBlock->getStyle());
|
self->startNewTextBlock(self->currentTextBlock->getStyle());
|
||||||
} else {
|
} else {
|
||||||
self->startNewTextBlock(TextBlock::JUSTIFIED);
|
self->startNewTextBlock((TextBlock::Style)self->paragraphAlignment);
|
||||||
}
|
}
|
||||||
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
|
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
|
||||||
self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth);
|
self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth);
|
||||||
@@ -116,13 +116,13 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EpdFontStyle fontStyle = REGULAR;
|
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
|
||||||
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
||||||
fontStyle = BOLD_ITALIC;
|
fontStyle = EpdFontFamily::BOLD_ITALIC;
|
||||||
} else if (self->boldUntilDepth < self->depth) {
|
} else if (self->boldUntilDepth < self->depth) {
|
||||||
fontStyle = BOLD;
|
fontStyle = EpdFontFamily::BOLD;
|
||||||
} else if (self->italicUntilDepth < self->depth) {
|
} else if (self->italicUntilDepth < self->depth) {
|
||||||
fontStyle = ITALIC;
|
fontStyle = EpdFontFamily::ITALIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
@@ -137,6 +137,21 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip soft-hyphen with UTF-8 representation (U+00AD) = 0xC2 0xAD
|
||||||
|
const XML_Char SHY_BYTE_1 = static_cast<XML_Char>(0xC2);
|
||||||
|
const XML_Char SHY_BYTE_2 = static_cast<XML_Char>(0xAD);
|
||||||
|
// 1. Check for the start of the 2-byte Soft Hyphen sequence
|
||||||
|
if (s[i] == SHY_BYTE_1) {
|
||||||
|
// 2. Check if the next byte exists AND if it completes the sequence
|
||||||
|
// We must check i + 1 < len to prevent reading past the end of the buffer.
|
||||||
|
if ((i + 1 < len) && (s[i + 1] == SHY_BYTE_2)) {
|
||||||
|
// Sequence 0xC2 0xAD found!
|
||||||
|
// Skip the current byte (0xC2) and the next byte (0xAD)
|
||||||
|
i++; // Increment 'i' one more time to skip the 0xAD byte
|
||||||
|
continue; // Skip the rest of the loop and move to the next iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we're about to run out of space, then cut the word off and start a new one
|
// If we're about to run out of space, then cut the word off and start a new one
|
||||||
if (self->partWordBufferIndex >= MAX_WORD_SIZE) {
|
if (self->partWordBufferIndex >= MAX_WORD_SIZE) {
|
||||||
self->partWordBuffer[self->partWordBufferIndex] = '\0';
|
self->partWordBuffer[self->partWordBufferIndex] = '\0';
|
||||||
@@ -172,13 +187,13 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
|||||||
matches(name, BOLD_TAGS, NUM_BOLD_TAGS) || matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS) || self->depth == 1;
|
matches(name, BOLD_TAGS, NUM_BOLD_TAGS) || matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS) || self->depth == 1;
|
||||||
|
|
||||||
if (shouldBreakText) {
|
if (shouldBreakText) {
|
||||||
EpdFontStyle fontStyle = REGULAR;
|
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
|
||||||
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
||||||
fontStyle = BOLD_ITALIC;
|
fontStyle = EpdFontFamily::BOLD_ITALIC;
|
||||||
} else if (self->boldUntilDepth < self->depth) {
|
} else if (self->boldUntilDepth < self->depth) {
|
||||||
fontStyle = BOLD;
|
fontStyle = EpdFontFamily::BOLD;
|
||||||
} else if (self->italicUntilDepth < self->depth) {
|
} else if (self->italicUntilDepth < self->depth) {
|
||||||
fontStyle = ITALIC;
|
fontStyle = EpdFontFamily::ITALIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->partWordBuffer[self->partWordBufferIndex] = '\0';
|
self->partWordBuffer[self->partWordBufferIndex] = '\0';
|
||||||
@@ -206,7 +221,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||||
startNewTextBlock(TextBlock::JUSTIFIED);
|
startNewTextBlock((TextBlock::Style)this->paragraphAlignment);
|
||||||
|
|
||||||
const XML_Parser parser = XML_ParserCreate(nullptr);
|
const XML_Parser parser = XML_ParserCreate(nullptr);
|
||||||
int done;
|
int done;
|
||||||
|
|||||||
@@ -33,10 +33,11 @@ class ChapterHtmlSlimParser {
|
|||||||
int fontId;
|
int fontId;
|
||||||
float lineCompression;
|
float lineCompression;
|
||||||
bool extraParagraphSpacing;
|
bool extraParagraphSpacing;
|
||||||
int viewportWidth;
|
uint8_t paragraphAlignment;
|
||||||
int viewportHeight;
|
uint16_t viewportWidth;
|
||||||
|
uint16_t viewportHeight;
|
||||||
|
|
||||||
void startNewTextBlock(TextBlock::BLOCK_STYLE style);
|
void startNewTextBlock(TextBlock::Style style);
|
||||||
void makePages();
|
void makePages();
|
||||||
// XML callbacks
|
// XML callbacks
|
||||||
static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts);
|
static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts);
|
||||||
@@ -45,8 +46,9 @@ class ChapterHtmlSlimParser {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
|
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
|
||||||
const float lineCompression, const bool extraParagraphSpacing, const int viewportWidth,
|
const float lineCompression, const bool extraParagraphSpacing,
|
||||||
const int viewportHeight,
|
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||||
|
const uint16_t viewportHeight,
|
||||||
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
|
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
|
||||||
const std::function<void(int)>& progressFn = nullptr)
|
const std::function<void(int)>& progressFn = nullptr)
|
||||||
: filepath(filepath),
|
: filepath(filepath),
|
||||||
@@ -54,6 +56,7 @@ class ChapterHtmlSlimParser {
|
|||||||
fontId(fontId),
|
fontId(fontId),
|
||||||
lineCompression(lineCompression),
|
lineCompression(lineCompression),
|
||||||
extraParagraphSpacing(extraParagraphSpacing),
|
extraParagraphSpacing(extraParagraphSpacing),
|
||||||
|
paragraphAlignment(paragraphAlignment),
|
||||||
viewportWidth(viewportWidth),
|
viewportWidth(viewportWidth),
|
||||||
viewportHeight(viewportHeight),
|
viewportHeight(viewportHeight),
|
||||||
completePageFn(completePageFn),
|
completePageFn(completePageFn),
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
std::string itemId;
|
std::string itemId;
|
||||||
std::string href;
|
std::string href;
|
||||||
std::string mediaType;
|
std::string mediaType;
|
||||||
|
std::string properties;
|
||||||
|
|
||||||
for (int i = 0; atts[i]; i += 2) {
|
for (int i = 0; atts[i]; i += 2) {
|
||||||
if (strcmp(atts[i], "id") == 0) {
|
if (strcmp(atts[i], "id") == 0) {
|
||||||
@@ -169,6 +170,8 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
href = self->baseContentPath + atts[i + 1];
|
href = self->baseContentPath + atts[i + 1];
|
||||||
} else if (strcmp(atts[i], "media-type") == 0) {
|
} else if (strcmp(atts[i], "media-type") == 0) {
|
||||||
mediaType = atts[i + 1];
|
mediaType = atts[i + 1];
|
||||||
|
} else if (strcmp(atts[i], "properties") == 0) {
|
||||||
|
properties = atts[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +191,15 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
href.c_str());
|
href.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EPUB 3: Check for nav document (properties contains "nav")
|
||||||
|
if (!properties.empty() && self->tocNavPath.empty()) {
|
||||||
|
// Properties is space-separated, check if "nav" is present as a word
|
||||||
|
if (properties == "nav" || properties.find("nav ") == 0 || properties.find(" nav") != std::string::npos) {
|
||||||
|
self->tocNavPath = href;
|
||||||
|
Serial.printf("[%lu] [COF] Found EPUB 3 nav document: %s\n", millis(), href.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class ContentOpfParser final : public Print {
|
|||||||
std::string title;
|
std::string title;
|
||||||
std::string author;
|
std::string author;
|
||||||
std::string tocNcxPath;
|
std::string tocNcxPath;
|
||||||
|
std::string tocNavPath; // EPUB 3 nav document path
|
||||||
std::string coverItemHref;
|
std::string coverItemHref;
|
||||||
std::string textReferenceHref;
|
std::string textReferenceHref;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#include "TocNavParser.h"
|
||||||
|
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
#include "../BookMetadataCache.h"
|
||||||
|
|
||||||
|
bool TocNavParser::setup() {
|
||||||
|
parser = XML_ParserCreate(nullptr);
|
||||||
|
if (!parser) {
|
||||||
|
Serial.printf("[%lu] [NAV] Couldn't allocate memory for parser\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
XML_SetUserData(parser, this);
|
||||||
|
XML_SetElementHandler(parser, startElement, endElement);
|
||||||
|
XML_SetCharacterDataHandler(parser, characterData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TocNavParser::~TocNavParser() {
|
||||||
|
if (parser) {
|
||||||
|
XML_StopParser(parser, XML_FALSE);
|
||||||
|
XML_SetElementHandler(parser, nullptr, nullptr);
|
||||||
|
XML_SetCharacterDataHandler(parser, nullptr);
|
||||||
|
XML_ParserFree(parser);
|
||||||
|
parser = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TocNavParser::write(const uint8_t data) { return write(&data, 1); }
|
||||||
|
|
||||||
|
size_t TocNavParser::write(const uint8_t* buffer, const size_t size) {
|
||||||
|
if (!parser) return 0;
|
||||||
|
|
||||||
|
const uint8_t* currentBufferPos = buffer;
|
||||||
|
auto remainingInBuffer = size;
|
||||||
|
|
||||||
|
while (remainingInBuffer > 0) {
|
||||||
|
void* const buf = XML_GetBuffer(parser, 1024);
|
||||||
|
if (!buf) {
|
||||||
|
Serial.printf("[%lu] [NAV] Couldn't allocate memory for buffer\n", millis());
|
||||||
|
XML_StopParser(parser, XML_FALSE);
|
||||||
|
XML_SetElementHandler(parser, nullptr, nullptr);
|
||||||
|
XML_SetCharacterDataHandler(parser, nullptr);
|
||||||
|
XML_ParserFree(parser);
|
||||||
|
parser = nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto toRead = remainingInBuffer < 1024 ? remainingInBuffer : 1024;
|
||||||
|
memcpy(buf, currentBufferPos, toRead);
|
||||||
|
|
||||||
|
if (XML_ParseBuffer(parser, static_cast<int>(toRead), remainingSize == toRead) == XML_STATUS_ERROR) {
|
||||||
|
Serial.printf("[%lu] [NAV] Parse error at line %lu: %s\n", millis(), XML_GetCurrentLineNumber(parser),
|
||||||
|
XML_ErrorString(XML_GetErrorCode(parser)));
|
||||||
|
XML_StopParser(parser, XML_FALSE);
|
||||||
|
XML_SetElementHandler(parser, nullptr, nullptr);
|
||||||
|
XML_SetCharacterDataHandler(parser, nullptr);
|
||||||
|
XML_ParserFree(parser);
|
||||||
|
parser = nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBufferPos += toRead;
|
||||||
|
remainingInBuffer -= toRead;
|
||||||
|
remainingSize -= toRead;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XMLCALL TocNavParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
|
||||||
|
auto* self = static_cast<TocNavParser*>(userData);
|
||||||
|
|
||||||
|
// Track HTML structure loosely - we mainly care about finding <nav epub:type="toc">
|
||||||
|
if (strcmp(name, "html") == 0) {
|
||||||
|
self->state = IN_HTML;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_HTML && strcmp(name, "body") == 0) {
|
||||||
|
self->state = IN_BODY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for <nav epub:type="toc"> anywhere in body (or nested elements)
|
||||||
|
if (self->state >= IN_BODY && strcmp(name, "nav") == 0) {
|
||||||
|
for (int i = 0; atts[i]; i += 2) {
|
||||||
|
if ((strcmp(atts[i], "epub:type") == 0 || strcmp(atts[i], "type") == 0) && strcmp(atts[i + 1], "toc") == 0) {
|
||||||
|
self->state = IN_NAV_TOC;
|
||||||
|
Serial.printf("[%lu] [NAV] Found nav toc element\n", millis());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only process ol/li/a if we're inside the toc nav
|
||||||
|
if (self->state < IN_NAV_TOC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "ol") == 0) {
|
||||||
|
self->olDepth++;
|
||||||
|
self->state = IN_OL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_OL && strcmp(name, "li") == 0) {
|
||||||
|
self->state = IN_LI;
|
||||||
|
self->currentLabel.clear();
|
||||||
|
self->currentHref.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->state == IN_LI && strcmp(name, "a") == 0) {
|
||||||
|
self->state = IN_ANCHOR;
|
||||||
|
// Get href attribute
|
||||||
|
for (int i = 0; atts[i]; i += 2) {
|
||||||
|
if (strcmp(atts[i], "href") == 0) {
|
||||||
|
self->currentHref = atts[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XMLCALL TocNavParser::characterData(void* userData, const XML_Char* s, const int len) {
|
||||||
|
auto* self = static_cast<TocNavParser*>(userData);
|
||||||
|
|
||||||
|
// Only collect text when inside an anchor within the TOC nav
|
||||||
|
if (self->state == IN_ANCHOR) {
|
||||||
|
self->currentLabel.append(s, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XMLCALL TocNavParser::endElement(void* userData, const XML_Char* name) {
|
||||||
|
auto* self = static_cast<TocNavParser*>(userData);
|
||||||
|
|
||||||
|
if (strcmp(name, "a") == 0 && self->state == IN_ANCHOR) {
|
||||||
|
// Create TOC entry when closing anchor tag (we have all data now)
|
||||||
|
if (!self->currentLabel.empty() && !self->currentHref.empty()) {
|
||||||
|
std::string href = self->baseContentPath + self->currentHref;
|
||||||
|
std::string anchor;
|
||||||
|
|
||||||
|
const size_t pos = href.find('#');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
anchor = href.substr(pos + 1);
|
||||||
|
href = href.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->cache) {
|
||||||
|
// olDepth gives us the nesting level (1-based from the outer ol)
|
||||||
|
self->cache->createTocEntry(self->currentLabel, href, anchor, self->olDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->currentLabel.clear();
|
||||||
|
self->currentHref.clear();
|
||||||
|
}
|
||||||
|
self->state = IN_LI;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "li") == 0 && (self->state == IN_LI || self->state == IN_OL)) {
|
||||||
|
self->state = IN_OL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "ol") == 0 && self->state >= IN_NAV_TOC) {
|
||||||
|
self->olDepth--;
|
||||||
|
if (self->olDepth == 0) {
|
||||||
|
self->state = IN_NAV_TOC;
|
||||||
|
} else {
|
||||||
|
self->state = IN_LI; // Back to parent li
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "nav") == 0 && self->state >= IN_NAV_TOC) {
|
||||||
|
self->state = IN_BODY;
|
||||||
|
Serial.printf("[%lu] [NAV] Finished parsing nav toc\n", millis());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Print.h>
|
||||||
|
#include <expat.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class BookMetadataCache;
|
||||||
|
|
||||||
|
// Parser for EPUB 3 nav.xhtml navigation documents
|
||||||
|
// Parses HTML5 nav elements with epub:type="toc" to extract table of contents
|
||||||
|
class TocNavParser final : public Print {
|
||||||
|
enum ParserState {
|
||||||
|
START,
|
||||||
|
IN_HTML,
|
||||||
|
IN_BODY,
|
||||||
|
IN_NAV_TOC, // Inside <nav epub:type="toc">
|
||||||
|
IN_OL, // Inside <ol>
|
||||||
|
IN_LI, // Inside <li>
|
||||||
|
IN_ANCHOR, // Inside <a>
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string& baseContentPath;
|
||||||
|
size_t remainingSize;
|
||||||
|
XML_Parser parser = nullptr;
|
||||||
|
ParserState state = START;
|
||||||
|
BookMetadataCache* cache;
|
||||||
|
|
||||||
|
// Track nesting depth for <ol> elements to determine TOC depth
|
||||||
|
uint8_t olDepth = 0;
|
||||||
|
// Current entry data being collected
|
||||||
|
std::string currentLabel;
|
||||||
|
std::string currentHref;
|
||||||
|
|
||||||
|
static void startElement(void* userData, const XML_Char* name, const XML_Char** atts);
|
||||||
|
static void characterData(void* userData, const XML_Char* s, int len);
|
||||||
|
static void endElement(void* userData, const XML_Char* name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TocNavParser(const std::string& baseContentPath, const size_t xmlSize, BookMetadataCache* cache)
|
||||||
|
: baseContentPath(baseContentPath), remainingSize(xmlSize), cache(cache) {}
|
||||||
|
~TocNavParser() override;
|
||||||
|
|
||||||
|
bool setup();
|
||||||
|
|
||||||
|
size_t write(uint8_t) override;
|
||||||
|
size_t write(const uint8_t* buffer, size_t size) override;
|
||||||
|
};
|
||||||
@@ -66,7 +66,7 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontStyle style) const {
|
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontFamily::Style style) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
if (fontMap.count(fontId) == 0) {
|
||||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -78,13 +78,13 @@ int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontS
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawCenteredText(const int fontId, const int y, const char* text, const bool black,
|
void GfxRenderer::drawCenteredText(const int fontId, const int y, const char* text, const bool black,
|
||||||
const EpdFontStyle style) const {
|
const EpdFontFamily::Style style) const {
|
||||||
const int x = (getScreenWidth() - getTextWidth(fontId, text, style)) / 2;
|
const int x = (getScreenWidth() - getTextWidth(fontId, text, style)) / 2;
|
||||||
drawText(fontId, x, y, text, black, style);
|
drawText(fontId, x, y, text, black, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawText(const int fontId, const int x, const int y, const char* text, const bool black,
|
void GfxRenderer::drawText(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||||
const EpdFontStyle style) const {
|
const EpdFontFamily::Style style) const {
|
||||||
const int yPos = y + getFontAscenderSize(fontId);
|
const int yPos = y + getFontAscenderSize(fontId);
|
||||||
int xpos = x;
|
int xpos = x;
|
||||||
|
|
||||||
@@ -238,6 +238,17 @@ void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) cons
|
|||||||
einkDisplay.displayBuffer(refreshMode);
|
einkDisplay.displayBuffer(refreshMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth,
|
||||||
|
const EpdFontFamily::Style style) const {
|
||||||
|
std::string item = text;
|
||||||
|
int itemWidth = getTextWidth(fontId, item.c_str(), style);
|
||||||
|
while (itemWidth > maxWidth && item.length() > 8) {
|
||||||
|
item.replace(item.length() - 5, 5, "...");
|
||||||
|
itemWidth = getTextWidth(fontId, item.c_str(), style);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Internal driver treats screen in command orientation; this library exposes a logical orientation
|
// Note: Internal driver treats screen in command orientation; this library exposes a logical orientation
|
||||||
int GfxRenderer::getScreenWidth() const {
|
int GfxRenderer::getScreenWidth() const {
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
@@ -273,7 +284,7 @@ int GfxRenderer::getSpaceWidth(const int fontId) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getGlyph(' ', REGULAR)->advanceX;
|
return fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
||||||
@@ -282,7 +293,7 @@ int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getData(REGULAR)->ascender;
|
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getLineHeight(const int fontId) const {
|
int GfxRenderer::getLineHeight(const int fontId) const {
|
||||||
@@ -291,7 +302,7 @@ int GfxRenderer::getLineHeight(const int fontId) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getData(REGULAR)->advanceY;
|
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->advanceY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3,
|
void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3,
|
||||||
@@ -316,6 +327,148 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) const {
|
||||||
|
const int screenWidth = getScreenWidth();
|
||||||
|
constexpr int buttonWidth = 40; // Width on screen (height when rotated)
|
||||||
|
constexpr int buttonHeight = 80; // Height on screen (width when rotated)
|
||||||
|
constexpr int buttonX = 5; // Distance from right edge
|
||||||
|
// Position for the button group - buttons share a border so they're adjacent
|
||||||
|
constexpr int topButtonY = 345; // Top button position
|
||||||
|
|
||||||
|
const char* labels[] = {topBtn, bottomBtn};
|
||||||
|
|
||||||
|
// Draw the shared border for both buttons as one unit
|
||||||
|
const int x = screenWidth - buttonX - buttonWidth;
|
||||||
|
|
||||||
|
// Draw top button outline (3 sides, bottom open)
|
||||||
|
if (topBtn != nullptr && topBtn[0] != '\0') {
|
||||||
|
drawLine(x, topButtonY, x + buttonWidth - 1, topButtonY); // Top
|
||||||
|
drawLine(x, topButtonY, x, topButtonY + buttonHeight - 1); // Left
|
||||||
|
drawLine(x + buttonWidth - 1, topButtonY, x + buttonWidth - 1, topButtonY + buttonHeight - 1); // Right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw shared middle border
|
||||||
|
if ((topBtn != nullptr && topBtn[0] != '\0') || (bottomBtn != nullptr && bottomBtn[0] != '\0')) {
|
||||||
|
drawLine(x, topButtonY + buttonHeight, x + buttonWidth - 1, topButtonY + buttonHeight); // Shared border
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw bottom button outline (3 sides, top is shared)
|
||||||
|
if (bottomBtn != nullptr && bottomBtn[0] != '\0') {
|
||||||
|
drawLine(x, topButtonY + buttonHeight, x, topButtonY + 2 * buttonHeight - 1); // Left
|
||||||
|
drawLine(x + buttonWidth - 1, topButtonY + buttonHeight, x + buttonWidth - 1,
|
||||||
|
topButtonY + 2 * buttonHeight - 1); // Right
|
||||||
|
drawLine(x, topButtonY + 2 * buttonHeight - 1, x + buttonWidth - 1, topButtonY + 2 * buttonHeight - 1); // Bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text for each button
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (labels[i] != nullptr && labels[i][0] != '\0') {
|
||||||
|
const int y = topButtonY + i * buttonHeight;
|
||||||
|
|
||||||
|
// Draw rotated text centered in the button
|
||||||
|
const int textWidth = getTextWidth(fontId, labels[i]);
|
||||||
|
const int textHeight = getTextHeight(fontId);
|
||||||
|
|
||||||
|
// Center the rotated text in the button
|
||||||
|
const int textX = x + (buttonWidth - textHeight) / 2;
|
||||||
|
const int textY = y + (buttonHeight + textWidth) / 2;
|
||||||
|
|
||||||
|
drawTextRotated90CW(fontId, textX, textY, labels[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GfxRenderer::getTextHeight(const int fontId) const {
|
||||||
|
if (fontMap.count(fontId) == 0) {
|
||||||
|
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||||
|
const EpdFontFamily::Style style) const {
|
||||||
|
// Cannot draw a NULL / empty string
|
||||||
|
if (text == nullptr || *text == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontMap.count(fontId) == 0) {
|
||||||
|
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto font = fontMap.at(fontId);
|
||||||
|
|
||||||
|
// No printable characters
|
||||||
|
if (!font.hasPrintableChars(text, style)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For 90° clockwise rotation:
|
||||||
|
// Original (glyphX, glyphY) -> Rotated (glyphY, -glyphX)
|
||||||
|
// Text reads from bottom to top
|
||||||
|
|
||||||
|
int yPos = y; // Current Y position (decreases as we draw characters)
|
||||||
|
|
||||||
|
uint32_t cp;
|
||||||
|
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
|
||||||
|
const EpdGlyph* glyph = font.getGlyph(cp, style);
|
||||||
|
if (!glyph) {
|
||||||
|
glyph = font.getGlyph('?', style);
|
||||||
|
}
|
||||||
|
if (!glyph) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int is2Bit = font.getData(style)->is2Bit;
|
||||||
|
const uint32_t offset = glyph->dataOffset;
|
||||||
|
const uint8_t width = glyph->width;
|
||||||
|
const uint8_t height = glyph->height;
|
||||||
|
const int left = glyph->left;
|
||||||
|
const int top = glyph->top;
|
||||||
|
|
||||||
|
const uint8_t* bitmap = &font.getData(style)->bitmap[offset];
|
||||||
|
|
||||||
|
if (bitmap != nullptr) {
|
||||||
|
for (int glyphY = 0; glyphY < height; glyphY++) {
|
||||||
|
for (int glyphX = 0; glyphX < width; glyphX++) {
|
||||||
|
const int pixelPosition = glyphY * width + glyphX;
|
||||||
|
|
||||||
|
// 90° clockwise rotation transformation:
|
||||||
|
// screenX = x + (ascender - top + glyphY)
|
||||||
|
// screenY = yPos - (left + glyphX)
|
||||||
|
const int screenX = x + (font.getData(style)->ascender - top + glyphY);
|
||||||
|
const int screenY = yPos - left - glyphX;
|
||||||
|
|
||||||
|
if (is2Bit) {
|
||||||
|
const uint8_t byte = bitmap[pixelPosition / 4];
|
||||||
|
const uint8_t bit_index = (3 - pixelPosition % 4) * 2;
|
||||||
|
const uint8_t bmpVal = 3 - (byte >> bit_index) & 0x3;
|
||||||
|
|
||||||
|
if (renderMode == BW && bmpVal < 3) {
|
||||||
|
drawPixel(screenX, screenY, black);
|
||||||
|
} else if (renderMode == GRAYSCALE_MSB && (bmpVal == 1 || bmpVal == 2)) {
|
||||||
|
drawPixel(screenX, screenY, false);
|
||||||
|
} else if (renderMode == GRAYSCALE_LSB && bmpVal == 1) {
|
||||||
|
drawPixel(screenX, screenY, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uint8_t byte = bitmap[pixelPosition / 8];
|
||||||
|
const uint8_t bit_index = 7 - (pixelPosition % 8);
|
||||||
|
|
||||||
|
if ((byte >> bit_index) & 1) {
|
||||||
|
drawPixel(screenX, screenY, black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to next character position (going up, so decrease Y)
|
||||||
|
yPos -= glyph->advanceX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); }
|
uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); }
|
||||||
|
|
||||||
size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; }
|
size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; }
|
||||||
@@ -436,7 +589,7 @@ void GfxRenderer::cleanupGrayscaleWithFrameBuffer() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
|
void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
|
||||||
const bool pixelState, const EpdFontStyle style) const {
|
const bool pixelState, const EpdFontFamily::Style style) const {
|
||||||
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);
|
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);
|
||||||
if (!glyph) {
|
if (!glyph) {
|
||||||
// TODO: Replace with fallback glyph property?
|
// TODO: Replace with fallback glyph property?
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class GfxRenderer {
|
|||||||
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
|
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
|
||||||
std::map<int, EpdFontFamily> fontMap;
|
std::map<int, EpdFontFamily> fontMap;
|
||||||
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
||||||
EpdFontStyle style) const;
|
EpdFontFamily::Style style) const;
|
||||||
void freeBwBufferChunks();
|
void freeBwBufferChunks();
|
||||||
void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const;
|
void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const;
|
||||||
|
|
||||||
@@ -69,16 +69,28 @@ class GfxRenderer {
|
|||||||
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight) const;
|
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight) const;
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
int getTextWidth(int fontId, const char* text, EpdFontStyle style = REGULAR) const;
|
int getTextWidth(int fontId, const char* text, EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
void drawCenteredText(int fontId, int y, const char* text, bool black = true, EpdFontStyle style = REGULAR) const;
|
void drawCenteredText(int fontId, int y, const char* text, bool black = true,
|
||||||
void drawText(int fontId, int x, int y, const char* text, bool black = true, EpdFontStyle style = REGULAR) const;
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
|
void drawText(int fontId, int x, int y, const char* text, bool black = true,
|
||||||
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
int getSpaceWidth(int fontId) const;
|
int getSpaceWidth(int fontId) const;
|
||||||
int getFontAscenderSize(int fontId) const;
|
int getFontAscenderSize(int fontId) const;
|
||||||
int getLineHeight(int fontId) const;
|
int getLineHeight(int fontId) const;
|
||||||
|
std::string truncatedText(int fontId, const char* text, int maxWidth,
|
||||||
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
|
|
||||||
// UI Components
|
// UI Components
|
||||||
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const;
|
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const;
|
||||||
|
void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helper for drawing rotated text (90 degrees clockwise, for side buttons)
|
||||||
|
void drawTextRotated90CW(int fontId, int x, int y, const char* text, bool black = true,
|
||||||
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
|
int getTextHeight(int fontId) const;
|
||||||
|
|
||||||
|
public:
|
||||||
// Grayscale functions
|
// Grayscale functions
|
||||||
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
||||||
void copyGrayscaleLsbBuffers() const;
|
void copyGrayscaleLsbBuffers() const;
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,5 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
crosspoint_version = 0.11.0
|
crosspoint_version = 0.12.0
|
||||||
default_envs = default
|
default_envs = default
|
||||||
|
|
||||||
[base]
|
[base]
|
||||||
@@ -26,6 +26,8 @@ build_flags =
|
|||||||
-DXML_GE=0
|
-DXML_GE=0
|
||||||
-DXML_CONTEXT_BYTES=1024
|
-DXML_CONTEXT_BYTES=1024
|
||||||
-std=c++2a
|
-std=c++2a
|
||||||
|
# Enable UTF-8 long file names in SdFat
|
||||||
|
-DUSE_UTF8_LONG_NAMES=1
|
||||||
|
|
||||||
; Board configuration
|
; Board configuration
|
||||||
board_build.flash_mode = dio
|
board_build.flash_mode = dio
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ CrossPointSettings CrossPointSettings::instance;
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 10;
|
constexpr uint8_t SETTINGS_COUNT = 13;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -37,6 +37,9 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, fontFamily);
|
serialization::writePod(outputFile, fontFamily);
|
||||||
serialization::writePod(outputFile, fontSize);
|
serialization::writePod(outputFile, fontSize);
|
||||||
serialization::writePod(outputFile, lineSpacing);
|
serialization::writePod(outputFile, lineSpacing);
|
||||||
|
serialization::writePod(outputFile, paragraphAlignment);
|
||||||
|
serialization::writePod(outputFile, sleepTimeout);
|
||||||
|
serialization::writePod(outputFile, refreshFrequency);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@@ -83,6 +86,12 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
serialization::readPod(inputFile, lineSpacing);
|
serialization::readPod(inputFile, lineSpacing);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, paragraphAlignment);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, sleepTimeout);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, refreshFrequency);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
@@ -92,7 +101,7 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
|
|
||||||
float CrossPointSettings::getReaderLineCompression() const {
|
float CrossPointSettings::getReaderLineCompression() const {
|
||||||
switch (fontFamily) {
|
switch (fontFamily) {
|
||||||
case ALEO:
|
case BOOKERLY:
|
||||||
default:
|
default:
|
||||||
switch (lineSpacing) {
|
switch (lineSpacing) {
|
||||||
case TIGHT:
|
case TIGHT:
|
||||||
@@ -126,20 +135,52 @@ float CrossPointSettings::getReaderLineCompression() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long CrossPointSettings::getSleepTimeoutMs() const {
|
||||||
|
switch (sleepTimeout) {
|
||||||
|
case SLEEP_1_MIN:
|
||||||
|
return 1UL * 60 * 1000;
|
||||||
|
case SLEEP_5_MIN:
|
||||||
|
return 5UL * 60 * 1000;
|
||||||
|
case SLEEP_10_MIN:
|
||||||
|
default:
|
||||||
|
return 10UL * 60 * 1000;
|
||||||
|
case SLEEP_15_MIN:
|
||||||
|
return 15UL * 60 * 1000;
|
||||||
|
case SLEEP_30_MIN:
|
||||||
|
return 30UL * 60 * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CrossPointSettings::getRefreshFrequency() const {
|
||||||
|
switch (refreshFrequency) {
|
||||||
|
case REFRESH_1:
|
||||||
|
return 1;
|
||||||
|
case REFRESH_5:
|
||||||
|
return 5;
|
||||||
|
case REFRESH_10:
|
||||||
|
return 10;
|
||||||
|
case REFRESH_15:
|
||||||
|
default:
|
||||||
|
return 15;
|
||||||
|
case REFRESH_30:
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int CrossPointSettings::getReaderFontId() const {
|
int CrossPointSettings::getReaderFontId() const {
|
||||||
switch (fontFamily) {
|
switch (fontFamily) {
|
||||||
case ALEO:
|
case BOOKERLY:
|
||||||
default:
|
default:
|
||||||
switch (fontSize) {
|
switch (fontSize) {
|
||||||
case SMALL:
|
case SMALL:
|
||||||
return ALEO_12_FONT_ID;
|
return BOOKERLY_12_FONT_ID;
|
||||||
case MEDIUM:
|
case MEDIUM:
|
||||||
default:
|
default:
|
||||||
return ALEO_14_FONT_ID;
|
return BOOKERLY_14_FONT_ID;
|
||||||
case LARGE:
|
case LARGE:
|
||||||
return ALEO_16_FONT_ID;
|
return BOOKERLY_16_FONT_ID;
|
||||||
case EXTRA_LARGE:
|
case EXTRA_LARGE:
|
||||||
return ALEO_18_FONT_ID;
|
return BOOKERLY_18_FONT_ID;
|
||||||
}
|
}
|
||||||
case NOTOSANS:
|
case NOTOSANS:
|
||||||
switch (fontSize) {
|
switch (fontSize) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class CrossPointSettings {
|
|||||||
// Front button layout options
|
// Front button layout options
|
||||||
// Default: Back, Confirm, Left, Right
|
// Default: Back, Confirm, Left, Right
|
||||||
// Swapped: Left, Right, Back, Confirm
|
// Swapped: Left, Right, Back, Confirm
|
||||||
enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1 };
|
enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1, LEFT_BACK_CONFIRM_RIGHT = 2 };
|
||||||
|
|
||||||
// Side button layout options
|
// Side button layout options
|
||||||
// Default: Previous, Next
|
// Default: Previous, Next
|
||||||
@@ -39,10 +39,17 @@ class CrossPointSettings {
|
|||||||
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 };
|
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 };
|
||||||
|
|
||||||
// Font family options
|
// Font family options
|
||||||
enum FONT_FAMILY { ALEO = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
|
enum FONT_FAMILY { BOOKERLY = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
|
||||||
// Font size options
|
// Font size options
|
||||||
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 };
|
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 };
|
||||||
enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2 };
|
enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2 };
|
||||||
|
enum PARAGRAPH_ALIGNMENT { JUSTIFIED = 0, LEFT_ALIGN = 1, CENTER_ALIGN = 2, RIGHT_ALIGN = 3 };
|
||||||
|
|
||||||
|
// Auto-sleep timeout options (in minutes)
|
||||||
|
enum SLEEP_TIMEOUT { SLEEP_1_MIN = 0, SLEEP_5_MIN = 1, SLEEP_10_MIN = 2, SLEEP_15_MIN = 3, SLEEP_30_MIN = 4 };
|
||||||
|
|
||||||
|
// E-ink refresh frequency (pages between full refreshes)
|
||||||
|
enum REFRESH_FREQUENCY { REFRESH_1 = 0, REFRESH_5 = 1, REFRESH_10 = 2, REFRESH_15 = 3, REFRESH_30 = 4 };
|
||||||
|
|
||||||
// Sleep screen settings
|
// Sleep screen settings
|
||||||
uint8_t sleepScreen = DARK;
|
uint8_t sleepScreen = DARK;
|
||||||
@@ -59,9 +66,14 @@ class CrossPointSettings {
|
|||||||
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
|
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
|
||||||
uint8_t sideButtonLayout = PREV_NEXT;
|
uint8_t sideButtonLayout = PREV_NEXT;
|
||||||
// Reader font settings
|
// Reader font settings
|
||||||
uint8_t fontFamily = ALEO;
|
uint8_t fontFamily = BOOKERLY;
|
||||||
uint8_t fontSize = MEDIUM;
|
uint8_t fontSize = MEDIUM;
|
||||||
uint8_t lineSpacing = NORMAL;
|
uint8_t lineSpacing = NORMAL;
|
||||||
|
uint8_t paragraphAlignment = JUSTIFIED;
|
||||||
|
// Auto-sleep timeout setting (default 10 minutes)
|
||||||
|
uint8_t sleepTimeout = SLEEP_10_MIN;
|
||||||
|
// E-ink refresh frequency (default 15 pages)
|
||||||
|
uint8_t refreshFrequency = REFRESH_15;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
@@ -75,6 +87,8 @@ class CrossPointSettings {
|
|||||||
bool loadFromFile();
|
bool loadFromFile();
|
||||||
|
|
||||||
float getReaderLineCompression() const;
|
float getReaderLineCompression() const;
|
||||||
|
unsigned long getSleepTimeoutMs() const;
|
||||||
|
int getRefreshFrequency() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro to access settings
|
// Helper macro to access settings
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
|||||||
switch (frontLayout) {
|
switch (frontLayout) {
|
||||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||||
return InputManager::BTN_LEFT;
|
return InputManager::BTN_LEFT;
|
||||||
|
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||||
|
return InputManager::BTN_CONFIRM;
|
||||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||||
default:
|
default:
|
||||||
return InputManager::BTN_BACK;
|
return InputManager::BTN_BACK;
|
||||||
@@ -19,6 +21,8 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
|||||||
switch (frontLayout) {
|
switch (frontLayout) {
|
||||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||||
return InputManager::BTN_RIGHT;
|
return InputManager::BTN_RIGHT;
|
||||||
|
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||||
|
return InputManager::BTN_LEFT;
|
||||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||||
default:
|
default:
|
||||||
return InputManager::BTN_CONFIRM;
|
return InputManager::BTN_CONFIRM;
|
||||||
@@ -26,6 +30,7 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
|||||||
case Button::Left:
|
case Button::Left:
|
||||||
switch (frontLayout) {
|
switch (frontLayout) {
|
||||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||||
|
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||||
return InputManager::BTN_BACK;
|
return InputManager::BTN_BACK;
|
||||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||||
default:
|
default:
|
||||||
@@ -36,6 +41,7 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
|||||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||||
return InputManager::BTN_CONFIRM;
|
return InputManager::BTN_CONFIRM;
|
||||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||||
|
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||||
default:
|
default:
|
||||||
return InputManager::BTN_RIGHT;
|
return InputManager::BTN_RIGHT;
|
||||||
}
|
}
|
||||||
@@ -85,6 +91,8 @@ MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const
|
|||||||
switch (layout) {
|
switch (layout) {
|
||||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||||
return {previous, next, back, confirm};
|
return {previous, next, back, confirm};
|
||||||
|
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||||
|
return {previous, back, confirm, next};
|
||||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||||
default:
|
default:
|
||||||
return {back, confirm, previous, next};
|
return {back, confirm, previous, next};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left,
|
|||||||
constexpr int batteryWidth = 15;
|
constexpr int batteryWidth = 15;
|
||||||
constexpr int batteryHeight = 10;
|
constexpr int batteryHeight = 10;
|
||||||
const int x = left;
|
const int x = left;
|
||||||
const int y = top + 7;
|
const int y = top + 8;
|
||||||
|
|
||||||
// Top line
|
// Top line
|
||||||
renderer.drawLine(x, y, x + batteryWidth - 4, y);
|
renderer.drawLine(x, y, x + batteryWidth - 4, y);
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ class Activity {
|
|||||||
virtual void onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }
|
virtual void onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }
|
||||||
virtual void loop() {}
|
virtual void loop() {}
|
||||||
virtual bool skipLoopDelay() { return false; }
|
virtual bool skipLoopDelay() { return false; }
|
||||||
|
virtual bool preventAutoSleep() { return false; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ void BootActivity::onEnter() {
|
|||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
|
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING");
|
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING");
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION);
|
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
#include <Xtc.h>
|
#include <Xtc.h>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
@@ -42,16 +40,16 @@ void SleepActivity::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SleepActivity::renderPopup(const char* message) const {
|
void SleepActivity::renderPopup(const char* message) const {
|
||||||
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message);
|
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD);
|
||||||
constexpr int margin = 20;
|
constexpr int margin = 20;
|
||||||
const int x = (renderer.getScreenWidth() - textWidth - margin * 2) / 2;
|
const int x = (renderer.getScreenWidth() - textWidth - margin * 2) / 2;
|
||||||
constexpr int y = 117;
|
constexpr int y = 117;
|
||||||
const int w = textWidth + margin * 2;
|
const int w = textWidth + margin * 2;
|
||||||
const int h = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2;
|
const int h = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2;
|
||||||
// renderer.clearScreen();
|
// renderer.clearScreen();
|
||||||
|
renderer.fillRect(x - 5, y - 5, w + 10, h + 10, true);
|
||||||
renderer.fillRect(x + 5, y + 5, w - 10, h - 10, false);
|
renderer.fillRect(x + 5, y + 5, w - 10, h - 10, false);
|
||||||
renderer.drawText(UI_12_FONT_ID, x + margin, y + margin, message);
|
renderer.drawText(UI_12_FONT_ID, x + margin, y + margin, message, true, EpdFontFamily::BOLD);
|
||||||
renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +127,7 @@ void SleepActivity::renderDefaultSleepScreen() const {
|
|||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
|
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING");
|
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING");
|
||||||
|
|
||||||
// Make sleep screen dark unless light is selected in settings
|
// Make sleep screen dark unless light is selected in settings
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ void HomeActivity::render() const {
|
|||||||
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
|
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
ScreenComponents::drawBattery(renderer, 20, pageHeight - 30);
|
ScreenComponents::drawBattery(renderer, 20, pageHeight - 70);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ void CrossPointWebServerActivity::render() const {
|
|||||||
} else if (state == WebServerActivityState::AP_STARTING) {
|
} else if (state == WebServerActivityState::AP_STARTING) {
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Starting Hotspot...", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Starting Hotspot...", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,13 +365,13 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
// Use consistent line spacing
|
// Use consistent line spacing
|
||||||
constexpr int LINE_SPACING = 28; // Space between lines
|
constexpr int LINE_SPACING = 28; // Space between lines
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (isApMode) {
|
if (isApMode) {
|
||||||
// AP mode display - center the content block
|
// AP mode display - center the content block
|
||||||
int startY = 55;
|
int startY = 55;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
std::string ssidInfo = "Network: " + connectedSSID;
|
std::string ssidInfo = "Network: " + connectedSSID;
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ssidInfo.c_str());
|
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ssidInfo.c_str());
|
||||||
@@ -387,7 +387,7 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
startY += 6 * 29 + 3 * LINE_SPACING;
|
startY += 6 * 29 + 3 * LINE_SPACING;
|
||||||
// Show primary URL (hostname)
|
// Show primary URL (hostname)
|
||||||
std::string hostnameUrl = std::string("http://") + AP_HOSTNAME + ".local/";
|
std::string hostnameUrl = std::string("http://") + AP_HOSTNAME + ".local/";
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str(), true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str(), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Show IP address as fallback
|
// Show IP address as fallback
|
||||||
std::string ipUrl = "or http://" + connectedIP + "/";
|
std::string ipUrl = "or http://" + connectedIP + "/";
|
||||||
@@ -412,7 +412,7 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
|
|
||||||
// Show web server URL prominently
|
// Show web server URL prominently
|
||||||
std::string webInfo = "http://" + connectedIP + "/";
|
std::string webInfo = "http://" + connectedIP + "/";
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, webInfo.c_str(), true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, webInfo.c_str(), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Also show hostname URL
|
// Also show hostname URL
|
||||||
std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/";
|
std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/";
|
||||||
|
|||||||
@@ -70,4 +70,5 @@ class CrossPointWebServerActivity final : public ActivityWithSubactivity {
|
|||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
bool skipLoopDelay() override { return webServer && webServer->isRunning(); }
|
bool skipLoopDelay() override { return webServer && webServer->isRunning(); }
|
||||||
|
bool preventAutoSleep() override { return webServer && webServer->isRunning(); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ void NetworkModeSelectionActivity::render() const {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw subtitle
|
// Draw subtitle
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?");
|
renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?");
|
||||||
|
|||||||
@@ -447,7 +447,16 @@ std::string WifiSelectionActivity::getSignalStrengthIndicator(const int32_t rssi
|
|||||||
|
|
||||||
void WifiSelectionActivity::displayTaskLoop() {
|
void WifiSelectionActivity::displayTaskLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// If a subactivity is active, yield CPU time but don't render
|
||||||
if (subActivity) {
|
if (subActivity) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't render if we're in PASSWORD_ENTRY state - we're just transitioning
|
||||||
|
// from the keyboard subactivity back to the main activity
|
||||||
|
if (state == WifiSelectionState::PASSWORD_ENTRY) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +505,7 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (networks.empty()) {
|
if (networks.empty()) {
|
||||||
// No networks found or scan failed
|
// No networks found or scan failed
|
||||||
@@ -577,7 +586,7 @@ void WifiSelectionActivity::renderConnecting() const {
|
|||||||
if (state == WifiSelectionState::SCANNING) {
|
if (state == WifiSelectionState::SCANNING) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, top, "Scanning...");
|
renderer.drawCenteredText(UI_10_FONT_ID, top, "Scanning...");
|
||||||
} else {
|
} else {
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecting...", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecting...", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
std::string ssidInfo = "to " + selectedSSID;
|
std::string ssidInfo = "to " + selectedSSID;
|
||||||
if (ssidInfo.length() > 25) {
|
if (ssidInfo.length() > 25) {
|
||||||
@@ -592,7 +601,7 @@ void WifiSelectionActivity::renderConnected() const {
|
|||||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
const auto top = (pageHeight - height * 4) / 2;
|
const auto top = (pageHeight - height * 4) / 2;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, top - 30, "Connected!", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, top - 30, "Connected!", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
std::string ssidInfo = "Network: " + selectedSSID;
|
std::string ssidInfo = "Network: " + selectedSSID;
|
||||||
if (ssidInfo.length() > 28) {
|
if (ssidInfo.length() > 28) {
|
||||||
@@ -612,7 +621,7 @@ void WifiSelectionActivity::renderSavePrompt() const {
|
|||||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
const auto top = (pageHeight - height * 3) / 2;
|
const auto top = (pageHeight - height * 3) / 2;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connected!", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connected!", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
std::string ssidInfo = "Network: " + selectedSSID;
|
std::string ssidInfo = "Network: " + selectedSSID;
|
||||||
if (ssidInfo.length() > 28) {
|
if (ssidInfo.length() > 28) {
|
||||||
@@ -651,7 +660,7 @@ void WifiSelectionActivity::renderConnectionFailed() const {
|
|||||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
const auto top = (pageHeight - height * 2) / 2;
|
const auto top = (pageHeight - height * 2) / 2;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, top - 20, "Connection Failed", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, top - 20, "Connection Failed", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, top + 20, connectionError.c_str());
|
renderer.drawCenteredText(UI_10_FONT_ID, top + 20, connectionError.c_str());
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Press any button to continue");
|
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Press any button to continue");
|
||||||
}
|
}
|
||||||
@@ -662,7 +671,7 @@ void WifiSelectionActivity::renderForgetPrompt() const {
|
|||||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
const auto top = (pageHeight - height * 3) / 2;
|
const auto top = (pageHeight - height * 3) / 2;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Forget Network?", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Forget Network?", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
std::string ssidInfo = "Network: " + selectedSSID;
|
std::string ssidInfo = "Network: " + selectedSSID;
|
||||||
if (ssidInfo.length() > 28) {
|
if (ssidInfo.length() > 28) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int pagesPerRefresh = 15;
|
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
||||||
constexpr unsigned long skipChapterMs = 700;
|
constexpr unsigned long skipChapterMs = 700;
|
||||||
constexpr unsigned long goHomeMs = 1000;
|
constexpr unsigned long goHomeMs = 1000;
|
||||||
constexpr int topPadding = 5;
|
constexpr int topPadding = 5;
|
||||||
@@ -244,7 +244,7 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
// Show end of book screen
|
// Show end of book screen
|
||||||
if (currentSpineIndex == epub->getSpineItemsCount()) {
|
if (currentSpineIndex == epub->getSpineItemsCount()) {
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "End of book", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "End of book", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -263,11 +263,12 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
|
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
|
||||||
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
|
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
|
||||||
|
|
||||||
const auto viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||||
const auto viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||||
|
|
||||||
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||||
SETTINGS.extraParagraphSpacing, viewportWidth, viewportHeight)) {
|
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||||
|
viewportHeight)) {
|
||||||
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||||
|
|
||||||
// Progress bar dimensions
|
// Progress bar dimensions
|
||||||
@@ -311,8 +312,8 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||||
SETTINGS.extraParagraphSpacing, viewportWidth, viewportHeight, progressSetup,
|
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||||
progressCallback)) {
|
viewportHeight, progressSetup, progressCallback)) {
|
||||||
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
||||||
section.reset();
|
section.reset();
|
||||||
return;
|
return;
|
||||||
@@ -332,7 +333,7 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
|
|
||||||
if (section->pageCount == 0) {
|
if (section->pageCount == 0) {
|
||||||
Serial.printf("[%lu] [ERS] No pages to render\n", millis());
|
Serial.printf("[%lu] [ERS] No pages to render\n", millis());
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Empty chapter", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Empty chapter", true, EpdFontFamily::BOLD);
|
||||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
@@ -340,7 +341,7 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
|
|
||||||
if (section->currentPage < 0 || section->currentPage >= section->pageCount) {
|
if (section->currentPage < 0 || section->currentPage >= section->pageCount) {
|
||||||
Serial.printf("[%lu] [ERS] Page out of bounds: %d (max %d)\n", millis(), section->currentPage, section->pageCount);
|
Serial.printf("[%lu] [ERS] Page out of bounds: %d (max %d)\n", millis(), section->currentPage, section->pageCount);
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Out of bounds", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Out of bounds", true, EpdFontFamily::BOLD);
|
||||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
@@ -378,7 +379,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
|||||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||||
if (pagesUntilFullRefresh <= 1) {
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||||
pagesUntilFullRefresh = pagesPerRefresh;
|
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||||
} else {
|
} else {
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
pagesUntilFullRefresh--;
|
pagesUntilFullRefresh--;
|
||||||
@@ -421,7 +422,7 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
|
|
||||||
// Position status bar near the bottom of the logical screen, regardless of orientation
|
// Position status bar near the bottom of the logical screen, regardless of orientation
|
||||||
const auto screenHeight = renderer.getScreenHeight();
|
const auto screenHeight = renderer.getScreenHeight();
|
||||||
const auto textY = screenHeight - orientedMarginBottom - 2;
|
const auto textY = screenHeight - orientedMarginBottom - 4;
|
||||||
int progressTextWidth = 0;
|
int progressTextWidth = 0;
|
||||||
|
|
||||||
if (showProgress) {
|
if (showProgress) {
|
||||||
|
|||||||
@@ -120,7 +120,10 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
|||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, BOLD);
|
|
||||||
|
const std::string title =
|
||||||
|
renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40, EpdFontFamily::BOLD);
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, title.c_str(), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||||
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
||||||
|
|||||||
@@ -30,11 +30,19 @@ void FileSelectionActivity::taskTrampoline(void* param) {
|
|||||||
void FileSelectionActivity::loadFiles() {
|
void FileSelectionActivity::loadFiles() {
|
||||||
files.clear();
|
files.clear();
|
||||||
selectorIndex = 0;
|
selectorIndex = 0;
|
||||||
|
|
||||||
auto root = SdMan.open(basepath.c_str());
|
auto root = SdMan.open(basepath.c_str());
|
||||||
|
if (!root || !root.isDirectory()) {
|
||||||
|
if (root) root.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.rewindDirectory();
|
||||||
|
|
||||||
char name[128];
|
char name[128];
|
||||||
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
|
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
|
||||||
file.getName(name, sizeof(name));
|
file.getName(name, sizeof(name));
|
||||||
if (name[0] == '.') {
|
if (name[0] == '.' || strcmp(name, "System Volume Information") == 0) {
|
||||||
file.close();
|
file.close();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -165,7 +173,7 @@ void FileSelectionActivity::render() const {
|
|||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Books", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Books", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");
|
const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");
|
||||||
@@ -180,12 +188,7 @@ void FileSelectionActivity::render() const {
|
|||||||
const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS;
|
const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS;
|
||||||
renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30);
|
||||||
for (int i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) {
|
for (int i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) {
|
||||||
auto item = files[i];
|
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), renderer.getScreenWidth() - 40);
|
||||||
int itemWidth = renderer.getTextWidth(UI_10_FONT_ID, item.c_str());
|
|
||||||
while (itemWidth > renderer.getScreenWidth() - 40 && item.length() > 8) {
|
|
||||||
item.replace(item.length() - 5, 5, "...");
|
|
||||||
itemWidth = renderer.getTextWidth(UI_10_FONT_ID, item.c_str());
|
|
||||||
}
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(), i != selectorIndex);
|
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(), i != selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
|
|||||||
onGoToXtcReader(std::move(xtc));
|
onGoToXtcReader(std::move(xtc));
|
||||||
} else {
|
} else {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC", REGULAR,
|
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC",
|
||||||
EInkDisplay::HALF_REFRESH));
|
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
onGoToFileSelection();
|
onGoToFileSelection();
|
||||||
}
|
}
|
||||||
@@ -80,8 +80,8 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
|
|||||||
onGoToEpubReader(std::move(epub));
|
onGoToEpubReader(std::move(epub));
|
||||||
} else {
|
} else {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub", REGULAR,
|
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub",
|
||||||
EInkDisplay::HALF_REFRESH));
|
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
onGoToFileSelection();
|
onGoToFileSelection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "XtcReaderChapterSelectionActivity.h"
|
#include "XtcReaderChapterSelectionActivity.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int pagesPerRefresh = 15;
|
|
||||||
constexpr unsigned long skipPageMs = 700;
|
constexpr unsigned long skipPageMs = 700;
|
||||||
constexpr unsigned long goHomeMs = 1000;
|
constexpr unsigned long goHomeMs = 1000;
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -165,7 +165,7 @@ void XtcReaderActivity::renderScreen() {
|
|||||||
if (currentPage >= xtc->getPageCount()) {
|
if (currentPage >= xtc->getPageCount()) {
|
||||||
// Show end of book screen
|
// Show end of book screen
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "End of book", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "End of book", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ void XtcReaderActivity::renderPage() {
|
|||||||
if (!pageBuffer) {
|
if (!pageBuffer) {
|
||||||
Serial.printf("[%lu] [XTR] Failed to allocate page buffer (%lu bytes)\n", millis(), pageBufferSize);
|
Serial.printf("[%lu] [XTR] Failed to allocate page buffer (%lu bytes)\n", millis(), pageBufferSize);
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Memory error", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Memory error", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ void XtcReaderActivity::renderPage() {
|
|||||||
Serial.printf("[%lu] [XTR] Failed to load page %lu\n", millis(), currentPage);
|
Serial.printf("[%lu] [XTR] Failed to load page %lu\n", millis(), currentPage);
|
||||||
free(pageBuffer);
|
free(pageBuffer);
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Page load error", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Page load error", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ void XtcReaderActivity::renderPage() {
|
|||||||
// Display BW with conditional refresh based on pagesUntilFullRefresh
|
// Display BW with conditional refresh based on pagesUntilFullRefresh
|
||||||
if (pagesUntilFullRefresh <= 1) {
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||||
pagesUntilFullRefresh = pagesPerRefresh;
|
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||||
} else {
|
} else {
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
pagesUntilFullRefresh--;
|
pagesUntilFullRefresh--;
|
||||||
@@ -346,7 +346,7 @@ void XtcReaderActivity::renderPage() {
|
|||||||
// Display with appropriate refresh
|
// Display with appropriate refresh
|
||||||
if (pagesUntilFullRefresh <= 1) {
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||||
pagesUntilFullRefresh = pagesPerRefresh;
|
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||||
} else {
|
} else {
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
pagesUntilFullRefresh--;
|
pagesUntilFullRefresh--;
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ void XtcReaderChapterSelectionActivity::renderScreen() {
|
|||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
const auto& chapters = xtc->getChapters();
|
const auto& chapters = xtc->getChapters();
|
||||||
if (chapters.empty()) {
|
if (chapters.empty()) {
|
||||||
|
|||||||
@@ -124,36 +124,30 @@ void OtaUpdateActivity::render() {
|
|||||||
lastUpdaterPercentage = static_cast<int>(updaterProgress * 100);
|
lastUpdaterPercentage = static_cast<int>(updaterProgress * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (state == CHECKING_FOR_UPDATE) {
|
if (state == CHECKING_FOR_UPDATE) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == WAITING_CONFIRMATION) {
|
if (state == WAITING_CONFIRMATION) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION);
|
renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str());
|
renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str());
|
||||||
|
|
||||||
renderer.drawRect(25, pageHeight - 40, 106, 40);
|
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
|
||||||
renderer.drawText(UI_10_FONT_ID, 25 + (105 - renderer.getTextWidth(UI_10_FONT_ID, "Cancel")) / 2, pageHeight - 35,
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
"Cancel");
|
|
||||||
|
|
||||||
renderer.drawRect(130, pageHeight - 40, 106, 40);
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 130 + (105 - renderer.getTextWidth(UI_10_FONT_ID, "Update")) / 2, pageHeight - 35,
|
|
||||||
"Update");
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == UPDATE_IN_PROGRESS) {
|
if (state == UPDATE_IN_PROGRESS) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawRect(20, 350, pageWidth - 40, 50);
|
renderer.drawRect(20, 350, pageWidth - 40, 50);
|
||||||
renderer.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
renderer.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 420,
|
renderer.drawCenteredText(UI_10_FONT_ID, 420,
|
||||||
@@ -166,19 +160,19 @@ void OtaUpdateActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == NO_UPDATE) {
|
if (state == NO_UPDATE) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == FAILED) {
|
if (state == FAILED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == FINISHED) {
|
if (state == FINISHED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update complete", true, BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update complete", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Press and hold power button to turn back on");
|
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Press and hold power button to turn back on");
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
state = SHUTTING_DOWN;
|
state = SHUTTING_DOWN;
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ class OtaUpdateActivity : public ActivityWithSubactivity {
|
|||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
bool preventAutoSleep() override { return state == CHECKING_FOR_UPDATE || state == UPDATE_IN_PROGRESS; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 11;
|
constexpr int settingsCount = 14;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
||||||
@@ -23,14 +23,29 @@ const SettingInfo settingsList[settingsCount] = {
|
|||||||
{"Front Button Layout",
|
{"Front Button Layout",
|
||||||
SettingType::ENUM,
|
SettingType::ENUM,
|
||||||
&CrossPointSettings::frontButtonLayout,
|
&CrossPointSettings::frontButtonLayout,
|
||||||
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm"}},
|
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}},
|
||||||
{"Side Button Layout (reader)",
|
{"Side Button Layout (reader)",
|
||||||
SettingType::ENUM,
|
SettingType::ENUM,
|
||||||
&CrossPointSettings::sideButtonLayout,
|
&CrossPointSettings::sideButtonLayout,
|
||||||
{"Prev, Next", "Next, Prev"}},
|
{"Prev, Next", "Next, Prev"}},
|
||||||
{"Reader Font Family", SettingType::ENUM, &CrossPointSettings::fontFamily, {"Aleo", "Noto Sans", "Open Dyslexic"}},
|
{"Reader Font Family",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::fontFamily,
|
||||||
|
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
|
||||||
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
||||||
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
||||||
|
{"Reader Paragraph Alignment",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::paragraphAlignment,
|
||||||
|
{"Justify", "Left", "Center", "Right"}},
|
||||||
|
{"Time to Sleep",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::sleepTimeout,
|
||||||
|
{"1 min", "5 min", "10 min", "15 min", "30 min"}},
|
||||||
|
{"Refresh Frequency",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::refreshFrequency,
|
||||||
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}},
|
||||||
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -160,7 +175,7 @@ void SettingsActivity::render() const {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw selection
|
// Draw selection
|
||||||
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
|
||||||
@@ -169,11 +184,6 @@ void SettingsActivity::render() const {
|
|||||||
for (int i = 0; i < settingsCount; i++) {
|
for (int i = 0; i < settingsCount; i++) {
|
||||||
const int settingY = 60 + i * 30; // 30 pixels between settings
|
const int settingY = 60 + i * 30; // 30 pixels between settings
|
||||||
|
|
||||||
// Draw selection indicator for the selected setting
|
|
||||||
if (i == selectedSettingIndex) {
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 5, settingY, ">");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw setting name
|
// Draw setting name
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);
|
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
|
|
||||||
class FullScreenMessageActivity final : public Activity {
|
class FullScreenMessageActivity final : public Activity {
|
||||||
std::string text;
|
std::string text;
|
||||||
EpdFontStyle style;
|
EpdFontFamily::Style style;
|
||||||
EInkDisplay::RefreshMode refreshMode;
|
EInkDisplay::RefreshMode refreshMode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FullScreenMessageActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string text,
|
explicit FullScreenMessageActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string text,
|
||||||
const EpdFontStyle style = REGULAR,
|
const EpdFontFamily::Style style = EpdFontFamily::REGULAR,
|
||||||
const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH)
|
const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH)
|
||||||
: Activity("FullScreenMessage", renderer, mappedInput),
|
: Activity("FullScreenMessage", renderer, mappedInput),
|
||||||
text(std::move(text)),
|
text(std::move(text)),
|
||||||
|
|||||||
@@ -329,9 +329,13 @@ void KeyboardEntryActivity::render() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw help text at absolute bottom of screen (consistent with other screens)
|
// Draw help text
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Left", "Right");
|
||||||
renderer.drawText(SMALL_FONT_ID, 10, pageHeight - 30, "Navigate: D-pad | Select: OK | Cancel: BACK");
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
|
// Draw side button hints for Up/Down navigation
|
||||||
|
renderer.drawSideButtonHints(UI_10_FONT_ID, "Up", "Down");
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
// The contents of this file are generated by ./lib/EpdFont/scripts/build-font-ids.sh
|
// The contents of this file are generated by ./lib/EpdFont/scripts/build-font-ids.sh
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ALEO_12_FONT_ID (2101191384)
|
#define BOOKERLY_12_FONT_ID (-142329172)
|
||||||
#define ALEO_14_FONT_ID (-1834656672)
|
#define BOOKERLY_14_FONT_ID (104246423)
|
||||||
#define ALEO_16_FONT_ID (-1982848762)
|
#define BOOKERLY_16_FONT_ID (1909382491)
|
||||||
#define ALEO_18_FONT_ID (-365302307)
|
#define BOOKERLY_18_FONT_ID (2056549737)
|
||||||
#define NOTOSANS_12_FONT_ID (-1646794343)
|
#define NOTOSANS_12_FONT_ID (-1646794343)
|
||||||
#define NOTOSANS_14_FONT_ID (-890242897)
|
#define NOTOSANS_14_FONT_ID (-890242897)
|
||||||
#define NOTOSANS_16_FONT_ID (241925189)
|
#define NOTOSANS_16_FONT_ID (241925189)
|
||||||
@@ -15,4 +15,4 @@
|
|||||||
#define OPENDYSLEXIC_14_FONT_ID (-1851285286)
|
#define OPENDYSLEXIC_14_FONT_ID (-1851285286)
|
||||||
#define UI_10_FONT_ID (-823541435)
|
#define UI_10_FONT_ID (-823541435)
|
||||||
#define UI_12_FONT_ID (-126318184)
|
#define UI_12_FONT_ID (-126318184)
|
||||||
#define SMALL_FONT_ID (-706443385)
|
#define SMALL_FONT_ID (-874196069)
|
||||||
|
|||||||
+41
-35
@@ -40,26 +40,30 @@ GfxRenderer renderer(einkDisplay);
|
|||||||
Activity* currentActivity;
|
Activity* currentActivity;
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
EpdFont aleo12RegularFont(&aleo_12_regular);
|
EpdFont bookerly12RegularFont(&bookerly_12_regular);
|
||||||
EpdFont aleo12BoldFont(&aleo_12_bold);
|
EpdFont bookerly12BoldFont(&bookerly_12_bold);
|
||||||
EpdFont aleo12ItalicFont(&aleo_12_italic);
|
EpdFont bookerly12ItalicFont(&bookerly_12_italic);
|
||||||
EpdFont aleo12BoldItalicFont(&aleo_12_bolditalic);
|
EpdFont bookerly12BoldItalicFont(&bookerly_12_bolditalic);
|
||||||
EpdFontFamily aleo12FontFamily(&aleo12RegularFont, &aleo12BoldFont, &aleo12ItalicFont, &aleo12BoldItalicFont);
|
EpdFontFamily bookerly12FontFamily(&bookerly12RegularFont, &bookerly12BoldFont, &bookerly12ItalicFont,
|
||||||
EpdFont aleo14RegularFont(&aleo_14_regular);
|
&bookerly12BoldItalicFont);
|
||||||
EpdFont aleo14BoldFont(&aleo_14_bold);
|
EpdFont bookerly14RegularFont(&bookerly_14_regular);
|
||||||
EpdFont aleo14ItalicFont(&aleo_14_italic);
|
EpdFont bookerly14BoldFont(&bookerly_14_bold);
|
||||||
EpdFont aleo14BoldItalicFont(&aleo_14_bolditalic);
|
EpdFont bookerly14ItalicFont(&bookerly_14_italic);
|
||||||
EpdFontFamily aleo14FontFamily(&aleo14RegularFont, &aleo14BoldFont, &aleo14ItalicFont, &aleo14BoldItalicFont);
|
EpdFont bookerly14BoldItalicFont(&bookerly_14_bolditalic);
|
||||||
EpdFont aleo16RegularFont(&aleo_16_regular);
|
EpdFontFamily bookerly14FontFamily(&bookerly14RegularFont, &bookerly14BoldFont, &bookerly14ItalicFont,
|
||||||
EpdFont aleo16BoldFont(&aleo_16_bold);
|
&bookerly14BoldItalicFont);
|
||||||
EpdFont aleo16ItalicFont(&aleo_16_italic);
|
EpdFont bookerly16RegularFont(&bookerly_16_regular);
|
||||||
EpdFont aleo16BoldItalicFont(&aleo_16_bolditalic);
|
EpdFont bookerly16BoldFont(&bookerly_16_bold);
|
||||||
EpdFontFamily aleo16FontFamily(&aleo16RegularFont, &aleo16BoldFont, &aleo16ItalicFont, &aleo16BoldItalicFont);
|
EpdFont bookerly16ItalicFont(&bookerly_16_italic);
|
||||||
EpdFont aleo18RegularFont(&aleo_18_regular);
|
EpdFont bookerly16BoldItalicFont(&bookerly_16_bolditalic);
|
||||||
EpdFont aleo18BoldFont(&aleo_18_bold);
|
EpdFontFamily bookerly16FontFamily(&bookerly16RegularFont, &bookerly16BoldFont, &bookerly16ItalicFont,
|
||||||
EpdFont aleo18ItalicFont(&aleo_18_italic);
|
&bookerly16BoldItalicFont);
|
||||||
EpdFont aleo18BoldItalicFont(&aleo_18_bolditalic);
|
EpdFont bookerly18RegularFont(&bookerly_18_regular);
|
||||||
EpdFontFamily aleo18FontFamily(&aleo18RegularFont, &aleo18BoldFont, &aleo18ItalicFont, &aleo18BoldItalicFont);
|
EpdFont bookerly18BoldFont(&bookerly_18_bold);
|
||||||
|
EpdFont bookerly18ItalicFont(&bookerly_18_italic);
|
||||||
|
EpdFont bookerly18BoldItalicFont(&bookerly_18_bolditalic);
|
||||||
|
EpdFontFamily bookerly18FontFamily(&bookerly18RegularFont, &bookerly18BoldFont, &bookerly18ItalicFont,
|
||||||
|
&bookerly18BoldItalicFont);
|
||||||
|
|
||||||
EpdFont notosans12RegularFont(¬osans_12_regular);
|
EpdFont notosans12RegularFont(¬osans_12_regular);
|
||||||
EpdFont notosans12BoldFont(¬osans_12_bold);
|
EpdFont notosans12BoldFont(¬osans_12_bold);
|
||||||
@@ -111,7 +115,7 @@ EpdFont opendyslexic14BoldItalicFont(&opendyslexic_14_bolditalic);
|
|||||||
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont,
|
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont,
|
||||||
&opendyslexic14BoldItalicFont);
|
&opendyslexic14BoldItalicFont);
|
||||||
|
|
||||||
EpdFont smallFont(&spacegrotesk);
|
EpdFont smallFont(¬osans_8_regular);
|
||||||
EpdFontFamily smallFontFamily(&smallFont);
|
EpdFontFamily smallFontFamily(&smallFont);
|
||||||
|
|
||||||
EpdFont ui10RegularFont(&ubuntu_10_regular);
|
EpdFont ui10RegularFont(&ubuntu_10_regular);
|
||||||
@@ -122,8 +126,6 @@ EpdFont ui12RegularFont(&ubuntu_12_regular);
|
|||||||
EpdFont ui12BoldFont(&ubuntu_12_bold);
|
EpdFont ui12BoldFont(&ubuntu_12_bold);
|
||||||
EpdFontFamily ui12FontFamily(&ui12RegularFont, &ui12BoldFont);
|
EpdFontFamily ui12FontFamily(&ui12RegularFont, &ui12BoldFont);
|
||||||
|
|
||||||
// Auto-sleep timeout (10 minutes of inactivity)
|
|
||||||
constexpr unsigned long AUTO_SLEEP_TIMEOUT_MS = 10 * 60 * 1000;
|
|
||||||
// measurement of power button press duration calibration value
|
// measurement of power button press duration calibration value
|
||||||
unsigned long t1 = 0;
|
unsigned long t1 = 0;
|
||||||
unsigned long t2 = 0;
|
unsigned long t2 = 0;
|
||||||
@@ -146,9 +148,11 @@ void verifyWakeupLongPress() {
|
|||||||
// Give the user up to 1000ms to start holding the power button, and must hold for SETTINGS.getPowerButtonDuration()
|
// Give the user up to 1000ms to start holding the power button, and must hold for SETTINGS.getPowerButtonDuration()
|
||||||
const auto start = millis();
|
const auto start = millis();
|
||||||
bool abort = false;
|
bool abort = false;
|
||||||
// It takes us some time to wake up from deep sleep, so we need to subtract that from the duration
|
// Subtract the current time, because inputManager only starts counting the HeldTime from the first update()
|
||||||
uint16_t calibration = 29;
|
// This way, we remove the time we already took to reach here from the duration,
|
||||||
uint16_t calibratedPressDuration =
|
// assuming the button was held until now from millis()==0 (i.e. device start time).
|
||||||
|
const uint16_t calibration = start;
|
||||||
|
const uint16_t calibratedPressDuration =
|
||||||
(calibration < SETTINGS.getPowerButtonDuration()) ? SETTINGS.getPowerButtonDuration() - calibration : 1;
|
(calibration < SETTINGS.getPowerButtonDuration()) ? SETTINGS.getPowerButtonDuration() - calibration : 1;
|
||||||
|
|
||||||
inputManager.update();
|
inputManager.update();
|
||||||
@@ -227,10 +231,10 @@ void onGoHome() {
|
|||||||
void setupDisplayAndFonts() {
|
void setupDisplayAndFonts() {
|
||||||
einkDisplay.begin();
|
einkDisplay.begin();
|
||||||
Serial.printf("[%lu] [ ] Display initialized\n", millis());
|
Serial.printf("[%lu] [ ] Display initialized\n", millis());
|
||||||
renderer.insertFont(ALEO_12_FONT_ID, aleo12FontFamily);
|
renderer.insertFont(BOOKERLY_12_FONT_ID, bookerly12FontFamily);
|
||||||
renderer.insertFont(ALEO_14_FONT_ID, aleo14FontFamily);
|
renderer.insertFont(BOOKERLY_14_FONT_ID, bookerly14FontFamily);
|
||||||
renderer.insertFont(ALEO_16_FONT_ID, aleo16FontFamily);
|
renderer.insertFont(BOOKERLY_16_FONT_ID, bookerly16FontFamily);
|
||||||
renderer.insertFont(ALEO_18_FONT_ID, aleo18FontFamily);
|
renderer.insertFont(BOOKERLY_18_FONT_ID, bookerly18FontFamily);
|
||||||
renderer.insertFont(NOTOSANS_12_FONT_ID, notosans12FontFamily);
|
renderer.insertFont(NOTOSANS_12_FONT_ID, notosans12FontFamily);
|
||||||
renderer.insertFont(NOTOSANS_14_FONT_ID, notosans14FontFamily);
|
renderer.insertFont(NOTOSANS_14_FONT_ID, notosans14FontFamily);
|
||||||
renderer.insertFont(NOTOSANS_16_FONT_ID, notosans16FontFamily);
|
renderer.insertFont(NOTOSANS_16_FONT_ID, notosans16FontFamily);
|
||||||
@@ -267,7 +271,7 @@ void setup() {
|
|||||||
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
|
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
|
||||||
setupDisplayAndFonts();
|
setupDisplayAndFonts();
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", BOLD));
|
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", EpdFontFamily::BOLD));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +316,16 @@ void loop() {
|
|||||||
lastMemPrint = millis();
|
lastMemPrint = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for any user activity (button press or release)
|
// Check for any user activity (button press or release) or active background work
|
||||||
static unsigned long lastActivityTime = millis();
|
static unsigned long lastActivityTime = millis();
|
||||||
if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased()) {
|
if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased() ||
|
||||||
|
(currentActivity && currentActivity->preventAutoSleep())) {
|
||||||
lastActivityTime = millis(); // Reset inactivity timer
|
lastActivityTime = millis(); // Reset inactivity timer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (millis() - lastActivityTime >= AUTO_SLEEP_TIMEOUT_MS) {
|
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
||||||
Serial.printf("[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n", millis(), AUTO_SLEEP_TIMEOUT_MS);
|
if (millis() - lastActivityTime >= sleepTimeoutMs) {
|
||||||
|
Serial.printf("[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n", millis(), sleepTimeoutMs);
|
||||||
enterDeepSleep();
|
enterDeepSleep();
|
||||||
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
|
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
|
||||||
return;
|
return;
|
||||||
|
|||||||
+26
-30
@@ -3,7 +3,6 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
#include <WiFiClientSecure.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr char latestReleaseUrl[] = "https://api.github.com/repos/daveallie/crosspoint-reader/releases/latest";
|
constexpr char latestReleaseUrl[] = "https://api.github.com/repos/daveallie/crosspoint-reader/releases/latest";
|
||||||
@@ -69,44 +68,41 @@ OtaUpdater::OtaUpdaterError OtaUpdater::checkForUpdate() {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OtaUpdater::isUpdateNewer() {
|
bool OtaUpdater::isUpdateNewer() const {
|
||||||
if (!updateAvailable || latestVersion.empty() || latestVersion == CROSSPOINT_VERSION) {
|
if (!updateAvailable || latestVersion.empty() || latestVersion == CROSSPOINT_VERSION) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int currentMajor, currentMinor, currentPatch;
|
||||||
|
int latestMajor, latestMinor, latestPatch;
|
||||||
|
|
||||||
|
const auto currentVersion = CROSSPOINT_VERSION;
|
||||||
|
|
||||||
// semantic version check (only match on 3 segments)
|
// semantic version check (only match on 3 segments)
|
||||||
const auto updateMajor = stoi(latestVersion.substr(0, latestVersion.find('.')));
|
sscanf(latestVersion.c_str(), "%d.%d.%d", &latestMajor, &latestMinor, &latestPatch);
|
||||||
const auto updateMinor = stoi(
|
sscanf(currentVersion, "%d.%d.%d", ¤tMajor, ¤tMinor, ¤tPatch);
|
||||||
latestVersion.substr(latestVersion.find('.') + 1, latestVersion.find_last_of('.') - latestVersion.find('.') - 1));
|
|
||||||
const auto updatePatch = stoi(latestVersion.substr(latestVersion.find_last_of('.') + 1));
|
|
||||||
|
|
||||||
std::string currentVersion = CROSSPOINT_VERSION;
|
/*
|
||||||
const auto currentMajor = stoi(currentVersion.substr(0, currentVersion.find('.')));
|
* Compare major versions.
|
||||||
const auto currentMinor = stoi(currentVersion.substr(
|
* If they differ, return true if latest major version greater than current major version
|
||||||
currentVersion.find('.') + 1, currentVersion.find_last_of('.') - currentVersion.find('.') - 1));
|
* otherwise return false.
|
||||||
const auto currentPatch = stoi(currentVersion.substr(currentVersion.find_last_of('.') + 1));
|
*/
|
||||||
|
if (latestMajor != currentMajor) return latestMajor > currentMajor;
|
||||||
|
|
||||||
if (updateMajor > currentMajor) {
|
/*
|
||||||
return true;
|
* Compare minor versions.
|
||||||
}
|
* If they differ, return true if latest minor version greater than current minor version
|
||||||
if (updateMajor < currentMajor) {
|
* otherwise return false.
|
||||||
return false;
|
*/
|
||||||
|
if (latestMinor != currentMinor) return latestMinor > currentMinor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check patch versions.
|
||||||
|
*/
|
||||||
|
return latestPatch > currentPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateMinor > currentMinor) {
|
const std::string& OtaUpdater::getLatestVersion() const { return latestVersion; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (updateMinor < currentMinor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatePatch > currentPatch) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& OtaUpdater::getLatestVersion() { return latestVersion; }
|
|
||||||
|
|
||||||
OtaUpdater::OtaUpdaterError OtaUpdater::installUpdate(const std::function<void(size_t, size_t)>& onProgress) {
|
OtaUpdater::OtaUpdaterError OtaUpdater::installUpdate(const std::function<void(size_t, size_t)>& onProgress) {
|
||||||
if (!isUpdateNewer()) {
|
if (!isUpdateNewer()) {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class OtaUpdater {
|
|||||||
size_t totalSize = 0;
|
size_t totalSize = 0;
|
||||||
|
|
||||||
OtaUpdater() = default;
|
OtaUpdater() = default;
|
||||||
bool isUpdateNewer();
|
bool isUpdateNewer() const;
|
||||||
const std::string& getLatestVersion();
|
const std::string& getLatestVersion() const;
|
||||||
OtaUpdaterError checkForUpdate();
|
OtaUpdaterError checkForUpdate();
|
||||||
OtaUpdaterError installUpdate(const std::function<void(size_t, size_t)>& onProgress);
|
OtaUpdaterError installUpdate(const std::function<void(size_t, size_t)>& onProgress);
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user