Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/windowing/winit/fonts/font_handle.rs
1// Neither macOS nor wasm make use of the "load font from path" functionality,
2// and so there's a lot of unused code in here. Instead of marking each of the
3// relevant functions with allow(dead_code), we'll do it at the module level
4// instead for simplicity.
5#![cfg_attr(
6 any(target_os = "macos", target_os = "windows", target_family = "wasm"),
7 allow(dead_code)
8)]
9 
10use owned_ttf_parser::{AsFaceRef, Face, FaceParsingError, OwnedFace};
11use std::fs::File;
12use std::path::PathBuf;
13 
14/// A handle that wraps around a font face.
15pub struct FontHandle {
16 data: FontData,
17}
18 
19/// Source data for a font to be loaded within the winit font system.
20pub enum FontData {
21 /// The font is to be loaded via bytes. This should be used sparingly since it requires loading the font into
22 /// memory.
23 Bytes(OwnedFace),
24 /// The font identified at the given `path` and `index` will be loaded.
25 /// NOTE the font will never be loaded into memory. Instead, data from the font will be read via a memory-mapped
26 /// file.
27 Path {
28 path: PathBuf,
29 index: u32,
30 is_monospace: bool,
31 },
32}
33 
34impl FontData {
35 /// Returns an [`Error`] if the [`FontData`] does not map to a valid font.
36 ///
37 /// A font is considered valid iff:
38 /// * The file referenced by [`FontData::Path`] exists and can be read.
39 /// * The data can be parsed into a valid [`ttf_parser::Face`].
40 /// * The font face contains a glyph for the 'm' character.
41 fn validate(&self) -> Result<(), Error> {
42 match self {
43 FontData::Bytes(_) => Ok(()),
44 FontData::Path { path, index, .. } => {
45 let file = File::open(path).map_err(|e| Error::Load {
46 path: path.clone(),
47 io_error: e,
48 })?;
49 let mmap = unsafe {
50 memmap2::Mmap::map(&file).map_err(|e| Error::Load {
51 path: path.clone(),
52 io_error: e,
53 })?
54 };
55 let face = Face::parse(&mmap, *index).map_err(|e| Error::Parse {
56 path: path.clone(),
57 parse_error: e,
58 })?;
59 
60 if face.as_face_ref().glyph_index('m').is_none() {
61 Err(Error::Validate { path: path.clone() })
62 } else {
63 Ok(())
64 }
65 }
66 }
67 }
68}
69 
70impl FontHandle {
71 pub fn new(path: impl Into<PathBuf>, index: u32, is_monospace: bool) -> Self {
72 Self {
73 data: FontData::Path {
74 path: path.into(),
75 index,
76 is_monospace,
77 },
78 }
79 }
80 
81 pub fn is_monospace(&self) -> bool {
82 match &self.data {
83 FontData::Path { is_monospace, .. } => *is_monospace,
84 FontData::Bytes(face) => face.as_face_ref().is_monospaced(),
85 }
86 }
87 
88 /// Validates the [`FontHandle`] is a parseable font.
89 pub fn validate_font_data(&self) -> Result<(), Error> {
90 self.data.validate()
91 }
92 
93 pub(super) fn into_data(self) -> FontData {
94 self.data
95 }
96 
97 #[allow(dead_code)]
98 pub(super) fn data(&self) -> &FontData {
99 &self.data
100 }
101}
102 
103impl From<OwnedFace> for FontHandle {
104 fn from(value: OwnedFace) -> Self {
105 Self {
106 data: FontData::Bytes(value),
107 }
108 }
109}
110 
111/// Errors associated with loading fonts
112#[derive(Debug, thiserror::Error)]
113pub enum Error {
114 /// Failed to load font data due to an underlying std::io::Error
115 #[error("Error loading font data for font {path}")]
116 Load {
117 path: PathBuf,
118 io_error: std::io::Error,
119 },
120 
121 /// Failed to parse the underlying data into a valid font
122 #[error("Error parsing font data for font {path}")]
123 Parse {
124 path: PathBuf,
125 parse_error: FaceParsingError,
126 },
127 
128 /// A font was properly loaded, but did not have a codepoint
129 /// for the letter m, indicating it would not work within Warp.
130 #[error("Font {path} does not have a valid codepoint for the letter m")]
131 Validate { path: PathBuf },
132}
133