mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-16 11:38:12 +00:00

WIP - refactor and extend font generation for non-ascii characters WIP - add czech characters mapping between UTF8 value and index WIP - regenerate font files with czech characters WIP - shorten czech button text, it was causing SHUTDOWN for some reason WIP - support UTF8 characters in fonts.c WIP - account for translation in tests WIP - small fixes WIP - fix last test WIP - support UTF8 also in Rust font operations WIP - add a script to find non-translated english strings in micropython code WIP - add a validator script for checking missing micropython translations WIP - translate remaining altcoins and other apps in core (fido, sdcard, TT layouts, ...) WIP - generate czech glyphs for TT fonts WIP - modify gen_font.py to account for negative bearing czech characters WIP - extend translation validation scripts, move them into core/tools WIP - translate TT layouts in Rust WIP - fix tests WIP - fix inverse coloring of nonprintable glyph WIP - add build and test pipelines for Czech language WIP - merge both JSON files together WIP - run new isort WIP - unify all the translation in Rust, expose to micropython TEMP - leave en_merged.json file, so it is accessible by translators with old link WIP - fixes WIP - add french characters and translation via Google Translator WIP - skip rustfmt in mako-created files WIP - revert all the font height changes causing false-positive UI diff WIP - fixes after rebase WIP - fix broken translations WIP - revert some wording changes causing UI diff WIP - improve validation and translate scripts, translate missing strings WIP - sort all keys alphabetically WIP - remove any usage of translation in bootloader WIP - add newline at the end of JSON file WIP - fix bitcoin-only strings check WIP - fix python support check WIP - add some missing translations WIP - fix SD card device test WIP - fix pystyle WIP - fix rust unittests WIP - fix click tests WIP - flag errors in french translations WIP - add script transferring translations data into a byte blob WIP - regenerate fr.rs WIP - store and read language translations from flash WIP - storing language name in storage WIP - sending language_data in apply_settings protobuf message WIP - separate protobuf message for translations, fixes WIP - set up translations area for TT as well WIP - get rid of TREZOR_LANG env variable during build WIP - make the firmware buildable for TT WIP - add basic device tests WIP - set language for tests WIP - counting with language when writing fixtures WIP - add todos WIP - fix CI WIP - unify translations, make titles CAPITAL WIP - translate missing english WIP - skip translations messages for T1 WIP - not changing tests names for english WIP - fix flake8 WIP - no test language setting for T1 WIP - clippy lint about complex data type WIP - fix some english UI diff for TR WIP - fix cstyle WIP - minimize the usage of #[cfg(feature = "micropython")] outside translations module WIP - minimize TT's UI diff WIP - fix ruststyle WIP - fix TR build WIP - advanced Shamir text change WIP - storing the language name as the first item in the translation data WIP - modify and extend tests after storing language name WIP - modify checklist sentence WIP - add TEST_LANG into Makefile for all the emu tests WIP - default arguments WIP - reimplement default arguments remove unneeded pub from get_info function WIP - Rust handling of object attributes lookups from upy - thanks Matejcik! WIP - generate mock interface for attribute-based translations lookups WIP - change function calls to object attributes WIP - symbolic link for unix/translations.c WIP - fix and improve the reading of translations - thanks Matejcik! WIP - add support for multiple languages in removing missing tests WIP - fix multiple-accounts warning in tests WIP - fix encoding of newlines in translations WIP - fix czech tutorial text WIP - fix czech click tests WIP - do not translate wire error messages WIP - add language options to click tests as well WIP - setup czech device tests in CI WIP - setup czech click tests in CI WIP - record czech device tests for TR WIP - record czech click tests for TR WIP - record czech device tests for TT WIP - record czech click tests for TT WIP - pystyle WIP - cstyle WIP - fix Rust micropython import dependency WIP - fix czech recordings WIP - support french translations in tests WIP - shorten some french words to fix the tests WIP - fix micropython cfg compilation WIP - record french click tests for TR WIP - record french device tests for TR WIP - record french device tests for TT WIP - record french click tests for TT WIP - fix french translations - shorten them WIP - translate missing french words WIP - fix click tests WIP - add french tests into CI WIP - pystyle WIP - allow for czech/french tests in update script WIP - update czech fixtures WIP - update french fixtures WIP - ruststyle WIP - disallow MPU to run it on hardware WIP - cstyle WIP - change translations delimiter from * to \x00 WIP - change translations protobufs WIP - remove language handling from storage WIP - add header into JSON files WIP - count with header in translations blob WIP - yml style fixes WIP - fix proto gen WIP - verify version and data hash WIP - fix loading test translations feat(core): allow access to translations area in firmware [no changelog] WIP - fixes after rebase WIP - increase the TT's translations area to 3 sectors WIP - dynamically read the maximum translations size WIP - record non-english tests from CI WIP - loading font data from translations blob WIP - bump translations version WIP - include czech and french glyph data WIP - whitelist another negative-bearing glyph WIP - remove czech/french glyphs from common font files WIP - fix language tests WIP - specific fonts for specific models WIP - revert the non-ascii font hardcoding WIP - include missing BIG font into nonprintable logic WIP - minor Rust code improvements WIP - include newlines at the end of json files WIP - move glyph Rust function to librust_fonts.h WIP - add all fonts into translations file WIP - move fonts into its own dir WIP - reflect separate dir for fonts WIP - not putting translations trezorhal into bootloader WIP - write and read multiple fonts into translations data WIP - silence pyright issue/notissue WIP - delete no more used translations/*.py imports WIP - fix bootloader builds by introducing translations feature and TRANSLATIONS flag WIP - fix TT's bootloader Rust build WIP - fix tests in non-english languages WIP - not search for UTF-8 when there are no translations data WIP - add colons to strings where missing WIP - fix language loading in tests WIP - fix signmessage input flow to work in all languages WIP - create offset table for translation strings WIP - code improvements WIP - record foreign language fixtures + sync with main in english WIP - do alignment check before reading u16 data WIP - allocate blob in RAM for translations data WIP - add TODO for blob generation WIP - record non-english device tests WIP - use bytes.align_to instead of messing with pointers WIP - fixtures WIP - remove unused import WIP - add order.py WIP - add order.json WIP - take order.json into account in creating general.rs WIP - take order.json into account in generating the blob WIP - style WIP - sort the language files WIP - remove unused file WIP - code improvements WIP - add TODO for homescreen notification WIP - translate plural forms WIP - translate time intervals WIP - sign translations with dev keys, validate signatures, improve robustness WIP - improve tests for translations WIP - add `trezorctl utils sign-translations` for production signing of the blob WIP - pyright fix WIP - changing TR progress loader offset - it was colliding with title WIP - show indeterminate loader when loading translations data WIP - record new and updated language tests WIP - show the change language title/prompt in the target language WIP - sort keys WIP - add crowdin-cli into shell.nix WIP - add crowdin sync script
297 lines
8.3 KiB
Rust
297 lines
8.3 KiB
Rust
use crate::{
|
|
trezorhal::display,
|
|
ui::{
|
|
constant,
|
|
geometry::{Offset, Point, Rect},
|
|
},
|
|
};
|
|
use core::slice;
|
|
|
|
use super::{get_color_table, get_offset, pixeldata, set_window, Color};
|
|
|
|
/// 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
|
|
pub struct Glyph {
|
|
/// Total width of the glyph itself
|
|
pub width: i16,
|
|
/// Total height of the glyph itself
|
|
pub height: i16,
|
|
/// Advance - how much to move the cursor after drawing this glyph
|
|
pub adv: i16,
|
|
/// Left-side horizontal bearing
|
|
pub bearing_x: i16,
|
|
/// Top-side vertical bearing
|
|
pub bearing_y: i16,
|
|
data: &'static [u8],
|
|
}
|
|
|
|
impl Glyph {
|
|
/// Construct a `Glyph` from a raw pointer.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This function is unsafe because the caller has to guarantee that `data`
|
|
/// is pointing to a memory containing a valid glyph data, that is:
|
|
/// - contains valid glyph metadata
|
|
/// - data has appropriate size
|
|
/// - data must have static lifetime
|
|
pub unsafe fn load(data: *const u8) -> Self {
|
|
unsafe {
|
|
let width = *data.offset(0) as i16;
|
|
let height = *data.offset(1) as i16;
|
|
|
|
let data_bits = constant::FONT_BPP * width * height;
|
|
|
|
let data_bytes = if data_bits % 8 == 0 {
|
|
data_bits / 8
|
|
} else {
|
|
(data_bits / 8) + 1
|
|
};
|
|
|
|
Glyph {
|
|
width,
|
|
height,
|
|
adv: *data.offset(2) as i16,
|
|
bearing_x: *data.offset(3) as i16,
|
|
bearing_y: *data.offset(4) as i16,
|
|
data: slice::from_raw_parts(data.offset(5), data_bytes as usize),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Space between the right edge of the glyph and the left edge of the next
|
|
/// bounding box.
|
|
pub const fn right_side_bearing(&self) -> i16 {
|
|
self.adv - self.width - self.bearing_x
|
|
}
|
|
|
|
pub fn print(&self, pos: Point, colortable: [Color; 16]) -> i16 {
|
|
let bearing = Offset::new(self.bearing_x, -self.bearing_y);
|
|
let size = Offset::new(self.width, self.height);
|
|
let pos_adj = pos + bearing;
|
|
let r = Rect::from_top_left_and_size(pos_adj, size);
|
|
|
|
let area = r.translate(get_offset());
|
|
let window = area.clamp(constant::screen());
|
|
|
|
set_window(window);
|
|
|
|
for y in window.y0..window.y1 {
|
|
for x in window.x0..window.x1 {
|
|
let p = Point::new(x, y);
|
|
let r = p - pos_adj;
|
|
let c = self.get_pixel_data(r);
|
|
pixeldata(colortable[c as usize]);
|
|
}
|
|
}
|
|
self.adv
|
|
}
|
|
|
|
pub fn unpack_bpp1(&self, a: i16) -> u8 {
|
|
let c_data = self.data[(a / 8) as usize];
|
|
((c_data >> (7 - (a % 8))) & 0x01) * 15
|
|
}
|
|
|
|
pub fn unpack_bpp2(&self, a: i16) -> u8 {
|
|
let c_data = self.data[(a / 4) as usize];
|
|
((c_data >> (6 - (a % 4) * 2)) & 0x03) * 5
|
|
}
|
|
|
|
pub fn unpack_bpp4(&self, a: i16) -> u8 {
|
|
let c_data = self.data[(a / 2) as usize];
|
|
(c_data >> (4 - (a % 2) * 4)) & 0x0F
|
|
}
|
|
|
|
pub fn unpack_bpp8(&self, a: i16) -> u8 {
|
|
let c_data = self.data[a as usize];
|
|
c_data >> 4
|
|
}
|
|
|
|
pub fn get_pixel_data(&self, p: Offset) -> u8 {
|
|
let a = p.x + p.y * self.width;
|
|
|
|
match constant::FONT_BPP {
|
|
1 => self.unpack_bpp1(a),
|
|
2 => self.unpack_bpp2(a),
|
|
4 => self.unpack_bpp4(a),
|
|
8 => self.unpack_bpp8(a),
|
|
_ => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Font constants. Keep in sync with FONT_ definitions in
|
|
/// `extmod/modtrezorui/fonts/fonts.h`.
|
|
#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
|
|
#[repr(u8)]
|
|
pub enum Font {
|
|
NORMAL = 1,
|
|
BOLD = 2,
|
|
MONO = 3,
|
|
BIG = 4,
|
|
DEMIBOLD = 5,
|
|
}
|
|
|
|
impl From<Font> for i32 {
|
|
fn from(font: Font) -> i32 {
|
|
-(font as i32)
|
|
}
|
|
}
|
|
|
|
impl Font {
|
|
pub fn text_width(self, text: &str) -> i16 {
|
|
display::text_width(text, self.into())
|
|
}
|
|
|
|
/// Supports UTF8 characters
|
|
fn get_glyph_from_char(self, c: char) -> Option<Glyph> {
|
|
let mut buffer = [0; 4];
|
|
let bytes = c.encode_utf8(&mut buffer);
|
|
for byte in bytes.bytes() {
|
|
if let Some(glyph) = self.get_glyph(byte) {
|
|
return Some(glyph);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Supports UTF8 characters
|
|
fn get_first_glyph_from_text(self, text: &str) -> Option<Glyph> {
|
|
if let Some(c) = text.chars().next() {
|
|
self.get_glyph_from_char(c)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Supports UTF8 characters
|
|
fn get_last_glyph_from_text(self, text: &str) -> Option<Glyph> {
|
|
if let Some(c) = text.chars().next_back() {
|
|
self.get_glyph_from_char(c)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Width of the text that is visible.
|
|
/// Not including the spaces before the first and after the last character.
|
|
pub fn visible_text_width(self, text: &str) -> i16 {
|
|
if text.is_empty() {
|
|
// No text, no width.
|
|
return 0;
|
|
}
|
|
|
|
let first_char_bearing = if let Some(glyph) = self.get_first_glyph_from_text(text) {
|
|
glyph.bearing_x
|
|
} else {
|
|
0
|
|
};
|
|
|
|
let last_char_bearing = if let Some(glyph) = self.get_last_glyph_from_text(text) {
|
|
glyph.right_side_bearing()
|
|
} else {
|
|
0
|
|
};
|
|
|
|
// Strip leftmost and rightmost spaces/bearings/margins.
|
|
self.text_width(text) - first_char_bearing - last_char_bearing
|
|
}
|
|
|
|
/// Returning the x-bearing (offset) of the first character.
|
|
/// Useful to enforce that the text is positioned correctly (e.g. centered).
|
|
pub fn start_x_bearing(self, text: &str) -> i16 {
|
|
if text.is_empty() {
|
|
return 0;
|
|
}
|
|
|
|
if let Some(glyph) = self.get_first_glyph_from_text(text) {
|
|
glyph.bearing_x
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
pub fn char_width(self, ch: char) -> i16 {
|
|
display::char_width(ch, self.into())
|
|
}
|
|
|
|
pub fn text_height(self) -> i16 {
|
|
display::text_height(self.into())
|
|
}
|
|
|
|
pub fn text_max_height(self) -> i16 {
|
|
display::text_max_height(self.into())
|
|
}
|
|
|
|
pub fn text_baseline(self) -> i16 {
|
|
display::text_baseline(self.into())
|
|
}
|
|
|
|
pub fn max_height(self) -> i16 {
|
|
display::text_max_height(self.into())
|
|
}
|
|
|
|
pub fn line_height(self) -> i16 {
|
|
constant::LINE_SPACE + self.text_height()
|
|
}
|
|
|
|
pub fn get_glyph(self, char_byte: u8) -> Option<Glyph> {
|
|
let gl_data = display::get_char_glyph(char_byte, self.into());
|
|
|
|
if gl_data.is_null() {
|
|
return None;
|
|
}
|
|
// SAFETY: Glyph::load is valid for data returned by get_char_glyph
|
|
unsafe { Some(Glyph::load(gl_data)) }
|
|
}
|
|
|
|
pub fn display_text(self, text: &str, baseline: Point, fg_color: Color, bg_color: Color) {
|
|
let colortable = get_color_table(fg_color, bg_color);
|
|
let mut adv_total = 0;
|
|
for c in text.bytes() {
|
|
let g = self.get_glyph(c);
|
|
if let Some(gly) = g {
|
|
let adv = gly.print(baseline + Offset::new(adv_total, 0), colortable);
|
|
adv_total += adv;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the length of the longest suffix from a given `text`
|
|
/// that will fit into the area `width` pixels wide.
|
|
pub fn longest_suffix(self, width: i16, text: &str) -> usize {
|
|
let mut text_width = 0;
|
|
for (chars_from_right, c) in text.chars().rev().enumerate() {
|
|
let c_width = self.char_width(c);
|
|
if text_width + c_width > width {
|
|
// Another character cannot be fitted, we're done.
|
|
return chars_from_right;
|
|
}
|
|
text_width += c_width;
|
|
}
|
|
|
|
text.len() // it fits in its entirety
|
|
}
|
|
}
|
|
|
|
pub trait GlyphMetrics {
|
|
fn char_width(&self, ch: char) -> i16;
|
|
fn text_width(&self, text: &str) -> i16;
|
|
fn line_height(&self) -> i16;
|
|
}
|
|
|
|
impl GlyphMetrics for Font {
|
|
fn char_width(&self, ch: char) -> i16 {
|
|
Font::char_width(*self, ch)
|
|
}
|
|
|
|
fn text_width(&self, text: &str) -> i16 {
|
|
Font::text_width(*self, text)
|
|
}
|
|
|
|
fn line_height(&self) -> i16 {
|
|
Font::line_height(*self)
|
|
}
|
|
}
|