From 6e09129d0c31ea7902538cd54e30027d1870068f Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Sat, 3 Oct 2026 02:03:24 +0200 Subject: [PATCH] fixed font loading --- Cargo.lock | 1 + Cargo.toml | 3 ++ crates/pstr-app/Cargo.toml | 3 ++ .../pstr-app/assets/fonts/Inter-Regular.ttf | Bin 411640 -> 409636 bytes .../pstr-app/assets/fonts/Inter-SemiBold.ttf | Bin 419744 -> 417740 bytes crates/pstr-app/src/theme.rs | 33 ++++++++++++++++-- 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed6728c..0221e7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4398,6 +4398,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "skrifa", "souvlaki", "tauri-winrt-notification", "tokio", diff --git a/Cargo.toml b/Cargo.toml index cd3914a..d859102 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,9 @@ egui = "0.35" # The icons on buttons. Glyphs like ⏭ and 🔊 come out of whichever fallback # font has them, at its own size and baseline; an icon font is one set. egui-phosphor = { version = "0.13", default-features = false, features = ["regular"] } +# Tests only: reads the bundled fonts' character maps. The version epaint +# already builds, so it costs no extra compile. +skrifa = { version = "0.42", default-features = false, features = ["std"] } # Poster thumbnails only: Proton renders them as JPEG, and a decoder that also # pulls in every exotic format is weight this app has no use for. image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] } diff --git a/crates/pstr-app/Cargo.toml b/crates/pstr-app/Cargo.toml index 06dd9b6..b40587e 100644 --- a/crates/pstr-app/Cargo.toml +++ b/crates/pstr-app/Cargo.toml @@ -34,6 +34,9 @@ directories.workspace = true chrono.workspace = true souvlaki.workspace = true +[dev-dependencies] +skrifa.workspace = true + [target.'cfg(target_os = "linux")'.dependencies] dbus.workspace = true diff --git a/crates/pstr-app/assets/fonts/Inter-Regular.ttf b/crates/pstr-app/assets/fonts/Inter-Regular.ttf index b7aaca8de1fe458399e17311d35d543a1fa2f984..d2cad374202f50664ad237b7733b43b97a5c9b49 100644 GIT binary patch delta 799 zcmY*XUr19?9R8i{oO?HSkIl?#;D(wHJ~Y=qYWY$PNrXbdB|TKSmTra1+Z?hMIL$y% zL{a1#t%t}5F*PCSf`o{VDk3zj2On1SAIYFVi$VRaw=@rY=R3de_kHL7xc6@K*oInd zy*Y6@FbP1VvdW68_2Mo+(EBq$%A~TY3m)^WM+{hM0idS3qRMr|u{v}efD8gatv}?G zm7|Fw0M`n@tMZ@tR3VZK40$6*oE)0euQGV{*8>}|~obMVM{k|;w zPaN(cKEU}Y*V0? z1358(2d6Qx1_XBsp7zSr9mVQpd+3Mv-`ABQFLNsgCs@AH{e;x0wO;no>7r;gA__Mn zkUfr-7B5830IjCkoj6GZMG+#ZW0c55O^=agqNbjr9Jg>SK*EM%!&zgDHn2{XWGVJT z*e@jUqOBz?Z5=_F6BdQn0K!|*B+6n~Y!O>cG1KI>1HfF52>!(ly<&{@^Sv0Pw`p(3e;ydyVE*vsV}gDLvs8r zaR2g8-)5~-qPPDOmpBfg1@*;uyahhT7x*Gi@@2l#f5x0=;L}JBb@&+>;Z~5eO!=^$ z6bjUfv!tY4nX(#c2&iedkk0Jyvz}BAyM=kR^e+4C?674S5Jm)5ZCPy9qn@8&qaE#b KqS^^*9QgwRCbOvk delta 2838 zcmY+G3vgA%8OOhK?mfFV_uPAsXHba~^3p(1q|~Gg(oV!iFa>M?l}8|iNPq-yNQ&B0 zE@*6cL;?ng(tvFVpkOKWQn7X%JEemZn2rMsMF+#~MPkIT!6FbaCH-$U2xjJY{@;FQ zch7#0b8ecJ-`P=j=eqt;vIY_PLwS=X7F@l5O)-&kA6a>!yn-pY2^GJ0$jTo{G;8X_ zf(J(2)7CVftlhOlG`qOGDDwWhOCQ3xji@B6cu7_AvOYiFLoB<4$nrlEnO8pN0ltD* zPA8EUnpadA!MZze{~|Onuk3|q3RO;Gl)qhrGuOVlDE75W%vbZEt+LfB{OJaEeENHLUx6!|U-$p00tNt}> z_6WH2IeZ|^*aK!@WOa{y6ePmfKOhIe#^K}?k9or*R#cF1#ZWO`OcXOjp;#c6iyE=ppOp`-owtQGlm4&ic9*}3GjDHkA6+f-G3aUP;pGs8e z>QR-aDpa+qQ?IK=wL=|KCsnhOR}S3h>gvLY5HJ0X_E_VsSlxpq-G{J6NMwn}#UwEk zhgc|9h?U}1(IA?{A#qwFL=->2ts_q9M~V&*^2EXEIM`p2$3&`R|NUqKs9@`iV4_J|VLrBQ@jRj6P=T zesAjAI*P?5>DbvcrzEgjjKRwLdvZfL5kmKd-wC&bZ+L^eH19rdxY+{l%Kh5%vW$M`9ReQ?>5cEc z`_xVUE!B6--P?SbV?3NfFEoGN_p;#Sc%!`0UamLR8{hEYR)4bXWob>B>-EE`yS`i1 zGwONxG;hYuJ6gS|pzwB&9(xPCvfF)LnYRx9t=%hd_5Kk1^oM)H`SyGEhxS4Hu>G-b zif_6TcV;=Wog$~0h1gx}ZdSl1v!~fKHl596g{+he&Xi zi8Zj-8vfqq^pD1aZSJJtg`f(yyHD`#dZE$`(2<2QI$fHDGB@VZ;buetf|e0PDnFkritZWV=ehu-}8`8|)9EKQq_|p%V=DVdzAIjpvF< z0F|@bp)=-c3ru=R=e7#Ln4@h1aY z1larVrnO&x5`&ow`flGLefqN@fUt z1x+vn+n_;%i#o~>@bK-q0PSUPpMdrTec`_dO#}mBV{bVaq`<~AWSSv(m7*kvU;u(0 z(4in3&4}n_4)_slL?%aphhd{Uaty!=&p(1b38ukDgc7|Y(L)>oNz`1!u$MewaQ+3w z_g*4HJQ=FjNYvl`ISgbVjtv726seA%f{iYXe*%#105YL0172EEwn6_2E1$u_eC0RT zh+1JUjjt1mjB0$Vpxh9=28|-U3UJg*>CFHggd)8f3*D;10BN;XLi-qO^o_ztQDeuT zNVUdBbSep?<6*0z8Q@XaXhfA~aOOZC0~M&hGZ)5U14A>ZN(0{stuk=^fL|E+Ip`7t zKM#El{0ae}Kh#nK*V+0tSO)*sP-I!X2>To8O9p-kT8;YSLL9YMH3qi;sw1s~|2lLX zcpYV$3`O^Ag4+LEgNvH0Mg!L=-2!%C{w$~t0yPhQ3q5Xd^PwleNz9o)90qEwnlUJY z>hyttuCFc$n#8Rnx`HOsurKS6)fT;!bfhNfhz0EZNk@Ebxv6Lk{Iiz($jAe)%<47z zGVuT$L}zDi7}2BS>VIP8#5$}mhQ+eCv<$0%!Ajnr;Q8OKX&AB9pHn}-@kngsE1ll| E0dln}_W%F@ diff --git a/crates/pstr-app/assets/fonts/Inter-SemiBold.ttf b/crates/pstr-app/assets/fonts/Inter-SemiBold.ttf index 47f8ab1d68144fc004b310d65d95180d502b449b..2187923abd248dc400a0eb20262e916a178e5dc8 100644 GIT binary patch delta 823 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 -> egui::FontDefinitions { .entry(Proportional) .or_default() .insert(0, "inter".into()); - // Right behind Inter. The icons are in the private use area, so nothing - // Inter has is shadowed by them. + // Right behind Inter. The icons are in the private use area, which the + // bundled Inter no longer maps, so neither font shadows the other. egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular); if let Some(cjk) = cjk { let name = "system-cjk-fallback".to_owned(); @@ -841,6 +849,27 @@ const RAMP_STOPS: usize = 64; mod tests { use super::*; + #[test] + fn the_bundled_inter_leaves_the_icon_codepoints_to_phosphor() { + use skrifa::MetadataProvider as _; + + for (name, bytes) in [("regular", INTER), ("semibold", INTER_SEMIBOLD)] { + let font = skrifa::FontRef::new(bytes).expect("bundled Inter parses"); + let shadowed: Vec = font + .charmap() + .mappings() + .map(|(codepoint, _)| codepoint) + .filter(|codepoint| (0xE000..=0xF8FF).contains(codepoint)) + .collect(); + assert!( + shadowed.is_empty(), + "Inter {name} maps {} private use codepoints, starting U+{:04X}", + shadowed.len(), + shadowed[0] + ); + } + } + #[test] fn the_shipped_default_is_the_palette_that_was_here_before() { let resolved = Palette::resolve(Appearance::default());