1use std::{collections::HashSet, hash::Hash};
4
5pub use read_fonts::tables::layout::LookupFlag;
6use read_fonts::FontRead;
7
8pub mod builders;
9#[cfg(test)]
10mod spec_tests;
11
12include!("../../generated/generated_layout.rs");
13
14macro_rules! lookup_type {
16 (gpos, $ty:ty, $val:expr) => {
17 impl LookupSubtable for $ty {
18 const TYPE: LookupType = LookupType::Gpos($val);
19 }
20 };
21
22 (gsub, $ty:ty, $val:expr) => {
23 impl LookupSubtable for $ty {
24 const TYPE: LookupType = LookupType::Gsub($val);
25 }
26 };
27}
28
29macro_rules! table_newtype {
35 ($name:ident, $inner:ident, $read_type:path) => {
36 #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
43 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44 pub struct $name($inner);
45
46 impl $name {
47 pub fn as_inner(&self) -> &$inner {
49 &self.0
50 }
51 }
52
53 impl std::ops::Deref for $name {
54 type Target = $inner;
55 fn deref(&self) -> &Self::Target {
56 &self.0
57 }
58 }
59
60 impl std::ops::DerefMut for $name {
61 fn deref_mut(&mut self) -> &mut Self::Target {
62 &mut self.0
63 }
64 }
65
66 impl FontWrite for $name {
67 fn write_into(&self, writer: &mut TableWriter) {
68 self.0.write_into(writer)
69 }
70
71 fn table_type(&self) -> crate::table_type::TableType {
72 self.0.table_type()
73 }
74 }
75
76 impl Validate for $name {
77 fn validate_impl(&self, ctx: &mut ValidationCtx) {
78 self.0.validate_impl(ctx)
79 }
80 }
81
82 impl<'a> FromObjRef<$read_type> for $name {
83 fn from_obj_ref(obj: &$read_type, _data: FontData) -> Self {
84 Self(FromObjRef::from_obj_ref(obj, _data))
85 }
86 }
87
88 impl<'a> FromTableRef<$read_type> for $name {}
89
90 impl From<$inner> for $name {
91 fn from(src: $inner) -> $name {
92 $name(src)
93 }
94 }
95 };
96}
97
98pub(crate) use lookup_type;
99pub(crate) use table_newtype;
100
101impl FontWrite for LookupFlag {
102 fn write_into(&self, writer: &mut TableWriter) {
103 self.to_bits().write_into(writer)
104 }
105}
106
107impl<T: LookupSubtable + FontWrite> FontWrite for Lookup<T> {
108 fn write_into(&self, writer: &mut TableWriter) {
109 T::TYPE.write_into(writer);
110 self.lookup_flag.write_into(writer);
111 u16::try_from(self.subtables.len())
112 .unwrap()
113 .write_into(writer);
114 self.subtables.write_into(writer);
115 self.mark_filtering_set.write_into(writer);
116 }
117
118 fn table_type(&self) -> crate::table_type::TableType {
119 T::TYPE.into()
120 }
121}
122
123impl Lookup<SequenceContext> {
124 pub fn into_concrete<T: From<SequenceContext>>(self) -> Lookup<T> {
126 let Lookup {
127 lookup_flag,
128 subtables,
129 mark_filtering_set,
130 } = self;
131 let subtables = subtables
132 .into_iter()
133 .map(|offset| OffsetMarker::new(offset.into_inner().into()))
134 .collect();
135 Lookup {
136 lookup_flag,
137 subtables,
138 mark_filtering_set,
139 }
140 }
141}
142
143impl Lookup<ChainedSequenceContext> {
144 pub fn into_concrete<T: From<ChainedSequenceContext>>(self) -> Lookup<T> {
146 let Lookup {
147 lookup_flag,
148 subtables,
149 mark_filtering_set,
150 } = self;
151 let subtables = subtables
152 .into_iter()
153 .map(|offset| OffsetMarker::new(offset.into_inner().into()))
154 .collect();
155 Lookup {
156 lookup_flag,
157 subtables,
158 mark_filtering_set,
159 }
160 }
161}
162
163pub trait LookupSubtable {
168 const TYPE: LookupType;
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Eq)]
174pub enum LookupType {
175 Gpos(u16),
176 Gsub(u16),
177}
178
179impl LookupType {
180 pub(crate) const GSUB_EXT_TYPE: u16 = 7;
181 pub(crate) const GPOS_EXT_TYPE: u16 = 9;
182 pub(crate) const PAIR_POS: u16 = 2;
183 pub(crate) const MARK_TO_BASE: u16 = 4;
184
185 pub(crate) fn to_raw(self) -> u16 {
186 match self {
187 LookupType::Gpos(val) => val,
188 LookupType::Gsub(val) => val,
189 }
190 }
191
192 pub(crate) fn promote(self) -> Self {
193 match self {
194 LookupType::Gpos(Self::GPOS_EXT_TYPE) | LookupType::Gsub(Self::GSUB_EXT_TYPE) => {
195 panic!("should never be promoting an extension subtable")
196 }
197 LookupType::Gpos(_) => LookupType::Gpos(Self::GPOS_EXT_TYPE),
198 LookupType::Gsub(_) => LookupType::Gsub(Self::GSUB_EXT_TYPE),
199 }
200 }
201}
202
203impl FontWrite for LookupType {
204 fn write_into(&self, writer: &mut TableWriter) {
205 self.to_raw().write_into(writer)
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
210#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
211pub enum FeatureParams {
212 StylisticSet(StylisticSetParams),
213 Size(SizeParams),
214 CharacterVariant(CharacterVariantParams),
215}
216
217impl FontWrite for FeatureParams {
218 fn write_into(&self, writer: &mut TableWriter) {
219 match self {
220 FeatureParams::StylisticSet(table) => table.write_into(writer),
221 FeatureParams::Size(table) => table.write_into(writer),
222 FeatureParams::CharacterVariant(table) => table.write_into(writer),
223 }
224 }
225}
226
227impl Validate for FeatureParams {
228 fn validate_impl(&self, ctx: &mut ValidationCtx) {
229 match self {
230 Self::StylisticSet(table) => table.validate_impl(ctx),
231 Self::Size(table) => table.validate_impl(ctx),
232 Self::CharacterVariant(table) => table.validate_impl(ctx),
233 }
234 }
235}
236
237impl FromObjRef<read_fonts::tables::layout::FeatureParams<'_>> for FeatureParams {
238 fn from_obj_ref(from: &read_fonts::tables::layout::FeatureParams, data: FontData) -> Self {
239 use read_fonts::tables::layout::FeatureParams as FromType;
240 match from {
241 FromType::Size(thing) => Self::Size(SizeParams::from_obj_ref(thing, data)),
242 FromType::StylisticSet(thing) => {
243 Self::StylisticSet(FromObjRef::from_obj_ref(thing, data))
244 }
245 FromType::CharacterVariant(thing) => {
246 Self::CharacterVariant(FromObjRef::from_obj_ref(thing, data))
247 }
248 }
249 }
250}
251
252impl FromTableRef<read_fonts::tables::layout::FeatureParams<'_>> for FeatureParams {}
253
254impl ClassDefFormat1 {
255 fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
256 self.class_value_array.iter().enumerate().map(|(i, cls)| {
257 (
258 GlyphId16::new(self.start_glyph_id.to_u16().saturating_add(i as u16)),
259 *cls,
260 )
261 })
262 }
263}
264
265impl ClassRangeRecord {
266 fn validate_glyph_range(&self, ctx: &mut ValidationCtx) {
267 if self.start_glyph_id > self.end_glyph_id {
268 ctx.report(format!(
269 "start_glyph_id {} larger than end_glyph_id {}",
270 self.start_glyph_id, self.end_glyph_id
271 ));
272 }
273 }
274
275 fn contains(&self, gid: GlyphId16) -> bool {
276 (self.start_glyph_id..=self.end_glyph_id).contains(&gid)
277 }
278}
279
280impl ClassDefFormat2 {
281 fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
282 self.class_range_records.iter().flat_map(|rcd| {
283 (rcd.start_glyph_id.to_u16()..=rcd.end_glyph_id.to_u16())
284 .map(|gid| (GlyphId16::new(gid), rcd.class))
285 })
286 }
287}
288
289impl ClassDef {
290 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
291 let (one, two) = match self {
292 Self::Format1(table) => (Some(table.iter()), None),
293 Self::Format2(table) => (None, Some(table.iter())),
294 };
295
296 one.into_iter().flatten().chain(two.into_iter().flatten())
297 }
298
299 pub fn get(&self, glyph: GlyphId16) -> u16 {
303 self.get_raw(glyph).unwrap_or(0)
304 }
305
306 fn get_raw(&self, glyph: GlyphId16) -> Option<u16> {
308 match self {
309 ClassDef::Format1(table) => glyph
310 .to_u16()
311 .checked_sub(table.start_glyph_id.to_u16())
312 .and_then(|idx| table.class_value_array.get(idx as usize))
313 .copied(),
314 ClassDef::Format2(table) => table
315 .class_range_records
316 .iter()
317 .find_map(|rec| rec.contains(glyph).then_some(rec.class)),
318 }
319 }
320
321 pub fn class_count(&self) -> u16 {
322 self.iter()
324 .map(|(_gid, cls)| cls)
325 .chain(std::iter::once(0))
326 .collect::<HashSet<_>>()
327 .len()
328 .try_into()
329 .unwrap()
330 }
331
332 pub fn is_empty(&self) -> bool {
334 match self {
335 Self::Format1(table) => table.class_value_array.is_empty(),
336 Self::Format2(table) => table.class_range_records.is_empty(),
337 }
338 }
339}
340
341impl CoverageFormat1 {
342 fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
343 self.glyph_array.iter().copied()
344 }
345
346 fn len(&self) -> usize {
347 self.glyph_array.len()
348 }
349}
350
351impl CoverageFormat2 {
352 fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
353 self.range_records
354 .iter()
355 .flat_map(|rcd| iter_gids(rcd.start_glyph_id, rcd.end_glyph_id))
356 }
357
358 fn len(&self) -> usize {
359 self.range_records
360 .iter()
361 .map(|rcd| {
362 rcd.end_glyph_id
363 .to_u16()
364 .saturating_sub(rcd.start_glyph_id.to_u16()) as usize
365 + 1
366 })
367 .sum()
368 }
369}
370
371impl CoverageTable {
372 pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
373 let (one, two) = match self {
374 Self::Format1(table) => (Some(table.iter()), None),
375 Self::Format2(table) => (None, Some(table.iter())),
376 };
377
378 one.into_iter().flatten().chain(two.into_iter().flatten())
379 }
380
381 pub fn len(&self) -> usize {
382 match self {
383 Self::Format1(table) => table.len(),
384 Self::Format2(table) => table.len(),
385 }
386 }
387
388 pub fn is_empty(&self) -> bool {
389 self.len() == 0
390 }
391}
392
393impl FromIterator<GlyphId16> for CoverageTable {
394 fn from_iter<T: IntoIterator<Item = GlyphId16>>(iter: T) -> Self {
395 let glyphs = iter.into_iter().collect::<Vec<_>>();
396 builders::CoverageTableBuilder::from_glyphs(glyphs).build()
397 }
398}
399
400impl From<Vec<GlyphId16>> for CoverageTable {
401 fn from(value: Vec<GlyphId16>) -> Self {
402 builders::CoverageTableBuilder::from_glyphs(value).build()
403 }
404}
405
406impl FromIterator<(GlyphId16, u16)> for ClassDef {
407 fn from_iter<T: IntoIterator<Item = (GlyphId16, u16)>>(iter: T) -> Self {
408 builders::ClassDefBuilderImpl::from_iter(iter).build()
409 }
410}
411
412impl RangeRecord {
413 pub fn iter_for_glyphs(glyphs: &[GlyphId16]) -> impl Iterator<Item = RangeRecord> + '_ {
419 let mut cur_range = glyphs.first().copied().map(|g| (g, g));
420 let mut len = 0u16;
421 let mut iter = glyphs.iter().skip(1).copied();
422
423 #[allow(clippy::while_let_on_iterator)]
424 std::iter::from_fn(move || {
425 while let Some(glyph) = iter.next() {
426 match cur_range {
427 None => return None,
428 Some((a, b)) if are_sequential(b, glyph) => cur_range = Some((a, glyph)),
429 Some((a, b)) => {
430 let result = RangeRecord {
431 start_glyph_id: a,
432 end_glyph_id: b,
433 start_coverage_index: len,
434 };
435 cur_range = Some((glyph, glyph));
436 len += 1 + b.to_u16().saturating_sub(a.to_u16());
437 return Some(result);
438 }
439 }
440 }
441 cur_range
442 .take()
443 .map(|(start_glyph_id, end_glyph_id)| RangeRecord {
444 start_glyph_id,
445 end_glyph_id,
446 start_coverage_index: len,
447 })
448 })
449 }
450}
451
452fn iter_gids(gid1: GlyphId16, gid2: GlyphId16) -> impl Iterator<Item = GlyphId16> {
453 (gid1.to_u16()..=gid2.to_u16()).map(GlyphId16::new)
454}
455
456fn are_sequential(gid1: GlyphId16, gid2: GlyphId16) -> bool {
457 gid2.to_u16().saturating_sub(gid1.to_u16()) == 1
458}
459
460impl Device {
461 pub fn new(start_size: u16, end_size: u16, values: &[i8]) -> Self {
462 debug_assert_eq!(
463 (start_size..=end_size).count(),
464 values.len(),
465 "device range and values must match"
466 );
467 let delta_format: DeltaFormat = values
468 .iter()
469 .map(|val| match val {
470 -2..=1 => DeltaFormat::Local2BitDeltas,
471 -8..=7 => DeltaFormat::Local4BitDeltas,
472 _ => DeltaFormat::Local8BitDeltas,
473 })
474 .max()
475 .unwrap_or_default();
476 let delta_value = encode_delta(delta_format, values);
477
478 Device {
479 start_size,
480 end_size,
481 delta_format,
482 delta_value,
483 }
484 }
485}
486
487impl DeviceOrVariationIndex {
488 pub fn device(start_size: u16, end_size: u16, values: &[i8]) -> Self {
490 DeviceOrVariationIndex::Device(Device::new(start_size, end_size, values))
491 }
492}
493
494impl FontWrite for PendingVariationIndex {
495 fn write_into(&self, _writer: &mut TableWriter) {
496 panic!(
497 "Attempted to write PendingVariationIndex.\n\
498 VariationIndex tables should always be resolved before compilation.\n\
499 Please report this bug at <https://github.com/googlefonts/fontations/issues>"
500 )
501 }
502}
503
504fn encode_delta(format: DeltaFormat, values: &[i8]) -> Vec<u16> {
505 let (chunk_size, mask, bits) = match format {
506 DeltaFormat::Local2BitDeltas => (8, 0b11, 2),
507 DeltaFormat::Local4BitDeltas => (4, 0b1111, 4),
508 DeltaFormat::Local8BitDeltas => (2, 0b11111111, 8),
509 _ => panic!("invalid format"),
510 };
511 values
512 .chunks(chunk_size)
513 .map(|chunk| encode_chunk(chunk, mask, bits))
514 .collect()
515}
516
517fn encode_chunk(chunk: &[i8], mask: u8, bits: usize) -> u16 {
518 let mut out = 0u16;
519 for (i, val) in chunk.iter().enumerate() {
520 out |= ((val.to_be_bytes()[0] & mask) as u16) << ((16 - bits) - i * bits);
521 }
522 out
523}
524
525impl From<VariationIndex> for u32 {
526 fn from(value: VariationIndex) -> Self {
527 ((value.delta_set_outer_index as u32) << 16) | value.delta_set_inner_index as u32
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 #[should_panic(expected = "array exceeds max length")]
537 fn array_len_smoke_test() {
538 let table = ScriptList {
539 script_records: vec![ScriptRecord {
540 script_tag: Tag::new(b"hihi"),
541 script: OffsetMarker::new(Script {
542 default_lang_sys: NullableOffsetMarker::new(None),
543 lang_sys_records: vec![LangSysRecord {
544 lang_sys_tag: Tag::new(b"coco"),
545 lang_sys: OffsetMarker::new(LangSys {
546 required_feature_index: 0xffff,
547 feature_indices: vec![69; (u16::MAX) as usize + 5],
548 }),
549 }],
550 }),
551 }],
552 };
553
554 table.validate().unwrap();
555 }
556
557 #[test]
558 #[should_panic(expected = "larger than end_glyph_id")]
559 fn validate_classdef_ranges() {
560 let classdef = ClassDefFormat2::new(vec![ClassRangeRecord::new(
561 GlyphId16::new(12),
562 GlyphId16::new(3),
563 7,
564 )]);
565
566 classdef.validate().unwrap();
567 }
568
569 #[test]
570 fn delta_encode() {
571 let inp = [1i8, 2, 3, -1];
572 let result = encode_delta(DeltaFormat::Local4BitDeltas, &inp);
573 assert_eq!(result.len(), 1);
574 assert_eq!(result[0], 0x123f_u16);
575
576 let inp = [1i8, 1, 1, 1, 1];
577 let result = encode_delta(DeltaFormat::Local2BitDeltas, &inp);
578 assert_eq!(result.len(), 1);
579 assert_eq!(result[0], 0x5540_u16);
580 }
581}