Compare commits

..

No commits in common. "941643cf97fb1934af3e1f150081158388ba2de0" and "e43fec79bed98f214cda8c91340bb6c9b383de90" have entirely different histories.

76 changed files with 45700 additions and 92740 deletions

View File

@ -2,7 +2,8 @@
#include <Utf8.h> #include <Utf8.h>
#include <algorithm> inline int min(const int a, const int b) { return a < b ? a : b; }
inline int max(const int a, const int b) { return a < b ? b : a; }
void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX, void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX,
int* maxY) const { int* maxY) const {
@ -31,10 +32,10 @@ void EpdFont::getTextBounds(const char* string, const int startX, const int star
continue; continue;
} }
*minX = std::min(*minX, cursorX + glyph->left); *minX = min(*minX, cursorX + glyph->left);
*maxX = std::max(*maxX, cursorX + glyph->left + glyph->width); *maxX = max(*maxX, cursorX + glyph->left + glyph->width);
*minY = std::min(*minY, cursorY + glyph->top - glyph->height); *minY = min(*minY, cursorY + glyph->top - glyph->height);
*maxY = std::max(*maxY, cursorY + glyph->top); *maxY = max(*maxY, cursorY + glyph->top);
cursorX += glyph->advanceX; cursorX += glyph->advanceX;
} }
} }

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

View File

@ -1,22 +1,21 @@
#pragma once #pragma once
#include <builtinFonts/bookerly_12_bold.h> #include <builtinFonts/aleo_12_bold.h>
#include <builtinFonts/bookerly_12_bolditalic.h> #include <builtinFonts/aleo_12_bolditalic.h>
#include <builtinFonts/bookerly_12_italic.h> #include <builtinFonts/aleo_12_italic.h>
#include <builtinFonts/bookerly_12_regular.h> #include <builtinFonts/aleo_12_regular.h>
#include <builtinFonts/bookerly_14_bold.h> #include <builtinFonts/aleo_14_bold.h>
#include <builtinFonts/bookerly_14_bolditalic.h> #include <builtinFonts/aleo_14_bolditalic.h>
#include <builtinFonts/bookerly_14_italic.h> #include <builtinFonts/aleo_14_italic.h>
#include <builtinFonts/bookerly_14_regular.h> #include <builtinFonts/aleo_14_regular.h>
#include <builtinFonts/bookerly_16_bold.h> #include <builtinFonts/aleo_16_bold.h>
#include <builtinFonts/bookerly_16_bolditalic.h> #include <builtinFonts/aleo_16_bolditalic.h>
#include <builtinFonts/bookerly_16_italic.h> #include <builtinFonts/aleo_16_italic.h>
#include <builtinFonts/bookerly_16_regular.h> #include <builtinFonts/aleo_16_regular.h>
#include <builtinFonts/bookerly_18_bold.h> #include <builtinFonts/aleo_18_bold.h>
#include <builtinFonts/bookerly_18_bolditalic.h> #include <builtinFonts/aleo_18_bolditalic.h>
#include <builtinFonts/bookerly_18_italic.h> #include <builtinFonts/aleo_18_italic.h>
#include <builtinFonts/bookerly_18_regular.h> #include <builtinFonts/aleo_18_regular.h>
#include <builtinFonts/notosans_8_regular.h>
#include <builtinFonts/notosans_12_bold.h> #include <builtinFonts/notosans_12_bold.h>
#include <builtinFonts/notosans_12_bolditalic.h> #include <builtinFonts/notosans_12_bolditalic.h>
#include <builtinFonts/notosans_12_italic.h> #include <builtinFonts/notosans_12_italic.h>
@ -49,6 +48,7 @@
#include <builtinFonts/opendyslexic_8_bolditalic.h> #include <builtinFonts/opendyslexic_8_bolditalic.h>
#include <builtinFonts/opendyslexic_8_italic.h> #include <builtinFonts/opendyslexic_8_italic.h>
#include <builtinFonts/opendyslexic_8_regular.h> #include <builtinFonts/opendyslexic_8_regular.h>
#include <builtinFonts/spacegrotesk.h>
#include <builtinFonts/ubuntu_10_bold.h> #include <builtinFonts/ubuntu_10_bold.h>
#include <builtinFonts/ubuntu_10_regular.h> #include <builtinFonts/ubuntu_10_regular.h>
#include <builtinFonts/ubuntu_12_bold.h> #include <builtinFonts/ubuntu_12_bold.h>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
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.

View File

@ -0,0 +1,93 @@
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.

View File

