1#![allow(clippy::match_on_vec_items)]
11#![allow(clippy::cast_possible_truncation)]
12pub use crate::raw::ttf::NameKind as StringKind;
13use crate::{
14 error::ParseResult,
15 raw::ttf::{GlyfOutline, SimpleGlyf, TrueTypeFont},
16 svg::SvgExt,
17};
18use std::{
19 borrow::Cow,
20 collections::{HashMap, HashSet},
21};
22
23#[derive(Debug, Clone)]
25pub struct Font {
26 glyphs: Vec<Glyph>,
27 strings: HashMap<StringKind, String>,
28}
29impl Font {
30 pub fn new(font_data: &[u8]) -> ParseResult<Self> {
35 let font = TrueTypeFont::new(font_data)?;
36 Ok(font.into())
37 }
38
39 pub fn from_file(path: impl AsRef<std::path::Path>) -> ParseResult<Self> {
44 let font_data = std::fs::read(path)?;
45 Self::new(&font_data)
46 }
47
48 #[must_use]
50 pub fn string(&self, kind: StringKind) -> Option<&str> {
51 self.strings.get(&kind).map(String::as_str)
52 }
53
54 #[must_use]
56 pub fn strings(&self) -> &HashMap<StringKind, String> {
57 &self.strings
58 }
59
60 #[must_use]
62 pub fn glyph(&self, codepoint: u32) -> Option<&Glyph> {
63 self.glyphs.iter().find(|g| g.codepoint == codepoint)
64 }
65
66 #[must_use]
68 pub fn glyph_named(&self, name: &str) -> Option<&Glyph> {
69 self.glyphs.iter().find(|g| g.name == name)
70 }
71
72 #[must_use]
74 pub fn glyphs(&self) -> &[Glyph] {
75 &self.glyphs
76 }
77}
78
79impl From<TrueTypeFont> for Font {
80 fn from(value: TrueTypeFont) -> Self {
81 let cmap = value.cmap_table;
82 let post = value.post_table;
83 let name = value.name_table;
84 let glyf = value.glyf_table;
85
86 let mut strings = HashMap::new();
87 for record in name.records {
88 strings.insert(record.name_id, record.name);
89 }
90
91 let mut glyphs = Vec::new();
92 let mut codepoint_hash = HashSet::new();
93 for (glyph_index, name) in post.glyph_names.into_iter().enumerate() {
94 let name = Cow::Owned(name);
95 let glyph_index = glyph_index as u16;
96
97 let codepoint = cmap.get_codepoint(glyph_index);
99 let codepoint = match codepoint {
100 Some(c) if glyph_index == 0 => c,
101 Some(c) if c != 0xFFFF => c,
102 _ => continue,
103 };
104
105 if !codepoint_hash.insert(codepoint) {
107 continue;
108 }
109
110 let outline = match glyf[glyph_index as usize] {
112 GlyfOutline::Simple(ref outline) => outline.clone(),
113 GlyfOutline::Compound(ref outline) => outline.as_simple(&glyf),
114 };
115 let preview = GlyphPreview::Ttf(outline);
116
117 glyphs.push(Glyph {
118 codepoint,
119 name,
120 preview,
121 });
122 }
123
124 Self { glyphs, strings }
125 }
126}
127
128#[derive(Debug, Clone)]
130pub enum GlyphPreview {
131 Ttf(SimpleGlyf),
133
134 Svg(Cow<'static, str>),
136}
137impl SvgExt for GlyphPreview {
138 fn to_svg(&self) -> String {
139 match self {
140 Self::Ttf(outline) => outline.to_svg(),
141 Self::Svg(svg) => svg.to_string(),
142 }
143 }
144}
145
146#[derive(Debug, Clone)]
148pub struct Glyph {
149 codepoint: u32,
150 name: Cow<'static, str>,
151 preview: GlyphPreview,
152}
153impl Glyph {
154 #[must_use]
156 pub const fn new(codepoint: u32, name: &'static str, preview: GlyphPreview) -> Self {
157 Self {
158 codepoint,
159 name: Cow::Borrowed(name),
160 preview,
161 }
162 }
163
164 #[must_use]
166 pub fn unicode_range(&self) -> &'static str {
167 crate::unicode_range::unicode_range(self.codepoint)
168 }
169
170 #[must_use]
172 pub fn codepoint(&self) -> u32 {
173 self.codepoint
174 }
175
176 #[must_use]
178 pub fn char(&self) -> char {
179 std::char::from_u32(self.codepoint).unwrap_or(char::REPLACEMENT_CHARACTER)
180 }
181
182 #[must_use]
184 pub fn name(&self) -> &str {
185 &self.name
186 }
187
188 #[must_use]
191 pub fn outline(&self) -> &GlyphPreview {
192 &self.preview
193 }
194
195 #[must_use]
197 pub fn svg_preview(&self) -> String {
198 self.preview.to_svg()
199 }
200
201 #[cfg(feature = "extended-svg")]
206 #[cfg_attr(docsrs, doc(cfg(feature = "extended-svg")))]
207 pub fn svgz_preview(&self) -> std::io::Result<Vec<u8>> {
208 self.preview.to_svgz()
209 }
210
211 #[cfg(feature = "extended-svg")]
216 #[cfg_attr(docsrs, doc(cfg(feature = "extended-svg")))]
217 pub fn svg_dataimage_url(&self) -> std::io::Result<String> {
218 self.preview.to_svg_dataimage_url()
219 }
220}
221
222impl From<Glyph> for char {
223 fn from(value: Glyph) -> Self {
224 value.char()
225 }
226}
227
228impl From<&Glyph> for char {
229 fn from(value: &Glyph) -> Self {
230 value.char()
231 }
232}
233
234impl From<Glyph> for u32 {
235 fn from(value: Glyph) -> Self {
236 value.codepoint()
237 }
238}
239
240impl From<&Glyph> for u32 {
241 fn from(value: &Glyph) -> Self {
242 value.codepoint()
243 }
244}
245
246impl std::fmt::Display for Glyph {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 write!(f, "{}", self.char())
249 }
250}