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
|
||||
|
||||
- [x] EPUB parsing and rendering
|
||||
- [x] EPUB parsing and rendering (EPUB 2 and EPUB 3)
|
||||
- [ ] Image support within EPUB
|
||||
- [x] Saved reading position
|
||||
- [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
|
||||
- [ ] EPUB picker with cover art
|
||||
- [x] Custom sleep screen
|
||||
- [ ] Cover sleep screen
|
||||
- [x] Cover sleep screen
|
||||
- [x] Wifi book upload
|
||||
- [ ] Wifi OTA updates
|
||||
- [ ] Configurable font, layout, and display options
|
||||
- [ ] Screen rotation
|
||||
- [x] Wifi OTA updates
|
||||
- [x] Configurable font, layout, and display options
|
||||
- [ ] User provided fonts
|
||||
- [ ] Full UTF support
|
||||
- [x] Screen rotation
|
||||
|
||||
See [the user guide](./USER_GUIDE.md) for instructions on operating CrossPoint.
|
||||
|
||||
|
||||
+39
-17
@@ -5,7 +5,7 @@ the device.
|
||||
|
||||
## 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
|
||||
| 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** |
|
||||
| **Right Side** | **Power**, **Volume Up**, **Volume Down** |
|
||||
|
||||
Button layout can be customized in **[Settings](#35-settings)**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Power & Startup
|
||||
|
||||
### 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.
|
||||
|
||||
### 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
|
||||
|
||||
The Home Screen is the main entry point to the firmware. From here you can navigate to the **Book Selection** screen,
|
||||
**Settings** screen, or **File Upload** 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](#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.
|
||||
|
||||
@@ -45,13 +48,13 @@ The Book Selection acts as a folder and file browser.
|
||||
and down through folders and books.
|
||||
* **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
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
- "Dark" (default) - The default dark sleep screen
|
||||
- "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)
|
||||
- **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,
|
||||
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.
|
||||
- **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
|
||||
|
||||
@@ -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.
|
||||
- **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.
|
||||
|
||||
> [!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.
|
||||
|
||||
### System Navigation
|
||||
* **Return to Home:** Press **Back** to close the book and return to the Book Selection screen.
|
||||
* **Chapter Menu:** Press **Confirm** to open the Table of Contents/Chapter Selection screen.
|
||||
* **Return to Book Selection:** Press **Back** to close the book and return to the **[Book Selection](#32-book-selection)** screen.
|
||||
* **Return to Home:** Press and hold **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:
|
||||
|
||||
* **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`
|
||||
|
||||

|
||||
### 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`
|
||||
|
||||

|
||||
### 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"
|
||||
|
||||
const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
||||
const EpdFont* EpdFontFamily::getFont(const Style style) const {
|
||||
if (style == BOLD && bold) {
|
||||
return bold;
|
||||
}
|
||||
@@ -22,16 +22,16 @@ const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
+14
-14
@@ -1,24 +1,24 @@
|
||||
#pragma once
|
||||
#include "EpdFont.h"
|
||||
|
||||
enum EpdFontStyle { REGULAR, BOLD, ITALIC, BOLD_ITALIC };
|
||||
|
||||
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* bold;
|
||||
const EpdFont* italic;
|
||||
const EpdFont* boldItalic;
|
||||
|
||||
const EpdFont* getFont(EpdFontStyle 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;
|
||||
const EpdFont* getFont(Style style) 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
|
||||
|
||||
#include <builtinFonts/aleo_12_bold.h>
|
||||
#include <builtinFonts/aleo_12_bolditalic.h>
|
||||
#include <builtinFonts/aleo_12_italic.h>
|
||||
#include <builtinFonts/aleo_12_regular.h>
|
||||
#include <builtinFonts/aleo_14_bold.h>
|
||||
#include <builtinFonts/aleo_14_bolditalic.h>
|
||||
#include <builtinFonts/aleo_14_italic.h>
|
||||
#include <builtinFonts/aleo_14_regular.h>
|
||||
#include <builtinFonts/aleo_16_bold.h>
|
||||
#include <builtinFonts/aleo_16_bolditalic.h>
|
||||
#include <builtinFonts/aleo_16_italic.h>
|
||||
#include <builtinFonts/aleo_16_regular.h>
|
||||
#include <builtinFonts/aleo_18_bold.h>
|
||||
#include <builtinFonts/aleo_18_bolditalic.h>
|
||||
#include <builtinFonts/aleo_18_italic.h>
|
||||
#include <builtinFonts/aleo_18_regular.h>
|
||||
#include <builtinFonts/bookerly_12_bold.h>
|
||||
#include <builtinFonts/bookerly_12_bolditalic.h>
|
||||
#include <builtinFonts/bookerly_12_italic.h>
|
||||
#include <builtinFonts/bookerly_12_regular.h>
|
||||
#include <builtinFonts/bookerly_14_bold.h>
|
||||
#include <builtinFonts/bookerly_14_bolditalic.h>
|
||||
#include <builtinFonts/bookerly_14_italic.h>
|
||||
#include <builtinFonts/bookerly_14_regular.h>
|
||||
#include <builtinFonts/bookerly_16_bold.h>
|
||||
#include <builtinFonts/bookerly_16_bolditalic.h>
|
||||
#include <builtinFonts/bookerly_16_italic.h>
|
||||
#include <builtinFonts/bookerly_16_regular.h>
|
||||
#include <builtinFonts/bookerly_18_bold.h>
|
||||
#include <builtinFonts/bookerly_18_bolditalic.h>
|
||||
#include <builtinFonts/bookerly_18_italic.h>
|
||||
#include <builtinFonts/bookerly_18_regular.h>
|
||||
#include <builtinFonts/notosans_8_regular.h>
|
||||
#include <builtinFonts/notosans_12_bold.h>
|
||||
#include <builtinFonts/notosans_12_bolditalic.h>
|
||||
#include <builtinFonts/notosans_12_italic.h>
|
||||
@@ -48,7 +49,6 @@
|
||||
#include <builtinFonts/opendyslexic_8_bolditalic.h>
|
||||
#include <builtinFonts/opendyslexic_8_italic.h>
|
||||
#include <builtinFonts/opendyslexic_8_regular.h>
|
||||
#include <builtinFonts/spacegrotesk.h>
|
||||
#include <builtinFonts/ubuntu_10_bold.h>
|
||||
#include <builtinFonts/ubuntu_10_regular.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 ""
|
||||
|
||||
echo "#define ALEO_12_FONT_ID ($(
|
||||
echo "#define BOOKERLY_12_FONT_ID ($(
|
||||
ruby -rdigest -e 'puts [
|
||||
"./aleo_12_regular.h",
|
||||
"./aleo_12_bold.h",
|
||||
"./aleo_12_bolditalic.h",
|
||||
"./aleo_12_italic.h",
|
||||
"./bookerly_12_regular.h",
|
||||
"./bookerly_12_bold.h",
|
||||
"./bookerly_12_bolditalic.h",
|
||||
"./bookerly_12_italic.h",
|
||||
].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 [
|
||||
"./aleo_14_regular.h",
|
||||
"./aleo_14_bold.h",
|
||||
"./aleo_14_bolditalic.h",
|
||||
"./aleo_14_italic.h",
|
||||
"./bookerly_14_regular.h",
|
||||
"./bookerly_14_bold.h",
|
||||
"./bookerly_14_bolditalic.h",
|
||||
"./bookerly_14_italic.h",
|
||||
].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 [
|
||||
"./aleo_16_regular.h",
|
||||
"./aleo_16_bold.h",
|
||||
"./aleo_16_bolditalic.h",
|
||||
"./aleo_16_italic.h",
|
||||
"./bookerly_16_regular.h",
|
||||
"./bookerly_16_bold.h",
|
||||
"./bookerly_16_bolditalic.h",
|
||||
"./bookerly_16_italic.h",
|
||||
].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 [
|
||||
"./aleo_18_regular.h",
|
||||
"./aleo_18_bold.h",
|
||||
"./aleo_18_bolditalic.h",
|
||||
"./aleo_18_italic.h",
|
||||
"./bookerly_18_regular.h",
|
||||
"./bookerly_18_bold.h",
|
||||
"./bookerly_18_bolditalic.h",
|
||||
"./bookerly_18_italic.h",
|
||||
].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 ($(
|
||||
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)'
|
||||
))"
|
||||
|
||||
@@ -5,14 +5,14 @@ set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
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)
|
||||
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
|
||||
font_name="aleo_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')"
|
||||
font_path="../builtinFonts/source/Aleo/Aleo-${style}.ttf"
|
||||
font_name="bookerly_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')"
|
||||
font_path="../builtinFonts/source/Bookerly/Bookerly-${style}.ttf"
|
||||
output_path="../builtinFonts/${font_name}.h"
|
||||
python fontconvert.py $font_name $size $font_path --2bit > $output_path
|
||||
echo "Generated $output_path"
|
||||
@@ -52,4 +52,4 @@ for size in ${UI_FONT_SIZES[@]}; do
|
||||
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/ContentOpfParser.h"
|
||||
#include "Epub/parsers/TocNavParser.h"
|
||||
#include "Epub/parsers/TocNcxParser.h"
|
||||
|
||||
bool Epub::findContentOpfFile(std::string* contentOpfFile) const {
|
||||
@@ -80,6 +81,10 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata) {
|
||||
tocNcxItem = opfParser.tocNcxPath;
|
||||
}
|
||||
|
||||
if (!opfParser.tocNavPath.empty()) {
|
||||
tocNavItem = opfParser.tocNavPath;
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [EBP] Successfully parsed content.opf\n", millis());
|
||||
return true;
|
||||
}
|
||||
@@ -141,6 +146,60 @@ bool Epub::parseTocNcxFile() const {
|
||||
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
|
||||
bool Epub::load(const bool buildIfMissing) {
|
||||
Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str());
|
||||
@@ -184,15 +243,31 @@ bool Epub::load(const bool buildIfMissing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TOC Pass
|
||||
// TOC Pass - try EPUB 3 nav first, fall back to NCX
|
||||
if (!bookMetadataCache->beginTocPass()) {
|
||||
Serial.printf("[%lu] [EBP] Could not begin writing toc pass\n", millis());
|
||||
return false;
|
||||
}
|
||||
if (!parseTocNcxFile()) {
|
||||
Serial.printf("[%lu] [EBP] Could not parse toc\n", millis());
|
||||
return false;
|
||||
|
||||
bool tocParsed = 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()) {
|
||||
Serial.printf("[%lu] [EBP] Could not end writing toc pass\n", millis());
|
||||
return false;
|
||||
|
||||
+4
-1
@@ -12,8 +12,10 @@
|
||||
class ZipFile;
|
||||
|
||||
class Epub {
|
||||
// the ncx file
|
||||
// the ncx file (EPUB 2)
|
||||
std::string tocNcxItem;
|
||||
// the nav file (EPUB 3)
|
||||
std::string tocNavItem;
|
||||
// where is the EPUBfile?
|
||||
std::string filepath;
|
||||
// the base path for items in the EPUB file
|
||||
@@ -26,6 +28,7 @@ class Epub {
|
||||
bool findContentOpfFile(std::string* contentOpfFile) const;
|
||||
bool parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata);
|
||||
bool parseTocNcxFile() const;
|
||||
bool parseTocNavFile() const;
|
||||
|
||||
public:
|
||||
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 {
|
||||
const uint32_t count = elements.size();
|
||||
const uint16_t count = elements.size();
|
||||
serialization::writePod(file, count);
|
||||
|
||||
for (const auto& el : elements) {
|
||||
@@ -49,10 +49,10 @@ bool Page::serialize(FsFile& file) const {
|
||||
std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||
auto page = std::unique_ptr<Page>(new Page());
|
||||
|
||||
uint32_t count;
|
||||
uint16_t count;
|
||||
serialization::readPod(file, count);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
uint8_t tag;
|
||||
serialization::readPod(file, tag);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
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 bool includeLastLine) {
|
||||
if (words.empty()) {
|
||||
@@ -188,7 +188,7 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
|
||||
// *** CRITICAL STEP: CONSUME DATA USING SPLICE ***
|
||||
std::list<std::string> lineWords;
|
||||
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);
|
||||
|
||||
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
|
||||
|
||||
@@ -14,8 +14,8 @@ class GfxRenderer;
|
||||
|
||||
class ParsedText {
|
||||
std::list<std::string> words;
|
||||
std::list<EpdFontStyle> wordStyles;
|
||||
TextBlock::BLOCK_STYLE style;
|
||||
std::list<EpdFontFamily::Style> wordStyles;
|
||||
TextBlock::Style style;
|
||||
bool extraParagraphSpacing;
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
explicit ParsedText(const TextBlock::BLOCK_STYLE style, const bool extraParagraphSpacing)
|
||||
explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing)
|
||||
: style(style), extraParagraphSpacing(extraParagraphSpacing) {}
|
||||
~ParsedText() = default;
|
||||
|
||||
void addWord(std::string word, EpdFontStyle fontStyle);
|
||||
void setStyle(const TextBlock::BLOCK_STYLE style) { this->style = style; }
|
||||
TextBlock::BLOCK_STYLE getStyle() const { return style; }
|
||||
void addWord(std::string word, EpdFontFamily::Style fontStyle);
|
||||
void setStyle(const TextBlock::Style style) { this->style = style; }
|
||||
TextBlock::Style getStyle() const { return style; }
|
||||
size_t size() const { return words.size(); }
|
||||
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,
|
||||
bool includeLastLine = true);
|
||||
};
|
||||
|
||||
+22
-14
@@ -7,9 +7,9 @@
|
||||
#include "parsers/ChapterHtmlSlimParser.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t SECTION_FILE_VERSION = 7;
|
||||
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) +
|
||||
sizeof(int) + sizeof(int) + sizeof(uint32_t);
|
||||
constexpr uint8_t SECTION_FILE_VERSION = 9;
|
||||
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
|
||||
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t);
|
||||
} // namespace
|
||||
|
||||
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,
|
||||
const int viewportWidth, const int viewportHeight) {
|
||||
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||
const uint16_t viewportHeight) {
|
||||
if (!file) {
|
||||
Serial.printf("[%lu] [SCT] File not open for writing header\n", millis());
|
||||
return;
|
||||
}
|
||||
static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) +
|
||||
sizeof(extraParagraphSpacing) + sizeof(viewportWidth) + sizeof(viewportHeight) +
|
||||
sizeof(pageCount) + sizeof(uint32_t),
|
||||
sizeof(extraParagraphSpacing) + sizeof(paragraphAlignment) + sizeof(viewportWidth) +
|
||||
sizeof(viewportHeight) + sizeof(pageCount) + sizeof(uint32_t),
|
||||
"Header size mismatch");
|
||||
serialization::writePod(file, SECTION_FILE_VERSION);
|
||||
serialization::writePod(file, fontId);
|
||||
serialization::writePod(file, lineCompression);
|
||||
serialization::writePod(file, extraParagraphSpacing);
|
||||
serialization::writePod(file, paragraphAlignment);
|
||||
serialization::writePod(file, viewportWidth);
|
||||
serialization::writePod(file, viewportHeight);
|
||||
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,
|
||||
const int viewportWidth, const int viewportHeight) {
|
||||
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||
const uint16_t viewportHeight) {
|
||||
if (!SdMan.openFileForRead("SCT", filePath, file)) {
|
||||
return false;
|
||||
}
|
||||
@@ -66,18 +69,21 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
|
||||
return false;
|
||||
}
|
||||
|
||||
int fileFontId, fileViewportWidth, fileViewportHeight;
|
||||
int fileFontId;
|
||||
uint16_t fileViewportWidth, fileViewportHeight;
|
||||
float fileLineCompression;
|
||||
bool fileExtraParagraphSpacing;
|
||||
uint8_t fileParagraphAlignment;
|
||||
serialization::readPod(file, fileFontId);
|
||||
serialization::readPod(file, fileLineCompression);
|
||||
serialization::readPod(file, fileExtraParagraphSpacing);
|
||||
serialization::readPod(file, fileParagraphAlignment);
|
||||
serialization::readPod(file, fileViewportWidth);
|
||||
serialization::readPod(file, fileViewportHeight);
|
||||
|
||||
if (fontId != fileFontId || lineCompression != fileLineCompression ||
|
||||
extraParagraphSpacing != fileExtraParagraphSpacing || viewportWidth != fileViewportWidth ||
|
||||
viewportHeight != fileViewportHeight) {
|
||||
extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment ||
|
||||
viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight) {
|
||||
file.close();
|
||||
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
|
||||
clearCache();
|
||||
@@ -108,8 +114,8 @@ bool Section::clearCache() const {
|
||||
}
|
||||
|
||||
bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||
const int viewportWidth, const int viewportHeight,
|
||||
const std::function<void()>& progressSetupFn,
|
||||
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||
const uint16_t viewportHeight, const std::function<void()>& progressSetupFn,
|
||||
const std::function<void(int)>& progressFn) {
|
||||
constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight);
|
||||
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
|
||||
viewportHeight);
|
||||
std::vector<uint32_t> lut = {};
|
||||
|
||||
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))); },
|
||||
progressFn);
|
||||
success = visitor.parseAndBuildPages();
|
||||
|
||||
@@ -14,12 +14,12 @@ class Section {
|
||||
std::string filePath;
|
||||
FsFile file;
|
||||
|
||||
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight);
|
||||
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||
uint16_t viewportWidth, uint16_t viewportHeight);
|
||||
uint32_t onPageComplete(std::unique_ptr<Page> page);
|
||||
|
||||
public:
|
||||
int pageCount = 0;
|
||||
uint16_t pageCount = 0;
|
||||
int currentPage = 0;
|
||||
|
||||
explicit Section(const std::shared_ptr<Epub>& epub, const int spineIndex, GfxRenderer& renderer)
|
||||
@@ -28,11 +28,12 @@ class Section {
|
||||
renderer(renderer),
|
||||
filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
|
||||
~Section() = default;
|
||||
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight);
|
||||
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||
uint16_t viewportWidth, uint16_t viewportHeight);
|
||||
bool clearCache() const;
|
||||
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight, const std::function<void()>& progressSetupFn = nullptr,
|
||||
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||
uint16_t viewportWidth, uint16_t viewportHeight,
|
||||
const std::function<void()>& progressSetupFn = nullptr,
|
||||
const std::function<void(int)>& progressFn = nullptr);
|
||||
std::unique_ptr<Page> loadPageFromSectionFile();
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ bool TextBlock::serialize(FsFile& file) const {
|
||||
}
|
||||
|
||||
// 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 (auto x : wordXpos) serialization::writePod(file, x);
|
||||
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) {
|
||||
uint32_t wc;
|
||||
uint16_t wc;
|
||||
std::list<std::string> words;
|
||||
std::list<uint16_t> wordXpos;
|
||||
std::list<EpdFontStyle> wordStyles;
|
||||
BLOCK_STYLE style;
|
||||
std::list<EpdFontFamily::Style> wordStyles;
|
||||
Style style;
|
||||
|
||||
// Word count
|
||||
serialization::readPod(file, wc);
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
#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 {
|
||||
public:
|
||||
enum BLOCK_STYLE : uint8_t {
|
||||
enum Style : uint8_t {
|
||||
JUSTIFIED = 0,
|
||||
LEFT_ALIGN = 1,
|
||||
CENTER_ALIGN = 2,
|
||||
@@ -21,16 +21,16 @@ class TextBlock final : public Block {
|
||||
private:
|
||||
std::list<std::string> words;
|
||||
std::list<uint16_t> wordXpos;
|
||||
std::list<EpdFontStyle> wordStyles;
|
||||
BLOCK_STYLE style;
|
||||
std::list<EpdFontFamily::Style> wordStyles;
|
||||
Style style;
|
||||
|
||||
public:
|
||||
explicit TextBlock(std::list<std::string> words, std::list<uint16_t> word_xpos, std::list<EpdFontStyle> word_styles,
|
||||
const BLOCK_STYLE style)
|
||||
explicit TextBlock(std::list<std::string> words, std::list<uint16_t> word_xpos,
|
||||
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) {}
|
||||
~TextBlock() override = default;
|
||||
void setStyle(const BLOCK_STYLE style) { this->style = style; }
|
||||
BLOCK_STYLE getStyle() const { return style; }
|
||||
void setStyle(const Style style) { this->style = style; }
|
||||
Style getStyle() const { return style; }
|
||||
bool isEmpty() override { return words.empty(); }
|
||||
void layout(GfxRenderer& renderer) override {};
|
||||
// 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
|
||||
void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::BLOCK_STYLE style) {
|
||||
void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) {
|
||||
if (currentTextBlock) {
|
||||
// already have a text block running and it is empty - just reuse it
|
||||
if (currentTextBlock->isEmpty()) {
|
||||
@@ -97,7 +97,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
||||
if (strcmp(name, "br") == 0) {
|
||||
self->startNewTextBlock(self->currentTextBlock->getStyle());
|
||||
} else {
|
||||
self->startNewTextBlock(TextBlock::JUSTIFIED);
|
||||
self->startNewTextBlock((TextBlock::Style)self->paragraphAlignment);
|
||||
}
|
||||
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
|
||||
self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth);
|
||||
@@ -116,13 +116,13 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
return;
|
||||
}
|
||||
|
||||
EpdFontStyle fontStyle = REGULAR;
|
||||
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
|
||||
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
||||
fontStyle = BOLD_ITALIC;
|
||||
fontStyle = EpdFontFamily::BOLD_ITALIC;
|
||||
} else if (self->boldUntilDepth < self->depth) {
|
||||
fontStyle = BOLD;
|
||||
fontStyle = EpdFontFamily::BOLD;
|
||||
} else if (self->italicUntilDepth < self->depth) {
|
||||
fontStyle = ITALIC;
|
||||
fontStyle = EpdFontFamily::ITALIC;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -137,6 +137,21 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
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 (self->partWordBufferIndex >= MAX_WORD_SIZE) {
|
||||
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;
|
||||
|
||||
if (shouldBreakText) {
|
||||
EpdFontStyle fontStyle = REGULAR;
|
||||
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
|
||||
if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) {
|
||||
fontStyle = BOLD_ITALIC;
|
||||
fontStyle = EpdFontFamily::BOLD_ITALIC;
|
||||
} else if (self->boldUntilDepth < self->depth) {
|
||||
fontStyle = BOLD;
|
||||
fontStyle = EpdFontFamily::BOLD;
|
||||
} else if (self->italicUntilDepth < self->depth) {
|
||||
fontStyle = ITALIC;
|
||||
fontStyle = EpdFontFamily::ITALIC;
|
||||
}
|
||||
|
||||
self->partWordBuffer[self->partWordBufferIndex] = '\0';
|
||||
@@ -206,7 +221,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
||||
}
|
||||
|
||||
bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
startNewTextBlock(TextBlock::JUSTIFIED);
|
||||
startNewTextBlock((TextBlock::Style)this->paragraphAlignment);
|
||||
|
||||
const XML_Parser parser = XML_ParserCreate(nullptr);
|
||||
int done;
|
||||
|
||||
@@ -33,10 +33,11 @@ class ChapterHtmlSlimParser {
|
||||
int fontId;
|
||||
float lineCompression;
|
||||
bool extraParagraphSpacing;
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
uint8_t paragraphAlignment;
|
||||
uint16_t viewportWidth;
|
||||
uint16_t viewportHeight;
|
||||
|
||||
void startNewTextBlock(TextBlock::BLOCK_STYLE style);
|
||||
void startNewTextBlock(TextBlock::Style style);
|
||||
void makePages();
|
||||
// XML callbacks
|
||||
static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts);
|
||||
@@ -45,8 +46,9 @@ class ChapterHtmlSlimParser {
|
||||
|
||||
public:
|
||||
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
|
||||
const float lineCompression, const bool extraParagraphSpacing, const int viewportWidth,
|
||||
const int viewportHeight,
|
||||
const float lineCompression, const bool extraParagraphSpacing,
|
||||
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(int)>& progressFn = nullptr)
|
||||
: filepath(filepath),
|
||||
@@ -54,6 +56,7 @@ class ChapterHtmlSlimParser {
|
||||
fontId(fontId),
|
||||
lineCompression(lineCompression),
|
||||
extraParagraphSpacing(extraParagraphSpacing),
|
||||
paragraphAlignment(paragraphAlignment),
|
||||
viewportWidth(viewportWidth),
|
||||
viewportHeight(viewportHeight),
|
||||
completePageFn(completePageFn),
|
||||
|
||||
@@ -161,6 +161,7 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
||||
std::string itemId;
|
||||
std::string href;
|
||||
std::string mediaType;
|
||||
std::string properties;
|
||||
|
||||
for (int i = 0; atts[i]; i += 2) {
|
||||
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];
|
||||
} else if (strcmp(atts[i], "media-type") == 0) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class ContentOpfParser final : public Print {
|
||||
std::string title;
|
||||
std::string author;
|
||||
std::string tocNcxPath;
|
||||
std::string tocNavPath; // EPUB 3 nav document path
|
||||
std::string coverItemHref;
|
||||
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) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
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,
|
||||
const EpdFontStyle style) const {
|
||||
const EpdFontFamily::Style style) const {
|
||||
const int x = (getScreenWidth() - getTextWidth(fontId, text, style)) / 2;
|
||||
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,
|
||||
const EpdFontStyle style) const {
|
||||
const EpdFontFamily::Style style) const {
|
||||
const int yPos = y + getFontAscenderSize(fontId);
|
||||
int xpos = x;
|
||||
|
||||
@@ -238,6 +238,17 @@ void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) cons
|
||||
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
|
||||
int GfxRenderer::getScreenWidth() const {
|
||||
switch (orientation) {
|
||||
@@ -273,7 +284,7 @@ int GfxRenderer::getSpaceWidth(const int fontId) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fontMap.at(fontId).getGlyph(' ', REGULAR)->advanceX;
|
||||
return fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
|
||||
}
|
||||
|
||||
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
||||
@@ -282,7 +293,7 @@ int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fontMap.at(fontId).getData(REGULAR)->ascender;
|
||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
||||
}
|
||||
|
||||
int GfxRenderer::getLineHeight(const int fontId) const {
|
||||
@@ -291,7 +302,7 @@ int GfxRenderer::getLineHeight(const int fontId) const {
|
||||
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,
|
||||
@@ -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(); }
|
||||
|
||||
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,
|
||||
const bool pixelState, const EpdFontStyle style) const {
|
||||
const bool pixelState, const EpdFontFamily::Style style) const {
|
||||
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);
|
||||
if (!glyph) {
|
||||
// TODO: Replace with fallback glyph property?
|
||||
|
||||
@@ -31,7 +31,7 @@ class GfxRenderer {
|
||||
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
|
||||
std::map<int, EpdFontFamily> fontMap;
|
||||
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 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;
|
||||
|
||||
// Text
|
||||
int getTextWidth(int fontId, const char* text, EpdFontStyle style = REGULAR) const;
|
||||
void drawCenteredText(int fontId, int y, const char* text, bool black = true, EpdFontStyle style = REGULAR) const;
|
||||
void drawText(int fontId, int x, int y, const char* text, bool black = true, 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,
|
||||
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 getFontAscenderSize(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
|
||||
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
|
||||
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
||||
void copyGrayscaleLsbBuffers() const;
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
[platformio]
|
||||
crosspoint_version = 0.11.0
|
||||
crosspoint_version = 0.12.0
|
||||
default_envs = default
|
||||
|
||||
[base]
|
||||
@@ -26,6 +26,8 @@ build_flags =
|
||||
-DXML_GE=0
|
||||
-DXML_CONTEXT_BYTES=1024
|
||||
-std=c++2a
|
||||
# Enable UTF-8 long file names in SdFat
|
||||
-DUSE_UTF8_LONG_NAMES=1
|
||||
|
||||
; Board configuration
|
||||
board_build.flash_mode = dio
|
||||
|
||||
@@ -12,7 +12,7 @@ CrossPointSettings CrossPointSettings::instance;
|
||||
namespace {
|
||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||
// 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";
|
||||
} // namespace
|
||||
|
||||
@@ -37,6 +37,9 @@ bool CrossPointSettings::saveToFile() const {
|
||||
serialization::writePod(outputFile, fontFamily);
|
||||
serialization::writePod(outputFile, fontSize);
|
||||
serialization::writePod(outputFile, lineSpacing);
|
||||
serialization::writePod(outputFile, paragraphAlignment);
|
||||
serialization::writePod(outputFile, sleepTimeout);
|
||||
serialization::writePod(outputFile, refreshFrequency);
|
||||
outputFile.close();
|
||||
|
||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||
@@ -83,6 +86,12 @@ bool CrossPointSettings::loadFromFile() {
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, lineSpacing);
|
||||
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);
|
||||
|
||||
inputFile.close();
|
||||
@@ -92,7 +101,7 @@ bool CrossPointSettings::loadFromFile() {
|
||||
|
||||
float CrossPointSettings::getReaderLineCompression() const {
|
||||
switch (fontFamily) {
|
||||
case ALEO:
|
||||
case BOOKERLY:
|
||||
default:
|
||||
switch (lineSpacing) {
|
||||
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 {
|
||||
switch (fontFamily) {
|
||||
case ALEO:
|
||||
case BOOKERLY:
|
||||
default:
|
||||
switch (fontSize) {
|
||||
case SMALL:
|
||||
return ALEO_12_FONT_ID;
|
||||
return BOOKERLY_12_FONT_ID;
|
||||
case MEDIUM:
|
||||
default:
|
||||
return ALEO_14_FONT_ID;
|
||||
return BOOKERLY_14_FONT_ID;
|
||||
case LARGE:
|
||||
return ALEO_16_FONT_ID;
|
||||
return BOOKERLY_16_FONT_ID;
|
||||
case EXTRA_LARGE:
|
||||
return ALEO_18_FONT_ID;
|
||||
return BOOKERLY_18_FONT_ID;
|
||||
}
|
||||
case NOTOSANS:
|
||||
switch (fontSize) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class CrossPointSettings {
|
||||
// Front button layout options
|
||||
// Default: Back, Confirm, Left, Right
|
||||
// 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
|
||||
// Default: Previous, Next
|
||||
@@ -39,10 +39,17 @@ class CrossPointSettings {
|
||||
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 };
|
||||
|
||||
// Font family options
|
||||
enum FONT_FAMILY { ALEO = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
|
||||
enum FONT_FAMILY { BOOKERLY = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
|
||||
// Font size options
|
||||
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 };
|
||||
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
|
||||
uint8_t sleepScreen = DARK;
|
||||
@@ -59,9 +66,14 @@ class CrossPointSettings {
|
||||
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
|
||||
uint8_t sideButtonLayout = PREV_NEXT;
|
||||
// Reader font settings
|
||||
uint8_t fontFamily = ALEO;
|
||||
uint8_t fontFamily = BOOKERLY;
|
||||
uint8_t fontSize = MEDIUM;
|
||||
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;
|
||||
|
||||
@@ -75,6 +87,8 @@ class CrossPointSettings {
|
||||
bool loadFromFile();
|
||||
|
||||
float getReaderLineCompression() const;
|
||||
unsigned long getSleepTimeoutMs() const;
|
||||
int getRefreshFrequency() const;
|
||||
};
|
||||
|
||||
// Helper macro to access settings
|
||||
|
||||
@@ -11,6 +11,8 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
||||
switch (frontLayout) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
return InputManager::BTN_LEFT;
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
return InputManager::BTN_CONFIRM;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
return InputManager::BTN_BACK;
|
||||
@@ -19,6 +21,8 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
||||
switch (frontLayout) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
return InputManager::BTN_RIGHT;
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
return InputManager::BTN_LEFT;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
return InputManager::BTN_CONFIRM;
|
||||
@@ -26,6 +30,7 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
||||
case Button::Left:
|
||||
switch (frontLayout) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
return InputManager::BTN_BACK;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
@@ -36,6 +41,7 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
return InputManager::BTN_CONFIRM;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
default:
|
||||
return InputManager::BTN_RIGHT;
|
||||
}
|
||||
@@ -85,6 +91,8 @@ MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const
|
||||
switch (layout) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
return {previous, next, back, confirm};
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
return {previous, back, confirm, next};
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
return {back, confirm, previous, next};
|
||||
|
||||
@@ -17,7 +17,7 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left,
|
||||
constexpr int batteryWidth = 15;
|
||||
constexpr int batteryHeight = 10;
|
||||
const int x = left;
|
||||
const int y = top + 7;
|
||||
const int y = top + 8;
|
||||
|
||||
// Top line
|
||||
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 loop() {}
|
||||
virtual bool skipLoopDelay() { return false; }
|
||||
virtual bool preventAutoSleep() { return false; }
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ void BootActivity::onEnter() {
|
||||
|
||||
renderer.clearScreen();
|
||||
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 - 30, CROSSPOINT_VERSION);
|
||||
renderer.displayBuffer();
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <SDCardManager.h>
|
||||
#include <Xtc.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "fontIds.h"
|
||||
@@ -42,16 +40,16 @@ void SleepActivity::onEnter() {
|
||||
}
|
||||
|
||||
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;
|
||||
const int x = (renderer.getScreenWidth() - textWidth - margin * 2) / 2;
|
||||
constexpr int y = 117;
|
||||
const int w = textWidth + margin * 2;
|
||||
const int h = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2;
|
||||
// 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.drawText(UI_12_FONT_ID, x + margin, y + margin, message);
|
||||
renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
|
||||
renderer.drawText(UI_12_FONT_ID, x + margin, y + margin, message, true, EpdFontFamily::BOLD);
|
||||
renderer.displayBuffer();
|
||||
}
|
||||
|
||||
@@ -129,7 +127,7 @@ void SleepActivity::renderDefaultSleepScreen() const {
|
||||
|
||||
renderer.clearScreen();
|
||||
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");
|
||||
|
||||
// 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");
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ void CrossPointWebServerActivity::render() const {
|
||||
} else if (state == WebServerActivityState::AP_STARTING) {
|
||||
renderer.clearScreen();
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -365,13 +365,13 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
||||
// Use consistent line spacing
|
||||
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) {
|
||||
// AP mode display - center the content block
|
||||
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;
|
||||
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;
|
||||
// Show primary URL (hostname)
|
||||
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
|
||||
std::string ipUrl = "or http://" + connectedIP + "/";
|
||||
@@ -412,7 +412,7 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
||||
|
||||
// Show web server URL prominently
|
||||
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
|
||||
std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/";
|
||||
|
||||
@@ -70,4 +70,5 @@ class CrossPointWebServerActivity final : public ActivityWithSubactivity {
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
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();
|
||||
|
||||
// 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
|
||||
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() {
|
||||
while (true) {
|
||||
// If a subactivity is active, yield CPU time but don't render
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -496,7 +505,7 @@ void WifiSelectionActivity::renderNetworkList() const {
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
|
||||
// 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()) {
|
||||
// No networks found or scan failed
|
||||
@@ -577,7 +586,7 @@ void WifiSelectionActivity::renderConnecting() const {
|
||||
if (state == WifiSelectionState::SCANNING) {
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, top, "Scanning...");
|
||||
} 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;
|
||||
if (ssidInfo.length() > 25) {
|
||||
@@ -592,7 +601,7 @@ void WifiSelectionActivity::renderConnected() const {
|
||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||
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;
|
||||
if (ssidInfo.length() > 28) {
|
||||
@@ -612,7 +621,7 @@ void WifiSelectionActivity::renderSavePrompt() const {
|
||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||
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;
|
||||
if (ssidInfo.length() > 28) {
|
||||
@@ -651,7 +660,7 @@ void WifiSelectionActivity::renderConnectionFailed() const {
|
||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||
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(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 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;
|
||||
if (ssidInfo.length() > 28) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
constexpr int pagesPerRefresh = 15;
|
||||
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
||||
constexpr unsigned long skipChapterMs = 700;
|
||||
constexpr unsigned long goHomeMs = 1000;
|
||||
constexpr int topPadding = 5;
|
||||
@@ -244,7 +244,7 @@ void EpubReaderActivity::renderScreen() {
|
||||
// Show end of book screen
|
||||
if (currentSpineIndex == epub->getSpineItemsCount()) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@@ -263,11 +263,12 @@ void EpubReaderActivity::renderScreen() {
|
||||
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));
|
||||
|
||||
const auto viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||
const auto viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||
|
||||
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());
|
||||
|
||||
// Progress bar dimensions
|
||||
@@ -311,8 +312,8 @@ void EpubReaderActivity::renderScreen() {
|
||||
};
|
||||
|
||||
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
SETTINGS.extraParagraphSpacing, viewportWidth, viewportHeight, progressSetup,
|
||||
progressCallback)) {
|
||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||
viewportHeight, progressSetup, progressCallback)) {
|
||||
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
||||
section.reset();
|
||||
return;
|
||||
@@ -332,7 +333,7 @@ void EpubReaderActivity::renderScreen() {
|
||||
|
||||
if (section->pageCount == 0) {
|
||||
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);
|
||||
renderer.displayBuffer();
|
||||
return;
|
||||
@@ -340,7 +341,7 @@ void EpubReaderActivity::renderScreen() {
|
||||
|
||||
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);
|
||||
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);
|
||||
renderer.displayBuffer();
|
||||
return;
|
||||
@@ -378,7 +379,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
pagesUntilFullRefresh = pagesPerRefresh;
|
||||
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||
} else {
|
||||
renderer.displayBuffer();
|
||||
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
|
||||
const auto screenHeight = renderer.getScreenHeight();
|
||||
const auto textY = screenHeight - orientedMarginBottom - 2;
|
||||
const auto textY = screenHeight - orientedMarginBottom - 4;
|
||||
int progressTextWidth = 0;
|
||||
|
||||
if (showProgress) {
|
||||
|
||||
@@ -120,7 +120,10 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
||||
|
||||
const auto pageWidth = renderer.getScreenWidth();
|
||||
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;
|
||||
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
||||
|
||||
@@ -30,11 +30,19 @@ void FileSelectionActivity::taskTrampoline(void* param) {
|
||||
void FileSelectionActivity::loadFiles() {
|
||||
files.clear();
|
||||
selectorIndex = 0;
|
||||
|
||||
auto root = SdMan.open(basepath.c_str());
|
||||
if (!root || !root.isDirectory()) {
|
||||
if (root) root.close();
|
||||
return;
|
||||
}
|
||||
|
||||
root.rewindDirectory();
|
||||
|
||||
char name[128];
|
||||
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
|
||||
file.getName(name, sizeof(name));
|
||||
if (name[0] == '.') {
|
||||
if (name[0] == '.' || strcmp(name, "System Volume Information") == 0) {
|
||||
file.close();
|
||||
continue;
|
||||
}
|
||||
@@ -165,7 +173,7 @@ void FileSelectionActivity::render() const {
|
||||
renderer.clearScreen();
|
||||
|
||||
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
|
||||
const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");
|
||||
@@ -180,12 +188,7 @@ void FileSelectionActivity::render() const {
|
||||
const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS;
|
||||
renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30);
|
||||
for (int i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) {
|
||||
auto item = files[i];
|
||||
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());
|
||||
}
|
||||
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), renderer.getScreenWidth() - 40);
|
||||
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));
|
||||
} else {
|
||||
exitActivity();
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC", REGULAR,
|
||||
EInkDisplay::HALF_REFRESH));
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC",
|
||||
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
|
||||
delay(2000);
|
||||
onGoToFileSelection();
|
||||
}
|
||||
@@ -80,8 +80,8 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
|
||||
onGoToEpubReader(std::move(epub));
|
||||
} else {
|
||||
exitActivity();
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub", REGULAR,
|
||||
EInkDisplay::HALF_REFRESH));
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub",
|
||||
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
|
||||
delay(2000);
|
||||
onGoToFileSelection();
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "XtcReaderChapterSelectionActivity.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
constexpr int pagesPerRefresh = 15;
|
||||
constexpr unsigned long skipPageMs = 700;
|
||||
constexpr unsigned long goHomeMs = 1000;
|
||||
} // namespace
|
||||
@@ -165,7 +165,7 @@ void XtcReaderActivity::renderScreen() {
|
||||
if (currentPage >= xtc->getPageCount()) {
|
||||
// Show end of book screen
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ void XtcReaderActivity::renderPage() {
|
||||
if (!pageBuffer) {
|
||||
Serial.printf("[%lu] [XTR] Failed to allocate page buffer (%lu bytes)\n", millis(), pageBufferSize);
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ void XtcReaderActivity::renderPage() {
|
||||
Serial.printf("[%lu] [XTR] Failed to load page %lu\n", millis(), currentPage);
|
||||
free(pageBuffer);
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ void XtcReaderActivity::renderPage() {
|
||||
// Display BW with conditional refresh based on pagesUntilFullRefresh
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
pagesUntilFullRefresh = pagesPerRefresh;
|
||||
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||
} else {
|
||||
renderer.displayBuffer();
|
||||
pagesUntilFullRefresh--;
|
||||
@@ -346,7 +346,7 @@ void XtcReaderActivity::renderPage() {
|
||||
// Display with appropriate refresh
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
pagesUntilFullRefresh = pagesPerRefresh;
|
||||
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||
} else {
|
||||
renderer.displayBuffer();
|
||||
pagesUntilFullRefresh--;
|
||||
|
||||
@@ -130,7 +130,7 @@ void XtcReaderChapterSelectionActivity::renderScreen() {
|
||||
|
||||
const auto pageWidth = renderer.getScreenWidth();
|
||||
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();
|
||||
if (chapters.empty()) {
|
||||
|
||||
@@ -124,36 +124,30 @@ void OtaUpdateActivity::render() {
|
||||
lastUpdaterPercentage = static_cast<int>(updaterProgress * 100);
|
||||
}
|
||||
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
const auto pageWidth = renderer.getScreenWidth();
|
||||
|
||||
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) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
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, 270, ("New Version: " + updater.getLatestVersion()).c_str());
|
||||
|
||||
renderer.drawRect(25, pageHeight - 40, 106, 40);
|
||||
renderer.drawText(UI_10_FONT_ID, 25 + (105 - renderer.getTextWidth(UI_10_FONT_ID, "Cancel")) / 2, pageHeight - 35,
|
||||
"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");
|
||||
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
|
||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
renderer.displayBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
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.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, 420,
|
||||
@@ -166,19 +160,19 @@ void OtaUpdateActivity::render() {
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
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.displayBuffer();
|
||||
state = SHUTTING_DOWN;
|
||||
|
||||
@@ -41,4 +41,5 @@ class OtaUpdateActivity : public ActivityWithSubactivity {
|
||||
void onEnter() override;
|
||||
void onExit() 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
|
||||
namespace {
|
||||
constexpr int settingsCount = 11;
|
||||
constexpr int settingsCount = 14;
|
||||
const SettingInfo settingsList[settingsCount] = {
|
||||
// Should match with SLEEP_SCREEN_MODE
|
||||
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
||||
@@ -23,14 +23,29 @@ const SettingInfo settingsList[settingsCount] = {
|
||||
{"Front Button Layout",
|
||||
SettingType::ENUM,
|
||||
&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)",
|
||||
SettingType::ENUM,
|
||||
&CrossPointSettings::sideButtonLayout,
|
||||
{"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 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, {}},
|
||||
};
|
||||
} // namespace
|
||||
@@ -160,7 +175,7 @@ void SettingsActivity::render() const {
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
|
||||
// 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
|
||||
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++) {
|
||||
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
|
||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
|
||||
class FullScreenMessageActivity final : public Activity {
|
||||
std::string text;
|
||||
EpdFontStyle style;
|
||||
EpdFontFamily::Style style;
|
||||
EInkDisplay::RefreshMode refreshMode;
|
||||
|
||||
public:
|
||||
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)
|
||||
: Activity("FullScreenMessage", renderer, mappedInput),
|
||||
text(std::move(text)),
|
||||
|
||||
@@ -329,9 +329,13 @@ void KeyboardEntryActivity::render() const {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw help text at absolute bottom of screen (consistent with other screens)
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
renderer.drawText(SMALL_FONT_ID, 10, pageHeight - 30, "Navigate: D-pad | Select: OK | Cancel: BACK");
|
||||
// Draw help text
|
||||
const auto labels = mappedInput.mapLabels("« Back", "Select", "Left", "Right");
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
// The contents of this file are generated by ./lib/EpdFont/scripts/build-font-ids.sh
|
||||
#pragma once
|
||||
|
||||
#define ALEO_12_FONT_ID (2101191384)
|
||||
#define ALEO_14_FONT_ID (-1834656672)
|
||||
#define ALEO_16_FONT_ID (-1982848762)
|
||||
#define ALEO_18_FONT_ID (-365302307)
|
||||
#define BOOKERLY_12_FONT_ID (-142329172)
|
||||
#define BOOKERLY_14_FONT_ID (104246423)
|
||||
#define BOOKERLY_16_FONT_ID (1909382491)
|
||||
#define BOOKERLY_18_FONT_ID (2056549737)
|
||||
#define NOTOSANS_12_FONT_ID (-1646794343)
|
||||
#define NOTOSANS_14_FONT_ID (-890242897)
|
||||
#define NOTOSANS_16_FONT_ID (241925189)
|
||||
@@ -15,4 +15,4 @@
|
||||
#define OPENDYSLEXIC_14_FONT_ID (-1851285286)
|
||||
#define UI_10_FONT_ID (-823541435)
|
||||
#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;
|
||||
|
||||
// Fonts
|
||||
EpdFont aleo12RegularFont(&aleo_12_regular);
|
||||
EpdFont aleo12BoldFont(&aleo_12_bold);
|
||||
EpdFont aleo12ItalicFont(&aleo_12_italic);
|
||||
EpdFont aleo12BoldItalicFont(&aleo_12_bolditalic);
|
||||
EpdFontFamily aleo12FontFamily(&aleo12RegularFont, &aleo12BoldFont, &aleo12ItalicFont, &aleo12BoldItalicFont);
|
||||
EpdFont aleo14RegularFont(&aleo_14_regular);
|
||||
EpdFont aleo14BoldFont(&aleo_14_bold);
|
||||
EpdFont aleo14ItalicFont(&aleo_14_italic);
|
||||
EpdFont aleo14BoldItalicFont(&aleo_14_bolditalic);
|
||||
EpdFontFamily aleo14FontFamily(&aleo14RegularFont, &aleo14BoldFont, &aleo14ItalicFont, &aleo14BoldItalicFont);
|
||||
EpdFont aleo16RegularFont(&aleo_16_regular);
|
||||
EpdFont aleo16BoldFont(&aleo_16_bold);
|
||||
EpdFont aleo16ItalicFont(&aleo_16_italic);
|
||||
EpdFont aleo16BoldItalicFont(&aleo_16_bolditalic);
|
||||
EpdFontFamily aleo16FontFamily(&aleo16RegularFont, &aleo16BoldFont, &aleo16ItalicFont, &aleo16BoldItalicFont);
|
||||
EpdFont aleo18RegularFont(&aleo_18_regular);
|
||||
EpdFont aleo18BoldFont(&aleo_18_bold);
|
||||
EpdFont aleo18ItalicFont(&aleo_18_italic);
|
||||
EpdFont aleo18BoldItalicFont(&aleo_18_bolditalic);
|
||||
EpdFontFamily aleo18FontFamily(&aleo18RegularFont, &aleo18BoldFont, &aleo18ItalicFont, &aleo18BoldItalicFont);
|
||||
EpdFont bookerly12RegularFont(&bookerly_12_regular);
|
||||
EpdFont bookerly12BoldFont(&bookerly_12_bold);
|
||||
EpdFont bookerly12ItalicFont(&bookerly_12_italic);
|
||||
EpdFont bookerly12BoldItalicFont(&bookerly_12_bolditalic);
|
||||
EpdFontFamily bookerly12FontFamily(&bookerly12RegularFont, &bookerly12BoldFont, &bookerly12ItalicFont,
|
||||
&bookerly12BoldItalicFont);
|
||||
EpdFont bookerly14RegularFont(&bookerly_14_regular);
|
||||
EpdFont bookerly14BoldFont(&bookerly_14_bold);
|
||||
EpdFont bookerly14ItalicFont(&bookerly_14_italic);
|
||||
EpdFont bookerly14BoldItalicFont(&bookerly_14_bolditalic);
|
||||
EpdFontFamily bookerly14FontFamily(&bookerly14RegularFont, &bookerly14BoldFont, &bookerly14ItalicFont,
|
||||
&bookerly14BoldItalicFont);
|
||||
EpdFont bookerly16RegularFont(&bookerly_16_regular);
|
||||
EpdFont bookerly16BoldFont(&bookerly_16_bold);
|
||||
EpdFont bookerly16ItalicFont(&bookerly_16_italic);
|
||||
EpdFont bookerly16BoldItalicFont(&bookerly_16_bolditalic);
|
||||
EpdFontFamily bookerly16FontFamily(&bookerly16RegularFont, &bookerly16BoldFont, &bookerly16ItalicFont,
|
||||
&bookerly16BoldItalicFont);
|
||||
EpdFont bookerly18RegularFont(&bookerly_18_regular);
|
||||
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 notosans12BoldFont(¬osans_12_bold);
|
||||
@@ -111,7 +115,7 @@ EpdFont opendyslexic14BoldItalicFont(&opendyslexic_14_bolditalic);
|
||||
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont,
|
||||
&opendyslexic14BoldItalicFont);
|
||||
|
||||
EpdFont smallFont(&spacegrotesk);
|
||||
EpdFont smallFont(¬osans_8_regular);
|
||||
EpdFontFamily smallFontFamily(&smallFont);
|
||||
|
||||
EpdFont ui10RegularFont(&ubuntu_10_regular);
|
||||
@@ -122,8 +126,6 @@ EpdFont ui12RegularFont(&ubuntu_12_regular);
|
||||
EpdFont ui12BoldFont(&ubuntu_12_bold);
|
||||
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
|
||||
unsigned long t1 = 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()
|
||||
const auto start = millis();
|
||||
bool abort = false;
|
||||
// It takes us some time to wake up from deep sleep, so we need to subtract that from the duration
|
||||
uint16_t calibration = 29;
|
||||
uint16_t calibratedPressDuration =
|
||||
// Subtract the current time, because inputManager only starts counting the HeldTime from the first update()
|
||||
// This way, we remove the time we already took to reach here from the duration,
|
||||
// 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;
|
||||
|
||||
inputManager.update();
|
||||
@@ -227,10 +231,10 @@ void onGoHome() {
|
||||
void setupDisplayAndFonts() {
|
||||
einkDisplay.begin();
|
||||
Serial.printf("[%lu] [ ] Display initialized\n", millis());
|
||||
renderer.insertFont(ALEO_12_FONT_ID, aleo12FontFamily);
|
||||
renderer.insertFont(ALEO_14_FONT_ID, aleo14FontFamily);
|
||||
renderer.insertFont(ALEO_16_FONT_ID, aleo16FontFamily);
|
||||
renderer.insertFont(ALEO_18_FONT_ID, aleo18FontFamily);
|
||||
renderer.insertFont(BOOKERLY_12_FONT_ID, bookerly12FontFamily);
|
||||
renderer.insertFont(BOOKERLY_14_FONT_ID, bookerly14FontFamily);
|
||||
renderer.insertFont(BOOKERLY_16_FONT_ID, bookerly16FontFamily);
|
||||
renderer.insertFont(BOOKERLY_18_FONT_ID, bookerly18FontFamily);
|
||||
renderer.insertFont(NOTOSANS_12_FONT_ID, notosans12FontFamily);
|
||||
renderer.insertFont(NOTOSANS_14_FONT_ID, notosans14FontFamily);
|
||||
renderer.insertFont(NOTOSANS_16_FONT_ID, notosans16FontFamily);
|
||||
@@ -267,7 +271,7 @@ void setup() {
|
||||
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
|
||||
setupDisplayAndFonts();
|
||||
exitActivity();
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", BOLD));
|
||||
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", EpdFontFamily::BOLD));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -312,14 +316,16 @@ void loop() {
|
||||
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();
|
||||
if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased()) {
|
||||
if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased() ||
|
||||
(currentActivity && currentActivity->preventAutoSleep())) {
|
||||
lastActivityTime = millis(); // Reset inactivity timer
|
||||
}
|
||||
|
||||
if (millis() - lastActivityTime >= AUTO_SLEEP_TIMEOUT_MS) {
|
||||
Serial.printf("[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n", millis(), AUTO_SLEEP_TIMEOUT_MS);
|
||||
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
||||
if (millis() - lastActivityTime >= sleepTimeoutMs) {
|
||||
Serial.printf("[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n", millis(), sleepTimeoutMs);
|
||||
enterDeepSleep();
|
||||
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
|
||||
return;
|
||||
|
||||
+25
-29
@@ -3,7 +3,6 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Update.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
namespace {
|
||||
constexpr char latestReleaseUrl[] = "https://api.github.com/repos/daveallie/crosspoint-reader/releases/latest";
|
||||
@@ -69,44 +68,41 @@ OtaUpdater::OtaUpdaterError OtaUpdater::checkForUpdate() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool OtaUpdater::isUpdateNewer() {
|
||||
bool OtaUpdater::isUpdateNewer() const {
|
||||
if (!updateAvailable || latestVersion.empty() || latestVersion == CROSSPOINT_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentMajor, currentMinor, currentPatch;
|
||||
int latestMajor, latestMinor, latestPatch;
|
||||
|
||||
const auto currentVersion = CROSSPOINT_VERSION;
|
||||
|
||||
// semantic version check (only match on 3 segments)
|
||||
const auto updateMajor = stoi(latestVersion.substr(0, latestVersion.find('.')));
|
||||
const auto updateMinor = stoi(
|
||||
latestVersion.substr(latestVersion.find('.') + 1, latestVersion.find_last_of('.') - latestVersion.find('.') - 1));
|
||||
const auto updatePatch = stoi(latestVersion.substr(latestVersion.find_last_of('.') + 1));
|
||||
sscanf(latestVersion.c_str(), "%d.%d.%d", &latestMajor, &latestMinor, &latestPatch);
|
||||
sscanf(currentVersion, "%d.%d.%d", ¤tMajor, ¤tMinor, ¤tPatch);
|
||||
|
||||
std::string currentVersion = CROSSPOINT_VERSION;
|
||||
const auto currentMajor = stoi(currentVersion.substr(0, currentVersion.find('.')));
|
||||
const auto currentMinor = stoi(currentVersion.substr(
|
||||
currentVersion.find('.') + 1, currentVersion.find_last_of('.') - currentVersion.find('.') - 1));
|
||||
const auto currentPatch = stoi(currentVersion.substr(currentVersion.find_last_of('.') + 1));
|
||||
/*
|
||||
* Compare major versions.
|
||||
* If they differ, return true if latest major version greater than current major version
|
||||
* otherwise return false.
|
||||
*/
|
||||
if (latestMajor != currentMajor) return latestMajor > currentMajor;
|
||||
|
||||
if (updateMajor > currentMajor) {
|
||||
return true;
|
||||
}
|
||||
if (updateMajor < currentMajor) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Compare minor versions.
|
||||
* If they differ, return true if latest minor version greater than current minor version
|
||||
* otherwise return false.
|
||||
*/
|
||||
if (latestMinor != currentMinor) return latestMinor > currentMinor;
|
||||
|
||||
if (updateMinor > currentMinor) {
|
||||
return true;
|
||||
}
|
||||
if (updateMinor < currentMinor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (updatePatch > currentPatch) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
/*
|
||||
* Check patch versions.
|
||||
*/
|
||||
return latestPatch > currentPatch;
|
||||
}
|
||||
|
||||
const std::string& OtaUpdater::getLatestVersion() { return latestVersion; }
|
||||
const std::string& OtaUpdater::getLatestVersion() const { return latestVersion; }
|
||||
|
||||
OtaUpdater::OtaUpdaterError OtaUpdater::installUpdate(const std::function<void(size_t, size_t)>& onProgress) {
|
||||
if (!isUpdateNewer()) {
|
||||
|
||||
@@ -23,8 +23,8 @@ class OtaUpdater {
|
||||
size_t totalSize = 0;
|
||||
|
||||
OtaUpdater() = default;
|
||||
bool isUpdateNewer();
|
||||
const std::string& getLatestVersion();
|
||||
bool isUpdateNewer() const;
|
||||
const std::string& getLatestVersion() const;
|
||||
OtaUpdaterError checkForUpdate();
|
||||
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