@ -0,0 +1,826 @@
/**
* 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,
};

View File

@ -8,39 +8,39 @@ echo "// The contents of this file are generated by ./lib/EpdFont/scripts/build-
echo "#pragma once" echo "#pragma once"
echo "" echo ""
echo "#define BOOKERLY_12_FONT_ID ($( echo "#define ALEO_12_FONT_ID ($(
ruby -rdigest -e 'puts [ ruby -rdigest -e 'puts [
"./bookerly_12_regular.h", "./aleo_12_regular.h",
"./bookerly_12_bold.h", "./aleo_12_bold.h",
"./bookerly_12_bolditalic.h", "./aleo_12_bolditalic.h",
"./bookerly_12_italic.h", "./aleo_12_italic.h",
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)' ].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
))" ))"
echo "#define BOOKERLY_14_FONT_ID ($( echo "#define ALEO_14_FONT_ID ($(
ruby -rdigest -e 'puts [ ruby -rdigest -e 'puts [
"./bookerly_14_regular.h", "./aleo_14_regular.h",
"./bookerly_14_bold.h", "./aleo_14_bold.h",
"./bookerly_14_bolditalic.h", "./aleo_14_bolditalic.h",
"./bookerly_14_italic.h", "./aleo_14_italic.h",
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)' ].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
))" ))"
echo "#define BOOKERLY_16_FONT_ID ($( echo "#define ALEO_16_FONT_ID ($(
ruby -rdigest -e 'puts [ ruby -rdigest -e 'puts [
"./bookerly_16_regular.h", "./aleo_16_regular.h",
"./bookerly_16_bold.h", "./aleo_16_bold.h",
"./bookerly_16_bolditalic.h", "./aleo_16_bolditalic.h",
"./bookerly_16_italic.h", "./aleo_16_italic.h",
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)' ].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
))" ))"
echo "#define BOOKERLY_18_FONT_ID ($( echo "#define ALEO_18_FONT_ID ($(
ruby -rdigest -e 'puts [ ruby -rdigest -e 'puts [
"./bookerly_18_regular.h", "./aleo_18_regular.h",
"./bookerly_18_bold.h", "./aleo_18_bold.h",
"./bookerly_18_bolditalic.h", "./aleo_18_bolditalic.h",
"./bookerly_18_italic.h", "./aleo_18_italic.h",
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)' ].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
))" ))"
@ -132,6 +132,6 @@ ruby -rdigest -e 'puts [
echo "#define SMALL_FONT_ID ($( echo "#define SMALL_FONT_ID ($(
ruby -rdigest -e 'puts [ ruby -rdigest -e 'puts [
"./notosans_8_regular.h", "./spacegrotesk.h",
].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)' ].map{|f| Digest::SHA256.hexdigest(File.read(f)).to_i(16) }.sum % (2 ** 32) - (2 ** 31)'
))" ))"

View File

@ -5,14 +5,14 @@ set -e
cd "$(dirname "$0")" cd "$(dirname "$0")"
READER_FONT_STYLES=("Regular" "Italic" "Bold" "BoldItalic") READER_FONT_STYLES=("Regular" "Italic" "Bold" "BoldItalic")
BOOKERLY_FONT_SIZES=(12 14 16 18) ALEO_FONT_SIZES=(12 14 16 18)
NOTOSANS_FONT_SIZES=(12 14 16 18) NOTOSANS_FONT_SIZES=(12 14 16 18)
OPENDYSLEXIC_FONT_SIZES=(8 10 12 14) OPENDYSLEXIC_FONT_SIZES=(8 10 12 14)
for size in ${BOOKERLY_FONT_SIZES[@]}; do for size in ${ALEO_FONT_SIZES[@]}; do
for style in ${READER_FONT_STYLES[@]}; do for style in ${READER_FONT_STYLES[@]}; do
font_name="bookerly_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')" font_name="aleo_${size}_$(echo $style | tr '[:upper:]' '[:lower:]')"
font_path="../builtinFonts/source/Bookerly/Bookerly-${style}.ttf" font_path="../builtinFonts/source/Aleo/Aleo-${style}.ttf"
output_path="../builtinFonts/${font_name}.h" output_path="../builtinFonts/${font_name}.h"
python fontconvert.py $font_name $size $font_path --2bit > $output_path python fontconvert.py $font_name $size $font_path --2bit > $output_path
echo "Generated $output_path" echo "Generated $output_path"
@ -52,4 +52,4 @@ for size in ${UI_FONT_SIZES[@]}; do
done done
done done
python fontconvert.py notosans_8_regular 8 ../builtinFonts/source/NotoSans/NotoSans-Regular.ttf > ../builtinFonts/notosans_8_regular.h python fontconvert.py spacegrotesk 8 ../builtinFonts/source/SpaceGrotesk/SpaceGrotesk-Regular.ttf > ../builtinFonts/spacegrotesk.h

View File

@ -72,9 +72,9 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata) {
// Grab data from opfParser into epub // Grab data from opfParser into epub
bookMetadata.title = opfParser.title; bookMetadata.title = opfParser.title;
bookMetadata.author = opfParser.author; // TODO: Parse author
bookMetadata.author = "";
bookMetadata.coverItemHref = opfParser.coverItemHref; bookMetadata.coverItemHref = opfParser.coverItemHref;
bookMetadata.textReferenceHref = opfParser.textReferenceHref;
if (!opfParser.tocNcxPath.empty()) { if (!opfParser.tocNcxPath.empty()) {
tocNcxItem = opfParser.tocNcxPath; tocNcxItem = opfParser.tocNcxPath;
@ -109,20 +109,17 @@ bool Epub::parseTocNcxFile() const {
if (!ncxParser.setup()) { if (!ncxParser.setup()) {
Serial.printf("[%lu] [EBP] Could not setup toc ncx parser\n", millis()); Serial.printf("[%lu] [EBP] Could not setup toc ncx parser\n", millis());
tempNcxFile.close();
return false; return false;
} }
const auto ncxBuffer = static_cast<uint8_t*>(malloc(1024)); const auto ncxBuffer = static_cast<uint8_t*>(malloc(1024));
if (!ncxBuffer) { if (!ncxBuffer) {
Serial.printf("[%lu] [EBP] Could not allocate memory for toc ncx parser\n", millis()); Serial.printf("[%lu] [EBP] Could not allocate memory for toc ncx parser\n", millis());
tempNcxFile.close();
return false; return false;
} }
while (tempNcxFile.available()) { while (tempNcxFile.available()) {
const auto readSize = tempNcxFile.read(ncxBuffer, 1024); const auto readSize = tempNcxFile.read(ncxBuffer, 1024);
if (readSize == 0) break;
const auto processedSize = ncxParser.write(ncxBuffer, readSize); const auto processedSize = ncxParser.write(ncxBuffer, readSize);
if (processedSize != readSize) { if (processedSize != readSize) {
@ -142,7 +139,7 @@ bool Epub::parseTocNcxFile() const {
} }
// load in the meta data for the epub file // load in the meta data for the epub file
bool Epub::load(const bool buildIfMissing) { bool Epub::load() {
Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str()); Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str());
// Initialize spine/TOC cache // Initialize spine/TOC cache
@ -154,11 +151,6 @@ bool Epub::load(const bool buildIfMissing) {
return true; return true;
} }
// If we didn't load from cache above and we aren't allowed to build, fail now
if (!buildIfMissing) {
return false;
}
// Cache doesn't exist or is invalid, build it // Cache doesn't exist or is invalid, build it
Serial.printf("[%lu] [EBP] Cache not found, building spine/TOC cache\n", millis()); Serial.printf("[%lu] [EBP] Cache not found, building spine/TOC cache\n", millis());
setupCacheDir(); setupCacheDir();
@ -261,15 +253,6 @@ const std::string& Epub::getTitle() const {
return bookMetadataCache->coreMetadata.title; return bookMetadataCache->coreMetadata.title;
} }
const std::string& Epub::getAuthor() const {
static std::string blank;
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) {
return blank;
}
return bookMetadataCache->coreMetadata.author;
}
std::string Epub::getCoverBmpPath() const { return cachePath + "/cover.bmp"; } std::string Epub::getCoverBmpPath() const { return cachePath + "/cover.bmp"; }
bool Epub::generateCoverBmp() const { bool Epub::generateCoverBmp() const {
@ -435,35 +418,6 @@ size_t Epub::getBookSize() const {
return getCumulativeSpineItemSize(getSpineItemsCount() - 1); return getCumulativeSpineItemSize(getSpineItemsCount() - 1);
} }
int Epub::getSpineIndexForTextReference() const {
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) {
Serial.printf("[%lu] [EBP] getSpineIndexForTextReference called but cache not loaded\n", millis());
return 0;
}
Serial.printf("[%lu] [ERS] Core Metadata: cover(%d)=%s, textReference(%d)=%s\n", millis(),
bookMetadataCache->coreMetadata.coverItemHref.size(),
bookMetadataCache->coreMetadata.coverItemHref.c_str(),
bookMetadataCache->coreMetadata.textReferenceHref.size(),
bookMetadataCache->coreMetadata.textReferenceHref.c_str());
if (bookMetadataCache->coreMetadata.textReferenceHref.empty()) {
// there was no textReference in epub, so we return 0 (the first chapter)
return 0;
}
// loop through spine items to get the correct index matching the text href
for (size_t i = 0; i < getSpineItemsCount(); i++) {
if (getSpineItem(i).href == bookMetadataCache->coreMetadata.textReferenceHref) {
Serial.printf("[%lu] [ERS] Text reference %s found at index %d\n", millis(),
bookMetadataCache->coreMetadata.textReferenceHref.c_str(), i);
return i;
}
}
// This should not happen, as we checked for empty textReferenceHref earlier
Serial.printf("[%lu] [EBP] Section not found for text reference\n", millis());
return 0;
}
// Calculate progress in book // Calculate progress in book
uint8_t Epub::calculateProgress(const int currentSpineIndex, const float currentSpineRead) const { uint8_t Epub::calculateProgress(const int currentSpineIndex, const float currentSpineRead) const {
const size_t bookSize = getBookSize(); const size_t bookSize = getBookSize();

View File

@ -34,13 +34,12 @@ class Epub {
} }
~Epub() = default; ~Epub() = default;
std::string& getBasePath() { return contentBasePath; } std::string& getBasePath() { return contentBasePath; }
bool load(bool buildIfMissing = true); bool load();
bool clearCache() const; bool clearCache() const;
void setupCacheDir() const; void setupCacheDir() const;
const std::string& getCachePath() const; const std::string& getCachePath() const;
const std::string& getPath() const; const std::string& getPath() const;
const std::string& getTitle() const; const std::string& getTitle() const;
const std::string& getAuthor() const;
std::string getCoverBmpPath() const; std::string getCoverBmpPath() const;
bool generateCoverBmp() const; bool generateCoverBmp() const;
uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr, uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr,
@ -54,7 +53,6 @@ class Epub {
int getSpineIndexForTocIndex(int tocIndex) const; int getSpineIndexForTocIndex(int tocIndex) const;
int getTocIndexForSpineIndex(int spineIndex) const; int getTocIndexForSpineIndex(int spineIndex) const;
size_t getCumulativeSpineItemSize(int spineIndex) const; size_t getCumulativeSpineItemSize(int spineIndex) const;
int getSpineIndexForTextReference() const;
size_t getBookSize() const; size_t getBookSize() const;
uint8_t calculateProgress(int currentSpineIndex, float currentSpineRead) const; uint8_t calculateProgress(int currentSpineIndex, float currentSpineRead) const;

View File

@ -9,7 +9,7 @@
#include "FsHelpers.h" #include "FsHelpers.h"
namespace { namespace {
constexpr uint8_t BOOK_CACHE_VERSION = 3; constexpr uint8_t BOOK_CACHE_VERSION = 2;
constexpr char bookBinFile[] = "/book.bin"; constexpr char bookBinFile[] = "/book.bin";
constexpr char tmpSpineBinFile[] = "/spine.bin.tmp"; constexpr char tmpSpineBinFile[] = "/spine.bin.tmp";
constexpr char tmpTocBinFile[] = "/toc.bin.tmp"; constexpr char tmpTocBinFile[] = "/toc.bin.tmp";
@ -87,8 +87,8 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
constexpr uint32_t headerASize = constexpr uint32_t headerASize =
sizeof(BOOK_CACHE_VERSION) + /* LUT Offset */ sizeof(uint32_t) + sizeof(spineCount) + sizeof(tocCount); sizeof(BOOK_CACHE_VERSION) + /* LUT Offset */ sizeof(uint32_t) + sizeof(spineCount) + sizeof(tocCount);
const uint32_t metadataSize = metadata.title.size() + metadata.author.size() + metadata.coverItemHref.size() + const uint32_t metadataSize =
metadata.textReferenceHref.size() + sizeof(uint32_t) * 4; metadata.title.size() + metadata.author.size() + metadata.coverItemHref.size() + sizeof(uint32_t) * 3;
const uint32_t lutSize = sizeof(uint32_t) * spineCount + sizeof(uint32_t) * tocCount; const uint32_t lutSize = sizeof(uint32_t) * spineCount + sizeof(uint32_t) * tocCount;
const uint32_t lutOffset = headerASize + metadataSize; const uint32_t lutOffset = headerASize + metadataSize;
@ -101,7 +101,6 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
serialization::writeString(bookFile, metadata.title); serialization::writeString(bookFile, metadata.title);
serialization::writeString(bookFile, metadata.author); serialization::writeString(bookFile, metadata.author);
serialization::writeString(bookFile, metadata.coverItemHref); serialization::writeString(bookFile, metadata.coverItemHref);
serialization::writeString(bookFile, metadata.textReferenceHref);
// Loop through spine entries, writing LUT positions // Loop through spine entries, writing LUT positions
spineFile.seek(0); spineFile.seek(0);
@ -290,7 +289,6 @@ bool BookMetadataCache::load() {
serialization::readString(bookFile, coreMetadata.title); serialization::readString(bookFile, coreMetadata.title);
serialization::readString(bookFile, coreMetadata.author); serialization::readString(bookFile, coreMetadata.author);
serialization::readString(bookFile, coreMetadata.coverItemHref); serialization::readString(bookFile, coreMetadata.coverItemHref);
serialization::readString(bookFile, coreMetadata.textReferenceHref);
loaded = true; loaded = true;
Serial.printf("[%lu] [BMC] Loaded cache data: %d spine, %d TOC entries\n", millis(), spineCount, tocCount); Serial.printf("[%lu] [BMC] Loaded cache data: %d spine, %d TOC entries\n", millis(), spineCount, tocCount);

View File

@ -10,7 +10,6 @@ class BookMetadataCache {
std::string title; std::string title;
std::string author; std::string author;
std::string coverItemHref; std::string coverItemHref;
std::string textReferenceHref;
}; };
struct SpineEntry { struct SpineEntry {

View File

@ -57,6 +57,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::BLOCK_STYLE style
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) { void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData); auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
(void)atts;
// Middle of skip // Middle of skip
if (self->skipUntilDepth < self->depth) { if (self->skipUntilDepth < self->depth) {
@ -92,7 +93,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) { if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) {
self->startNewTextBlock(TextBlock::CENTER_ALIGN); self->startNewTextBlock(TextBlock::CENTER_ALIGN);
self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth); self->boldUntilDepth = min(self->boldUntilDepth, self->depth);
} else if (matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS)) { } else if (matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS)) {
if (strcmp(name, "br") == 0) { if (strcmp(name, "br") == 0) {
self->startNewTextBlock(self->currentTextBlock->getStyle()); self->startNewTextBlock(self->currentTextBlock->getStyle());
@ -100,9 +101,9 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
self->startNewTextBlock(TextBlock::JUSTIFIED); self->startNewTextBlock(TextBlock::JUSTIFIED);
} }
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) { } else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth); self->boldUntilDepth = min(self->boldUntilDepth, self->depth);
} else if (matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS)) { } else if (matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS)) {
self->italicUntilDepth = std::min(self->italicUntilDepth, self->depth); self->italicUntilDepth = min(self->italicUntilDepth, self->depth);
} }
self->depth += 1; self->depth += 1;
@ -161,6 +162,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) { void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData); auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
(void)name;
if (self->partWordBufferIndex > 0) { if (self->partWordBufferIndex > 0) {
// Only flush out part word buffer if we're closing a block tag or are at the top of the HTML file. // Only flush out part word buffer if we're closing a block tag or are at the top of the HTML file.
@ -243,9 +245,9 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
const size_t len = file.read(buf, 1024); const size_t len = file.read(static_cast<uint8_t*>(buf), 1024);
if (len == 0 && file.available() > 0) { if (len == 0) {
Serial.printf("[%lu] [EHP] File read error\n", millis()); Serial.printf("[%lu] [EHP] File read error\n", millis());
XML_StopParser(parser, XML_FALSE); // Stop any pending processing XML_StopParser(parser, XML_FALSE); // Stop any pending processing
XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks

View File

@ -102,11 +102,6 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
return; return;
} }
if (self->state == IN_METADATA && strcmp(name, "dc:creator") == 0) {
self->state = IN_BOOK_AUTHOR;
return;
}
if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) { if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) {
self->state = IN_MANIFEST; self->state = IN_MANIFEST;
if (!SdMan.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) { if (!SdMan.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
@ -127,18 +122,6 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
return; return;
} }
if (self->state == IN_PACKAGE && (strcmp(name, "guide") == 0 || strcmp(name, "opf:guide") == 0)) {
self->state = IN_GUIDE;
// TODO Remove print
Serial.printf("[%lu] [COF] Entering guide state.\n", millis());
if (!SdMan.openFileForRead("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
Serial.printf(
"[%lu] [COF] Couldn't open temp items file for reading. This is probably going to be a fatal error.\n",
millis());
}
return;
}
if (self->state == IN_METADATA && (strcmp(name, "meta") == 0 || strcmp(name, "opf:meta") == 0)) { if (self->state == IN_METADATA && (strcmp(name, "meta") == 0 || strcmp(name, "opf:meta") == 0)) {
bool isCover = false; bool isCover = false;
std::string coverItemId; std::string coverItemId;
@ -217,29 +200,6 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
return; return;
} }
} }
// parse the guide
if (self->state == IN_GUIDE && (strcmp(name, "reference") == 0 || strcmp(name, "opf:reference") == 0)) {
std::string type;
std::string textHref;
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "type") == 0) {
type = atts[i + 1];
if (type == "text" || type == "start") {
continue;
} else {
Serial.printf("[%lu] [COF] Skipping non-text reference in guide: %s\n", millis(), type.c_str());
break;
}
} else if (strcmp(atts[i], "href") == 0) {
textHref = self->baseContentPath + atts[i + 1];
}
}
if ((type == "text" || (type == "start" && !self->textReferenceHref.empty())) && (textHref.length() > 0)) {
Serial.printf("[%lu] [COF] Found %s reference in guide: %s.\n", millis(), type.c_str(), textHref.c_str());
self->textReferenceHref = textHref;
}
return;
}
} }
void XMLCALL ContentOpfParser::characterData(void* userData, const XML_Char* s, const int len) { void XMLCALL ContentOpfParser::characterData(void* userData, const XML_Char* s, const int len) {
@ -249,11 +209,6 @@ void XMLCALL ContentOpfParser::characterData(void* userData, const XML_Char* s,
self->title.append(s, len); self->title.append(s, len);
return; return;
} }
if (self->state == IN_BOOK_AUTHOR) {
self->author.append(s, len);
return;
}
} }
void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name) { void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name) {
@ -266,12 +221,6 @@ void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name)
return; return;
} }
if (self->state == IN_GUIDE && (strcmp(name, "guide") == 0 || strcmp(name, "opf:guide") == 0)) {
self->state = IN_PACKAGE;
self->tempItemStore.close();
return;
}
if (self->state == IN_MANIFEST && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) { if (self->state == IN_MANIFEST && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) {
self->state = IN_PACKAGE; self->state = IN_PACKAGE;
self->tempItemStore.close(); self->tempItemStore.close();
@ -283,11 +232,6 @@ void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name)
return; return;
} }
if (self->state == IN_BOOK_AUTHOR && strcmp(name, "dc:creator") == 0) {
self->state = IN_METADATA;
return;
}
if (self->state == IN_METADATA && (strcmp(name, "metadata") == 0 || strcmp(name, "opf:metadata") == 0)) { if (self->state == IN_METADATA && (strcmp(name, "metadata") == 0 || strcmp(name, "opf:metadata") == 0)) {
self->state = IN_PACKAGE; self->state = IN_PACKAGE;
return; return;

View File

@ -12,10 +12,8 @@ class ContentOpfParser final : public Print {
IN_PACKAGE, IN_PACKAGE,
IN_METADATA, IN_METADATA,
IN_BOOK_TITLE, IN_BOOK_TITLE,
IN_BOOK_AUTHOR,
IN_MANIFEST, IN_MANIFEST,
IN_SPINE, IN_SPINE,
IN_GUIDE,
}; };
const std::string& cachePath; const std::string& cachePath;
@ -33,10 +31,8 @@ class ContentOpfParser final : public Print {
public: public:
std::string title; std::string title;
std::string author;
std::string tocNcxPath; std::string tocNcxPath;
std::string coverItemHref; std::string coverItemHref;
std::string textReferenceHref;
explicit ContentOpfParser(const std::string& cachePath, const std::string& baseContentPath, const size_t xmlSize, explicit ContentOpfParser(const std::string& cachePath, const std::string& baseContentPath, const size_t xmlSize,
BookMetadataCache* cache) BookMetadataCache* cache)

View File

@ -37,7 +37,7 @@ class GfxRenderer {
public: public:
explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {} explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {}
~GfxRenderer() { freeBwBufferChunks(); } ~GfxRenderer() = default;
static constexpr int VIEWABLE_MARGIN_TOP = 9; static constexpr int VIEWABLE_MARGIN_TOP = 9;
static constexpr int VIEWABLE_MARGIN_RIGHT = 3; static constexpr int VIEWABLE_MARGIN_RIGHT = 3;

View File

@ -1,5 +1,5 @@
[platformio] [platformio]
crosspoint_version = 0.11.1 crosspoint_version = 0.10.0
default_envs = default default_envs = default
[base] [base]

View File

@ -92,7 +92,7 @@ bool CrossPointSettings::loadFromFile() {
float CrossPointSettings::getReaderLineCompression() const { float CrossPointSettings::getReaderLineCompression() const {
switch (fontFamily) { switch (fontFamily) {
case BOOKERLY: case ALEO:
default: default:
switch (lineSpacing) { switch (lineSpacing) {
case TIGHT: case TIGHT:
@ -128,18 +128,18 @@ float CrossPointSettings::getReaderLineCompression() const {
int CrossPointSettings::getReaderFontId() const { int CrossPointSettings::getReaderFontId() const {
switch (fontFamily) { switch (fontFamily) {
case BOOKERLY: case ALEO:
default: default:
switch (fontSize) { switch (fontSize) {
case SMALL: case SMALL:
return BOOKERLY_12_FONT_ID; return ALEO_12_FONT_ID;
case MEDIUM: case MEDIUM:
default: default:
return BOOKERLY_14_FONT_ID; return ALEO_14_FONT_ID;
case LARGE: case LARGE:
return BOOKERLY_16_FONT_ID; return ALEO_16_FONT_ID;
case EXTRA_LARGE: case EXTRA_LARGE:
return BOOKERLY_18_FONT_ID; return ALEO_18_FONT_ID;
} }
case NOTOSANS: case NOTOSANS:
switch (fontSize) { switch (fontSize) {

View File

@ -31,7 +31,7 @@ class CrossPointSettings {
// Front button layout options // Front button layout options
// Default: Back, Confirm, Left, Right // Default: Back, Confirm, Left, Right
// Swapped: Left, Right, Back, Confirm // Swapped: Left, Right, Back, Confirm
enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1, LEFT_BACK_CONFIRM_RIGHT = 2 }; enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1 };
// Side button layout options // Side button layout options
// Default: Previous, Next // Default: Previous, Next
@ -39,7 +39,7 @@ class CrossPointSettings {
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 }; enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 };
// Font family options // Font family options
enum FONT_FAMILY { BOOKERLY = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 }; enum FONT_FAMILY { ALEO = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
// Font size options // Font size options
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 }; enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 };
enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2 }; enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2 };
@ -59,7 +59,7 @@ class CrossPointSettings {
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT; uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
uint8_t sideButtonLayout = PREV_NEXT; uint8_t sideButtonLayout = PREV_NEXT;
// Reader font settings // Reader font settings
uint8_t fontFamily = BOOKERLY; uint8_t fontFamily = ALEO;
uint8_t fontSize = MEDIUM; uint8_t fontSize = MEDIUM;
uint8_t lineSpacing = NORMAL; uint8_t lineSpacing = NORMAL;

View File

@ -11,8 +11,6 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
switch (frontLayout) { switch (frontLayout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_LEFT; return InputManager::BTN_LEFT;
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
return InputManager::BTN_CONFIRM;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default: default:
return InputManager::BTN_BACK; return InputManager::BTN_BACK;
@ -21,8 +19,6 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
switch (frontLayout) { switch (frontLayout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_RIGHT; return InputManager::BTN_RIGHT;
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
return InputManager::BTN_LEFT;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default: default:
return InputManager::BTN_CONFIRM; return InputManager::BTN_CONFIRM;
@ -30,7 +26,6 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
case Button::Left: case Button::Left:
switch (frontLayout) { switch (frontLayout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
return InputManager::BTN_BACK; return InputManager::BTN_BACK;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default: default:
@ -41,7 +36,6 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_CONFIRM; return InputManager::BTN_CONFIRM;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
default: default:
return InputManager::BTN_RIGHT; return InputManager::BTN_RIGHT;
} }
@ -91,8 +85,6 @@ MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const
switch (layout) { switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return {previous, next, back, confirm}; return {previous, next, back, confirm};
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
return {previous, back, confirm, next};
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default: default:
return {back, confirm, previous, next}; return {back, confirm, previous, next};

View File

@ -1,41 +0,0 @@
#include "ScreenComponents.h"
#include <GfxRenderer.h>
#include <string>
#include "Battery.h"
#include "fontIds.h"
void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top) {
// Left aligned battery icon and percentage
const uint16_t percentage = battery.readPercentage();
const auto percentageText = std::to_string(percentage) + "%";
renderer.drawText(SMALL_FONT_ID, left + 20, top, percentageText.c_str());
// 1 column on left, 2 columns on right, 5 columns of battery body
constexpr int batteryWidth = 15;
constexpr int batteryHeight = 10;
const int x = left;
const int y = top + 8;
// Top line
renderer.drawLine(x, y, x + batteryWidth - 4, y);
// Bottom line
renderer.drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
// Left line
renderer.drawLine(x, y, x, y + batteryHeight - 1);
// Battery end
renderer.drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
renderer.drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 1, y + 2);
renderer.drawLine(x + batteryWidth - 3, y + batteryHeight - 3, x + batteryWidth - 1, y + batteryHeight - 3);
renderer.drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
// The +1 is to round up, so that we always fill at least one pixel
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
if (filledWidth > batteryWidth - 5) {
filledWidth = batteryWidth - 5; // Ensure we don't overflow
}
renderer.fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
}

View File

@ -1,8 +0,0 @@
#pragma once
class GfxRenderer;
class ScreenComponents {
public:
static void drawBattery(const GfxRenderer& renderer, int left, int top);
};

View File

@ -1,12 +1,10 @@
#include "HomeActivity.h" #include "HomeActivity.h"
#include <Epub.h>
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include "CrossPointState.h" #include "CrossPointState.h"
#include "MappedInputManager.h" #include "MappedInputManager.h"
#include "ScreenComponents.h"
#include "fontIds.h" #include "fontIds.h"
void HomeActivity::taskTrampoline(void* param) { void HomeActivity::taskTrampoline(void* param) {
@ -24,33 +22,6 @@ void HomeActivity::onEnter() {
// Check if we have a book to continue reading // Check if we have a book to continue reading
hasContinueReading = !APP_STATE.openEpubPath.empty() && SdMan.exists(APP_STATE.openEpubPath.c_str()); hasContinueReading = !APP_STATE.openEpubPath.empty() && SdMan.exists(APP_STATE.openEpubPath.c_str());
if (hasContinueReading) {
// Extract filename from path for display
lastBookTitle = APP_STATE.openEpubPath;
const size_t lastSlash = lastBookTitle.find_last_of('/');
if (lastSlash != std::string::npos) {
lastBookTitle = lastBookTitle.substr(lastSlash + 1);
}
const std::string ext4 = lastBookTitle.length() >= 4 ? lastBookTitle.substr(lastBookTitle.length() - 4) : "";
const std::string ext5 = lastBookTitle.length() >= 5 ? lastBookTitle.substr(lastBookTitle.length() - 5) : "";
// If epub, try to load the metadata for title/author
if (ext5 == ".epub") {
Epub epub(APP_STATE.openEpubPath, "/.crosspoint");
epub.load(false);
if (!epub.getTitle().empty()) {
lastBookTitle = std::string(epub.getTitle());
}
if (!epub.getAuthor().empty()) {
lastBookAuthor = std::string(epub.getAuthor());
}
} else if (ext5 == ".xtch") {
lastBookTitle.resize(lastBookTitle.length() - 5);
} else if (ext4 == ".xtc") {
lastBookTitle.resize(lastBookTitle.length() - 4);
}
}
selectorIndex = 0; selectorIndex = 0;
// Trigger first update // Trigger first update
@ -132,191 +103,52 @@ void HomeActivity::render() const {
renderer.clearScreen(); renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight(); renderer.drawCenteredText(UI_12_FONT_ID, 10, "CrossPoint Reader", true, BOLD);
constexpr int margin = 20; // Draw selection
constexpr int bottomMargin = 60; renderer.fillRect(0, 60 + selectorIndex * 30 - 2, pageWidth - 1, 30);
// --- Top "book" card for the current title (selectorIndex == 0) --- int menuY = 60;
const int bookWidth = pageWidth / 2; int menuIndex = 0;
const int bookHeight = pageHeight / 2;
const int bookX = (pageWidth - bookWidth) / 2;
constexpr int bookY = 30;
const bool bookSelected = hasContinueReading && selectorIndex == 0;
// Draw book card regardless, fill with message based on `hasContinueReading`
{
if (bookSelected) {
renderer.fillRect(bookX, bookY, bookWidth, bookHeight);
} else {
renderer.drawRect(bookX, bookY, bookWidth, bookHeight);
}
// Bookmark icon in the top-right corner of the card
const int bookmarkWidth = bookWidth / 8;
const int bookmarkHeight = bookHeight / 5;
const int bookmarkX = bookX + bookWidth - bookmarkWidth - 8;
constexpr int bookmarkY = bookY + 1;
// Main bookmark body (solid)
renderer.fillRect(bookmarkX, bookmarkY, bookmarkWidth, bookmarkHeight, !bookSelected);
// Carve out an inverted triangle notch at the bottom center to create angled points
const int notchHeight = bookmarkHeight / 2; // depth of the notch
for (int i = 0; i < notchHeight; ++i) {
const int y = bookmarkY + bookmarkHeight - 1 - i;
const int xStart = bookmarkX + i;
const int width = bookmarkWidth - 2 * i;
if (width <= 0) {
break;
}
// Draw a horizontal strip in the opposite color to "cut" the notch
renderer.fillRect(xStart, y, width, 1, bookSelected);
}
}
if (hasContinueReading) { if (hasContinueReading) {
// Split into words (avoid stringstream to keep this light on the MCU) // Extract filename from path for display
std::vector<std::string> words; std::string bookName = APP_STATE.openEpubPath;
words.reserve(8); const size_t lastSlash = bookName.find_last_of('/');
size_t pos = 0; if (lastSlash != std::string::npos) {
while (pos < lastBookTitle.size()) { bookName = bookName.substr(lastSlash + 1);
while (pos < lastBookTitle.size() && lastBookTitle[pos] == ' ') { }
++pos; // Remove .epub extension
} if (bookName.length() > 5 && bookName.substr(bookName.length() - 5) == ".epub") {
if (pos >= lastBookTitle.size()) { bookName.resize(bookName.length() - 5);
break;
}
const size_t start = pos;
while (pos < lastBookTitle.size() && lastBookTitle[pos] != ' ') {
++pos;
}
words.emplace_back(lastBookTitle.substr(start, pos - start));
} }
std::vector<std::string> lines; // Truncate if too long
std::string currentLine; std::string continueLabel = "Continue: " + bookName;
// Extra padding inside the card so text doesn't hug the border int itemWidth = renderer.getTextWidth(UI_10_FONT_ID, continueLabel.c_str());
const int maxLineWidth = bookWidth - 40; while (itemWidth > renderer.getScreenWidth() - 40 && continueLabel.length() > 8) {
const int spaceWidth = renderer.getSpaceWidth(UI_12_FONT_ID); continueLabel.replace(continueLabel.length() - 5, 5, "...");
itemWidth = renderer.getTextWidth(UI_10_FONT_ID, continueLabel.c_str());
for (auto& i : words) { Serial.printf("[%lu] [HOM] width: %lu, pageWidth: %lu\n", millis(), itemWidth, pageWidth);
// If we just hit the line limit (3), stop processing words
if (lines.size() >= 3) {
// Limit to 3 lines
// Still have words left, so add ellipsis to last line
lines.back().append("...");
while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) {
lines.back().resize(lines.back().size() - 5);
lines.back().append("...");
}
break;
}
int wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str());
while (wordWidth > maxLineWidth && i.size() > 5) {
// Word itself is too long, trim it
i.resize(i.size() - 5);
i.append("...");
wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str());
}
int newLineWidth = renderer.getTextWidth(UI_12_FONT_ID, currentLine.c_str());
if (newLineWidth > 0) {
newLineWidth += spaceWidth;
}
newLineWidth += wordWidth;
if (newLineWidth > maxLineWidth && !currentLine.empty()) {
// New line too long, push old line
lines.push_back(currentLine);
currentLine = i;
} else {
currentLine.append(" ").append(i);
}
} }
// If lower than the line limit, push remaining words renderer.drawText(UI_10_FONT_ID, 20, menuY, continueLabel.c_str(), selectorIndex != menuIndex);
if (!currentLine.empty() && lines.size() < 3) { menuY += 30;
lines.push_back(currentLine); menuIndex++;
}
// Book title text
int totalTextHeight = renderer.getLineHeight(UI_12_FONT_ID) * static_cast<int>(lines.size());
if (!lastBookAuthor.empty()) {
totalTextHeight += renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
}
// Vertically center the title block within the card
int titleYStart = bookY + (bookHeight - totalTextHeight) / 2;
for (const auto& line : lines) {
renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(), !bookSelected);
titleYStart += renderer.getLineHeight(UI_12_FONT_ID);
}
if (!lastBookAuthor.empty()) {
titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2;
std::string trimmedAuthor = lastBookAuthor;
// Trim author if too long
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) {
trimmedAuthor.resize(trimmedAuthor.size() - 5);
trimmedAuthor.append("...");
}
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected);
}
renderer.drawCenteredText(UI_10_FONT_ID, bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2,
"Continue Reading", !bookSelected);
} else {
// No book to continue reading
const int y =
bookY + (bookHeight - renderer.getLineHeight(UI_12_FONT_ID) - renderer.getLineHeight(UI_10_FONT_ID)) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, y, "No open book");
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
} }
// --- Bottom menu tiles (indices 1-3) --- renderer.drawText(UI_10_FONT_ID, 20, menuY, "Browse", selectorIndex != menuIndex);
const int menuTileWidth = pageWidth - 2 * margin; menuY += 30;
constexpr int menuTileHeight = 50; menuIndex++;
constexpr int menuSpacing = 10;
constexpr int totalMenuHeight = 3 * menuTileHeight + 2 * menuSpacing;
int menuStartY = bookY + bookHeight + 20; renderer.drawText(UI_10_FONT_ID, 20, menuY, "File transfer", selectorIndex != menuIndex);
// Ensure we don't collide with the bottom button legend menuY += 30;
const int maxMenuStartY = pageHeight - bottomMargin - totalMenuHeight - margin; menuIndex++;
if (menuStartY > maxMenuStartY) {
menuStartY = maxMenuStartY;
}
for (int i = 0; i < 3; ++i) { renderer.drawText(UI_10_FONT_ID, 20, menuY, "Settings", selectorIndex != menuIndex);
constexpr const char* items[3] = {"Browse files", "File transfer", "Settings"};
const int overallIndex = i + (getMenuItemCount() - 3);
constexpr int tileX = margin;
const int tileY = menuStartY + i * (menuTileHeight + menuSpacing);
const bool selected = selectorIndex == overallIndex;
if (selected) { const auto labels = mappedInput.mapLabels("Back", "Confirm", "Left", "Right");
renderer.fillRect(tileX, tileY, menuTileWidth, menuTileHeight);
} else {
renderer.drawRect(tileX, tileY, menuTileWidth, menuTileHeight);
}
const char* label = items[i];
const int textWidth = renderer.getTextWidth(UI_10_FONT_ID, label);
const int textX = tileX + (menuTileWidth - textWidth) / 2;
const int lineHeight = renderer.getLineHeight(UI_10_FONT_ID);
const int textY = tileY + (menuTileHeight - lineHeight) / 2; // vertically centered assuming y is top of text
// Invert text when the tile is selected, to contrast with the filled background
renderer.drawText(UI_10_FONT_ID, textX, textY, label, !selected);
}
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
ScreenComponents::drawBattery(renderer, 20, pageHeight - 70);
renderer.displayBuffer(); renderer.displayBuffer();
} }

View File

@ -13,8 +13,6 @@ class HomeActivity final : public Activity {
int selectorIndex = 0; int selectorIndex = 0;
bool updateRequired = false; bool updateRequired = false;
bool hasContinueReading = false; bool hasContinueReading = false;
std::string lastBookTitle;
std::string lastBookAuthor;
const std::function<void()> onContinueReading; const std::function<void()> onContinueReading;
const std::function<void()> onReaderOpen; const std::function<void()> onReaderOpen;
const std::function<void()> onSettingsOpen; const std::function<void()> onSettingsOpen;

View File

@ -97,7 +97,7 @@ void NetworkModeSelectionActivity::render() const {
const auto pageHeight = renderer.getScreenHeight(); const auto pageHeight = renderer.getScreenHeight();
// Draw header // Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "File Transfer", true, BOLD);
// Draw subtitle // Draw subtitle
renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?"); renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?");

View File

@ -496,7 +496,7 @@ void WifiSelectionActivity::renderNetworkList() const {
const auto pageHeight = renderer.getScreenHeight(); const auto pageHeight = renderer.getScreenHeight();
// Draw header // Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "WiFi Networks", true, BOLD);
if (networks.empty()) { if (networks.empty()) {
// No networks found or scan failed // No networks found or scan failed

View File

@ -5,11 +5,11 @@
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include "Battery.h"
#include "CrossPointSettings.h" #include "CrossPointSettings.h"
#include "CrossPointState.h" #include "CrossPointState.h"
#include "EpubReaderChapterSelectionActivity.h" #include "EpubReaderChapterSelectionActivity.h"
#include "MappedInputManager.h" #include "MappedInputManager.h"
#include "ScreenComponents.h"
#include "fontIds.h" #include "fontIds.h"
namespace { namespace {
@ -65,16 +65,6 @@ void EpubReaderActivity::onEnter() {
} }
f.close(); f.close();
} }
// We may want a better condition to detect if we are opening for the first time.
// This will trigger if the book is re-opened at Chapter 0.
if (currentSpineIndex == 0) {
int textSpineIndex = epub->getSpineIndexForTextReference();
if (textSpineIndex != 0) {
currentSpineIndex = textSpineIndex;
Serial.printf("[%lu] [ERS] Opened for first time, navigating to text reference at index %d\n", millis(),
textSpineIndex);
}
}
// Save current epub as last opened epub // Save current epub as last opened epub
APP_STATE.openEpubPath = epub->getPath(); APP_STATE.openEpubPath = epub->getPath();
@ -421,7 +411,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
// Position status bar near the bottom of the logical screen, regardless of orientation // Position status bar near the bottom of the logical screen, regardless of orientation
const auto screenHeight = renderer.getScreenHeight(); const auto screenHeight = renderer.getScreenHeight();
const auto textY = screenHeight - orientedMarginBottom - 4; const auto textY = screenHeight - orientedMarginBottom - 2;
int percentageTextWidth = 0;
int progressTextWidth = 0; int progressTextWidth = 0;
if (showProgress) { if (showProgress) {
@ -438,13 +429,42 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
} }
if (showBattery) { if (showBattery) {
ScreenComponents::drawBattery(renderer, orientedMarginLeft, textY); // Left aligned battery icon and percentage
const uint16_t percentage = battery.readPercentage();
const auto percentageText = std::to_string(percentage) + "%";
percentageTextWidth = renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
renderer.drawText(SMALL_FONT_ID, 20 + orientedMarginLeft, textY, percentageText.c_str());
// 1 column on left, 2 columns on right, 5 columns of battery body
constexpr int batteryWidth = 15;
constexpr int batteryHeight = 10;
const int x = orientedMarginLeft;
const int y = screenHeight - orientedMarginBottom + 5;
// Top line
renderer.drawLine(x, y, x + batteryWidth - 4, y);
// Bottom line
renderer.drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
// Left line
renderer.drawLine(x, y, x, y + batteryHeight - 1);
// Battery end
renderer.drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
renderer.drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 1, y + 2);
renderer.drawLine(x + batteryWidth - 3, y + batteryHeight - 3, x + batteryWidth - 1, y + batteryHeight - 3);
renderer.drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
// The +1 is to round up, so that we always fill at least one pixel
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
if (filledWidth > batteryWidth - 5) {
filledWidth = batteryWidth - 5; // Ensure we don't overflow
}
renderer.fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
} }
if (showChapterTitle) { if (showChapterTitle) {
// Centered chatper title text // Centered chatper title text
// Page width minus existing content with 30px padding on each side // Page width minus existing content with 30px padding on each side
const int titleMarginLeft = 50 + 30 + orientedMarginLeft; // 50px for battery const int titleMarginLeft = 20 + percentageTextWidth + 30 + orientedMarginLeft;
const int titleMarginRight = progressTextWidth + 30 + orientedMarginRight; const int titleMarginRight = progressTextWidth + 30 + orientedMarginRight;
const int availableTextWidth = renderer.getScreenWidth() - titleMarginLeft - titleMarginRight; const int availableTextWidth = renderer.getScreenWidth() - titleMarginLeft - titleMarginRight;
const int tocIndex = epub->getTocIndexForSpineIndex(currentSpineIndex); const int tocIndex = epub->getTocIndexForSpineIndex(currentSpineIndex);

View File

@ -120,7 +120,7 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
const int pageItems = getPageItems(); const int pageItems = getPageItems();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "Select Chapter", true, BOLD);
const auto pageStartIndex = selectorIndex / pageItems * pageItems; const auto pageStartIndex = selectorIndex / pageItems * pageItems;
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30); renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);

View File

@ -165,7 +165,7 @@ void FileSelectionActivity::render() const {
renderer.clearScreen(); renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Books", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "Books", true, BOLD);
// Help text // Help text
const auto labels = mappedInput.mapLabels("« Home", "Open", "", ""); const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");

View File

@ -130,7 +130,7 @@ void XtcReaderChapterSelectionActivity::renderScreen() {
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
const int pageItems = getPageItems(); const int pageItems = getPageItems();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "Select Chapter", true, BOLD);
const auto& chapters = xtc->getChapters(); const auto& chapters = xtc->getChapters();
if (chapters.empty()) { if (chapters.empty()) {

View File

@ -128,7 +128,7 @@ void OtaUpdateActivity::render() {
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
renderer.clearScreen(); renderer.clearScreen();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "Update", true, BOLD);
if (state == CHECKING_FOR_UPDATE) { if (state == CHECKING_FOR_UPDATE) {
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, BOLD); renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, BOLD);

View File

@ -23,15 +23,12 @@ const SettingInfo settingsList[settingsCount] = {
{"Front Button Layout", {"Front Button Layout",
SettingType::ENUM, SettingType::ENUM,
&CrossPointSettings::frontButtonLayout, &CrossPointSettings::frontButtonLayout,
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}}, {"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm"}},
{"Side Button Layout (reader)", {"Side Button Layout (reader)",
SettingType::ENUM, SettingType::ENUM,
&CrossPointSettings::sideButtonLayout, &CrossPointSettings::sideButtonLayout,
{"Prev, Next", "Next, Prev"}}, {"Prev, Next", "Next, Prev"}},
{"Reader Font Family", {"Reader Font Family", SettingType::ENUM, &CrossPointSettings::fontFamily, {"Aleo", "Noto Sans", "Open Dyslexic"}},
SettingType::ENUM,
&CrossPointSettings::fontFamily,
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}}, {"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}}, {"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
{"Check for updates", SettingType::ACTION, nullptr, {}}, {"Check for updates", SettingType::ACTION, nullptr, {}},
@ -163,7 +160,7 @@ void SettingsActivity::render() const {
const auto pageHeight = renderer.getScreenHeight(); const auto pageHeight = renderer.getScreenHeight();
// Draw header // Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, BOLD); renderer.drawCenteredText(UI_12_FONT_ID, 10, "Settings", true, BOLD);
// Draw selection // Draw selection
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30); renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
@ -172,6 +169,11 @@ void SettingsActivity::render() const {
for (int i = 0; i < settingsCount; i++) { for (int i = 0; i < settingsCount; i++) {
const int settingY = 60 + i * 30; // 30 pixels between settings const int settingY = 60 + i * 30; // 30 pixels between settings
// Draw selection indicator for the selected setting
if (i == selectedSettingIndex) {
renderer.drawText(UI_10_FONT_ID, 5, settingY, ">");
}
// Draw setting name // Draw setting name
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex); renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);

View File

@ -1,10 +1,10 @@
// The contents of this file are generated by ./lib/EpdFont/scripts/build-font-ids.sh // The contents of this file are generated by ./lib/EpdFont/scripts/build-font-ids.sh
#pragma once #pragma once
#define BOOKERLY_12_FONT_ID (-142329172) #define ALEO_12_FONT_ID (2101191384)
#define BOOKERLY_14_FONT_ID (104246423) #define ALEO_14_FONT_ID (-1834656672)
#define BOOKERLY_16_FONT_ID (1909382491) #define ALEO_16_FONT_ID (-1982848762)
#define BOOKERLY_18_FONT_ID (2056549737) #define ALEO_18_FONT_ID (-365302307)
#define NOTOSANS_12_FONT_ID (-1646794343) #define NOTOSANS_12_FONT_ID (-1646794343)
#define NOTOSANS_14_FONT_ID (-890242897) #define NOTOSANS_14_FONT_ID (-890242897)
#define NOTOSANS_16_FONT_ID (241925189) #define NOTOSANS_16_FONT_ID (241925189)
@ -15,4 +15,4 @@
#define OPENDYSLEXIC_14_FONT_ID (-1851285286) #define OPENDYSLEXIC_14_FONT_ID (-1851285286)
#define UI_10_FONT_ID (-823541435) #define UI_10_FONT_ID (-823541435)
#define UI_12_FONT_ID (-126318184) #define UI_12_FONT_ID (-126318184)
#define SMALL_FONT_ID (-874196069) #define SMALL_FONT_ID (-706443385)

View File

@ -40,30 +40,26 @@ GfxRenderer renderer(einkDisplay);
Activity* currentActivity; Activity* currentActivity;
// Fonts // Fonts
EpdFont bookerly12RegularFont(&bookerly_12_regular); EpdFont aleo12RegularFont(&aleo_12_regular);
EpdFont bookerly12BoldFont(&bookerly_12_bold); EpdFont aleo12BoldFont(&aleo_12_bold);
EpdFont bookerly12ItalicFont(&bookerly_12_italic); EpdFont aleo12ItalicFont(&aleo_12_italic);
EpdFont bookerly12BoldItalicFont(&bookerly_12_bolditalic); EpdFont aleo12BoldItalicFont(&aleo_12_bolditalic);
EpdFontFamily bookerly12FontFamily(&bookerly12RegularFont, &bookerly12BoldFont, &bookerly12ItalicFont, EpdFontFamily aleo12FontFamily(&aleo12RegularFont, &aleo12BoldFont, &aleo12ItalicFont, &aleo12BoldItalicFont);
&bookerly12BoldItalicFont); EpdFont aleo14RegularFont(&aleo_14_regular);
EpdFont bookerly14RegularFont(&bookerly_14_regular); EpdFont aleo14BoldFont(&aleo_14_bold);
EpdFont bookerly14BoldFont(&bookerly_14_bold); EpdFont aleo14ItalicFont(&aleo_14_italic);
EpdFont bookerly14ItalicFont(&bookerly_14_italic); EpdFont aleo14BoldItalicFont(&aleo_14_bolditalic);
EpdFont bookerly14BoldItalicFont(&bookerly_14_bolditalic); EpdFontFamily aleo14FontFamily(&aleo14RegularFont, &aleo14BoldFont, &aleo14ItalicFont, &aleo14BoldItalicFont);
EpdFontFamily bookerly14FontFamily(&bookerly14RegularFont, &bookerly14BoldFont, &bookerly14ItalicFont, EpdFont aleo16RegularFont(&aleo_16_regular);
&bookerly14BoldItalicFont); EpdFont aleo16BoldFont(&aleo_16_bold);
EpdFont bookerly16RegularFont(&bookerly_16_regular); EpdFont aleo16ItalicFont(&aleo_16_italic);
EpdFont bookerly16BoldFont(&bookerly_16_bold); EpdFont aleo16BoldItalicFont(&aleo_16_bolditalic);
EpdFont bookerly16ItalicFont(&bookerly_16_italic); EpdFontFamily aleo16FontFamily(&aleo16RegularFont, &aleo16BoldFont, &aleo16ItalicFont, &aleo16BoldItalicFont);
EpdFont bookerly16BoldItalicFont(&bookerly_16_bolditalic); EpdFont aleo18RegularFont(&aleo_18_regular);
EpdFontFamily bookerly16FontFamily(&bookerly16RegularFont, &bookerly16BoldFont, &bookerly16ItalicFont, EpdFont aleo18BoldFont(&aleo_18_bold);
&bookerly16BoldItalicFont); EpdFont aleo18ItalicFont(&aleo_18_italic);
EpdFont bookerly18RegularFont(&bookerly_18_regular); EpdFont aleo18BoldItalicFont(&aleo_18_bolditalic);
EpdFont bookerly18BoldFont(&bookerly_18_bold); EpdFontFamily aleo18FontFamily(&aleo18RegularFont, &aleo18BoldFont, &aleo18ItalicFont, &aleo18BoldItalicFont);
EpdFont bookerly18ItalicFont(&bookerly_18_italic);
EpdFont bookerly18BoldItalicFont(&bookerly_18_bolditalic);
EpdFontFamily bookerly18FontFamily(&bookerly18RegularFont, &bookerly18BoldFont, &bookerly18ItalicFont,
&bookerly18BoldItalicFont);
EpdFont notosans12RegularFont(&notosans_12_regular); EpdFont notosans12RegularFont(&notosans_12_regular);
EpdFont notosans12BoldFont(&notosans_12_bold); EpdFont notosans12BoldFont(&notosans_12_bold);
@ -115,7 +111,7 @@ EpdFont opendyslexic14BoldItalicFont(&opendyslexic_14_bolditalic);
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont, EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont,
&opendyslexic14BoldItalicFont); &opendyslexic14BoldItalicFont);
EpdFont smallFont(&notosans_8_regular); EpdFont smallFont(&spacegrotesk);
EpdFontFamily smallFontFamily(&smallFont); EpdFontFamily smallFontFamily(&smallFont);
EpdFont ui10RegularFont(&ubuntu_10_regular); EpdFont ui10RegularFont(&ubuntu_10_regular);
@ -231,10 +227,10 @@ void onGoHome() {
void setupDisplayAndFonts() { void setupDisplayAndFonts() {
einkDisplay.begin(); einkDisplay.begin();
Serial.printf("[%lu] [ ] Display initialized\n", millis()); Serial.printf("[%lu] [ ] Display initialized\n", millis());
renderer.insertFont(BOOKERLY_12_FONT_ID, bookerly12FontFamily); renderer.insertFont(ALEO_12_FONT_ID, aleo12FontFamily);
renderer.insertFont(BOOKERLY_14_FONT_ID, bookerly14FontFamily); renderer.insertFont(ALEO_14_FONT_ID, aleo14FontFamily);
renderer.insertFont(BOOKERLY_16_FONT_ID, bookerly16FontFamily); renderer.insertFont(ALEO_16_FONT_ID, aleo16FontFamily);
renderer.insertFont(BOOKERLY_18_FONT_ID, bookerly18FontFamily); renderer.insertFont(ALEO_18_FONT_ID, aleo18FontFamily);
renderer.insertFont(NOTOSANS_12_FONT_ID, notosans12FontFamily); renderer.insertFont(NOTOSANS_12_FONT_ID, notosans12FontFamily);
renderer.insertFont(NOTOSANS_14_FONT_ID, notosans14FontFamily); renderer.insertFont(NOTOSANS_14_FONT_ID, notosans14FontFamily);
renderer.insertFont(NOTOSANS_16_FONT_ID, notosans16FontFamily); renderer.insertFont(NOTOSANS_16_FONT_ID, notosans16FontFamily);

View File

@ -50,14 +50,6 @@ void CrossPointWebServer::begin() {
Serial.printf("[%lu] [WEB] Creating web server on port %d...\n", millis(), port); Serial.printf("[%lu] [WEB] Creating web server on port %d...\n", millis(), port);
server.reset(new WebServer(port)); server.reset(new WebServer(port));
// Disable WiFi sleep to improve responsiveness and prevent 'unreachable' errors.
// This is critical for reliable web server operation on ESP32.
WiFi.setSleep(false);
// Note: WebServer class doesn't have setNoDelay() in the standard ESP32 library.
// We rely on disabling WiFi sleep for responsiveness.
Serial.printf("[%lu] [WEB] [MEM] Free heap after WebServer allocation: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEB] [MEM] Free heap after WebServer allocation: %d bytes\n", millis(), ESP.getFreeHeap());
if (!server) { if (!server) {
@ -165,16 +157,15 @@ void CrossPointWebServer::handleStatus() const {
// Get correct IP based on AP vs STA mode // Get correct IP based on AP vs STA mode
const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString(); const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString();
JsonDocument doc; String json = "{";
doc["version"] = CROSSPOINT_VERSION; json += "\"version\":\"" + String(CROSSPOINT_VERSION) + "\",";
doc["ip"] = ipAddr; json += "\"ip\":\"" + ipAddr + "\",";
doc["mode"] = apMode ? "AP" : "STA"; json += "\"mode\":\"" + String(apMode ? "AP" : "STA") + "\",";
doc["rssi"] = apMode ? 0 : WiFi.RSSI(); json += "\"rssi\":" + String(apMode ? 0 : WiFi.RSSI()) + ","; // RSSI not applicable in AP mode
doc["freeHeap"] = ESP.getFreeHeap(); json += "\"freeHeap\":" + String(ESP.getFreeHeap()) + ",";
doc["uptime"] = millis() / 1000; json += "\"uptime\":" + String(millis() / 1000);
json += "}";
String json;
serializeJson(doc, json);
server->send(200, "application/json", json); server->send(200, "application/json", json);
} }
@ -229,7 +220,6 @@ void CrossPointWebServer::scanFiles(const char* path, const std::function<void(F
} }
file.close(); file.close();
yield(); // Yield to allow WiFi and other tasks to process during long scans
file = root.openNextFile(); file = root.openNextFile();
} }
root.close(); root.close();
@ -264,15 +254,12 @@ void CrossPointWebServer::handleFileListData() const {
char output[512]; char output[512];
constexpr size_t outputSize = sizeof(output); constexpr size_t outputSize = sizeof(output);
bool seenFirst = false; bool seenFirst = false;
JsonDocument doc; scanFiles(currentPath.c_str(), [this, &output, seenFirst](const FileInfo& info) mutable {
JsonDocument doc;
scanFiles(currentPath.c_str(), [this, &output, &doc, seenFirst](const FileInfo& info) mutable {
doc.clear();
doc["name"] = info.name; doc["name"] = info.name;
doc["size"] = info.size; doc["size"] = info.size;
doc["isDirectory"] = info.isDirectory; doc["isDirectory"] = info.isDirectory;
doc["isEpub"] = info.isEpub; doc["isEpub"] = info.isEpub;
const size_t written = serializeJson(doc, output, outputSize); const size_t written = serializeJson(doc, output, outputSize);
if (written >= outputSize) { if (written >= outputSize) {
// JSON output truncated; skip this entry to avoid sending malformed JSON // JSON output truncated; skip this entry to avoid sending malformed JSON