mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-13 10:08:08 +00:00
WIP - utf8 iterator for C
This commit is contained in:
parent
5c9995b777
commit
d594c7c3af
@ -99,9 +99,9 @@ void display_text_render_buffer(const char *text, int textlen, int font,
|
|||||||
int baseline = font_baseline(font);
|
int baseline = font_baseline(font);
|
||||||
|
|
||||||
// render glyphs
|
// render glyphs
|
||||||
for (int c_idx = 0; c_idx < textlen; c_idx++) {
|
font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen);
|
||||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[c_idx]);
|
const uint8_t *g = NULL;
|
||||||
if (!g) continue;
|
while (font_next_glyph(&iter, &g)) {
|
||||||
const uint8_t w = g[0]; // width
|
const uint8_t w = g[0]; // width
|
||||||
const uint8_t h = g[1]; // height
|
const uint8_t h = g[1]; // height
|
||||||
const uint8_t adv = g[2]; // advance
|
const uint8_t adv = g[2]; // advance
|
||||||
@ -307,9 +307,9 @@ static void display_text_render(int x, int y, const char *text, int textlen,
|
|||||||
set_color_table(colortable, fgcolor, bgcolor);
|
set_color_table(colortable, fgcolor, bgcolor);
|
||||||
|
|
||||||
// render glyphs
|
// render glyphs
|
||||||
for (int c_idx = 0; c_idx < textlen; c_idx++) {
|
font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen);
|
||||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[c_idx]);
|
const uint8_t *g = NULL;
|
||||||
if (!g) continue;
|
while (font_next_glyph(&iter, &g)) {
|
||||||
const uint8_t w = g[0]; // width
|
const uint8_t w = g[0]; // width
|
||||||
const uint8_t h = g[1]; // height
|
const uint8_t h = g[1]; // height
|
||||||
const uint8_t adv = g[2]; // advance
|
const uint8_t adv = g[2]; // advance
|
||||||
@ -363,9 +363,9 @@ static void display_text_render(int x, int y, const char *text, int textlen,
|
|||||||
set_color_table(colortable, fgcolor, bgcolor);
|
set_color_table(colortable, fgcolor, bgcolor);
|
||||||
|
|
||||||
// render glyphs
|
// render glyphs
|
||||||
for (int i = 0; i < textlen; i++) {
|
font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen);
|
||||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[i]);
|
const uint8_t *g = NULL;
|
||||||
if (!g) continue;
|
while (font_next_glyph(&iter, &g)) {
|
||||||
const uint8_t w = g[0]; // width
|
const uint8_t w = g[0]; // width
|
||||||
const uint8_t h = g[1]; // height
|
const uint8_t h = g[1]; // height
|
||||||
const uint8_t adv = g[2]; // advance
|
const uint8_t adv = g[2]; // advance
|
||||||
@ -433,9 +433,9 @@ int display_text_width(const char *text, int textlen, int font) {
|
|||||||
if (textlen < 0) {
|
if (textlen < 0) {
|
||||||
textlen = strlen(text);
|
textlen = strlen(text);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < textlen; i++) {
|
font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen);
|
||||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[i]);
|
const uint8_t *g = NULL;
|
||||||
if (!g) continue;
|
while (font_next_glyph(&iter, &g)) {
|
||||||
const uint8_t adv = g[2]; // advance
|
const uint8_t adv = g[2]; // advance
|
||||||
width += adv;
|
width += adv;
|
||||||
/*
|
/*
|
||||||
@ -452,35 +452,6 @@ int display_text_width(const char *text, int textlen, int font) {
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns how many characters of the string can be used before exceeding
|
|
||||||
// the requested width. Tries to avoid breaking words if possible.
|
|
||||||
int display_text_split(const char *text, int textlen, int font,
|
|
||||||
int requested_width) {
|
|
||||||
int width = 0;
|
|
||||||
int lastspace = 0;
|
|
||||||
// determine text length if not provided
|
|
||||||
if (textlen < 0) {
|
|
||||||
textlen = strlen(text);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < textlen; i++) {
|
|
||||||
if (text[i] == ' ') {
|
|
||||||
lastspace = i;
|
|
||||||
}
|
|
||||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[i]);
|
|
||||||
if (!g) continue;
|
|
||||||
const uint8_t adv = g[2]; // advance
|
|
||||||
width += adv;
|
|
||||||
if (width > requested_width) {
|
|
||||||
if (lastspace > 0) {
|
|
||||||
return lastspace;
|
|
||||||
} else {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return textlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TREZOR_PRODTEST
|
#ifdef TREZOR_PRODTEST
|
||||||
|
|
||||||
#include "qr-code-generator/qrcodegen.h"
|
#include "qr-code-generator/qrcodegen.h"
|
||||||
|
@ -59,8 +59,6 @@ void display_text_center(int x, int y, const char *text, int textlen, int font,
|
|||||||
void display_text_right(int x, int y, const char *text, int textlen, int font,
|
void display_text_right(int x, int y, const char *text, int textlen, int font,
|
||||||
uint16_t fgcolor, uint16_t bgcolor);
|
uint16_t fgcolor, uint16_t bgcolor);
|
||||||
int display_text_width(const char *text, int textlen, int font);
|
int display_text_width(const char *text, int textlen, int font);
|
||||||
int display_text_split(const char *text, int textlen, int font,
|
|
||||||
int requested_width);
|
|
||||||
void display_text_render_buffer(const char *text, int textlen, int font,
|
void display_text_render_buffer(const char *text, int textlen, int font,
|
||||||
buffer_text_t *buffer, int text_offset);
|
buffer_text_t *buffer, int text_offset);
|
||||||
|
|
||||||
|
@ -18,46 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifdef TRANSLATIONS
|
#ifdef TRANSLATIONS
|
||||||
#include "librust_fonts.h"
|
#include "librust_fonts.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: make it return uint32_t (needs logic to assemble at most 4 bytes
|
#define UNICODE_BADCHAR 0xFFFD
|
||||||
// together)
|
|
||||||
static uint16_t convert_char_utf8(const uint8_t c) {
|
|
||||||
// Considering only two-byte UTF-8 characters currently
|
|
||||||
static uint8_t first_utf8_byte = 0;
|
|
||||||
|
|
||||||
// non-printable ASCII character
|
|
||||||
if (c < ' ') {
|
|
||||||
first_utf8_byte = 0;
|
|
||||||
return 0x7F;
|
|
||||||
}
|
|
||||||
|
|
||||||
// regular ASCII character
|
|
||||||
if (c < 0x80) {
|
|
||||||
first_utf8_byte = 0;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTF-8 handling: https://en.wikipedia.org/wiki/UTF-8#Encoding
|
|
||||||
|
|
||||||
// bytes 11xxxxxx are first bytes of UTF-8 characters
|
|
||||||
if (c >= 0xC0) {
|
|
||||||
first_utf8_byte = c;
|
|
||||||
return 0; // not print this
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first_utf8_byte) {
|
|
||||||
// encountered a successive UTF-8 character ...
|
|
||||||
return ((uint16_t)first_utf8_byte << 8) | c;
|
|
||||||
} else {
|
|
||||||
// ... or they are just non-printable ASCII characters
|
|
||||||
return 0x7F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int font_height(int font) {
|
int font_height(int font) {
|
||||||
switch (font) {
|
switch (font) {
|
||||||
@ -137,55 +105,76 @@ int font_baseline(int font) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t *font_get_glyph(int font, uint8_t c) {
|
font_glyph_iter_t font_glyph_iter_init(const int font, const uint8_t *text,
|
||||||
uint16_t c_2bytes = convert_char_utf8(c);
|
const int len) {
|
||||||
if (!c_2bytes) return 0;
|
return (font_glyph_iter_t){
|
||||||
bool is_printable = c_2bytes != 0x7F;
|
.font = font,
|
||||||
|
.text = text,
|
||||||
|
.remaining = len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TRANSLATIONS
|
#define IS_UTF8_CONTINUE(c) (((c) & 0b11000000) == 0b10000000)
|
||||||
// found UTF8 character
|
|
||||||
// it is not hardcoded in firmware fonts, it must be extracted from the
|
static uint16_t next_utf8_codepoint(font_glyph_iter_t *iter) {
|
||||||
// embedded blob
|
uint16_t out;
|
||||||
if (c_2bytes > 0xFF) {
|
assert(iter->remaining > 0);
|
||||||
PointerData glyph_data = get_utf8_glyph(c_2bytes, font);
|
// 1-byte UTF-8 character
|
||||||
// TODO: is it better to use (!glyph_data.ptr) or (glyph_data.ptr == NULL)?
|
if (iter->text[0] < 0x7f) {
|
||||||
// first one does not require import
|
out = iter->text[0];
|
||||||
if (glyph_data.ptr != NULL) {
|
++iter->text;
|
||||||
return glyph_data.ptr;
|
--iter->remaining;
|
||||||
} else {
|
return out;
|
||||||
is_printable = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
// 2-byte UTF-8 character
|
||||||
|
if (iter->remaining >= 2 && ((iter->text[0] & 0b11100000) == 0b11000000) &&
|
||||||
// printable ASCII character
|
IS_UTF8_CONTINUE(iter->text[1])) {
|
||||||
if (is_printable && c_2bytes >= ' ' && c_2bytes <= 126) {
|
out = (((uint16_t)iter->text[0] & 0b00011111) << 6) |
|
||||||
switch (font) {
|
(iter->text[1] & 0b00111111);
|
||||||
#ifdef TREZOR_FONT_NORMAL_ENABLE
|
iter->text += 2;
|
||||||
case FONT_NORMAL:
|
iter->remaining -= 2;
|
||||||
return FONT_NORMAL_DATA[c_2bytes - ' '];
|
return out;
|
||||||
#endif
|
}
|
||||||
#ifdef TREZOR_FONT_DEMIBOLD_ENABLE
|
// 3-byte UTF-8 character
|
||||||
case FONT_DEMIBOLD:
|
if (iter->remaining >= 3 && ((iter->text[0] & 0b11110000) == 0b11100000) &&
|
||||||
return FONT_DEMIBOLD_DATA[c_2bytes - ' '];
|
IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2])) {
|
||||||
#endif
|
out = (((uint16_t)iter->text[0] & 0b00001111) << 12) |
|
||||||
#ifdef TREZOR_FONT_BOLD_ENABLE
|
(((uint16_t)iter->text[1] & 0b00111111) << 6) |
|
||||||
case FONT_BOLD:
|
(iter->text[2] & 0b00111111);
|
||||||
return FONT_BOLD_DATA[c_2bytes - ' '];
|
iter->text += 3;
|
||||||
#endif
|
iter->remaining -= 3;
|
||||||
#ifdef TREZOR_FONT_MONO_ENABLE
|
return out;
|
||||||
case FONT_MONO:
|
}
|
||||||
return FONT_MONO_DATA[c_2bytes - ' '];
|
// 4-byte UTF-8 character
|
||||||
#endif
|
if (iter->remaining >= 4 && ((iter->text[0] & 0b11111000) == 0b11110000) &&
|
||||||
#ifdef TREZOR_FONT_BIG_ENABLE
|
IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2]) &&
|
||||||
case FONT_BIG:
|
IS_UTF8_CONTINUE(iter->text[3])) {
|
||||||
return FONT_BIG_DATA[c_2bytes - ' '];
|
// we use 16-bit codepoints, so we can't represent 4-byte UTF-8 characters
|
||||||
#endif
|
iter->text += 4;
|
||||||
}
|
iter->remaining -= 4;
|
||||||
return 0;
|
return UNICODE_BADCHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-printable character
|
++iter->text;
|
||||||
|
--iter->remaining;
|
||||||
|
return UNICODE_BADCHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool font_next_glyph(font_glyph_iter_t *iter, const uint8_t **out) {
|
||||||
|
if (iter->remaining <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16_t c = next_utf8_codepoint(iter);
|
||||||
|
*out = font_get_glyph(iter->font, c);
|
||||||
|
if (*out == NULL) {
|
||||||
|
// should not happen but ¯\_(ツ)_/¯
|
||||||
|
return font_next_glyph(iter, out);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *font_nonprintable_glyph(int font) {
|
||||||
#define PASTER(s) s##_glyph_nonprintable
|
#define PASTER(s) s##_glyph_nonprintable
|
||||||
#define NONPRINTABLE_GLYPH(s) PASTER(s)
|
#define NONPRINTABLE_GLYPH(s) PASTER(s)
|
||||||
|
|
||||||
@ -210,6 +199,50 @@ const uint8_t *font_get_glyph(int font, uint8_t c) {
|
|||||||
case FONT_BIG:
|
case FONT_BIG:
|
||||||
return NONPRINTABLE_GLYPH(FONT_BIG_DATA);
|
return NONPRINTABLE_GLYPH(FONT_BIG_DATA);
|
||||||
#endif
|
#endif
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
const uint8_t *font_get_glyph(int font, uint16_t c) {
|
||||||
|
#ifdef TRANSLATIONS
|
||||||
|
// found UTF8 character
|
||||||
|
// it is not hardcoded in firmware fonts, it must be extracted from the
|
||||||
|
// embedded blob
|
||||||
|
if (c >= 0x7F) {
|
||||||
|
const uint8_t *g = get_utf8_glyph(c, font);
|
||||||
|
if (g != NULL) {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// printable ASCII character
|
||||||
|
if (c >= ' ' && c < 0x7F) {
|
||||||
|
switch (font) {
|
||||||
|
#ifdef TREZOR_FONT_NORMAL_ENABLE
|
||||||
|
case FONT_NORMAL:
|
||||||
|
return FONT_NORMAL_DATA[c - ' '];
|
||||||
|
#endif
|
||||||
|
#ifdef TREZOR_FONT_DEMIBOLD_ENABLE
|
||||||
|
case FONT_DEMIBOLD:
|
||||||
|
return FONT_DEMIBOLD_DATA[c - ' '];
|
||||||
|
#endif
|
||||||
|
#ifdef TREZOR_FONT_BOLD_ENABLE
|
||||||
|
case FONT_BOLD:
|
||||||
|
return FONT_BOLD_DATA[c - ' '];
|
||||||
|
#endif
|
||||||
|
#ifdef TREZOR_FONT_MONO_ENABLE
|
||||||
|
case FONT_MONO:
|
||||||
|
return FONT_MONO_DATA[c - ' '];
|
||||||
|
#endif
|
||||||
|
#ifdef TREZOR_FONT_BIG_ENABLE
|
||||||
|
case FONT_BIG:
|
||||||
|
return FONT_BIG_DATA[c - ' '];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return font_nonprintable_glyph(font);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#ifndef _FONTS_H
|
#ifndef _FONTS_H
|
||||||
#define _FONTS_H
|
#define _FONTS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "fonts/font_bitmap.h"
|
#include "fonts/font_bitmap.h"
|
||||||
#include TREZOR_BOARD
|
#include TREZOR_BOARD
|
||||||
|
|
||||||
@ -117,6 +119,17 @@
|
|||||||
int font_height(int font);
|
int font_height(int font);
|
||||||
int font_max_height(int font);
|
int font_max_height(int font);
|
||||||
int font_baseline(int font);
|
int font_baseline(int font);
|
||||||
const uint8_t *font_get_glyph(int font, uint8_t c);
|
const uint8_t *font_get_glyph(int font, uint16_t c);
|
||||||
|
const uint8_t *font_nonprintable_glyph(int font);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const int font;
|
||||||
|
const uint8_t *text;
|
||||||
|
int remaining;
|
||||||
|
} font_glyph_iter_t;
|
||||||
|
|
||||||
|
font_glyph_iter_t font_glyph_iter_init(const int font, const uint8_t *text,
|
||||||
|
const int len);
|
||||||
|
bool font_next_glyph(font_glyph_iter_t *iter, const uint8_t **out);
|
||||||
|
|
||||||
#endif //_FONTS_H
|
#endif //_FONTS_H
|
||||||
|
Loading…
Reference in New Issue
Block a user