allsorts_subset_browser/
tables.rs

1//! OpenType font table parsing and writing.
2
3pub mod cmap;
4pub mod glyf;
5pub mod loca;
6pub mod os2;
7pub mod svg;
8pub mod variable_fonts;
9
10use std::borrow::Cow;
11use std::convert::TryFrom;
12use std::fmt::{self, Formatter};
13
14use bitflags::bitflags;
15use encoding_rs::Encoding;
16
17use crate::binary::read::{
18    CheckIndex, ReadArray, ReadArrayCow, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope,
19    ReadUnchecked,
20};
21use crate::binary::write::{Placeholder, WriteBinary, WriteContext};
22use crate::binary::{I16Be, I32Be, I64Be, U16Be, U32Be};
23use crate::error::{ParseError, WriteError};
24use crate::tag;
25use crate::{size, SafeFrom};
26
27/// Magic value identifying a CFF font (`OTTO`)
28pub const CFF_MAGIC: u32 = tag::OTTO;
29
30/// Magic number identifying TrueType 1.0
31///
32/// The version number 1.0 as a 16.16 fixed-point value, indicating TrueType glyph data.
33pub const TTF_MAGIC: u32 = 0x00010000;
34
35/// Magic number identifying TrueType (Apple version)
36pub const TRUE_MAGIC: u32 = tag::TRUE;
37
38/// Magic value identifying a TrueType font collection `ttcf`
39pub const TTCF_MAGIC: u32 = tag::TTCF;
40
41/// 32-bit signed fixed-point number (16.16)
42///
43/// The integer component is a signed 16-bit integer. The fraction is an unsigned
44/// 16-bit numerator for denominator of 0xFFFF (65635). I.e scale of 1/65535.
45#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
46pub struct Fixed(i32);
47
48/// Date represented in number of seconds since 12:00 midnight, January 1, 1904
49///
50/// The value is represented as a signed 64-bit integer.
51type LongDateTime = i64;
52
53pub trait FontTableProvider {
54    /// Return data for the specified table if present
55    fn table_data(&self, tag: u32) -> Result<Option<Cow<'_, [u8]>>, ParseError>;
56
57    fn has_table(&self, tag: u32) -> bool;
58
59    fn read_table_data(&self, tag: u32) -> Result<Cow<'_, [u8]>, ParseError> {
60        self.table_data(tag)?.ok_or(ParseError::MissingValue)
61    }
62
63    /// The tags of the tables within this font.
64    ///
65    /// Returns `None` if the tags cannot be determined.
66    fn table_tags(&self) -> Option<Vec<u32>>;
67}
68
69pub trait SfntVersion {
70    fn sfnt_version(&self) -> u32;
71}
72
73/// The F2DOT14 format consists of a signed, 2’s complement integer and an unsigned fraction.
74///
75/// To compute the actual value, take the integer and add the fraction.
76#[repr(transparent)]
77#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
78pub struct F2Dot14(i16);
79
80/// The size of the offsets in the `loca` table
81///
82/// <https://docs.microsoft.com/en-us/typography/opentype/spec/loca>
83#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub enum IndexToLocFormat {
85    /// Offsets are 16-bit. The actual local offset divided by 2 is stored.
86    Short,
87    /// Offsets are 32-bit. The actual local offset is stored.
88    Long,
89}
90
91#[derive(Clone)]
92pub struct OpenTypeFont<'a> {
93    pub scope: ReadScope<'a>,
94    pub data: OpenTypeData<'a>,
95}
96
97/// An OpenTypeFont containing a single font or a collection of fonts
98#[derive(Clone)]
99pub enum OpenTypeData<'a> {
100    Single(OffsetTable<'a>),
101    Collection(TTCHeader<'a>),
102}
103
104/// TrueType collection header
105#[derive(Clone)]
106pub struct TTCHeader<'a> {
107    pub major_version: u16,
108    pub minor_version: u16,
109    pub offset_tables: ReadArray<'a, U32Be>,
110    // TODO add digital signature fields
111}
112
113/// OpenType Offset Table
114///
115/// <https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font>
116#[derive(Clone)]
117pub struct OffsetTable<'a> {
118    pub sfnt_version: u32,
119    pub search_range: u16,
120    pub entry_selector: u16,
121    pub range_shift: u16,
122    pub table_records: ReadArray<'a, TableRecord>,
123}
124
125pub struct OffsetTableFontProvider<'a> {
126    scope: ReadScope<'a>,
127    offset_table: OffsetTable<'a>,
128}
129
130/// An entry in the Offset Table
131///
132/// <https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font>
133#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash)]
134pub struct TableRecord {
135    pub table_tag: u32,
136    pub checksum: u32,
137    pub offset: u32,
138    pub length: u32,
139}
140
141/// `head` table
142///
143/// <https://docs.microsoft.com/en-us/typography/opentype/spec/head>
144#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
145pub struct HeadTable {
146    pub major_version: u16,
147    pub minor_version: u16,
148    pub font_revision: Fixed,
149    pub check_sum_adjustment: u32,
150    pub magic_number: u32,
151    pub flags: u16,
152    pub units_per_em: u16,
153    pub created: LongDateTime,
154    pub modified: LongDateTime,
155    pub x_min: i16,
156    pub y_min: i16,
157    pub x_max: i16,
158    pub y_max: i16,
159    pub mac_style: MacStyle,
160    pub lowest_rec_ppem: u16,
161    pub font_direction_hint: i16,
162    pub index_to_loc_format: IndexToLocFormat,
163    pub glyph_data_format: i16,
164}
165
166bitflags! {
167    /// macStyle field in `head`
168    pub struct MacStyle: u16 {
169        const BOLD = 1 << 0;
170        const ITALIC = 1 << 1;
171        const UNDERLINE = 1 << 2;
172        const OUTLINE = 1 << 3;
173        const SHADOW = 1 << 4;
174        const CONDENSED = 1 << 5;
175        const EXTENDED = 1 << 6;
176        // Bits 7–15: Reserved (set to 0).
177    }
178}
179
180/// `hhea` horizontal header table
181///
182/// > This table contains information for horizontal layout.
183///
184/// <https://docs.microsoft.com/en-us/typography/opentype/spec/hhea>
185///
186/// This struct is also used for the `vhea` table.
187#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
188pub struct HheaTable {
189    pub ascender: i16,
190    pub descender: i16,
191    pub line_gap: i16,
192    pub advance_width_max: u16,
193    pub min_left_side_bearing: i16,
194    pub min_right_side_bearing: i16,
195    pub x_max_extent: i16,
196    pub caret_slope_rise: i16,
197    pub caret_slope_run: i16,
198    pub caret_offset: i16,
199    pub num_h_metrics: u16,
200}
201
202/// `hmtx` horizontal metrics table
203///
204/// <https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx>
205///
206/// This struct is also used for `vmtx` table.
207#[derive(Debug)]
208pub struct HmtxTable<'a> {
209    pub h_metrics: ReadArrayCow<'a, LongHorMetric>,
210    pub left_side_bearings: ReadArrayCow<'a, I16Be>,
211}
212
213/// A `longHorMetric` record in the `hmtx` table.
214///
215/// <https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx>
216///
217/// This struct is also used for LongVerMetric `vmtx` table.
218#[derive(Debug, PartialEq, Copy, Clone)]
219pub struct LongHorMetric {
220    pub advance_width: u16,
221    pub lsb: i16,
222}
223
224/// maxp - Maximum profile
225///
226/// This table establishes the memory requirements for this font. Fonts with CFF data must use
227/// Version 0.5 of this table, specifying only the numGlyphs field. Fonts with TrueType outlines
228/// must use Version 1.0 of this table, where all data is required.
229///
230/// <https://docs.microsoft.com/en-us/typography/opentype/spec/maxp>
231#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
232pub struct MaxpTable {
233    pub num_glyphs: u16,
234    /// Extra fields, present if maxp table is version 1.0, absent if version 0.5.
235    pub version1_sub_table: Option<MaxpVersion1SubTable>,
236}
237
238#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
239pub struct MaxpVersion1SubTable {
240    /// Maximum points in a non-composite glyph.
241    pub max_points: u16,
242    /// Maximum contours in a non-composite glyph.
243    pub max_contours: u16,
244    /// Maximum points in a composite glyph.
245    pub max_composite_points: u16,
246    /// Maximum contours in a composite glyph.
247    pub max_composite_contours: u16,
248    /// 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should
249    /// be set to 2 in most cases.
250    pub max_zones: u16,
251    /// Maximum points used in Z0.
252    pub max_twilight_points: u16,
253    /// Number of Storage Area locations.
254    pub max_storage: u16,
255    /// Number of FDEFs, equal to the highest function number + 1.
256    pub max_function_defs: u16,
257    /// Number of IDEFs.
258    pub max_instruction_defs: u16,
259    /// Maximum stack depth across Font Program ('fpgm' table), CVT Program ('prep' table) and all
260    /// glyph instructions (in the 'glyf' table).
261    pub max_stack_elements: u16,
262    /// Maximum byte count for glyph instructions.
263    pub max_size_of_instructions: u16,
264    /// Maximum number of components referenced at “top level” for any composite glyph.
265    pub max_component_elements: u16,
266    /// Maximum levels of recursion; 1 for simple components.
267    pub max_component_depth: u16,
268}
269
270/// `name` table
271///
272/// <https://docs.microsoft.com/en-us/typography/opentype/spec/name>
273pub struct NameTable<'a> {
274    pub string_storage: ReadScope<'a>,
275    pub name_records: ReadArray<'a, NameRecord>,
276    /// Language tag records
277    ///
278    /// Present if `name` table version is 1 or newer. If present, language ids >= 0x8000 refer to
279    /// language tag records with 0x8000 the first record, 0x8001 the second, etc.
280    pub opt_langtag_records: Option<ReadArray<'a, LangTagRecord>>,
281}
282
283/// Record within the `name` table
284#[derive(Debug)]
285pub struct NameRecord {
286    pub platform_id: u16,
287    pub encoding_id: u16,
288    pub language_id: u16,
289    pub name_id: u16,
290    pub length: u16,
291    pub offset: u16,
292}
293
294/// Language-tag record within the `name` table
295pub struct LangTagRecord {
296    pub length: u16,
297    pub offset: u16,
298}
299
300/// cvt — Control Value Table
301///
302/// <https://learn.microsoft.com/en-us/typography/opentype/spec/cvt>
303pub struct CvtTable<'a> {
304    pub values: ReadArrayCow<'a, I16Be>,
305}
306
307impl<'a> OpenTypeFont<'a> {
308    pub fn table_provider(&self, index: usize) -> Result<OffsetTableFontProvider<'a>, ParseError> {
309        self.offset_table(index)
310            .map(|offset_table| OffsetTableFontProvider {
311                offset_table: offset_table.into_owned(),
312                scope: self.scope,
313            })
314    }
315
316    pub fn offset_table<'b>(
317        &'b self,
318        index: usize,
319    ) -> Result<Cow<'b, OffsetTable<'a>>, ParseError> {
320        match &self.data {
321            OpenTypeData::Single(offset_table) => Ok(Cow::Borrowed(offset_table)),
322            OpenTypeData::Collection(ttc) => {
323                ttc.offset_tables.check_index(index)?;
324                let offset = usize::try_from(ttc.offset_tables.get_item(index))?;
325                let offset_table = self.scope.offset(offset).read::<OffsetTable<'_>>()?;
326                Ok(Cow::Owned(offset_table))
327            }
328        }
329    }
330}
331
332impl<'b> ReadBinary for OpenTypeFont<'b> {
333    type HostType<'a> = OpenTypeFont<'a>;
334
335    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
336        let scope = ctxt.scope();
337        let mut peek = ctxt.clone();
338        let magic = peek.read_u32be()?;
339        match magic {
340            TTF_MAGIC | TRUE_MAGIC | CFF_MAGIC => {
341                let offset_table = ctxt.read::<OffsetTable<'_>>()?;
342                let font = OpenTypeData::Single(offset_table);
343                Ok(OpenTypeFont { scope, data: font })
344            }
345            TTCF_MAGIC => {
346                let ttc_header = ctxt.read::<TTCHeader<'_>>()?;
347                let font = OpenTypeData::Collection(ttc_header);
348                Ok(OpenTypeFont { scope, data: font })
349            }
350            _ => Err(ParseError::BadVersion),
351        }
352    }
353}
354
355impl<'b> ReadBinary for TTCHeader<'b> {
356    type HostType<'a> = TTCHeader<'a>;
357
358    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
359        let ttc_tag = ctxt.read_u32be()?;
360        match ttc_tag {
361            TTCF_MAGIC => {
362                let major_version = ctxt.read_u16be()?;
363                let minor_version = ctxt.read_u16be()?;
364                ctxt.check(major_version == 1 || major_version == 2)?;
365                let num_fonts = usize::try_from(ctxt.read_u32be()?)?;
366                let offset_tables = ctxt.read_array::<U32Be>(num_fonts)?;
367                // TODO read digital signature fields in TTCHeader version 2
368                Ok(TTCHeader {
369                    major_version,
370                    minor_version,
371                    offset_tables,
372                })
373            }
374            _ => Err(ParseError::BadVersion),
375        }
376    }
377}
378
379impl<'b> ReadBinary for OffsetTable<'b> {
380    type HostType<'a> = OffsetTable<'a>;
381
382    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
383        let sfnt_version = ctxt.read_u32be()?;
384        match sfnt_version {
385            TTF_MAGIC | TRUE_MAGIC | CFF_MAGIC => {
386                let num_tables = ctxt.read_u16be()?;
387                let search_range = ctxt.read_u16be()?;
388                let entry_selector = ctxt.read_u16be()?;
389                let range_shift = ctxt.read_u16be()?;
390                let table_records = ctxt.read_array::<TableRecord>(usize::from(num_tables))?;
391                Ok(OffsetTable {
392                    sfnt_version,
393                    search_range,
394                    entry_selector,
395                    range_shift,
396                    table_records,
397                })
398            }
399            _ => Err(ParseError::BadVersion),
400        }
401    }
402}
403
404impl<'a> FontTableProvider for OffsetTableFontProvider<'a> {
405    fn table_data(&self, tag: u32) -> Result<Option<Cow<'_, [u8]>>, ParseError> {
406        self.offset_table
407            .read_table(&self.scope, tag)
408            .map(|scope| scope.map(|scope| Cow::Borrowed(scope.data())))
409    }
410
411    fn has_table(&self, tag: u32) -> bool {
412        self.offset_table.find_table_record(tag).is_some()
413    }
414
415    fn table_tags(&self) -> Option<Vec<u32>> {
416        Some(
417            self.offset_table
418                .table_records
419                .iter()
420                .map(|rec| rec.table_tag)
421                .collect(),
422        )
423    }
424}
425
426impl<'a> SfntVersion for OffsetTableFontProvider<'a> {
427    fn sfnt_version(&self) -> u32 {
428        self.offset_table.sfnt_version
429    }
430}
431
432impl ReadFrom for TableRecord {
433    type ReadType = ((U32Be, U32Be), (U32Be, U32Be));
434    fn read_from(((table_tag, checksum), (offset, length)): ((u32, u32), (u32, u32))) -> Self {
435        TableRecord {
436            table_tag,
437            checksum,
438            offset,
439            length,
440        }
441    }
442}
443
444impl WriteBinary<&Self> for TableRecord {
445    type Output = ();
446
447    fn write<C: WriteContext>(ctxt: &mut C, table: &TableRecord) -> Result<(), WriteError> {
448        U32Be::write(ctxt, table.table_tag)?;
449        U32Be::write(ctxt, table.checksum)?;
450        U32Be::write(ctxt, table.offset)?;
451        U32Be::write(ctxt, table.length)?;
452
453        Ok(())
454    }
455}
456
457impl<'a> OffsetTable<'a> {
458    pub fn find_table_record(&self, tag: u32) -> Option<TableRecord> {
459        self.table_records
460            .iter()
461            .find(|table_record| table_record.table_tag == tag)
462    }
463
464    pub fn read_table(
465        &self,
466        scope: &ReadScope<'a>,
467        tag: u32,
468    ) -> Result<Option<ReadScope<'a>>, ParseError> {
469        if let Some(table_record) = self.find_table_record(tag) {
470            let table = table_record.read_table(scope)?;
471            Ok(Some(table))
472        } else {
473            Ok(None)
474        }
475    }
476}
477
478impl TableRecord {
479    pub const SIZE: usize = 4 * size::U32;
480
481    pub fn read_table<'a>(&self, scope: &ReadScope<'a>) -> Result<ReadScope<'a>, ParseError> {
482        let offset = usize::try_from(self.offset)?;
483        let length = usize::try_from(self.length)?;
484        scope.offset_length(offset, length)
485    }
486}
487
488impl ReadBinary for HeadTable {
489    type HostType<'a> = Self;
490
491    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
492        let major_version = ctxt.read::<U16Be>()?;
493        let minor_version = ctxt.read::<U16Be>()?;
494        let font_revision = ctxt.read::<Fixed>()?;
495        let check_sum_adjustment = ctxt.read::<U32Be>()?;
496        let magic_number = ctxt.read::<U32Be>()?;
497        ctxt.check(magic_number == 0x5F0F3CF5)?;
498        let flags = ctxt.read::<U16Be>()?;
499        let units_per_em = ctxt.read::<U16Be>()?;
500        let created = ctxt.read::<I64Be>()?;
501        let modified = ctxt.read::<I64Be>()?;
502        let x_min = ctxt.read::<I16Be>()?;
503        let y_min = ctxt.read::<I16Be>()?;
504        let x_max = ctxt.read::<I16Be>()?;
505        let y_max = ctxt.read::<I16Be>()?;
506        let mac_style = ctxt.read::<U16Be>().map(MacStyle::from_bits_truncate)?;
507        let lowest_rec_ppem = ctxt.read::<U16Be>()?;
508        let font_direction_hint = ctxt.read::<I16Be>()?;
509        let index_to_loc_format = ctxt.read::<IndexToLocFormat>()?;
510        let glyph_data_format = ctxt.read::<I16Be>()?;
511
512        Ok(HeadTable {
513            major_version,
514            minor_version,
515            font_revision,
516            check_sum_adjustment,
517            magic_number,
518            flags,
519            units_per_em,
520            created,
521            modified,
522            x_min,
523            y_min,
524            x_max,
525            y_max,
526            mac_style,
527            lowest_rec_ppem,
528            font_direction_hint,
529            index_to_loc_format,
530            glyph_data_format,
531        })
532    }
533}
534
535impl WriteBinary<&Self> for HeadTable {
536    type Output = Placeholder<U32Be, u32>;
537
538    /// Writes the table to the `WriteContext` and returns a placeholder to the
539    /// `check_sum_adjustment` field.
540    ///
541    /// The `check_sum_adjustment` field requires special handling to calculate. See:
542    /// <https://docs.microsoft.com/en-us/typography/opentype/spec/head>
543    fn write<C: WriteContext>(ctxt: &mut C, table: &HeadTable) -> Result<Self::Output, WriteError> {
544        U16Be::write(ctxt, table.major_version)?;
545        U16Be::write(ctxt, table.minor_version)?;
546        Fixed::write(ctxt, table.font_revision)?;
547        let check_sum_adjustment = ctxt.placeholder()?;
548        U32Be::write(ctxt, table.magic_number)?;
549        U16Be::write(ctxt, table.flags)?;
550        U16Be::write(ctxt, table.units_per_em)?;
551        I64Be::write(ctxt, table.created)?;
552        I64Be::write(ctxt, table.modified)?;
553        I16Be::write(ctxt, table.x_min)?;
554        I16Be::write(ctxt, table.y_min)?;
555        I16Be::write(ctxt, table.x_max)?;
556        I16Be::write(ctxt, table.y_max)?;
557        U16Be::write(ctxt, table.mac_style.bits())?;
558        U16Be::write(ctxt, table.lowest_rec_ppem)?;
559        I16Be::write(ctxt, table.font_direction_hint)?;
560        IndexToLocFormat::write(ctxt, table.index_to_loc_format)?;
561        I16Be::write(ctxt, table.glyph_data_format)?;
562
563        Ok(check_sum_adjustment)
564    }
565}
566
567impl HeadTable {
568    // macStyle:
569    // Bit 0: Bold (if set to 1);
570    // Bit 1: Italic (if set to 1)
571    // Bit 2: Underline (if set to 1)
572    // Bit 3: Outline (if set to 1)
573    // Bit 4: Shadow (if set to 1)
574    // Bit 5: Condensed (if set to 1)
575    // Bit 6: Extended (if set to 1)
576    // Bits 7–15: Reserved (set to 0).
577    // https://docs.microsoft.com/en-us/typography/opentype/spec/head
578    pub fn is_bold(&self) -> bool {
579        self.mac_style.contains(MacStyle::BOLD)
580    }
581
582    pub fn is_italic(&self) -> bool {
583        self.mac_style.contains(MacStyle::ITALIC)
584    }
585}
586
587impl ReadBinary for HheaTable {
588    type HostType<'a> = Self;
589
590    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
591        let major_version = ctxt.read_u16be()?;
592        let _minor_version = ctxt.read_u16be()?;
593        ctxt.check(major_version == 1)?;
594        let ascender = ctxt.read_i16be()?;
595        let descender = ctxt.read_i16be()?;
596        let line_gap = ctxt.read_i16be()?;
597        let advance_width_max = ctxt.read_u16be()?;
598        let min_left_side_bearing = ctxt.read_i16be()?;
599        let min_right_side_bearing = ctxt.read_i16be()?;
600        let x_max_extent = ctxt.read_i16be()?;
601        let caret_slope_rise = ctxt.read_i16be()?;
602        let caret_slope_run = ctxt.read_i16be()?;
603        let caret_offset = ctxt.read_i16be()?;
604        let _reserved1 = ctxt.read_i16be()?;
605        let _reserved2 = ctxt.read_i16be()?;
606        let _reserved3 = ctxt.read_i16be()?;
607        let _reserved4 = ctxt.read_i16be()?;
608        let metric_data_format = ctxt.read_i16be()?;
609        ctxt.check(metric_data_format == 0)?;
610        let num_h_metrics = ctxt.read_u16be()?;
611
612        Ok(HheaTable {
613            ascender,
614            descender,
615            line_gap,
616            advance_width_max,
617            min_left_side_bearing,
618            min_right_side_bearing,
619            x_max_extent,
620            caret_slope_rise,
621            caret_slope_run,
622            caret_offset,
623            num_h_metrics,
624        })
625    }
626}
627
628impl WriteBinary<&Self> for HheaTable {
629    type Output = ();
630
631    fn write<C: WriteContext>(ctxt: &mut C, table: &HheaTable) -> Result<(), WriteError> {
632        U16Be::write(ctxt, 1u16)?; // major_version
633        U16Be::write(ctxt, 0u16)?; // minor_version
634
635        I16Be::write(ctxt, table.ascender)?;
636        I16Be::write(ctxt, table.descender)?;
637        I16Be::write(ctxt, table.line_gap)?;
638        U16Be::write(ctxt, table.advance_width_max)?;
639        I16Be::write(ctxt, table.min_left_side_bearing)?;
640        I16Be::write(ctxt, table.min_right_side_bearing)?;
641        I16Be::write(ctxt, table.x_max_extent)?;
642        I16Be::write(ctxt, table.caret_slope_rise)?;
643        I16Be::write(ctxt, table.caret_slope_run)?;
644        I16Be::write(ctxt, table.caret_offset)?;
645
646        I16Be::write(ctxt, 0i16)?; // reserved
647        I16Be::write(ctxt, 0i16)?; // reserved
648        I16Be::write(ctxt, 0i16)?; // reserved
649        I16Be::write(ctxt, 0i16)?; // reserved
650
651        I16Be::write(ctxt, 0i16)?; // metric_data_format
652
653        U16Be::write(ctxt, table.num_h_metrics)?;
654
655        Ok(())
656    }
657}
658
659impl<'b> ReadBinaryDep for HmtxTable<'b> {
660    type Args<'a> = (usize, usize); // num_glyphs, num_h_metrics
661    type HostType<'a> = HmtxTable<'a>;
662
663    fn read_dep<'a>(
664        ctxt: &mut ReadCtxt<'a>,
665        (num_glyphs, num_h_metrics): (usize, usize),
666    ) -> Result<Self::HostType<'a>, ParseError> {
667        let h_metrics = ctxt.read_array::<LongHorMetric>(num_h_metrics)?;
668        let left_side_bearings =
669            ctxt.read_array::<I16Be>(num_glyphs.saturating_sub(num_h_metrics))?;
670        Ok(HmtxTable {
671            h_metrics: ReadArrayCow::Borrowed(h_metrics),
672            left_side_bearings: ReadArrayCow::Borrowed(left_side_bearings),
673        })
674    }
675}
676
677impl<'a> WriteBinary<&Self> for HmtxTable<'a> {
678    type Output = ();
679
680    fn write<C: WriteContext>(ctxt: &mut C, table: &HmtxTable<'a>) -> Result<(), WriteError> {
681        ReadArrayCow::write(ctxt, &table.h_metrics)?;
682        ReadArrayCow::write(ctxt, &table.left_side_bearings)?;
683
684        Ok(())
685    }
686}
687
688impl<'a> HmtxTable<'a> {
689    /// Retrieve the horizontal advance for glyph with index `glyph_id`.
690    pub fn horizontal_advance(&self, glyph_id: u16) -> Result<u16, ParseError> {
691        if self.h_metrics.is_empty() {
692            return Err(ParseError::BadIndex);
693        }
694
695        // This is largely the same as `metric` below but it avoids a lookup in the
696        // `left_side_bearings` array.
697        let glyph_id = usize::from(glyph_id);
698        let num_h_metrics = self.h_metrics.len();
699        let metric = if glyph_id < num_h_metrics {
700            self.h_metrics.read_item(glyph_id)
701        } else {
702            // As an optimization, the number of records can be less than the number of glyphs, in
703            // which case the advance width value of the last record applies to all remaining glyph
704            // IDs.
705            // https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx
706            self.h_metrics.read_item(num_h_metrics - 1)
707        };
708        metric.map(|metric| metric.advance_width)
709    }
710
711    /// Retrieve the advance and left-side bearing for glyph with index `glyph_id`.
712    pub fn metric(&self, glyph_id: u16) -> Result<LongHorMetric, ParseError> {
713        if self.h_metrics.is_empty() {
714            return Err(ParseError::BadIndex);
715        }
716
717        let glyph_id = usize::from(glyph_id);
718        let num_h_metrics = self.h_metrics.len();
719        if glyph_id < num_h_metrics {
720            self.h_metrics.read_item(glyph_id)
721        } else {
722            // As an optimization, the number of records can be less than the number of glyphs, in
723            // which case the advance width value of the last record applies to all remaining glyph
724            // IDs. If numberOfHMetrics is less than the total number of glyphs, then the hMetrics
725            // array is followed by an array for the left side bearing values of the remaining
726            // glyphs.
727            // https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx
728            let mut metric = self.h_metrics.read_item(num_h_metrics - 1)?;
729            let lsb_index = glyph_id - num_h_metrics;
730            metric.lsb = self
731                .left_side_bearings
732                .check_index(lsb_index)
733                .and_then(|_| self.left_side_bearings.read_item(lsb_index))?;
734            Ok(metric)
735        }
736    }
737}
738
739impl ReadFrom for LongHorMetric {
740    type ReadType = (U16Be, I16Be);
741    fn read_from((advance_width, lsb): (u16, i16)) -> Self {
742        LongHorMetric { advance_width, lsb }
743    }
744}
745
746impl WriteBinary for LongHorMetric {
747    type Output = ();
748
749    fn write<C: WriteContext>(ctxt: &mut C, metric: LongHorMetric) -> Result<(), WriteError> {
750        U16Be::write(ctxt, metric.advance_width)?;
751        I16Be::write(ctxt, metric.lsb)?;
752
753        Ok(())
754    }
755}
756
757impl ReadBinary for MaxpTable {
758    type HostType<'a> = Self;
759
760    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
761        let version = ctxt.read_u32be()?;
762        let num_glyphs = ctxt.read_u16be()?;
763        let sub_table = if version == 0x00010000 {
764            Some(ctxt.read::<MaxpVersion1SubTable>()?)
765        } else {
766            None
767        };
768        Ok(MaxpTable {
769            num_glyphs,
770            version1_sub_table: sub_table,
771        })
772    }
773}
774
775impl WriteBinary<&Self> for MaxpTable {
776    type Output = ();
777
778    fn write<C: WriteContext>(ctxt: &mut C, table: &MaxpTable) -> Result<(), WriteError> {
779        if let Some(sub_table) = &table.version1_sub_table {
780            U32Be::write(ctxt, 0x00010000u32)?; // version 1.0
781            U16Be::write(ctxt, table.num_glyphs)?;
782            MaxpVersion1SubTable::write(ctxt, sub_table)?;
783        } else {
784            U32Be::write(ctxt, 0x00005000u32)?; // version 0.5
785            U16Be::write(ctxt, table.num_glyphs)?;
786        }
787        Ok(())
788    }
789}
790
791impl ReadBinary for MaxpVersion1SubTable {
792    type HostType<'a> = Self;
793
794    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
795        let max_points = ctxt.read_u16be()?;
796        let max_contours = ctxt.read_u16be()?;
797        let max_composite_points = ctxt.read_u16be()?;
798        let max_composite_contours = ctxt.read_u16be()?;
799        let max_zones = ctxt.read_u16be()?;
800        let max_twilight_points = ctxt.read_u16be()?;
801        let max_storage = ctxt.read_u16be()?;
802        let max_function_defs = ctxt.read_u16be()?;
803        let max_instruction_defs = ctxt.read_u16be()?;
804        let max_stack_elements = ctxt.read_u16be()?;
805        let max_size_of_instructions = ctxt.read_u16be()?;
806        let max_component_elements = ctxt.read_u16be()?;
807        let max_component_depth = ctxt.read_u16be()?;
808
809        Ok(MaxpVersion1SubTable {
810            max_points,
811            max_contours,
812            max_composite_points,
813            max_composite_contours,
814            max_zones,
815            max_twilight_points,
816            max_storage,
817            max_function_defs,
818            max_instruction_defs,
819            max_stack_elements,
820            max_size_of_instructions,
821            max_component_elements,
822            max_component_depth,
823        })
824    }
825}
826
827impl WriteBinary<&Self> for MaxpVersion1SubTable {
828    type Output = ();
829
830    fn write<C: WriteContext>(
831        ctxt: &mut C,
832        table: &MaxpVersion1SubTable,
833    ) -> Result<(), WriteError> {
834        U16Be::write(ctxt, table.max_points)?;
835        U16Be::write(ctxt, table.max_contours)?;
836        U16Be::write(ctxt, table.max_composite_points)?;
837        U16Be::write(ctxt, table.max_composite_contours)?;
838        U16Be::write(ctxt, table.max_zones)?;
839        U16Be::write(ctxt, table.max_twilight_points)?;
840        U16Be::write(ctxt, table.max_storage)?;
841        U16Be::write(ctxt, table.max_function_defs)?;
842        U16Be::write(ctxt, table.max_instruction_defs)?;
843        U16Be::write(ctxt, table.max_stack_elements)?;
844        U16Be::write(ctxt, table.max_size_of_instructions)?;
845        U16Be::write(ctxt, table.max_component_elements)?;
846        U16Be::write(ctxt, table.max_component_depth)?;
847
848        Ok(())
849    }
850}
851
852impl NameTable<'_> {
853    pub const COPYRIGHT_NOTICE: u16 = 0;
854    pub const FONT_FAMILY_NAME: u16 = 1;
855    pub const FONT_SUBFAMILY_NAME: u16 = 2;
856    pub const UNIQUE_FONT_IDENTIFIER: u16 = 3;
857    pub const FULL_FONT_NAME: u16 = 4;
858    pub const VERSION_STRING: u16 = 5;
859    pub const POSTSCRIPT_NAME: u16 = 6;
860    pub const TRADEMARK: u16 = 7;
861    pub const MANUFACTURER_NAME: u16 = 8;
862    pub const DESIGNER: u16 = 9;
863    pub const DESCRIPTION: u16 = 10;
864    pub const URL_VENDOR: u16 = 11;
865    pub const URL_DESIGNER: u16 = 12;
866    pub const LICENSE_DESCRIPTION: u16 = 13;
867    pub const LICENSE_INFO_URL: u16 = 14;
868    pub const TYPOGRAPHIC_FAMILY_NAME: u16 = 16;
869    pub const TYPOGRAPHIC_SUBFAMILY_NAME: u16 = 17;
870    pub const COMPATIBLE_FULL: u16 = 18; // (Macintosh only)
871    pub const SAMPLE_TEXT: u16 = 19;
872    pub const POSTSCRIPT_CID_FINDFONT_NAME: u16 = 20;
873    pub const WWS_FAMILY_NAME: u16 = 21; // WWS = Weight, width, slope
874    pub const WWS_SUBFAMILY_NAME: u16 = 22;
875    pub const LIGHT_BACKGROUND_PALETTE: u16 = 23;
876    pub const DARK_BACKGROUND_PALETTE: u16 = 24;
877    pub const VARIATIONS_POSTSCRIPT_NAME_PREFIX: u16 = 25;
878
879    /// Return a string for the supplied `name_id`.
880    ///
881    /// Returns the first match in this order:
882    ///
883    /// 1. Unicode platform
884    /// 2. Windows platform, English language ids
885    /// 3. Apple platform, Roman language id
886    pub fn string_for_id(&self, name_id: u16) -> Option<String> {
887        self.name_records
888            .iter()
889            .find_map(|record| {
890                if record.name_id != name_id {
891                    return None;
892                }
893                // Match English records
894                match (record.platform_id, record.encoding_id, record.language_id) {
895                    // Unicode
896                    (0, _, _) => Some((record, encoding_rs::UTF_16BE)),
897                    // Windows Unicode BMP, English language ids
898                    // https://learn.microsoft.com/en-us/typography/opentype/spec/name#windows-language-ids
899                    (
900                        3,
901                        1,
902                        0x0C09 | 0x2809 | 0x1009 | 0x2409 | 0x4009 | 0x1809 | 0x2009 | 0x4409
903                        | 0x1409 | 0x3409 | 0x4809 | 0x1C09 | 0x2C09 | 0x0809 | 0x0409 | 0x3009,
904                    ) => Some((record, encoding_rs::UTF_16BE)),
905                    // Windows Unicode full, English language ids
906                    // https://learn.microsoft.com/en-us/typography/opentype/spec/name#windows-language-ids
907                    (
908                        3,
909                        10,
910                        0x0C09 | 0x2809 | 0x1009 | 0x2409 | 0x4009 | 0x1809 | 0x2009 | 0x4409
911                        | 0x1409 | 0x3409 | 0x4809 | 0x1C09 | 0x2C09 | 0x0809 | 0x0409 | 0x3009,
912                    ) => Some((record, encoding_rs::UTF_16BE)),
913                    // Apple, Roman Script, English
914                    (1, 0, 0) => Some((record, encoding_rs::MACINTOSH)),
915                    _ => None,
916                }
917            })
918            .and_then(|(record, encoding)| {
919                let offset = usize::from(record.offset);
920                let length = usize::from(record.length);
921                let name_data = self
922                    .string_storage
923                    .offset_length(offset, length)
924                    .ok()?
925                    .data();
926                Some(decode(encoding, name_data))
927            })
928    }
929}
930
931pub(crate) fn decode(encoding: &'static Encoding, data: &[u8]) -> String {
932    let mut decoder = encoding.new_decoder();
933    let size = decoder.max_utf8_buffer_length(data.len()).unwrap();
934    let mut s = String::with_capacity(size);
935    let (_res, _read, _repl) = decoder.decode_to_string(data, &mut s, true);
936    s
937}
938
939fn utf16be_encode(string: &str) -> Vec<u8> {
940    string
941        .encode_utf16()
942        .flat_map(|codeunit| codeunit.to_be_bytes())
943        .collect()
944}
945
946impl<'b> ReadBinary for NameTable<'b> {
947    type HostType<'a> = NameTable<'a>;
948
949    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
950        let scope = ctxt.scope();
951
952        let format = ctxt.read_u16be()?;
953        ctxt.check(format <= 1)?;
954        let count = usize::from(ctxt.read_u16be()?);
955        let string_offset = usize::from(ctxt.read_u16be()?);
956        let string_storage = scope.offset(string_offset);
957        let name_records = ctxt.read_array::<NameRecord>(count)?;
958        let opt_langtag_records = if format > 0 {
959            let langtag_count = usize::from(ctxt.read_u16be()?);
960            let langtag_records = ctxt.read_array::<LangTagRecord>(langtag_count)?;
961            Some(langtag_records)
962        } else {
963            None
964        };
965
966        Ok(NameTable {
967            string_storage,
968            name_records,
969            opt_langtag_records,
970        })
971    }
972}
973
974impl<'a> WriteBinary<&Self> for NameTable<'a> {
975    type Output = ();
976
977    fn write<C: WriteContext>(ctxt: &mut C, name: &NameTable<'a>) -> Result<(), WriteError> {
978        let format = name.opt_langtag_records.as_ref().map_or(0u16, |_| 1);
979        U16Be::write(ctxt, format)?;
980        U16Be::write(ctxt, u16::try_from(name.name_records.len())?)?; // count
981        let string_offset = ctxt.placeholder::<U16Be, _>()?;
982        <&ReadArray<'a, _>>::write(ctxt, &name.name_records)?;
983
984        if let Some(lang_tag_records) = &name.opt_langtag_records {
985            U16Be::write(ctxt, u16::try_from(lang_tag_records.len())?)?; // lang_tag_count
986            <&ReadArray<'a, _>>::write(ctxt, lang_tag_records)?;
987        }
988
989        ctxt.write_placeholder(string_offset, u16::try_from(ctxt.bytes_written())?)?;
990        ctxt.write_bytes(name.string_storage.data())?;
991
992        Ok(())
993    }
994}
995
996impl ReadFrom for NameRecord {
997    type ReadType = ((U16Be, U16Be, U16Be), (U16Be, U16Be, U16Be));
998    fn read_from(
999        ((platform_id, encoding_id, language_id), (name_id, length, offset)): (
1000            (u16, u16, u16),
1001            (u16, u16, u16),
1002        ),
1003    ) -> Self {
1004        NameRecord {
1005            platform_id,
1006            encoding_id,
1007            language_id,
1008            name_id,
1009            length,
1010            offset,
1011        }
1012    }
1013}
1014
1015impl WriteBinary for NameRecord {
1016    type Output = ();
1017
1018    fn write<C: WriteContext>(ctxt: &mut C, record: NameRecord) -> Result<(), WriteError> {
1019        U16Be::write(ctxt, record.platform_id)?;
1020        U16Be::write(ctxt, record.encoding_id)?;
1021        U16Be::write(ctxt, record.language_id)?;
1022        U16Be::write(ctxt, record.name_id)?;
1023        U16Be::write(ctxt, record.length)?;
1024        U16Be::write(ctxt, record.offset)?;
1025
1026        Ok(())
1027    }
1028}
1029
1030impl ReadFrom for LangTagRecord {
1031    type ReadType = (U16Be, U16Be);
1032    fn read_from((length, offset): (u16, u16)) -> Self {
1033        LangTagRecord { length, offset }
1034    }
1035}
1036
1037impl WriteBinary for LangTagRecord {
1038    type Output = ();
1039
1040    fn write<C: WriteContext>(ctxt: &mut C, record: LangTagRecord) -> Result<(), WriteError> {
1041        U16Be::write(ctxt, record.length)?;
1042        U16Be::write(ctxt, record.offset)?;
1043
1044        Ok(())
1045    }
1046}
1047
1048impl ReadBinaryDep for CvtTable<'_> {
1049    type Args<'a> = u32;
1050    type HostType<'a> = CvtTable<'a>;
1051
1052    fn read_dep<'a>(
1053        ctxt: &mut ReadCtxt<'a>,
1054        length: u32,
1055    ) -> Result<Self::HostType<'a>, ParseError> {
1056        let length = usize::safe_from(length);
1057        // The table contains 'n' values, where n is just as many values can be read for the
1058        // size of the table. We assume that `ctxt` is limited to the length of the table
1059        //
1060        // > The length of the table must be an integral number of FWORD units.
1061        ctxt.check(length % I16Be::SIZE == 0)?;
1062        let n = length / I16Be::SIZE;
1063        let values = ctxt.read_array(n)?;
1064        Ok(CvtTable {
1065            values: ReadArrayCow::Borrowed(values),
1066        })
1067    }
1068}
1069
1070impl WriteBinary<&Self> for CvtTable<'_> {
1071    type Output = ();
1072
1073    fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<(), WriteError> {
1074        ReadArrayCow::write(ctxt, &table.values)
1075    }
1076}
1077
1078impl ReadFrom for F2Dot14 {
1079    type ReadType = I16Be;
1080
1081    fn read_from(value: i16) -> Self {
1082        F2Dot14(value)
1083    }
1084}
1085
1086impl WriteBinary for F2Dot14 {
1087    type Output = ();
1088
1089    fn write<C: WriteContext>(ctxt: &mut C, val: Self) -> Result<(), WriteError> {
1090        I16Be::write(ctxt, val.0)
1091    }
1092}
1093
1094impl ReadBinary for IndexToLocFormat {
1095    type HostType<'a> = Self;
1096
1097    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
1098        let index_to_loc_format = ctxt.read_i16be()?;
1099
1100        match index_to_loc_format {
1101            0 => Ok(IndexToLocFormat::Short),
1102            1 => Ok(IndexToLocFormat::Long),
1103            _ => Err(ParseError::BadValue),
1104        }
1105    }
1106}
1107
1108impl WriteBinary for IndexToLocFormat {
1109    type Output = ();
1110
1111    fn write<C: WriteContext>(ctxt: &mut C, index_to_loc_format: Self) -> Result<(), WriteError> {
1112        match index_to_loc_format {
1113            IndexToLocFormat::Short => I16Be::write(ctxt, 0i16),
1114            IndexToLocFormat::Long => I16Be::write(ctxt, 1i16),
1115        }
1116    }
1117}
1118
1119impl Fixed {
1120    /// Create a new `Fixed` with a raw 16.16 value.
1121    pub const fn from_raw(value: i32) -> Fixed {
1122        Fixed(value)
1123    }
1124
1125    pub fn raw_value(&self) -> i32 {
1126        self.0
1127    }
1128
1129    pub fn abs(&self) -> Fixed {
1130        Fixed(self.0.abs())
1131    }
1132}
1133
1134impl std::ops::Add for Fixed {
1135    type Output = Self;
1136
1137    fn add(self, rhs: Self) -> Self::Output {
1138        Fixed(self.0.wrapping_add(rhs.0))
1139    }
1140}
1141
1142impl std::ops::Sub for Fixed {
1143    type Output = Self;
1144
1145    fn sub(self, rhs: Self) -> Self::Output {
1146        Fixed(self.0.wrapping_sub(rhs.0))
1147    }
1148}
1149
1150impl std::ops::Mul for Fixed {
1151    type Output = Self;
1152
1153    fn mul(self, rhs: Self) -> Self::Output {
1154        let a = i64::from(self.0);
1155        let b = i64::from(rhs.0);
1156        Fixed(((a * b) >> 16) as i32)
1157    }
1158}
1159
1160impl std::ops::Div for Fixed {
1161    type Output = Self;
1162
1163    fn div(self, rhs: Self) -> Self::Output {
1164        let a = i64::from(self.0);
1165        let b = i64::from(rhs.0);
1166        if b == 0 {
1167            // Closest we have to infinity. Same as what FreeType does
1168            // https://gitlab.freedesktop.org/freetype/freetype/-/blob/a20de84e1608f9eb1d0391d7322b2e0e0f235aba/src/base/ftcalc.c#L267
1169            return Fixed(0x7FFFFFFF);
1170        }
1171
1172        Fixed(((a << 16) / b) as i32)
1173    }
1174}
1175
1176impl std::ops::Neg for Fixed {
1177    type Output = Self;
1178
1179    fn neg(self) -> Self::Output {
1180        Fixed(-self.0)
1181    }
1182}
1183
1184impl fmt::Debug for Fixed {
1185    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1186        f.debug_tuple("Fixed").field(&f32::from(*self)).finish()
1187    }
1188}
1189
1190impl ReadFrom for Fixed {
1191    type ReadType = I32Be;
1192
1193    fn read_from(value: i32) -> Self {
1194        Fixed(value)
1195    }
1196}
1197
1198impl WriteBinary for Fixed {
1199    type Output = ();
1200
1201    fn write<C: WriteContext>(ctxt: &mut C, val: Self) -> Result<(), WriteError> {
1202        I32Be::write(ctxt, val.0)
1203    }
1204}
1205
1206impl From<Fixed> for f32 {
1207    fn from(value: Fixed) -> f32 {
1208        (f64::from(value.0) / 65536.0) as f32
1209    }
1210}
1211
1212// When converting from float or double data types to 16.16, the following method must be used:
1213//
1214// 1. Multiply the fractional component by 65536, and round the result to the nearest integer (for
1215//    fractional values of 0.5 and higher, take the next higher integer; for other fractional
1216//    values, truncate). Store the result in the low-order word.
1217// 2. Move the two’s-complement representation of the integer component into the high-order word
1218//
1219// https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization
1220impl From<f32> for Fixed {
1221    fn from(value: f32) -> Self {
1222        let sign = value.signum() as i32;
1223        let value = value.abs();
1224        let fract = (value.fract() * 65536.0).round() as i32;
1225        let int = value.trunc() as i32;
1226        Fixed::from_raw(((int << 16) | fract) * sign)
1227    }
1228}
1229
1230impl From<f64> for Fixed {
1231    fn from(value: f64) -> Self {
1232        let sign = value.signum() as i32;
1233        let value = value.abs();
1234        let fract = (value.fract() * 65536.0).round() as i32;
1235        let int = value.trunc() as i32;
1236        Fixed::from_raw(((int << 16) | fract) * sign)
1237    }
1238}
1239
1240impl From<i32> for Fixed {
1241    fn from(value: i32) -> Self {
1242        Fixed::from_raw(value << 16)
1243    }
1244}
1245
1246impl From<F2Dot14> for Fixed {
1247    fn from(fixed: F2Dot14) -> Self {
1248        Fixed(i32::from(fixed.0) << 2)
1249    }
1250}
1251
1252impl F2Dot14 {
1253    pub fn from_raw(value: i16) -> Self {
1254        F2Dot14(value)
1255    }
1256
1257    pub fn raw_value(&self) -> i16 {
1258        self.0
1259    }
1260}
1261
1262impl std::ops::Add for F2Dot14 {
1263    type Output = Self;
1264
1265    fn add(self, rhs: Self) -> Self::Output {
1266        F2Dot14(self.0.wrapping_add(rhs.0))
1267    }
1268}
1269
1270impl std::ops::Sub for F2Dot14 {
1271    type Output = Self;
1272
1273    fn sub(self, rhs: Self) -> Self::Output {
1274        F2Dot14(self.0.wrapping_sub(rhs.0))
1275    }
1276}
1277
1278impl std::ops::Mul for F2Dot14 {
1279    type Output = Self;
1280
1281    fn mul(self, rhs: Self) -> Self::Output {
1282        let a = i32::from(self.0);
1283        let b = i32::from(rhs.0);
1284        F2Dot14(((a * b) >> 14) as i16)
1285    }
1286}
1287
1288impl std::ops::Div for F2Dot14 {
1289    type Output = Self;
1290
1291    fn div(self, rhs: Self) -> Self::Output {
1292        let a = i32::from(self.0);
1293        let b = i32::from(rhs.0);
1294        if b == 0 {
1295            // Closest we have to infinity.
1296            return F2Dot14(0x7FFF);
1297        }
1298
1299        F2Dot14(((a << 14) / b) as i16)
1300    }
1301}
1302
1303impl std::ops::Neg for F2Dot14 {
1304    type Output = Self;
1305
1306    fn neg(self) -> Self::Output {
1307        F2Dot14(-self.0)
1308    }
1309}
1310
1311impl fmt::Debug for F2Dot14 {
1312    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1313        f.debug_tuple("F2Dot14").field(&f32::from(*self)).finish()
1314    }
1315}
1316
1317impl From<Fixed> for F2Dot14 {
1318    fn from(fixed: Fixed) -> Self {
1319        // Convert the final, normalized 16.16 coordinate value to 2.14 by this method: add
1320        // 0x00000002, and sign-extend shift to the right by 2.
1321        // https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization
1322        F2Dot14(((fixed.0 + 2) >> 2) as i16)
1323    }
1324}
1325
1326impl From<f32> for F2Dot14 {
1327    fn from(value: f32) -> Self {
1328        let sign = value.signum() as i16;
1329        let value = value.abs();
1330        let fract = (value.fract() * 16384.0).round() as i16;
1331        let int = value.trunc() as i16;
1332        F2Dot14::from_raw(((int << 14) | fract).wrapping_mul(sign))
1333    }
1334}
1335
1336impl From<i16> for F2Dot14 {
1337    fn from(value: i16) -> Self {
1338        F2Dot14::from_raw(value << 14)
1339    }
1340}
1341
1342impl From<F2Dot14> for f32 {
1343    fn from(value: F2Dot14) -> Self {
1344        f32::from(value.0) / 16384.
1345    }
1346}
1347
1348impl<T: FontTableProvider> FontTableProvider for Box<T> {
1349    fn table_data(&self, tag: u32) -> Result<Option<Cow<'_, [u8]>>, ParseError> {
1350        self.as_ref().table_data(tag)
1351    }
1352
1353    fn has_table(&self, tag: u32) -> bool {
1354        self.as_ref().has_table(tag)
1355    }
1356
1357    fn table_tags(&self) -> Option<Vec<u32>> {
1358        self.as_ref().table_tags()
1359    }
1360}
1361
1362impl<T: SfntVersion> SfntVersion for Box<T> {
1363    fn sfnt_version(&self) -> u32 {
1364        self.as_ref().sfnt_version()
1365    }
1366}
1367
1368pub mod owned {
1369    //! Owned versions of tables.
1370
1371    use std::borrow::Cow;
1372    use std::convert::TryFrom;
1373
1374    use super::utf16be_encode;
1375    use crate::binary::write::{Placeholder, WriteBinary, WriteContext};
1376    use crate::binary::U16Be;
1377    use crate::error::{ParseError, WriteError};
1378
1379    /// An owned `name` table.
1380    ///
1381    /// Can be created from [super::NameTable] using `TryFrom`.
1382    ///
1383    /// <https://docs.microsoft.com/en-us/typography/opentype/spec/name>
1384    pub struct NameTable<'a> {
1385        pub name_records: Vec<NameRecord<'a>>,
1386        /// UTF-16BE encoded language tag strings
1387        pub langtag_records: Vec<Cow<'a, [u8]>>,
1388    }
1389
1390    /// Record within the `name` table.
1391    pub struct NameRecord<'a> {
1392        pub platform_id: u16,
1393        pub encoding_id: u16,
1394        pub language_id: u16,
1395        pub name_id: u16,
1396        pub string: Cow<'a, [u8]>,
1397    }
1398
1399    impl<'a> NameTable<'a> {
1400        /// Replace all instances of `name_id` with a Unicode entry with the value `string`.
1401        pub fn replace_entries(&mut self, name_id: u16, string: &str) {
1402            self.remove_entries(name_id);
1403            let replacement = NameRecord {
1404                platform_id: 0, // Unicode
1405                encoding_id: 4, // full repertoire
1406                language_id: 0,
1407                name_id,
1408                string: Cow::from(utf16be_encode(string)),
1409            };
1410            self.name_records.push(replacement);
1411        }
1412
1413        pub fn remove_entries(&mut self, name_id: u16) {
1414            self.name_records.retain(|record| record.name_id != name_id);
1415        }
1416    }
1417
1418    impl<'a> TryFrom<&super::NameTable<'a>> for NameTable<'a> {
1419        type Error = ParseError;
1420
1421        fn try_from(name: &super::NameTable<'a>) -> Result<NameTable<'a>, ParseError> {
1422            let name_records = name
1423                .name_records
1424                .iter()
1425                .map(|record| {
1426                    let string = name
1427                        .string_storage
1428                        .offset_length(usize::from(record.offset), usize::from(record.length))
1429                        .map(|scope| Cow::from(scope.data()))?;
1430                    Ok(NameRecord {
1431                        platform_id: record.platform_id,
1432                        encoding_id: record.encoding_id,
1433                        language_id: record.language_id,
1434                        name_id: record.name_id,
1435                        string,
1436                    })
1437                })
1438                .collect::<Result<Vec<_>, ParseError>>()?;
1439            let langtag_records = name
1440                .opt_langtag_records
1441                .as_ref()
1442                .map(|langtag_records| {
1443                    langtag_records
1444                        .iter()
1445                        .map(|record| {
1446                            name.string_storage
1447                                .offset_length(
1448                                    usize::from(record.offset),
1449                                    usize::from(record.length),
1450                                )
1451                                .map(|scope| Cow::from(scope.data()))
1452                        })
1453                        .collect::<Result<Vec<_>, _>>()
1454                })
1455                .transpose()?
1456                .unwrap_or_else(Vec::new);
1457
1458            Ok(NameTable {
1459                name_records,
1460                langtag_records,
1461            })
1462        }
1463    }
1464
1465    impl<'a> WriteBinary<&Self> for NameTable<'a> {
1466        type Output = ();
1467
1468        fn write<C: WriteContext>(ctxt: &mut C, name: &NameTable<'a>) -> Result<(), WriteError> {
1469            let format = if name.langtag_records.is_empty() {
1470                0u16
1471            } else {
1472                1
1473            };
1474            U16Be::write(ctxt, format)?;
1475            U16Be::write(ctxt, u16::try_from(name.name_records.len())?)?; // count
1476            let string_offset = ctxt.placeholder::<U16Be, _>()?;
1477            let name_record_offsets = name
1478                .name_records
1479                .iter()
1480                .map(|record| NameRecord::write(ctxt, record))
1481                .collect::<Result<Vec<_>, _>>()?;
1482
1483            if !name.langtag_records.is_empty() {
1484                // langtag count
1485                U16Be::write(ctxt, u16::try_from(name.langtag_records.len())?)?;
1486            }
1487            let langtag_record_offsets = name
1488                .langtag_records
1489                .iter()
1490                .map(|record| {
1491                    U16Be::write(ctxt, u16::try_from(record.len())?)?;
1492                    ctxt.placeholder::<U16Be, _>()
1493                })
1494                .collect::<Result<Vec<_>, _>>()?;
1495
1496            let string_start = ctxt.bytes_written();
1497            ctxt.write_placeholder(string_offset, u16::try_from(string_start)?)?;
1498
1499            // Write the string data
1500            let lang_tags = name.langtag_records.iter().zip(langtag_record_offsets);
1501            let records = name
1502                .name_records
1503                .iter()
1504                .map(|rec| &rec.string)
1505                .zip(name_record_offsets)
1506                .chain(lang_tags);
1507
1508            for (string, placeholder) in records {
1509                ctxt.write_placeholder(
1510                    placeholder,
1511                    u16::try_from(ctxt.bytes_written() - string_start)?,
1512                )?;
1513                ctxt.write_bytes(string)?;
1514            }
1515
1516            Ok(())
1517        }
1518    }
1519
1520    impl<'a> WriteBinary<&Self> for NameRecord<'a> {
1521        type Output = Placeholder<U16Be, u16>;
1522
1523        fn write<C: WriteContext>(ctxt: &mut C, record: &Self) -> Result<Self::Output, WriteError> {
1524            U16Be::write(ctxt, record.platform_id)?;
1525            U16Be::write(ctxt, record.encoding_id)?;
1526            U16Be::write(ctxt, record.language_id)?;
1527            U16Be::write(ctxt, record.name_id)?;
1528            U16Be::write(ctxt, u16::try_from(record.string.len())?)?;
1529            let offset = ctxt.placeholder()?;
1530            Ok(offset)
1531        }
1532    }
1533}
1534
1535#[cfg(test)]
1536mod tests {
1537    use super::{
1538        owned, F2Dot14, Fixed, HeadTable, HmtxTable, NameTable, OpenTypeData, OpenTypeFont,
1539    };
1540    use crate::assert_close;
1541    use crate::binary::read::ReadScope;
1542    use crate::binary::write::{WriteBinary, WriteBuffer, WriteContext};
1543    use crate::tests::{assert_close, assert_f2dot14_close, assert_fixed_close, read_fixture};
1544    use std::convert::TryFrom;
1545
1546    const NAME_DATA: &[u8] = include_bytes!("../tests/fonts/opentype/name.bin");
1547
1548    #[test]
1549    fn test_write_head_table() {
1550        // Read a head table in, then write it back out and compare it
1551        let head_data = include_bytes!("../tests/fonts/opentype/head.bin");
1552        let head = ReadScope::new(head_data).read::<HeadTable>().unwrap();
1553        let checksum_adjustment = head.check_sum_adjustment;
1554
1555        let mut ctxt = WriteBuffer::new();
1556        let placeholder = HeadTable::write(&mut ctxt, &head).unwrap();
1557        ctxt.write_placeholder(placeholder, checksum_adjustment)
1558            .unwrap();
1559
1560        assert_eq!(ctxt.bytes(), &head_data[..]);
1561    }
1562
1563    #[test]
1564    fn test_write_hmtx_table() {
1565        // Read a hmtx table in, then write it back out and compare it
1566        let hmtx_data = include_bytes!("../tests/fonts/opentype/hmtx.bin");
1567        let num_glyphs = 1264;
1568        let num_h_metrics = 1264;
1569        let hmtx = ReadScope::new(hmtx_data)
1570            .read_dep::<HmtxTable<'_>>((num_glyphs, num_h_metrics))
1571            .unwrap();
1572
1573        let mut ctxt = WriteBuffer::new();
1574        HmtxTable::write(&mut ctxt, &hmtx).unwrap();
1575
1576        assert_eq!(ctxt.bytes(), &hmtx_data[..]);
1577    }
1578
1579    #[test]
1580    fn test_write_name_table() {
1581        // Read a name table in, then write it back out and compare it
1582        let name = ReadScope::new(NAME_DATA).read::<NameTable<'_>>().unwrap();
1583
1584        let mut ctxt = WriteBuffer::new();
1585        NameTable::write(&mut ctxt, &name).unwrap();
1586
1587        assert_eq!(ctxt.bytes(), &NAME_DATA[..]);
1588    }
1589
1590    #[test]
1591    fn roundtrip_owned_name_table() {
1592        // Test that NameTable can be converted to owned variant, written, and read back the same
1593        let name = ReadScope::new(NAME_DATA).read::<NameTable<'_>>().unwrap();
1594        let owned = owned::NameTable::try_from(&name).unwrap();
1595
1596        let mut ctxt = WriteBuffer::new();
1597        owned::NameTable::write(&mut ctxt, &owned).unwrap();
1598
1599        assert_eq!(ctxt.bytes(), &NAME_DATA[..]);
1600    }
1601
1602    #[test]
1603    fn f32_from_f2dot14() {
1604        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
1605        assert_close(f32::from(F2Dot14(0x7fff)), 1.999939);
1606        assert_close(f32::from(F2Dot14(0x7000)), 1.75);
1607        assert_close(f32::from(F2Dot14(0x0001)), 0.000061);
1608        assert_close(f32::from(F2Dot14(0x0000)), 0.0);
1609        assert_close(f32::from(F2Dot14(-1 /* 0xFFFF */)), -0.000061);
1610        assert_close(f32::from(F2Dot14(-32768 /* 0x8000 */)), -2.0);
1611    }
1612
1613    #[test]
1614    fn f2dot14_from_f32() {
1615        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
1616        assert_eq!(F2Dot14::from(1.999939), F2Dot14::from_raw(0x7fff));
1617        assert_eq!(F2Dot14::from(1.75), F2Dot14::from_raw(0x7000));
1618        assert_eq!(F2Dot14::from(0.000061), F2Dot14::from_raw(0x0001));
1619        assert_eq!(F2Dot14::from(0.0), F2Dot14::from_raw(0x0000));
1620        assert_eq!(F2Dot14::from(-0.000061), F2Dot14::from_raw(-1 /* 0xffff */));
1621        assert_close!(f32::from(F2Dot14::from(-1.4)), -1.4, 1. / 16384.);
1622        assert_eq!(F2Dot14::from(-2.0), F2Dot14::from_raw(-32768 /* 0x8000 */));
1623    }
1624
1625    #[test]
1626    fn f2dot14_from_fixed() {
1627        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
1628        assert_eq!(
1629            F2Dot14::from(Fixed::from(1.999939)),
1630            F2Dot14::from_raw(0x7fff)
1631        );
1632        assert_eq!(F2Dot14::from(Fixed::from(1.75)), F2Dot14::from_raw(0x7000));
1633        assert_eq!(
1634            F2Dot14::from(Fixed::from(0.000061)),
1635            F2Dot14::from_raw(0x0001)
1636        );
1637        assert_eq!(F2Dot14::from(Fixed::from(0.0)), F2Dot14::from_raw(0x0000));
1638        assert_eq!(
1639            F2Dot14::from(Fixed::from(-0.000061)),
1640            F2Dot14::from_raw(-1 /* 0xffff */)
1641        );
1642        assert_eq!(
1643            F2Dot14::from(Fixed::from(-2.0)),
1644            F2Dot14::from_raw(-32768 /* 0x8000 */)
1645        );
1646    }
1647
1648    #[test]
1649    fn f32_from_fixed() {
1650        assert_close(f32::from(Fixed(0x7fff_0000)), 32767.);
1651        assert_close(f32::from(Fixed(0x7000_0001)), 28672.0001);
1652        assert_close(f32::from(Fixed(0x0001_0000)), 1.0);
1653        assert_close(f32::from(Fixed(0x0000_0000)), 0.0);
1654        assert_close(
1655            f32::from(Fixed(i32::from_be_bytes([0xff; 4]))),
1656            -0.000015259,
1657        );
1658        assert_close(f32::from(Fixed(0x7fff_ffff)), 32768.0);
1659    }
1660
1661    #[test]
1662    fn fixed_from_f32() {
1663        assert_eq!(Fixed::from(32767.0_f32), Fixed(0x7fff_0000));
1664        assert_eq!(Fixed::from(28672.0001_f32), Fixed(0x7000_0000));
1665        assert_eq!(Fixed::from(1.0_f32), Fixed(0x0001_0000));
1666        assert_eq!(Fixed::from(-1.0_f32), Fixed(-65536));
1667        assert_eq!(Fixed::from(0.0_f32), Fixed(0x0000_0000));
1668        assert_eq!(Fixed::from(0.000015259_f32), Fixed(1));
1669        assert_eq!(Fixed::from(32768.0_f32), Fixed(-0x8000_0000));
1670        assert_eq!(Fixed::from(1.23_f32), Fixed(0x0001_3ae1));
1671        assert_close!(f32::from(Fixed::from(-1.4_f32)), -1.4, 1. / 65536.);
1672    }
1673
1674    #[test]
1675    fn fixed_from_i32() {
1676        assert_eq!(Fixed::from(32767), Fixed(0x7fff_0000));
1677        assert_eq!(Fixed::from(28672), Fixed(0x7000_0000));
1678        assert_eq!(Fixed::from(1), Fixed(0x0001_0000));
1679        assert_eq!(Fixed::from(0), Fixed(0x0000_0000));
1680        assert_eq!(Fixed::from(-0), Fixed(0));
1681        assert_eq!(Fixed::from(32768), Fixed(-0x8000_0000));
1682    }
1683
1684    #[test]
1685    fn fixed_from_f2dot14() {
1686        assert_eq!(Fixed::from(F2Dot14::from(0.5)), Fixed(0x0000_8000));
1687    }
1688
1689    #[test]
1690    fn fixed_add() {
1691        assert_eq!(Fixed(10) + Fixed(20), Fixed(30));
1692        assert_fixed_close(Fixed::from(0.1) + Fixed::from(0.2), 0.3);
1693        assert_fixed_close(Fixed::from(-0.1) + Fixed::from(0.4), 0.3);
1694        assert_eq!(Fixed(i32::MAX) + Fixed(1), Fixed(-0x80000000)); // overflow
1695    }
1696
1697    #[test]
1698    fn fixed_sub() {
1699        assert_eq!(Fixed(10) - Fixed(20), Fixed(-10));
1700        assert_fixed_close(Fixed::from(0.1) - Fixed::from(0.2), -0.1);
1701        assert_fixed_close(Fixed::from(-0.1) - Fixed::from(0.4), -0.5);
1702        assert_eq!(Fixed(i32::MIN) - Fixed(1), Fixed(0x7fffffff)); // underflow
1703    }
1704
1705    #[test]
1706    fn fixed_mul() {
1707        assert_eq!(Fixed(0x2_0000) * Fixed(0x4_0000), Fixed(0x8_0000));
1708        assert_fixed_close(Fixed::from(0.1) * Fixed::from(0.2), 0.02);
1709        assert_fixed_close(Fixed::from(-0.1) * Fixed::from(0.4), -0.04);
1710    }
1711
1712    #[test]
1713    fn fixed_div() {
1714        assert_eq!(Fixed(0x4_0000) / Fixed(0x2_0000), Fixed(0x2_0000));
1715        assert_fixed_close(Fixed::from(0.1) / Fixed::from(0.2), 0.5);
1716        assert_fixed_close(Fixed::from(-0.1) / Fixed::from(0.4), -0.25);
1717        assert_eq!(Fixed(0x4_0000) / Fixed(0), Fixed(0x7FFFFFFF)); // div 0
1718    }
1719
1720    #[test]
1721    fn fixed_neg() {
1722        assert_eq!(-Fixed(0x4_0000), Fixed(-0x4_0000));
1723        assert_fixed_close(-Fixed::from(0.1), -0.1);
1724        assert_fixed_close(-Fixed::from(-0.25), 0.25);
1725        assert_eq!(-Fixed(0x7FFFFFFF), Fixed(-0x7FFFFFFF));
1726    }
1727
1728    #[test]
1729    fn fixed_abs() {
1730        assert_fixed_close(Fixed::from(-1.0).abs(), 1.0);
1731        assert_fixed_close(Fixed::from(1.0).abs(), 1.0);
1732        assert_eq!(Fixed(-0x7FFFFFFF).abs(), Fixed(0x7FFFFFFF));
1733    }
1734
1735    #[test]
1736    fn f2dot14_add() {
1737        assert_eq!(Fixed(10) + Fixed(20), Fixed(30));
1738        assert_f2dot14_close(F2Dot14::from(0.1) + F2Dot14::from(0.2), 0.3);
1739        assert_f2dot14_close(F2Dot14::from(-0.1) + F2Dot14::from(0.4), 0.3);
1740        assert_eq!(F2Dot14(i16::MAX) + F2Dot14(1), F2Dot14(-0x8000)); // overflow
1741    }
1742
1743    #[test]
1744    fn f2dot14_sub() {
1745        assert_eq!(F2Dot14(10) - F2Dot14(20), F2Dot14(-10));
1746        assert_f2dot14_close(F2Dot14::from(0.1) - F2Dot14::from(0.2), -0.1);
1747        assert_f2dot14_close(F2Dot14::from(-0.1) - F2Dot14::from(0.4), -0.5);
1748        assert_eq!(F2Dot14(i16::MIN) - F2Dot14(1), F2Dot14(0x7fff)); // underflow
1749    }
1750
1751    #[test]
1752    fn f2dot14_mul() {
1753        assert_f2dot14_close(F2Dot14::from(0.1) * F2Dot14::from(0.2), 0.02);
1754        assert_f2dot14_close(F2Dot14::from(-0.1) * F2Dot14::from(0.4), -0.04);
1755    }
1756
1757    #[test]
1758    fn f2dot14_div() {
1759        assert_f2dot14_close(F2Dot14::from(0.1) / F2Dot14::from(0.2), 0.5);
1760        assert_f2dot14_close(F2Dot14::from(-0.1) / F2Dot14::from(0.4), -0.25);
1761        assert_eq!(F2Dot14(0x4_000) / F2Dot14(0), F2Dot14(0x7FFF)); // div 0
1762    }
1763
1764    #[test]
1765    fn f2dot14_neg() {
1766        assert_eq!(-F2Dot14(0x1_000), F2Dot14(-0x1_000));
1767        assert_f2dot14_close(-F2Dot14::from(0.1), -0.1);
1768        assert_f2dot14_close(-F2Dot14::from(-0.25), 0.25);
1769        assert_eq!(-F2Dot14(0x7FFF), F2Dot14(-0x7FFF));
1770    }
1771
1772    #[test]
1773    fn read_true_magic() {
1774        let buffer = read_fixture("tests/fonts/variable/Zycon.ttf");
1775        let fontfile = ReadScope::new(&buffer)
1776            .read::<OpenTypeFont<'_>>()
1777            .expect("error reading OpenTypeFile");
1778        let offset_table = match fontfile.data {
1779            OpenTypeData::Single(font) => font,
1780            OpenTypeData::Collection(_) => unreachable!(),
1781        };
1782        assert_eq!(offset_table.table_records.len(), 12);
1783    }
1784}