diff --git a/common/unicode_sorta.hpp b/common/unicode_sorta.hpp new file mode 100644 index 00000000..3a6856e9 --- /dev/null +++ b/common/unicode_sorta.hpp @@ -0,0 +1,211 @@ +#pragma once + +#include + +/* +This file exists to allow space-efficient rendering of accented characters. + +It permits rudimentary support for unicode characters by providing a lookup +table to map anything prefixed with 0xc3 to its non-accented equivilent. +*/ + +namespace unicode_sorta { + +const char PAGE_194_START = 194; +const char PAGE_195_START = 195; + +// Codepage is probably completely the wrong terminology here +// but character pairs come in the form 0xc2 0x00 and 0xc3 0x00. +enum codepage_t : uint8_t { + PAGE_194, + PAGE_195 +}; + +const uint8_t UNICODE_PREFIX = 0xc3; + +enum accents : uint8_t { + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_TILDE, + ACCENT_DIAERESIS, + ACCENT_RING_ABOVE, + ACCENT_STROKE, + ACCENT_CEDILLA, + ACCENT_NONE, +}; + +static const accents char_accent[] = { + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_TILDE, + ACCENT_DIAERESIS, + ACCENT_RING_ABOVE, + ACCENT_NONE, + ACCENT_CEDILLA, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_NONE, + ACCENT_TILDE, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_TILDE, + ACCENT_DIAERESIS, + ACCENT_NONE, + ACCENT_STROKE, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_ACUTE, + ACCENT_NONE, + ACCENT_NONE, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_TILDE, + ACCENT_DIAERESIS, + ACCENT_RING_ABOVE, + ACCENT_NONE, + ACCENT_CEDILLA, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_NONE, + ACCENT_TILDE, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_TILDE, + ACCENT_DIAERESIS, + ACCENT_NONE, + ACCENT_STROKE, + ACCENT_GRAVE, + ACCENT_ACUTE, + ACCENT_CIRCUMFLEX, + ACCENT_DIAERESIS, + ACCENT_ACUTE, + ACCENT_NONE, + ACCENT_DIAERESIS +}; + +// Codepage 194. Starts at 0x80 but the first 32 are non-printable +// Since we're already implementing LUTs for various accented characters, +// it makes sense for us poor brits to grab a usable £, too! +static const char char_base_194[] = { + ' ', // - c2 a0 - NO-BREAK SPACE + 'i', // ¡ - c2 a1 - INVERTED EXCLAMATION MARK + ' ', // ¢ - c2 a2 - CENT SIGN + '\x85', // £ - c2 a3 - POUND SIGN + ' ', // ¤ - c2 a4 - CURRENCY SIGN + '\x86', // ¥ - c2 a5 - YEN SIGN + ' ', // ¦ - c2 a6 - BROKEN BAR + 'S', // § - c2 a7 - SECTION SIGN + ' ', // ¨ - c2 a8 - DIAERESIS + '\x87', // © - c2 a9 - COPYRIGHT SIGN + 'a', // ª - c2 aa - FEMININE ORDINAL INDICATOR + '<', // « - c2 ab - LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + ' ', // ¬ - c2 ac - NOT SIGN + ' ', // ­ - c2 ad - SOFT HYPHEN + 'R', // ® - c2 ae - REGISTERED SIGN + ' ', // ¯ - c2 af - MACRON + '\x88', // ° - c2 b0 - DEGREE SIGN + ' ', // ± - c2 b1 - PLUS-MINUS SIGN + '2', // ² - c2 b2 - SUPERSCRIPT TWO + '3', // ³ - c2 b3 - SUPERSCRIPT THREE + ' ', // ´ - c2 b4 - ACUTE ACCENT + ' ', // µ - c2 b5 - MICRO SIGN + ' ', // ¶ - c2 b6 - PILCROW SIGN + ' ', // · - c2 b7 - MIDDLE DOT + ' ', // ¸ - c2 b8 - CEDILLA + ' ', // ¹ - c2 b9 - SUPERSCRIPT ONE + ' ', // º - c2 ba - MASCULINE ORDINAL INDICATOR + ' ', // » - c2 bb - RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + ' ', // ¼ - c2 bc - VULGAR FRACTION ONE QUARTER + ' ', // ½ - c2 bd - VULGAR FRACTION ONE HALF + ' ', // ¾ - c2 be - VULGAR FRACTION THREE QUARTERS + ' ', // ¿ - c2 bf - INVERTED QUESTION MARK +}; + +// Codepage 195. Starts at 0x80 +static const char char_base_195[] = { + 'A', // À - c3 80 - A + GRAVE + 'A', // Á - c3 81 - A + ACUTE + 'A', // Â - c3 82 - A + CIRCUMFLEX + 'A', // Ã - c3 83 - A + TILDE + 'A', // Ä - c3 84 - A + DIAERESIS + 'A', // Å - c3 85 - A + RING ABOVE + '\x80', // Æ - c3 86 - AE + 'C', // Ç - c3 87 - C + CEDILLA + 'E', // È - c3 88 - E + GRAVE + 'E', // É - c3 89 - E + ACUTE + 'E', // Ê - c3 8a - E + CIRCUMFLEX + 'E', // Ë - c3 8b - E + DIAERESIS + 'I', // Ì - c3 8c - I + GRAVE + 'I', // Í - c3 8d - I + ACUTE + 'I', // Î - c3 8e - I + CIRCUMFLEX + 'I', // Ï - c3 8f - I + DIAERESIS + 'D', // Ð - c3 90 - ETH + 'N', // Ñ - c3 91 - N + TILDE + 'O', // Ò - c3 92 - O + GRAVE + 'O', // Ó - c3 93 - O + ACUTE + 'O', // Ô - c3 94 - O + CIRCUMFLEX + 'O', // Õ - c3 95 - O + TILDE + 'O', // Ö - c3 96 - O + DIAERESIS + 'x', // × - c3 97 - MULTIPLICATION SIGN + 'O', // Ø - c3 98 - O + STROKE + 'U', // Ù - c3 99 - U + GRAVE + 'U', // Ú - c3 9a - U + ACUTE + 'U', // Û - c3 9b - U + CIRCUMFLEX + 'U', // Ü - c3 9c - U + DIAERESIS + 'Y', // Ý - c3 9d - Y + ACUTE + '\x81', // Þ - c3 9e - THORN + '\x82', // ß - c3 9f - SHARP S + 'a', // à - c3 a0 - A + GRAVE + 'a', // á - c3 a1 - A + ACUTE + 'a', // â - c3 a2 - A + CIRCUMFLEX + 'a', // ã - c3 a3 - A + TILDE + 'a', // ä - c3 a4 - A + DIAERESIS + 'a', // å - c3 a5 - A + RING ABOVE + '\x83', // æ - c3 a6 - AE + 'c', // ç - c3 a7 - C + CEDILLA + 'e', // è - c3 a8 - E + GRAVE + 'e', // é - c3 a9 - E + ACUTE + 'e', // ê - c3 aa - E + CIRCUMFLEX + 'e', // ë - c3 ab - E + DIAERESIS + 'i', // ì - c3 ac - I + GRAVE + 'i', // í - c3 ad - I + ACUTE + 'i', // î - c3 ae - I + CIRCUMFLEX + 'i', // ï - c3 af - I + DIAERESIS + 'o', // ð - c3 b0 - ETH + 'n', // ñ - c3 b1 - N + TILDE + 'o', // ò - c3 b2 - O + GRAVE + 'o', // ó - c3 b3 - O + ACUTE + 'o', // ô - c3 b4 - O + CIRCUMFLEX + 'o', // õ - c3 b5 - O + TILDE + 'o', // ö - c3 b6 - O + DIAERESIS + '/', // ÷ - c3 b7 - DIVISION SIGN + 'o', // ø - c3 b8 - O + STROKE + 'u', // ù - c3 b9 - U + GRAVE + 'u', // ú - c3 ba - U + ACUTE + 'u', // û - c3 bb - U + CIRCUMFLEX + 'u', // ü - c3 bc - U + DIAERESIS + 'y', // ý - c3 bd - Y + ACUTE + '\x84', // þ - c3 be - THORN + 'y', // ÿ - c3 bf - Y + DIAERESIS +}; + +} \ No newline at end of file diff --git a/libraries/badger2040/badger2040.cpp b/libraries/badger2040/badger2040.cpp index c3644787..78dc200d 100644 --- a/libraries/badger2040/badger2040.cpp +++ b/libraries/badger2040/badger2040.cpp @@ -349,7 +349,7 @@ namespace pimoroni { } } - void Badger2040::text(std::string message, int32_t x, int32_t y, float s, float a) { + void Badger2040::text(std::string message, int32_t x, int32_t y, float s, float a, uint8_t letter_spacing) { if (_bitmap_font) { bitmap::text(_bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) { for(auto px = 0; px < w; px++) { @@ -357,7 +357,7 @@ namespace pimoroni { pixel(x + px, y + py); } } - }, message, x, y, 296 - x, std::max(1.0f, s)); + }, message, x, y, 296 - x, std::max(1.0f, s), letter_spacing); } else { hershey::text(_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) { line(x1, y1, x2, y2); @@ -365,8 +365,8 @@ namespace pimoroni { } } - int32_t Badger2040::measure_text(std::string message, float s) { - if (_bitmap_font) return bitmap::measure_text(_bitmap_font, message, std::max(1.0f, s)); + int32_t Badger2040::measure_text(std::string message, float s, uint8_t letter_spacing) { + if (_bitmap_font) return bitmap::measure_text(_bitmap_font, message, std::max(1.0f, s), letter_spacing); return hershey::measure_text(_font, message, s); } @@ -382,6 +382,9 @@ namespace pimoroni { } else if (name == "bitmap8") { _bitmap_font = &font8; _font = nullptr; + } else if (name == "bitmap14_outline") { + _bitmap_font = &font14_outline; + _font = nullptr; } else { // check that font exists and assign it if(hershey::fonts.find(name) != hershey::fonts.end()) { diff --git a/libraries/badger2040/badger2040.hpp b/libraries/badger2040/badger2040.hpp index 0ee75d19..774a9e67 100644 --- a/libraries/badger2040/badger2040.hpp +++ b/libraries/badger2040/badger2040.hpp @@ -8,6 +8,7 @@ #include "libraries/bitmap_fonts/bitmap_fonts.hpp" #include "libraries/bitmap_fonts/font6_data.hpp" #include "libraries/bitmap_fonts/font8_data.hpp" +#include "libraries/bitmap_fonts/font14_outline_data.hpp" namespace pimoroni { @@ -68,10 +69,10 @@ namespace pimoroni { // text (fonts: sans, sans_bold, gothic, cursive_bold, cursive, serif_italic, serif, serif_bold) const hershey::font_glyph_t* glyph_data(unsigned char c); - void text(std::string message, int32_t x, int32_t y, float s = 1.0f, float a = 0.0f); + void text(std::string message, int32_t x, int32_t y, float s = 1.0f, float a = 0.0f, uint8_t letter_spacing = 1); int32_t glyph(unsigned char c, int32_t x, int32_t y, float s = 1.0f, float a = 0.0f); - int32_t measure_text(std::string message, float s = 1.0f); + int32_t measure_text(std::string message, float s = 1.0f, uint8_t letter_spacing = 1); int32_t measure_glyph(unsigned char c, float s = 1.0f); void debug_command(uint8_t command, size_t len, const uint8_t *data); diff --git a/libraries/bitmap_fonts/bitmap_fonts.cpp b/libraries/bitmap_fonts/bitmap_fonts.cpp index 72c71fd6..e563de08 100644 --- a/libraries/bitmap_fonts/bitmap_fonts.cpp +++ b/libraries/bitmap_fonts/bitmap_fonts.cpp @@ -1,36 +1,127 @@ #include "bitmap_fonts.hpp" namespace bitmap { - int32_t measure_character(const font_t *font, const char c, const uint8_t scale) { - uint8_t char_index = c - 32; + int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage) { + if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp + return 0; + } + + uint8_t char_index = c; + + if(char_index > 127) { + if(codepage == unicode_sorta::PAGE_195) { + char_index = unicode_sorta::char_base_195[c - 128]; + } else { + char_index = unicode_sorta::char_base_194[c - 128 - 32]; + } + } + + char_index -= 32; + return font->widths[char_index] * scale; } - int32_t measure_text(const font_t *font, const std::string &t, const uint8_t scale) { + int32_t measure_text(const font_t *font, const std::string &t, const uint8_t scale, const uint8_t letter_spacing) { int32_t text_width = 0; + unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195; for(auto c : t) { - text_width += measure_character(font, c, scale); + if(c == unicode_sorta::PAGE_194_START) { + codepage = unicode_sorta::PAGE_194; + continue; + } else if (c == unicode_sorta::PAGE_195_START) { + continue; + } + text_width += measure_character(font, c, scale, codepage); + text_width += letter_spacing * scale; + codepage = unicode_sorta::PAGE_195; // Reset back to default } return text_width; } - void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale) { - uint8_t char_index = c - 32; + void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale, unicode_sorta::codepage_t codepage) { + if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp + return; + } - const uint8_t *d = &font->data[char_index * font->max_width]; + uint8_t char_index = c; + unicode_sorta::accents char_accent = unicode_sorta::ACCENT_NONE; + + // Remap any chars that fall outside of the 7-bit ASCII range + // using our unicode fudge lookup table. + if(char_index > 127) { + if(codepage == unicode_sorta::PAGE_195) { + char_index = unicode_sorta::char_base_195[c - 128]; + char_accent = unicode_sorta::char_accent[c - 128]; + } else { + char_index = unicode_sorta::char_base_194[c - 128 - 32]; + char_accent = unicode_sorta::ACCENT_NONE; + } + } + + // We don't map font data for the first 32 non-printable ASCII chars + char_index -= 32; + + // If our font is taller than 8 pixels it must be two bytes per column + bool two_bytes_per_column = font->height > 8; + + // Figure out how many bytes we need to skip per char to find our data in the array + uint8_t bytes_per_char = two_bytes_per_column ? font->max_width * 2 : font->max_width; + + // Get a pointer to the start of the data for this character + const uint8_t *d = &font->data[char_index * bytes_per_char]; + + // Accents can be up to 8 pixels tall on both 8bit and 16bit fonts + // Each accent's data is font->max_width bytes + 2 offset bytes long + const uint8_t *a = &font->data[(base_chars + extra_chars) * bytes_per_char + char_accent * (font->max_width + 2)]; + + // Effectively shift off the first two bytes of accent data- + // these are the lower and uppercase accent offsets + const uint8_t offset_lower = *a++; + const uint8_t offset_upper = *a++; + + // Pick which offset we should use based on the case of the char + // This is only valid for A-Z a-z. + // Note this magic number is relative to the start of printable ASCII chars. + uint8_t accent_offset = char_index < 65 ? offset_upper : offset_lower; + + // Offset our y position to account for our column canvas being 32 pixels + int y_offset = y - (8 * scale); + + // Iterate through each horizontal column of font (and accent) data for(uint8_t cx = 0; cx < font->widths[char_index]; cx++) { - for(uint8_t cy = 0; cy < font->height; cy++) { - if((1U << cy) & *d) { - rectangle(x + (cx * scale), y + (cy * scale), scale, scale); + // Our maximum bitmap font height will be 16 pixels + // give ourselves a 32 pixel high canvas in which to plot the char and accent. + // We shift the char down 8 pixels to make room for an accent above. + uint32_t data = *d << 8; + + // For fonts that are taller than 8 pixels (up to 16) they need two bytes + if(two_bytes_per_column) { + d++; + data <<= 8; // Move down the first byte + data |= *d << 8; // Add the second byte + } + + // If the char has an accent, merge it into the column data at its offset + if(char_accent != unicode_sorta::ACCENT_NONE) { + data |= *a << accent_offset; + } + + // Draw the 32 pixel column + for(uint8_t cy = 0; cy < 32; cy++) { + if((1U << cy) & data) { + rectangle(x + (cx * scale), y_offset + (cy * scale), scale, scale); } } + // Move to the next columns of char and accent data d++; + a++; } } - void text(const font_t *font, rect_func rectangle, const std::string &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale) { + void text(const font_t *font, rect_func rectangle, const std::string &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale, const uint8_t letter_spacing) { uint32_t co = 0, lo = 0; // character and line (if wrapping) offset + unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195; size_t i = 0; while(i < t.length()) { @@ -43,7 +134,14 @@ namespace bitmap { uint16_t word_width = 0; for(size_t j = i; j < next_space; j++) { - word_width += font->widths[t[j] - 32] * scale; + if (t[j] == unicode_sorta::PAGE_194_START) { + codepage = unicode_sorta::PAGE_194; + continue; + } else if (t[j] == unicode_sorta::PAGE_195_START) { + continue; + } + word_width += measure_character(font, t[j], scale, codepage); + codepage = unicode_sorta::PAGE_195; } // if this word would exceed the wrap limit then @@ -55,8 +153,16 @@ namespace bitmap { // draw word for(size_t j = i; j < next_space; j++) { - character(font, rectangle, t[j], x + co, y + lo, scale); - co += font->widths[t[j] - 32] * scale; + if (t[j] == unicode_sorta::PAGE_194_START) { + codepage = unicode_sorta::PAGE_194; + continue; + } else if (t[j] == unicode_sorta::PAGE_195_START) { + continue; + } + character(font, rectangle, t[j], x + co, y + lo, scale, codepage); + co += measure_character(font, t[j], scale, codepage); + co += letter_spacing * scale; + codepage = unicode_sorta::PAGE_195; } // move character offset to end of word and add a space diff --git a/libraries/bitmap_fonts/bitmap_fonts.hpp b/libraries/bitmap_fonts/bitmap_fonts.hpp index 432cafd7..9860e00d 100644 --- a/libraries/bitmap_fonts/bitmap_fonts.hpp +++ b/libraries/bitmap_fonts/bitmap_fonts.hpp @@ -3,20 +3,24 @@ #include #include #include +#include "common/unicode_sorta.hpp" namespace bitmap { + const int base_chars = 96; // 96 printable ASCII chars + const int extra_chars = 9; // Extra chars we've rempped that aren't just an ASCII char plus an accent + struct font_t { const uint8_t height; const uint8_t max_width; - const uint8_t widths[96]; + const uint8_t widths[base_chars + extra_chars]; const uint8_t data[]; }; typedef std::function rect_func; - int32_t measure_character(const font_t *font, const char c, const uint8_t scale); - int32_t measure_text(const font_t *font, const std::string &t, const uint8_t scale); + int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195); + int32_t measure_text(const font_t *font, const std::string &t, const uint8_t scale = 2, const uint8_t letter_spacing = 1); - void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2); - void text(const font_t *font, rect_func rectangle, const std::string &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2); + void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195); + void text(const font_t *font, rect_func rectangle, const std::string &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2, const uint8_t letter_spacing = 1); } \ No newline at end of file diff --git a/libraries/bitmap_fonts/font14_outline_data.hpp b/libraries/bitmap_fonts/font14_outline_data.hpp new file mode 100644 index 00000000..41b14d6b --- /dev/null +++ b/libraries/bitmap_fonts/font14_outline_data.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include "bitmap_fonts.hpp" + +const bitmap::font_t font14_outline { + .height = 14, + .max_width = 10, + .widths = { + 5, 3, 5,10, 7,10,10, 3, 5, 5, 6, 7, 4, 7, 3, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 4, 5, 8, 5, 7, + 8, 7, 6, 6, 6, 6, 6, 7, 7, 7, 7, 6, 6,10, 7, 8, + 6, 9, 6, 6, 7, 7, 7, 9, 7, 7, 7, 5, 7, 5, 7, 8, + 5, 8, 6, 5, 6, 6, 5, 7, 6, 3, 5, 6, 5, 9, 6, 7, + 6, 7, 5, 5, 6, 6, 7, 9, 7, 7, 7, 5, 3, 5, 8, 5, +// Extra +// Æ Þ ß æ þ £ ¥ © ° + 9, 6, 6, 10, 6, 6, 6, 6, 5 + }, + .data = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x0f, 0xfc, 0x0a, 0x04, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ! + 0x00, 0x7c, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " + 0x03, 0xf0, 0x02, 0xd0, 0x0e, 0xdc, 0x08, 0x04, 0x0e, 0xdc, 0x0e, 0xdc, 0x08, 0x04, 0x0e, 0xdc, 0x02, 0xd0, 0x03, 0xf0, // // + 0x0e, 0xf8, 0x0b, 0x8c, 0x1b, 0x76, 0x10, 0x02, 0x1b, 0x76, 0x0c, 0xd4, 0x07, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // $ + 0x00, 0x38, 0x00, 0x6c, 0x0f, 0x54, 0x09, 0xec, 0x0e, 0x78, 0x07, 0x9c, 0x0d, 0xe4, 0x0a, 0xbc, 0x0d, 0x80, 0x07, 0x00, // % + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // & + 0x00, 0x7c, 0x00, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' + 0x03, 0xf0, 0x0e, 0x1c, 0x19, 0xe6, 0x17, 0x3a, 0x1c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ( + 0x1c, 0x0e, 0x17, 0x3a, 0x19, 0xe6, 0x0e, 0x1c, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ) + 0x00, 0xfc, 0x00, 0xb4, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xb4, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // * + 0x01, 0xc0, 0x01, 0x40, 0x07, 0x70, 0x04, 0x10, 0x07, 0x70, 0x01, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + + 0x1c, 0x00, 0x17, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // , + 0x01, 0xc0, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - + 0x0e, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // . + 0x1e, 0x00, 0x13, 0x80, 0x1c, 0xe0, 0x07, 0x38, 0x01, 0xce, 0x00, 0x72, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // / + + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xf4, 0x0a, 0x14, 0x0b, 0xf4, 0x0c, 0x0c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 + 0x0e, 0x70, 0x0a, 0x58, 0x0b, 0xec, 0x08, 0x04, 0x0b, 0xfc, 0x0a, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 + 0x0e, 0x38, 0x0b, 0x2c, 0x09, 0xb4, 0x0a, 0xd4, 0x0b, 0x74, 0x0b, 0x8c, 0x0e, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 + 0x07, 0x38, 0x0d, 0x2c, 0x0b, 0x34, 0x0b, 0xf4, 0x0b, 0x34, 0x0c, 0xcc, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3 + 0x03, 0xc0, 0x02, 0x70, 0x02, 0x98, 0x0e, 0xec, 0x08, 0x04, 0x0e, 0xfc, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 + 0x0e, 0xfc, 0x0a, 0x84, 0x0a, 0xb4, 0x0a, 0xb4, 0x0b, 0xb4, 0x0c, 0x74, 0x07, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5 + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xb4, 0x0a, 0xb4, 0x0b, 0xb4, 0x0c, 0x74, 0x07, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 + 0x00, 0x1c, 0x00, 0x14, 0x0f, 0x94, 0x08, 0xf4, 0x0f, 0x34, 0x01, 0xc4, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 + 0x07, 0xf8, 0x0c, 0x4c, 0x0b, 0xb4, 0x0a, 0xb4, 0x0b, 0xb4, 0x0c, 0x4c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 + 0x0e, 0xf8, 0x0b, 0x8c, 0x0b, 0x74, 0x0b, 0x54, 0x0b, 0x74, 0x0c, 0x0c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9 + 0x0e, 0x1c, 0x0a, 0x14, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // : + 0x1c, 0x00, 0x17, 0x1c, 0x19, 0x14, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ; + 0x03, 0x80, 0x06, 0xc0, 0x0d, 0x60, 0x0b, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // < + 0x0e, 0xe0, 0x0a, 0xa0, 0x0a, 0xa0, 0x0a, 0xa0, 0x0a, 0xa0, 0x0a, 0xa0, 0x0a, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, // = + 0x0e, 0xe0, 0x0b, 0xa0, 0x0d, 0x60, 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // > + 0x00, 0x00, 0x00, 0x1c, 0x0f, 0xd4, 0x0a, 0x74, 0x0f, 0xb4, 0x00, 0xcc, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ? + + 0x0f, 0xf0, 0x18, 0x18, 0x37, 0xec, 0x2c, 0x74, 0x2b, 0xb4, 0x2b, 0xb4, 0x3c, 0x0c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, // @ + 0x0f, 0x80, 0x08, 0xf0, 0x0f, 0x1c, 0x01, 0x64, 0x0f, 0x1c, 0x08, 0xf0, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xb4, 0x0b, 0xb4, 0x0c, 0x4c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xf4, 0x0a, 0x14, 0x0a, 0x14, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // C + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xf4, 0x0b, 0xf4, 0x0c, 0x0c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xb4, 0x0a, 0xb4, 0x0a, 0xb4, 0x0e, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // E + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xb4, 0x00, 0xb4, 0x00, 0xf4, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // F + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xf4, 0x0b, 0xd4, 0x0b, 0x54, 0x0c, 0x5c, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // G + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xbc, 0x00, 0xa0, 0x0f, 0xbc, 0x08, 0x04, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // H + 0x0e, 0x1c, 0x0a, 0x14, 0x0b, 0xf4, 0x08, 0x04, 0x0b, 0xf4, 0x0a, 0x14, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // I + 0x0e, 0x1c, 0x0a, 0x14, 0x0b, 0xf4, 0x0c, 0x04, 0x07, 0xf4, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // J + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xbc, 0x0e, 0x5c, 0x09, 0xe4, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // K + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xfc, 0x0a, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // L + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xec, 0x00, 0xd8, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xd8, 0x0f, 0xec, 0x08, 0x04, 0x0f, 0xfc, // M + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xcc, 0x07, 0x38, 0x0c, 0xfc, 0x08, 0x04, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // N + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xf4, 0x0a, 0x14, 0x0a, 0x14, 0x0b, 0xf4, 0x0c, 0x0c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, // O + + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0x74, 0x01, 0x74, 0x01, 0x8c, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // P + 0x07, 0xf8, 0x0c, 0x0c, 0x0b, 0xf4, 0x0a, 0x14, 0x0a, 0x14, 0x1b, 0xf4, 0x14, 0x0c, 0x17, 0xf8, 0x1c, 0x00, 0x00, 0x00, // Q + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0x74, 0x0e, 0x74, 0x09, 0x8c, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // R + 0x0e, 0xf8, 0x0b, 0x8c, 0x0b, 0x74, 0x0b, 0x54, 0x0c, 0xd4, 0x07, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // S + 0x00, 0x1c, 0x00, 0x14, 0x0f, 0xf4, 0x08, 0x04, 0x0f, 0xf4, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // T + 0x07, 0xfc, 0x0c, 0x04, 0x0b, 0xfc, 0x0a, 0x00, 0x0b, 0xfc, 0x0c, 0x04, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U + 0x01, 0xfc, 0x07, 0x04, 0x0c, 0xfc, 0x0b, 0x80, 0x0c, 0xfc, 0x07, 0x04, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V + 0x01, 0xfc, 0x07, 0x04, 0x0c, 0xfc, 0x0b, 0xc0, 0x0c, 0x40, 0x0b, 0xc0, 0x0c, 0xfc, 0x07, 0x04, 0x01, 0xfc, 0x00, 0x00, // W + 0x0f, 0x3c, 0x09, 0xe4, 0x0e, 0xdc, 0x03, 0x30, 0x0e, 0xdc, 0x09, 0xe4, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X + 0x00, 0x3c, 0x00, 0xe4, 0x0f, 0x9c, 0x08, 0x70, 0x0f, 0x9c, 0x00, 0xe4, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Y + 0x0f, 0x1c, 0x09, 0x94, 0x0a, 0xf4, 0x0b, 0x34, 0x0b, 0xd4, 0x0a, 0x64, 0x0e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Z + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xf4, 0x0a, 0x14, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x1e, 0x00, 0x72, 0x01, 0xce, 0x07, 0x38, 0x1c, 0xe0, 0x13, 0x80, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // "\" + 0x0e, 0x1c, 0x0a, 0x14, 0x0b, 0xf4, 0x08, 0x04, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x70, 0x00, 0x58, 0x00, 0x6c, 0x00, 0x34, 0x00, 0x6c, 0x00, 0x58, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ^ + 0x1c, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, // _ + + 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x36, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ` + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0a, 0xa0, 0x0b, 0xa0, 0x0c, 0x60, 0x0b, 0xc0, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, // a + 0x0f, 0xfc, 0x08, 0x04, 0x0b, 0xbc, 0x0b, 0xa0, 0x0c, 0x60, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // b + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0a, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // c + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0b, 0xbc, 0x08, 0x04, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // d + 0x07, 0xc0, 0x0c, 0x60, 0x0a, 0xa0, 0x0a, 0xa0, 0x0b, 0x60, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // e + 0x0f, 0xf8, 0x08, 0x0c, 0x0f, 0xb4, 0x00, 0xf4, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f + 0x1f, 0xc0, 0x36, 0x60, 0x2d, 0xa0, 0x2d, 0xa0, 0x2d, 0xa0, 0x30, 0x60, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // g + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xbc, 0x0f, 0xa0, 0x08, 0x60, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // h + 0x0f, 0xf8, 0x08, 0x28, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // i + 0x1c, 0x00, 0x14, 0x00, 0x17, 0xf8, 0x18, 0x28, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // j + 0x0f, 0xfc, 0x08, 0x04, 0x0e, 0xfc, 0x0d, 0x60, 0x0b, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // k + 0x07, 0xfc, 0x0c, 0x04, 0x0b, 0xfc, 0x0a, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // l + 0x0f, 0xc0, 0x08, 0x60, 0x0f, 0xa0, 0x07, 0xa0, 0x04, 0x60, 0x07, 0xa0, 0x0f, 0xa0, 0x08, 0x60, 0x0f, 0xc0, 0x00, 0x00, // m + 0x0f, 0xc0, 0x08, 0x60, 0x0f, 0xa0, 0x0f, 0xa0, 0x08, 0x60, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // n + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0a, 0xa0, 0x0b, 0xa0, 0x0c, 0x60, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // o + + 0x3f, 0xe0, 0x20, 0x20, 0x3d, 0xa0, 0x05, 0xa0, 0x06, 0x60, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // p + 0x03, 0xc0, 0x06, 0x60, 0x05, 0xa0, 0x3d, 0xa0, 0x20, 0x20, 0x37, 0xe0, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // q + 0x0f, 0xc0, 0x08, 0x60, 0x0f, 0xa0, 0x00, 0xa0, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r + 0x0f, 0xc0, 0x0b, 0x60, 0x0a, 0xa0, 0x0d, 0xa0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // s + 0x01, 0xc0, 0x07, 0x70, 0x0c, 0x10, 0x0b, 0x70, 0x0b, 0xc0, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // t + 0x07, 0xe0, 0x0c, 0x20, 0x0b, 0xe0, 0x0b, 0xe0, 0x0c, 0x20, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // u + 0x01, 0xe0, 0x07, 0x20, 0x0c, 0xe0, 0x0b, 0x80, 0x0c, 0xe0, 0x07, 0x20, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // v + 0x01, 0xe0, 0x07, 0x20, 0x0c, 0xe0, 0x0b, 0x80, 0x0c, 0x80, 0x0b, 0x80, 0x0c, 0xe0, 0x07, 0x20, 0x01, 0xe0, 0x00, 0x00, // w + 0x0e, 0xe0, 0x0b, 0xa0, 0x0d, 0x60, 0x06, 0xc0, 0x0d, 0x60, 0x0b, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // x + 0x1d, 0xe0, 0x17, 0x20, 0x1a, 0xe0, 0x0d, 0x80, 0x06, 0xe0, 0x03, 0x20, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // y + 0x0e, 0xe0, 0x0b, 0xa0, 0x09, 0xa0, 0x0a, 0xa0, 0x0b, 0x20, 0x0b, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // z + 0x01, 0xe0, 0x0f, 0x3c, 0x18, 0xc6, 0x17, 0xfa, 0x1c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x1f, 0xfe, 0x10, 0x02, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // | + 0x1c, 0x0e, 0x17, 0xfa, 0x18, 0xc6, 0x0f, 0x3c, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x03, 0x80, 0x02, 0xc0, 0x03, 0x40, 0x03, 0x40, 0x02, 0xc0, 0x02, 0xc0, 0x03, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, // ~ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + // Extra + 0x0f, 0x80, 0x08, 0xf0, 0x0f, 0x1c, 0x01, 0x64, 0x08, 0x04, 0x0b, 0xb4, 0x0a, 0xb4, 0x0a, 0xb4, 0x0e, 0xfc, 0x00, 0x00, // Æ + 0x3f, 0xe0, 0x20, 0x20, 0x3d, 0xa0, 0x05, 0xa0, 0x06, 0x60, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Þ + 0x0f, 0xfc, 0x08, 0x04, 0x0f, 0xb4, 0x0b, 0xb4, 0x0c, 0x4c, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ß + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0a, 0xa0, 0x0b, 0xa0, 0x0c, 0x60, 0x0a, 0xa0, 0x0a, 0xa0, 0x0b, 0x60, 0x0f, 0xc0, // æ + 0x3f, 0xe0, 0x20, 0x20, 0x3d, 0xa0, 0x05, 0xa0, 0x06, 0x60, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // þ + 0x00, 0xe0, 0x0f, 0xb8, 0x08, 0x04, 0x0b, 0xb4, 0x0a, 0xb4, 0x0e, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // £ + 0x00, 0x3c, 0x00, 0xe4, 0x0f, 0x9c, 0x08, 0x70, 0x0f, 0x9c, 0x00, 0xe4, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ¥ + 0x07, 0xc0, 0x0c, 0x60, 0x0b, 0xa0, 0x0a, 0xa0, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // © + 0x00, 0x38, 0x00, 0x44, 0x00, 0x54, 0x00, 0x44, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ° + // Accents + Offsets + // All chars are shifted 8px down into a 32 pixel canvas for combining with accents. + // Accent shift values (the first two numbers in each line below) move the accent down to meet them. + // These are the shift values for lower and UPPER case letters respectively. + 9,6, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Grave + 9,6, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Acute + 9,6, 0x00, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Circumflex + 9,6, 0x00, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Tilde + 10,7, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Diaresis + 9,6, 0x00, 0x02, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ring Above + 12,10, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, // Stroke + 16,16, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cedilla + } +}; \ No newline at end of file diff --git a/libraries/bitmap_fonts/font6_data.hpp b/libraries/bitmap_fonts/font6_data.hpp index 7b199476..f334d977 100644 --- a/libraries/bitmap_fonts/font6_data.hpp +++ b/libraries/bitmap_fonts/font6_data.hpp @@ -6,12 +6,15 @@ const bitmap::font_t font6 { .height = 6, .max_width = 6, .widths = { - 3, 2, 4, 6, 6, 6, 7, 2, 3, 3, 4, 4, 2, 4, 2, 4, - 6, 3, 5, 5, 6, 5, 6, 6, 6, 6, 2, 2, 4, 4, 4, 5, - 7, 6, 6, 5, 6, 5, 5, 6, 5, 4, 5, 5, 5, 6, 6, 6, - 6, 6, 6, 5, 6, 6, 6, 6, 5, 5, 5, 3, 4, 3, 4, 4, - 3, 6, 6, 5, 6, 5, 5, 6, 5, 4, 5, 5, 5, 6, 6, 6, - 6, 6, 6, 5, 6, 6, 6, 6, 5, 5, 5, 4, 2, 4, 4, 2 + 3, 1, 3, 5, 5, 5, 6, 1, 2, 2, 3, 3, 1, 3, 1, 3, + 5, 2, 4, 4, 5, 4, 5, 5, 5, 5, 1, 1, 3, 3, 3, 4, + 6, 5, 5, 4, 5, 4, 4, 5, 4, 3, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 4, 5, 5, 5, 5, 4, 4, 4, 2, 3, 2, 3, 3, + 2, 5, 5, 4, 5, 4, 4, 5, 4, 3, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 4, 5, 5, 5, 5, 4, 4, 4, 3, 1, 3, 3, 1, +// Extra +// Æ Þ ß æ þ £ ¥ © ° + 5, 5, 4, 5, 5, 4, 4, 4, 3 }, .data = { 0x00,0x00,0x00,0x00,0x00,0x00, // @@ -109,6 +112,28 @@ const bitmap::font_t font6 { 0x3e,0x00,0x00,0x00,0x00,0x00, // | 0x22,0x3e,0x08,0x00,0x00,0x00, // } 0x04,0x02,0x02,0x00,0x00,0x00, // ~ - 0x00,0x00,0x00,0x00,0x00,0x00 + 0x00,0x00,0x00,0x00,0x00,0x00, + // Extra + 0x3c,0x12,0x3c,0x2a,0x2a,0x00, // Æ + 0x3f,0x12,0x12,0x12,0x0e,0x00, // Þ + 0x3e,0x0a,0x2a,0x34,0x00,0x00, // ß + 0x3c,0x12,0x3c,0x2a,0x2a,0x00, // æ + 0x3f,0x12,0x12,0x12,0x0e,0x00, // þ + 0x08,0x3c,0x2a,0x2a,0x00,0x00, // £ + 0x26,0x28,0x28,0x1e,0x00,0x00, // ¥s + 0x1c,0x22,0x22,0x22,0x00,0x00, // © + 0x02,0x05,0x02,0x00,0x00,0x00, // ° + // Accents + Offsets + // All chars are shifted 8px down into a 32 pixel canvas for combining with accents. + // Accent shift values (the first two numbers in each line below) move the accent down to meet them. + // These are the shift values for lower and UPPER case letters respectively. + 6,6, 0x00,0x00,0x01,0x02,0x00,0x00, // Grave + 6,6, 0x00,0x00,0x02,0x01,0x00,0x00, // Acute + 6,6, 0x00,0x02,0x01,0x02,0x00,0x00, // Circumflex + 6,6, 0x00,0x01,0x02,0x01,0x02,0x00, // Tilde + 6,6, 0x00,0x01,0x00,0x01,0x00,0x00, // Diaresis + 6,6, 0x00,0x02,0x05,0x02,0x00,0x00, // Ring Above + 6,6, 0x00,0x40,0x20,0x10,0x00,0x00, // Stroke + 10,10, 0x00,0x00,0x28,0x10,0x00,0x00 // Cedilla } }; \ No newline at end of file diff --git a/libraries/bitmap_fonts/font8_data.hpp b/libraries/bitmap_fonts/font8_data.hpp index ad9601dc..358c894b 100644 --- a/libraries/bitmap_fonts/font8_data.hpp +++ b/libraries/bitmap_fonts/font8_data.hpp @@ -6,12 +6,15 @@ const bitmap::font_t font8 { .height = 8, .max_width = 6, .widths = { - 2, 2, 4, 6, 5, 5, 5, 2, 4, 4, 4, 4, 3, 4, 3, 5, - 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 2, 3, 4, 4, 4, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 6, 5, 5, - 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 5, 3, 5, 3, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 4, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 4, 2, 4, 5, 2 + 3, 1, 3, 5, 4, 4, 4, 1, 3, 3, 3, 3, 2, 3, 2, 4, + 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 1, 2, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 5, 4, 4, + 4, 4, 4, 4, 5, 4, 4, 5, 4, 4, 4, 2, 4, 2, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 3, 5, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 3, 1, 3, 4, 1, +// Extra +// Æ Þ ß æ þ £ ¥ © ° + 5, 4, 4, 5, 4, 4, 4, 4, 3 }, .data = { 0x00,0x00,0x00,0x00,0x00,0x00, // @@ -109,6 +112,28 @@ const bitmap::font_t font8 { 0x7f,0x00,0x00,0x00,0x00,0x00, // | 0x41,0x3e,0x08,0x00,0x00,0x00, // } 0x08,0x04,0x08,0x04,0x00,0x00, // ~ - 0x00,0x00,0x00,0x00,0x00,0x00 + 0x00,0x00,0x00,0x00,0x00,0x00, + // Extra + 0x7e,0x09,0x7f,0x49,0x49,0x00, // Æ + 0x7e,0x24,0x24,0x18,0x00,0x00, // Þ + 0x7e,0x09,0x49,0x36,0x00,0x00, // ß + 0x20,0x54,0x78,0x54,0x58,0x00, // æ + 0x7f,0x24,0x24,0x18,0x00,0x00, // þ + 0x08,0x7e,0x49,0x41,0x00,0x00, // £ + 0x47,0x48,0x48,0x3f,0x00,0x00, // ¥ + 0x38,0x44,0x44,0x28,0x00,0x00, // © + 0x02,0x05,0x02,0x00,0x00,0x00, // ° + // Accents + Offsets + // All chars are shifted 8px down into a 32 pixel canvas for combining with accents. + // Accent shift values (the first two numbers in each line below) move the accent down to meet them. + // These are the shift values for lower and UPPER case letters respectively. + 6,4, 0x00,0x00,0x01,0x02,0x00,0x00, // Grave + 6,4, 0x00,0x00,0x02,0x01,0x00,0x00, // Acute + 6,4, 0x00,0x02,0x01,0x02,0x00,0x00, // Circumflex + 6,4, 0x00,0x01,0x02,0x01,0x02,0x00, // Tilde + 6,4, 0x00,0x01,0x00,0x01,0x00,0x00, // Diaresis + 6,4, 0x00,0x02,0x05,0x02,0x00,0x00, // Ring Above + 6,4, 0x00,0x80,0x40,0x00,0x00,0x00, // Stroke + 9,9, 0x00,0x00,0xa0,0x40,0x00,0x00 // Cedilla } }; \ No newline at end of file diff --git a/libraries/hershey_fonts/hershey_fonts.cpp b/libraries/hershey_fonts/hershey_fonts.cpp index cd9df968..7234488c 100644 --- a/libraries/hershey_fonts/hershey_fonts.cpp +++ b/libraries/hershey_fonts/hershey_fonts.cpp @@ -1,4 +1,5 @@ #include "hershey_fonts.hpp" +#include "common/unicode_sorta.hpp" #include namespace hershey { @@ -18,10 +19,14 @@ namespace hershey { } const font_glyph_t* glyph_data(const font_t* font, unsigned char c) { - if(c < 32 || c > 127) { + if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp return nullptr; } + if(c > 127) { + c = unicode_sorta::char_base_195[c - 128]; + } + return &font->chars[c - 32]; } diff --git a/micropython/examples/badger2040/fonts.py b/micropython/examples/badger2040/fonts.py index 9cf3d366..bc420a48 100644 --- a/micropython/examples/badger2040/fonts.py +++ b/micropython/examples/badger2040/fonts.py @@ -2,17 +2,25 @@ import badger2040 import badger_os # Global Constants -FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic") +FONT_NAMES = ( + ("sans", 0.7, 2), + ("gothic", 0.7, 2), + ("cursive", 0.7, 2), + ("serif", 0.7, 2), + ("serif_italic", 0.7, 2), + ("bitmap6", 3, 1), + ("bitmap8", 2, 1), + ("bitmap14_outline", 1, 1) +) WIDTH = badger2040.WIDTH HEIGHT = badger2040.HEIGHT MENU_TEXT_SIZE = 0.5 -MENU_SPACING = 20 +MENU_SPACING = 16 MENU_WIDTH = 84 -MENU_PADDING = 2 +MENU_PADDING = 5 -TEXT_SIZE = 0.8 TEXT_INDENT = MENU_WIDTH + 10 ARROW_THICKNESS = 3 @@ -59,27 +67,28 @@ def draw_frame(): # Draw the fonts and menu def draw_fonts(): - display.font("sans") + display.font("bitmap8") display.thickness(1) for i in range(len(FONT_NAMES)): - name = FONT_NAMES[i] + name, size, thickness = FONT_NAMES[i] display.pen(0) if i == state["selected_font"]: display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING) display.pen(15) - display.text(name, MENU_PADDING, (i * MENU_SPACING) + (MENU_SPACING // 2), MENU_TEXT_SIZE) + display.text(name, MENU_PADDING, (i * MENU_SPACING) + int((MENU_SPACING - 8) / 2), MENU_TEXT_SIZE) - display.font(FONT_NAMES[state["selected_font"]]) - display.thickness(2) + name, size, thickness = FONT_NAMES[state["selected_font"]] + display.font(name) + display.thickness(thickness) + + y = 0 if name.startswith("bitmap") else 10 display.pen(0) - display.text("The quick", TEXT_INDENT, 10, TEXT_SIZE) - display.text("brown fox", TEXT_INDENT, 32, TEXT_SIZE) - display.text("jumped over", TEXT_INDENT, 54, TEXT_SIZE) - display.text("the lazy dog.", TEXT_INDENT, 76, TEXT_SIZE) - display.text("0123456789", TEXT_INDENT, 98, TEXT_SIZE) - display.text("!\"£$%^&*()", TEXT_INDENT, 120, TEXT_SIZE) + for line in ("The quick", "brown fox", "jumps over", "the lazy dog.", "0123456789", "!\"£$%^&*()"): + display.text(line, TEXT_INDENT, y, size) + y += 22 + display.thickness(1) display.update() diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 104f5dc3..fbd29c26 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -408,14 +408,15 @@ mp_obj_t Badger2040_icon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_obj_t Badger2040_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_message, ARG_x, ARG_y, ARG_scale, ARG_rotation }; + enum { ARG_self, ARG_message, ARG_x, ARG_y, ARG_scale, ARG_rotation, ARG_letter_spacing }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_message, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_rotation, MP_ARG_OBJ, {.u_obj = mp_const_none} } + { MP_QSTR_rotation, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_letter_spacing, MP_ARG_INT, {.u_int = 1} } }; // Parse args. @@ -423,6 +424,7 @@ mp_obj_t Badger2040_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); std::string message = mp_obj_to_string_r(args[ARG_message].u_obj); + int spacing = args[ARG_letter_spacing].u_int; int x = args[ARG_x].u_int; int y = args[ARG_y].u_int; float scale = 1.0f; @@ -435,7 +437,7 @@ mp_obj_t Badger2040_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a } _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Badger2040_obj_t); - self->badger2040->text(message, x, y, scale, rotation); + self->badger2040->text(message, x, y, scale, rotation, spacing); return mp_const_none; } @@ -444,7 +446,7 @@ mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale, ARG_rotation }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -455,7 +457,8 @@ mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - int c = args[ARG_char].u_int; + int c = 0; + int x = args[ARG_x].u_int; int y = args[ARG_y].u_int; float scale = 1.0f; @@ -468,23 +471,34 @@ mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ } _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Badger2040_obj_t); - self->badger2040->glyph(c, x, y, scale, rotation); + + if (mp_obj_is_int(args[ARG_char].u_obj)) { + c = (uint8_t)mp_obj_get_int(args[ARG_char].u_obj); + self->badger2040->glyph(c, x, y, scale, rotation); + } else if (mp_obj_is_str_or_bytes(args[ARG_char].u_obj)) { + std::string message = mp_obj_to_string_r(args[ARG_char].u_obj); + self->badger2040->text(message, x, y, scale, rotation); + } else { + mp_raise_TypeError("glyph: expected char or string."); + } return mp_const_none; } mp_obj_t Badger2040_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_message, ARG_scale }; + enum { ARG_self, ARG_message, ARG_scale, ARG_letter_spacing }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_message, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_letter_spacing, MP_ARG_INT, {.u_int = 1} } }; // Parse args. mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + int spacing = args[ARG_letter_spacing].u_int; std::string message = mp_obj_to_string_r(args[ARG_message].u_obj); float scale = 1.0f; if (args[ARG_scale].u_obj != mp_const_none) { @@ -492,7 +506,7 @@ mp_obj_t Badger2040_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map } _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Badger2040_obj_t); - return mp_obj_new_int(self->badger2040->measure_text(message, scale)); + return mp_obj_new_int(self->badger2040->measure_text(message, scale, spacing)); } mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {