1 //! OpenType Layout common table formats
2
3 #[path = "./lookupflag.rs"]
4 mod lookupflag;
5
6 use core::cmp::Ordering;
7
8 pub use lookupflag::LookupFlag;
9
10 #[cfg(test)]
11 #[path = "../tests/layout.rs"]
12 mod spec_tests;
13
14 include!("../../generated/generated_layout.rs");
15
16 impl<'a, T: FontRead<'a>> Lookup<'a, T> {
get_subtable(&self, offset: Offset16) -> Result<T, ReadError>17 pub fn get_subtable(&self, offset: Offset16) -> Result<T, ReadError> {
18 self.resolve_offset(offset)
19 }
20
21 #[cfg(feature = "traversal")]
traverse_lookup_flag(&self) -> traversal::FieldType<'a>22 fn traverse_lookup_flag(&self) -> traversal::FieldType<'a> {
23 self.lookup_flag().to_bits().into()
24 }
25 }
26
27 /// A trait that abstracts the behaviour of an extension subtable
28 ///
29 /// This is necessary because GPOS and GSUB have different concrete types
30 /// for their extension lookups.
31 pub trait ExtensionLookup<'a, T: FontRead<'a>>: FontRead<'a> {
extension(&self) -> Result<T, ReadError>32 fn extension(&self) -> Result<T, ReadError>;
33 }
34
35 /// an array of subtables, maybe behind extension lookups
36 ///
37 /// This is used to implement more ergonomic access to lookup subtables for
38 /// GPOS & GSUB lookup tables.
39 pub enum Subtables<'a, T: FontRead<'a>, Ext: ExtensionLookup<'a, T>> {
40 Subtable(ArrayOfOffsets<'a, T>),
41 Extension(ArrayOfOffsets<'a, Ext>),
42 }
43
44 impl<'a, T: FontRead<'a> + 'a, Ext: ExtensionLookup<'a, T> + 'a> Subtables<'a, T, Ext> {
45 /// create a new subtables array given offests to non-extension subtables
new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self46 pub(crate) fn new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
47 Subtables::Subtable(ArrayOfOffsets::new(offsets, data, ()))
48 }
49
50 /// create a new subtables array given offsets to extension subtables
new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self51 pub(crate) fn new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
52 Subtables::Extension(ArrayOfOffsets::new(offsets, data, ()))
53 }
54
55 /// The number of subtables in this collection
len(&self) -> usize56 pub fn len(&self) -> usize {
57 match self {
58 Subtables::Subtable(inner) => inner.len(),
59 Subtables::Extension(inner) => inner.len(),
60 }
61 }
62
is_empty(&self) -> bool63 pub fn is_empty(&self) -> bool {
64 self.len() == 0
65 }
66
67 /// Return the subtable at the given index
get(&self, idx: usize) -> Result<T, ReadError>68 pub fn get(&self, idx: usize) -> Result<T, ReadError> {
69 match self {
70 Subtables::Subtable(inner) => inner.get(idx),
71 Subtables::Extension(inner) => inner.get(idx).and_then(|ext| ext.extension()),
72 }
73 }
74
75 /// Return an iterator over all the subtables in the collection
iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a76 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
77 let (left, right) = match self {
78 Subtables::Subtable(inner) => (Some(inner.iter()), None),
79 Subtables::Extension(inner) => (
80 None,
81 Some(inner.iter().map(|ext| ext.and_then(|ext| ext.extension()))),
82 ),
83 };
84 left.into_iter()
85 .flatten()
86 .chain(right.into_iter().flatten())
87 }
88 }
89
90 /// An enum for different possible tables referenced by [Feature::feature_params_offset]
91 pub enum FeatureParams<'a> {
92 StylisticSet(StylisticSetParams<'a>),
93 Size(SizeParams<'a>),
94 CharacterVariant(CharacterVariantParams<'a>),
95 }
96
97 impl ReadArgs for FeatureParams<'_> {
98 type Args = Tag;
99 }
100
101 impl<'a> FontReadWithArgs<'a> for FeatureParams<'a> {
read_with_args(bytes: FontData<'a>, args: &Tag) -> Result<FeatureParams<'a>, ReadError>102 fn read_with_args(bytes: FontData<'a>, args: &Tag) -> Result<FeatureParams<'a>, ReadError> {
103 match *args {
104 t if t == Tag::new(b"size") => SizeParams::read(bytes).map(Self::Size),
105 // to whoever is debugging this dumb bug I wrote: I'm sorry.
106 t if &t.to_raw()[..2] == b"ss" => {
107 StylisticSetParams::read(bytes).map(Self::StylisticSet)
108 }
109 t if &t.to_raw()[..2] == b"cv" => {
110 CharacterVariantParams::read(bytes).map(Self::CharacterVariant)
111 }
112 // NOTE: what even is our error condition here? an offset exists but
113 // we don't know the tag?
114 _ => Err(ReadError::InvalidFormat(0xdead)),
115 }
116 }
117 }
118
119 #[cfg(feature = "traversal")]
120 impl<'a> SomeTable<'a> for FeatureParams<'a> {
type_name(&self) -> &str121 fn type_name(&self) -> &str {
122 match self {
123 FeatureParams::StylisticSet(table) => table.type_name(),
124 FeatureParams::Size(table) => table.type_name(),
125 FeatureParams::CharacterVariant(table) => table.type_name(),
126 }
127 }
128
get_field(&self, idx: usize) -> Option<Field<'a>>129 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
130 match self {
131 FeatureParams::StylisticSet(table) => table.get_field(idx),
132 FeatureParams::Size(table) => table.get_field(idx),
133 FeatureParams::CharacterVariant(table) => table.get_field(idx),
134 }
135 }
136 }
137
138 impl FeatureTableSubstitutionRecord {
alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError>139 pub fn alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
140 self.alternate_feature_offset()
141 .resolve_with_args(data, &Tag::new(b"NULL"))
142 }
143 }
144
145 impl<'a> CoverageTable<'a> {
iter(&self) -> impl Iterator<Item = GlyphId> + 'a146 pub fn iter(&self) -> impl Iterator<Item = GlyphId> + 'a {
147 // all one expression so that we have a single return type
148 let (iter1, iter2) = match self {
149 CoverageTable::Format1(t) => (Some(t.glyph_array().iter().map(|g| g.get())), None),
150 CoverageTable::Format2(t) => {
151 let iter = t.range_records().iter().flat_map(RangeRecord::iter);
152 (None, Some(iter))
153 }
154 };
155
156 iter1
157 .into_iter()
158 .flatten()
159 .chain(iter2.into_iter().flatten())
160 }
161
162 /// If this glyph is in the coverage table, returns its index
get(&self, gid: GlyphId) -> Option<u16>163 pub fn get(&self, gid: GlyphId) -> Option<u16> {
164 match self {
165 CoverageTable::Format1(sub) => sub.get(gid),
166 CoverageTable::Format2(sub) => sub.get(gid),
167 }
168 }
169 }
170
171 impl CoverageFormat1<'_> {
172 /// If this glyph is in the coverage table, returns its index
get(&self, gid: GlyphId) -> Option<u16>173 pub fn get(&self, gid: GlyphId) -> Option<u16> {
174 let be_glyph: BigEndian<GlyphId> = gid.into();
175 self.glyph_array()
176 .binary_search(&be_glyph)
177 .ok()
178 .map(|idx| idx as _)
179 }
180 }
181
182 impl CoverageFormat2<'_> {
183 /// If this glyph is in the coverage table, returns its index
get(&self, gid: GlyphId) -> Option<u16>184 pub fn get(&self, gid: GlyphId) -> Option<u16> {
185 self.range_records()
186 .binary_search_by(|rec| {
187 if rec.end_glyph_id() < gid {
188 Ordering::Less
189 } else if rec.start_glyph_id() > gid {
190 Ordering::Greater
191 } else {
192 Ordering::Equal
193 }
194 })
195 .ok()
196 .map(|idx| {
197 let rec = &self.range_records()[idx];
198 rec.start_coverage_index() + gid.to_u16() - rec.start_glyph_id().to_u16()
199 })
200 }
201 }
202
203 impl RangeRecord {
iter(&self) -> impl Iterator<Item = GlyphId> + '_204 fn iter(&self) -> impl Iterator<Item = GlyphId> + '_ {
205 (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId::new)
206 }
207 }
208
209 impl DeltaFormat {
value_count(self, start_size: u16, end_size: u16) -> usize210 pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
211 let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
212 let val_per_word = match self {
213 DeltaFormat::Local2BitDeltas => 8,
214 DeltaFormat::Local4BitDeltas => 4,
215 DeltaFormat::Local8BitDeltas => 2,
216 _ => return 0,
217 };
218
219 let count = range_len / val_per_word;
220 let extra = (range_len % val_per_word).min(1);
221 count + extra
222 }
223 }
224
225 // we as a 'format' in codegen, and the generic error type for an invalid format
226 // stores the value as an i64, so we need this conversion.
227 impl From<DeltaFormat> for i64 {
from(value: DeltaFormat) -> Self228 fn from(value: DeltaFormat) -> Self {
229 value as u16 as _
230 }
231 }
232
233 impl<'a> ClassDefFormat1<'a> {
234 /// Get the class for this glyph id
get(&self, gid: GlyphId) -> u16235 pub fn get(&self, gid: GlyphId) -> u16 {
236 if gid < self.start_glyph_id() {
237 return 0;
238 }
239 let idx = gid.to_u16() - self.start_glyph_id().to_u16();
240 self.class_value_array()
241 .get(idx as usize)
242 .map(|x| x.get())
243 .unwrap_or(0)
244 }
245
246 /// Iterate over each glyph and its class.
iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + 'a247 pub fn iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + 'a {
248 let start = self.start_glyph_id();
249 self.class_value_array()
250 .iter()
251 .enumerate()
252 .map(move |(i, val)| {
253 let gid = start.to_u16().saturating_add(i as u16);
254 (GlyphId::new(gid), val.get())
255 })
256 }
257 }
258
259 impl<'a> ClassDefFormat2<'a> {
260 /// Get the class for this glyph id
get(&self, gid: GlyphId) -> u16261 pub fn get(&self, gid: GlyphId) -> u16 {
262 let records = self.class_range_records();
263 let ix = match records.binary_search_by(|rec| rec.start_glyph_id().cmp(&gid)) {
264 Ok(ix) => ix,
265 Err(ix) => ix.saturating_sub(1),
266 };
267 if let Some(record) = records.get(ix) {
268 if (record.start_glyph_id()..=record.end_glyph_id()).contains(&gid) {
269 return record.class();
270 }
271 }
272 0
273 }
274
275 /// Iterate over each glyph and its class.
iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + 'a276 pub fn iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + 'a {
277 self.class_range_records().iter().flat_map(|range| {
278 let start = range.start_glyph_id().to_u16();
279 let end = range.end_glyph_id().to_u16();
280 (start..=end).map(|gid| (GlyphId::new(gid), range.class()))
281 })
282 }
283 }
284
285 impl ClassDef<'_> {
286 /// Get the class for this glyph id
get(&self, gid: GlyphId) -> u16287 pub fn get(&self, gid: GlyphId) -> u16 {
288 match self {
289 ClassDef::Format1(table) => table.get(gid),
290 ClassDef::Format2(table) => table.get(gid),
291 }
292 }
293
294 /// Iterate over each glyph and its class.
295 ///
296 /// This will not include class 0 unless it has been explicitly assigned.
iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + '_297 pub fn iter(&self) -> impl Iterator<Item = (GlyphId, u16)> + '_ {
298 let (one, two) = match self {
299 ClassDef::Format1(inner) => (Some(inner.iter()), None),
300 ClassDef::Format2(inner) => (None, Some(inner.iter())),
301 };
302 one.into_iter().flatten().chain(two.into_iter().flatten())
303 }
304 }
305
306 impl<'a> Device<'a> {
307 /// Iterate over the decoded values for this device
iter(&self) -> impl Iterator<Item = i8> + 'a308 pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
309 let format = self.delta_format();
310 let mut n = (self.end_size() - self.start_size()) as usize + 1;
311 let deltas_per_word = match format {
312 DeltaFormat::Local2BitDeltas => 8,
313 DeltaFormat::Local4BitDeltas => 4,
314 DeltaFormat::Local8BitDeltas => 2,
315 _ => 0,
316 };
317
318 self.delta_value().iter().flat_map(move |val| {
319 let iter = iter_packed_values(val.get(), format, n);
320 n = n.saturating_sub(deltas_per_word);
321 iter
322 })
323 }
324 }
325
iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8>326 fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
327 let mut decoded = [None; 8];
328 let (mask, sign_mask, bits) = match format {
329 DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
330 DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
331 DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
332 _ => (0, 0, 0),
333 };
334
335 let max_per_word = 16 / bits;
336 #[allow(clippy::needless_range_loop)] // enumerate() feels weird here
337 for i in 0..n.min(max_per_word) {
338 let mask = mask << ((16 - bits) - i * bits);
339 let val = (raw & mask) >> ((16 - bits) - i * bits);
340 let sign = val & sign_mask != 0;
341
342 let val = if sign {
343 // it is 2023 and I am googling to remember how twos compliment works
344 -((((!val) & mask) + 1) as i8)
345 } else {
346 val as i8
347 };
348 decoded[i] = Some(val)
349 }
350 decoded.into_iter().flatten()
351 }
352
353 #[cfg(test)]
354 mod tests {
355 use super::*;
356
357 #[test]
coverage_get_format1()358 fn coverage_get_format1() {
359 // manually generated, corresponding to the glyphs (1, 7, 13, 27, 44);
360 const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
361
362 let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
363 assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
364 assert_eq!(coverage.get(GlyphId::new(2)), None);
365 assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
366 assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
367 assert_eq!(coverage.get(GlyphId::new(45)), None);
368 }
369
370 #[test]
coverage_get_format2()371 fn coverage_get_format2() {
372 // manually generated, corresponding to glyphs (5..10) and (30..40).
373 const COV2_DATA: FontData =
374 FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
375 let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
376 assert_eq!(coverage.get(GlyphId::new(2)), None);
377 assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
378 assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
379 assert_eq!(coverage.get(GlyphId::new(10)), None);
380 assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
381 assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
382 assert_eq!(coverage.get(GlyphId::new(40)), None);
383 }
384
385 #[test]
classdef_get_format2()386 fn classdef_get_format2() {
387 let classdef = ClassDef::read(FontData::new(
388 font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
389 ))
390 .unwrap();
391 assert!(matches!(classdef, ClassDef::Format2(..)));
392 let gid_class_pairs = [
393 (616, 1),
394 (617, 1),
395 (618, 1),
396 (624, 1),
397 (625, 1),
398 (626, 1),
399 (652, 2),
400 (653, 2),
401 (654, 2),
402 (655, 2),
403 (661, 2),
404 ];
405 for (gid, class) in gid_class_pairs {
406 assert_eq!(classdef.get(GlyphId::new(gid)), class);
407 }
408 for (gid, class) in classdef.iter() {
409 assert_eq!(classdef.get(gid), class);
410 }
411 }
412
413 #[test]
delta_decode()414 fn delta_decode() {
415 // these examples come from the spec
416 assert_eq!(
417 iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
418 &[1, 2, 3, -1]
419 );
420
421 assert_eq!(
422 iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
423 &[1, 1, 1, 1, 1]
424 );
425 }
426
427 #[test]
delta_decode_all()428 fn delta_decode_all() {
429 // manually generated with write-fonts
430 let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
431 let device = Device::read(bytes.into()).unwrap();
432 assert_eq!(
433 device.iter().collect::<Vec<_>>(),
434 &[1i8, -12, 30, -11, 101, 8, 42]
435 );
436 }
437 }
438