diff --git a/core/embed/gfx/fonts/fonts.c b/core/embed/gfx/fonts/fonts.c index d0484dfe76a..446a8bfe24f 100644 --- a/core/embed/gfx/fonts/fonts.c +++ b/core/embed/gfx/fonts/fonts.c @@ -20,9 +20,6 @@ #include #include "fonts.h" -#ifdef TRANSLATIONS -#include "librust_fonts.h" -#endif // include selectively based on the SCons variables #ifdef TREZOR_FONT_NORMAL_ENABLE @@ -53,7 +50,7 @@ #define PASTER(font_name) font_name##_info #define FONT_INFO(font_name) PASTER(font_name) -static const font_info_t *get_font_info(font_id_t font_id) { +const font_info_t *get_font_info(font_id_t font_id) { switch (font_id) { #ifdef TREZOR_FONT_NORMAL_ENABLE case FONT_NORMAL: @@ -92,35 +89,13 @@ static const font_info_t *get_font_info(font_id_t font_id) { } } -int font_height(font_id_t font_id) { - const font_info_t *font_info = get_font_info(font_id); - return font_info ? font_info->height : 0; -} - -int font_max_height(font_id_t font) { - const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->max_height : 0; -} - -int font_baseline(font_id_t font) { +const uint8_t *font_nonprintable_glyph(font_id_t font) { const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->baseline : 0; + return font_info ? font_info->glyph_nonprintable : NULL; } -const uint8_t *font_get_glyph(font_id_t 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 +const uint8_t *font_get_glyph(font_id_t font, char c) { + // support only printable ASCII character if (c >= ' ' && c < 0x7F) { const font_info_t *font_info = get_font_info(font); if (font_info == NULL) { @@ -132,93 +107,41 @@ const uint8_t *font_get_glyph(font_id_t font, uint16_t c) { return font_nonprintable_glyph(font); } -const uint8_t *font_nonprintable_glyph(font_id_t font) { +int font_baseline(font_id_t font) { const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->glyph_nonprintable : NULL; -} - -font_glyph_iter_t font_glyph_iter_init(font_id_t font, const uint8_t *text, - const int len) { - return (font_glyph_iter_t){ - .font = font, - .text = text, - .remaining = len, - }; -} - -#define UNICODE_BADCHAR 0xFFFD -#define IS_UTF8_CONTINUE(c) (((c) & 0b11000000) == 0b10000000) - -static uint16_t next_utf8_codepoint(font_glyph_iter_t *iter) { - uint16_t out; - assert(iter->remaining > 0); - // 1-byte UTF-8 character - if (iter->text[0] < 0x7f) { - out = iter->text[0]; - ++iter->text; - --iter->remaining; - return out; - } - // 2-byte UTF-8 character - if (iter->remaining >= 2 && ((iter->text[0] & 0b11100000) == 0b11000000) && - IS_UTF8_CONTINUE(iter->text[1])) { - out = (((uint16_t)iter->text[0] & 0b00011111) << 6) | - (iter->text[1] & 0b00111111); - iter->text += 2; - iter->remaining -= 2; - return out; - } - // 3-byte UTF-8 character - if (iter->remaining >= 3 && ((iter->text[0] & 0b11110000) == 0b11100000) && - IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2])) { - out = (((uint16_t)iter->text[0] & 0b00001111) << 12) | - (((uint16_t)iter->text[1] & 0b00111111) << 6) | - (iter->text[2] & 0b00111111); - iter->text += 3; - iter->remaining -= 3; - return out; - } - // 4-byte UTF-8 character - if (iter->remaining >= 4 && ((iter->text[0] & 0b11111000) == 0b11110000) && - IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2]) && - IS_UTF8_CONTINUE(iter->text[3])) { - // we use 16-bit codepoints, so we can't represent 4-byte UTF-8 characters - iter->text += 4; - iter->remaining -= 4; - return UNICODE_BADCHAR; - } - - ++iter->text; - --iter->remaining; - return UNICODE_BADCHAR; + return font_info ? font_info->baseline : 0; } -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; - } +int font_max_height(font_id_t font) { + const font_info_t *font_info = get_font_info(font); + return font_info ? font_info->max_height : 0; } -// compute the width of the text (in pixels) +// compute the width of the ASCII text (in pixels) int font_text_width(font_id_t font, const char *text, int textlen) { int width = 0; // determine text length if not provided if (textlen < 0) { textlen = strlen(text); } - font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen); - const uint8_t *g = NULL; - while (font_next_glyph(&iter, &g)) { - const uint8_t adv = g[2]; // advance + + const font_info_t *font_info = get_font_info(font); + if (font_info == NULL) { + return 0; + } + + for (int i = 0; i < textlen; ++i) { + const uint8_t *glyph; + char c = text[i]; + + if (c >= ' ' && c < 0x7F) { + glyph = font_info->glyph_data[c - ' ']; + } else { + glyph = font_info->glyph_nonprintable; + } + const uint8_t adv = glyph[2]; // advance width += adv; } + return width; } diff --git a/core/embed/gfx/fonts/fonts.h b/core/embed/gfx/fonts/fonts.h index dd95ebe22fc..4e3aced1ad9 100644 --- a/core/embed/gfx/fonts/fonts.h +++ b/core/embed/gfx/fonts/fonts.h @@ -20,8 +20,7 @@ #ifndef _FONTS_H #define _FONTS_H -#include -#include +#include #include "font_bitmap.h" #ifdef USE_RGB_COLORS @@ -51,22 +50,11 @@ typedef enum { FONT_SUB = -8, } font_id_t; -/// Font glyph iterator structure -typedef struct { - const font_id_t font; - const uint8_t *text; - int remaining; -} font_glyph_iter_t; +const font_info_t *get_font_info(font_id_t font_id); +const uint8_t *font_get_glyph(font_id_t font, const char c); -int font_height(font_id_t font); -int font_max_height(font_id_t font); int font_baseline(font_id_t font); -const uint8_t *font_get_glyph(font_id_t font, const uint16_t c); -const uint8_t *font_nonprintable_glyph(font_id_t font); - -font_glyph_iter_t font_glyph_iter_init(font_id_t font, const uint8_t *text, - const int len); -bool font_next_glyph(font_glyph_iter_t *iter, const uint8_t **out); +int font_max_height(font_id_t font); int font_text_width(font_id_t font, const char *text, int textlen); #endif //_FONTS_H diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 4a9e1da1147..83e4022894d 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -379,11 +379,9 @@ fn generate_trezorhal_bindings() { .allowlist_function("gfx_mono8_blend_mono4") .allowlist_function("gfx_bitblt_wait") // fonts - .allowlist_function("font_height") - .allowlist_function("font_max_height") - .allowlist_function("font_baseline") - .allowlist_function("font_get_glyph") - .allowlist_function("font_text_width") + .allowlist_type("font_info_t") + .allowlist_function("get_font_info") + // .allowlist_function("font_get_glyph") // uzlib .allowlist_function("uzlib_uncompress_init") .allowlist_function("uzlib_uncompress") diff --git a/core/embed/rust/librust_fonts.h b/core/embed/rust/librust_fonts.h deleted file mode 100644 index 17249c4b1b7..00000000000 --- a/core/embed/rust/librust_fonts.h +++ /dev/null @@ -1 +0,0 @@ -const uint8_t *get_utf8_glyph(uint16_t char_code, int font); diff --git a/core/embed/rust/src/translations/mod.rs b/core/embed/rust/src/translations/mod.rs index 3d19ef4b488..17b9544cd73 100644 --- a/core/embed/rust/src/translations/mod.rs +++ b/core/embed/rust/src/translations/mod.rs @@ -7,17 +7,15 @@ mod public_keys; mod translated_string; pub use translated_string::TranslatedString as TR; + +use crate::ui::display::Font; pub const DEFAULT_LANGUAGE: &str = "en-US"; /// # Safety /// /// Returned pointer will only point to valid font data for as long as /// the flash content is not invalidated by `erase()` or `write()`. -#[no_mangle] -pub unsafe extern "C" fn get_utf8_glyph(codepoint: cty::uint16_t, font: cty::c_int) -> *const u8 { - // C will send a negative number - let font_abs = font.unsigned_abs() as u16; - +pub unsafe fn get_utf8_glyph(codepoint: u16, font: Font) -> *const u8 { // SAFETY: Reference is discarded at the end of the function. // We do return a _pointer_ to the same memory location, but the pointer is // always valid. @@ -27,7 +25,7 @@ pub unsafe extern "C" fn get_utf8_glyph(codepoint: cty::uint16_t, font: cty::c_i let Some(tr) = translations.as_ref() else { return core::ptr::null(); }; - if let Some(glyph) = tr.font(font_abs).and_then(|t| t.get(codepoint)) { + if let Some(glyph) = tr.font(font as u16).and_then(|t| t.get(codepoint)) { glyph.as_ptr() } else { core::ptr::null() diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index 5c3b51eb0c2..d972780044a 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -5,40 +5,25 @@ use core::ptr; pub use ffi::{DISPLAY_RESX, DISPLAY_RESY}; +pub type FontInfo = ffi::font_info_t; + pub fn backlight(val: i32) -> i32 { unsafe { ffi::display_set_backlight(val) } } -pub fn text_width(text: &str, font: i32) -> i16 { +pub fn get_font_info(font: i32) -> Option { + // SAFETY: + // - `ffi::get_font_info` returns either null (for invalid fonts) or a pointer + // to a static font_info_t struct + // - The font_info_t data is in ROM, making it immutable and static + // - The font_info_t contains pointers to static glyph data arrays also in ROM + // - All font data is generated at compile time and included in the binary unsafe { - ffi::font_text_width(font, text.as_ptr() as _, text.len() as _) - .try_into() - .unwrap_or(i16::MAX) + let font = ffi::get_font_info(font); + Some(*font.as_ref()?) } } -pub fn char_width(ch: char, font: i32) -> i16 { - let mut buf = [0u8; 4]; - let encoding = ch.encode_utf8(&mut buf); - text_width(encoding, font) -} - -pub fn get_char_glyph(ch: u16, font: i32) -> *const u8 { - unsafe { ffi::font_get_glyph(font, ch) } -} - -pub fn text_height(font: i32) -> i16 { - unsafe { ffi::font_height(font).try_into().unwrap_or(i16::MAX) } -} - -pub fn text_max_height(font: i32) -> i16 { - unsafe { ffi::font_max_height(font).try_into().unwrap_or(i16::MAX) } -} - -pub fn text_baseline(font: i32) -> i16 { - unsafe { ffi::font_baseline(font).try_into().unwrap_or(i16::MAX) } -} - pub fn sync() { // NOTE: The sync operation is not called for tests because the linker // would otherwise report missing symbols if the tests are built with ASAN. diff --git a/core/embed/rust/src/ui/display/font.rs b/core/embed/rust/src/ui/display/font.rs index f126c853015..0e55786c9b5 100644 --- a/core/embed/rust/src/ui/display/font.rs +++ b/core/embed/rust/src/ui/display/font.rs @@ -8,6 +8,9 @@ use crate::{ }; use core::slice; +#[cfg(feature = "translations")] +use crate::translations::get_utf8_glyph; + /// Representation of a single glyph. /// We use standard typographic terms. For a nice explanation, see, e.g., /// the FreeType docs at https://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html @@ -129,8 +132,9 @@ impl From for i32 { } impl Font { + /// Supports UTF8 characters pub fn text_width(self, text: &str) -> i16 { - display::text_width(text, self.into()) + text.chars().fold(0, |acc, c| acc + self.char_width(c)) } /// Supports UTF8 characters @@ -198,31 +202,26 @@ impl Font { return 0; } - if let Some(glyph) = self.get_first_glyph_from_text(text) { - glyph.bearing_x - } else { - 0 - } + self.get_first_glyph_from_text(text) + .map_or(0, |glyph| glyph.bearing_x) } pub fn char_width(self, ch: char) -> i16 { - display::char_width(ch, self.into()) + self.get_glyph(ch).adv } pub fn text_height(self) -> i16 { - display::text_height(self.into()) + display::get_font_info(self.into()).map_or(i16::MAX, |font| font.height.try_into().unwrap()) } pub fn text_max_height(self) -> i16 { - display::text_max_height(self.into()) + display::get_font_info(self.into()) + .map_or(i16::MAX, |font| font.max_height.try_into().unwrap()) } pub fn text_baseline(self) -> i16 { - display::text_baseline(self.into()) - } - - pub fn max_height(self) -> i16 { - display::text_max_height(self.into()) + display::get_font_info(self.into()) + .map_or(i16::MAX, |font| font.baseline.try_into().unwrap()) } pub fn line_height(self) -> i16 { @@ -248,6 +247,29 @@ impl Font { (start + end + self.visible_text_height(text)) / 2 } + fn get_glyph_data(&self, codepoint: u16) -> *const u8 { + display::get_font_info((*self).into()).map_or(core::ptr::null(), |font_info| { + #[cfg(feature = "translations")] + { + if codepoint >= 0x7F { + // UTF8 character from embedded blob + return unsafe { get_utf8_glyph(codepoint, *self) }; + } + } + + if codepoint >= ' ' as u16 && codepoint < 0x7F { + // ASCII character + unsafe { + *font_info + .glyph_data + .offset((codepoint - ' ' as u16) as isize) + } + } else { + font_info.glyph_nonprintable + } + }) + } + pub fn get_glyph(self, ch: char) -> Glyph { /* have the non-breaking space counted for width but not counted as a * breaking point */ @@ -255,7 +277,7 @@ impl Font { '\u{00a0}' => '\u{0020}', c => c, }; - let gl_data = display::get_char_glyph(ch as u16, self.into()); + let gl_data = self.get_glyph_data(ch as u16); ensure!(!gl_data.is_null(), "Failed to load glyph"); // SAFETY: Glyph::load is valid for data returned by get_char_glyph diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index f2290fd316b..4a8d3c95095 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -77,7 +77,7 @@ impl TextOverlay { area, text: text.into(), font, - max_height: font.max_height(), + max_height: font.text_max_height(), baseline: font.text_baseline(), } } diff --git a/core/embed/upymod/rustmods.c b/core/embed/upymod/rustmods.c index 2673bceb554..128c06ecb61 100644 --- a/core/embed/upymod/rustmods.c +++ b/core/embed/upymod/rustmods.c @@ -18,7 +18,6 @@ */ #include "librust.h" -#include "librust_fonts.h" #include "py/runtime.h" #if MICROPY_PY_TREZORUI2