1use crate::{Font, be_u16, find_table_full, outline::OutlineError};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum EmbeddingFormat {
7 TrueType,
9 OpenTypeCff,
14}
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Subset {
18 pub bytes: Vec<u8>,
19 pub glyph_map: Vec<u16>,
21 pub format: EmbeddingFormat,
22}
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum SubsetErrorKind {
26 UnsupportedFormat,
27 UnsupportedOperator,
28 MissingTable,
29 InvalidGlyph,
30 Malformed,
31 BudgetExceeded,
32 Capacity,
33}
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct SubsetError {
38 pub kind: SubsetErrorKind,
39 pub table: [u8; 4],
40 pub glyph: Option<u16>,
41 pub offset: Option<usize>,
42}
43impl SubsetError {
44 pub(crate) const fn new(
45 kind: SubsetErrorKind,
46 table: [u8; 4],
47 glyph: Option<u16>,
48 offset: Option<usize>,
49 ) -> Self {
50 Self {
51 kind,
52 table,
53 glyph,
54 offset,
55 }
56 }
57}
58impl core::fmt::Display for SubsetError {
59 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60 write!(
61 f,
62 "font subset {:?} in {:?}, glyph {:?}, offset {:?}",
63 self.kind, self.table, self.glyph, self.offset
64 )
65 }
66}
67impl std::error::Error for SubsetError {}
68
69impl Font {
70 pub fn try_subset(&self, keep: &[char]) -> Result<Subset, SubsetError> {
73 let glyphs: Vec<_> = keep.iter().map(|&ch| self.glyph_index(ch)).collect();
74 self.try_subset_impl(&glyphs, keep, true)
75 }
76 pub fn try_subset_glyphs(
78 &self,
79 glyphs: &[u16],
80 cmap_chars: &[char],
81 ) -> Result<Subset, SubsetError> {
82 self.try_subset_impl(glyphs, cmap_chars, false)
83 }
84 pub fn try_subset_glyphs_with_lookup(
86 &self,
87 glyphs: &[u16],
88 cmap_chars: &[char],
89 ) -> Result<Subset, SubsetError> {
90 self.try_subset_glyphs(glyphs, cmap_chars)
91 }
92 fn try_subset_impl(
93 &self,
94 glyphs: &[u16],
95 chars: &[char],
96 web: bool,
97 ) -> Result<Subset, SubsetError> {
98 if !self.has_glyf_outlines() {
99 return crate::cff::subset(self, glyphs, chars);
100 }
101 self.validate_font_metrics()?;
102 let (_, len) = find_table_full(&self.data, b"loca").ok_or(SubsetError::new(
103 SubsetErrorKind::MissingTable,
104 *b"loca",
105 None,
106 None,
107 ))?;
108 if len < (usize::from(self.num_glyphs) + 1) * if self.loca_long { 4 } else { 2 } {
109 return Err(SubsetError::new(
110 SubsetErrorKind::Malformed,
111 *b"loca",
112 None,
113 Some(len),
114 ));
115 }
116 let (bytes, glyph_map) = self.subset_core(glyphs, chars, web, true)?;
117 Ok(Subset {
118 bytes,
119 glyph_map,
120 format: EmbeddingFormat::TrueType,
121 })
122 }
123 pub(crate) fn validate_font_metrics(&self) -> Result<(), SubsetError> {
124 if self.units_per_em == 0 || self.num_glyphs == 0 {
125 return Err(SubsetError::new(
126 SubsetErrorKind::Malformed,
127 *b"head",
128 None,
129 None,
130 ));
131 }
132 for (tag, minimum) in [
133 (b"head", 54),
134 (b"hhea", 36),
135 (b"maxp", 6),
136 (
137 b"hmtx",
138 usize::from(self.num_h_metrics) * 4
139 + usize::from(self.num_glyphs.saturating_sub(self.num_h_metrics)) * 2,
140 ),
141 ] {
142 let (offset, len) = find_table_full(&self.data, tag).ok_or(SubsetError::new(
143 SubsetErrorKind::MissingTable,
144 *tag,
145 None,
146 None,
147 ))?;
148 if len < minimum
149 || offset
150 .checked_add(len)
151 .is_none_or(|end| end > self.data.len())
152 {
153 return Err(SubsetError::new(
154 SubsetErrorKind::Malformed,
155 *tag,
156 None,
157 Some(len),
158 ));
159 }
160 }
161 if self.num_h_metrics == 0 || self.num_h_metrics > self.num_glyphs {
162 return Err(SubsetError::new(
163 SubsetErrorKind::Malformed,
164 *b"hhea",
165 None,
166 Some(34),
167 ));
168 }
169 Ok(())
170 }
171 pub(crate) fn validate_subset_glyph(&self, gid: u16) -> Result<(), SubsetError> {
172 let error = |kind, offset| SubsetError::new(kind, *b"glyf", Some(gid), offset);
173 if gid >= self.num_glyphs {
174 return Err(error(SubsetErrorKind::InvalidGlyph, None));
175 }
176 self.glyph_outline(gid).map_err(|e| {
177 error(
178 match e {
179 OutlineError::BudgetExceeded => SubsetErrorKind::BudgetExceeded,
180 _ => SubsetErrorKind::Malformed,
181 },
182 None,
183 )
184 })?;
185 let d = self
186 .glyph_data(gid)
187 .ok_or(error(SubsetErrorKind::Malformed, None))?;
188 if !self.is_composite(gid) {
189 return Ok(());
190 }
191 let mut p = 10;
192 loop {
193 let bad = error(SubsetErrorKind::Malformed, Some(p));
194 let flags = be_u16(d, p).ok_or(bad)?;
195 let child = be_u16(d, p + 2).ok_or(bad)?;
196 if child >= self.num_glyphs {
197 return Err(error(SubsetErrorKind::InvalidGlyph, Some(p + 2)));
198 }
199 p += 4 + if flags & 1 != 0 { 4 } else { 2 };
200 p += if flags & 8 != 0 {
201 2
202 } else if flags & 64 != 0 {
203 4
204 } else if flags & 128 != 0 {
205 8
206 } else {
207 0
208 };
209 if p > d.len() {
210 return Err(bad);
211 }
212 if flags & 32 == 0 {
213 if flags & 256 != 0 {
214 let n = usize::from(be_u16(d, p).ok_or(bad)?);
215 if p + 2 + n > d.len() {
216 return Err(bad);
217 }
218 }
219 return Ok(());
220 }
221 }
222 }
223}