allsorts_subset_browser/
woff2.rs

1//! Reading of the WOFF2 font format.
2//!
3//! <https://www.w3.org/TR/WOFF2/>
4
5mod collection;
6mod lut;
7
8use std::borrow::Cow;
9use std::collections::HashMap;
10use std::convert::TryFrom;
11use std::io::{Cursor, Read};
12
13use bitflags::bitflags;
14use itertools::Either;
15
16use self::lut::{XYTriplet, COORD_LUT, KNOWN_TABLE_TAGS};
17use crate::binary::read::{
18    ReadArray, ReadArrayCow, ReadBinary, ReadBinaryDep, ReadBuf, ReadCtxt, ReadFrom, ReadScope,
19};
20use crate::binary::{write, I16Be, U16Be, U8};
21use crate::error::{ParseError, ReadWriteError};
22use crate::tables::glyf::{
23    BoundingBox, CompositeGlyph, CompositeGlyphs, GlyfRecord, GlyfTable, Glyph, Point, SimpleGlyph,
24    SimpleGlyphFlag,
25};
26use crate::tables::loca::{owned, LocaTable};
27use crate::tables::{
28    FontTableProvider, HeadTable, HheaTable, HmtxTable, IndexToLocFormat, LongHorMetric, MaxpTable,
29    SfntVersion, TTCF_MAGIC,
30};
31use crate::{read_table, tag};
32
33pub const MAGIC: u32 = tag!(b"wOF2");
34// This is the default size of the buffer in the brotli crate.
35// There's no guidance on how to choose this value.
36const BROTLI_DECODER_BUFFER_SIZE: usize = 4096;
37const BITS_0_TO_5: u8 = 0x3F;
38const LOWEST_UCODE: u16 = 253;
39
40/// UIntBase128, Variable-length encoding of 32-bit unsigned integers.
41#[derive(Copy, Clone)]
42pub enum U32Base128 {}
43
44/// 255UInt16, Variable-length encoding of a 16-bit unsigned integer for optimized intermediate
45/// font data storage.
46#[derive(Copy, Clone)]
47pub enum PackedU16 {}
48
49#[derive(Clone, Copy)]
50struct WoffFlag(u8);
51
52#[derive(Clone)]
53pub struct Woff2Font<'a> {
54    pub scope: ReadScope<'a>,
55    pub woff_header: Woff2Header,
56    // We have to read and parse the table directory to know where the font tables are stored
57    // so in doing so we hold onto the TableDirectoryEntries produced as a result
58    pub table_directory: Vec<TableDirectoryEntry>,
59    pub collection_directory: Option<collection::Directory>,
60    pub table_data_block: Vec<u8>,
61}
62
63pub struct Woff2TableProvider {
64    flavor: u32,
65    tables: HashMap<u32, Box<[u8]>>,
66}
67
68#[derive(Clone, Eq, PartialEq, Debug)]
69pub struct Woff2Header {
70    pub flavor: u32,
71    pub length: u32,
72    pub num_tables: u16,
73    pub total_sfnt_size: u32,
74    pub total_compressed_size: u32,
75    pub _major_version: u16,
76    pub _minor_version: u16,
77    pub meta_offset: u32,
78    pub meta_length: u32,
79    pub meta_orig_length: u32,
80    pub priv_offset: u32,
81    pub priv_length: u32,
82}
83
84#[derive(Copy, Clone, Eq, PartialEq, Debug)]
85pub struct TableDirectoryEntry {
86    pub tag: u32,
87    pub offset: usize,
88    pub orig_length: u32,
89    pub transform_length: Option<u32>,
90}
91
92struct TransformedGlyphTable<'a> {
93    /// Number of glyphs
94    num_glyphs: u16,
95    /// Offset format for loca table, should be consistent with indexToLocFormat of the original head table (see [OFF] specification)
96    _index_format: u16,
97    /// Stream of i16 values representing number of contours for each glyph record
98    n_contour_scope: ReadScope<'a>,
99    /// Stream of values representing number of outline points for each contour in glyph records
100    n_points_scope: ReadScope<'a>,
101    /// Stream of u8 values representing flag values for each outline point.
102    flag_scope: ReadScope<'a>,
103    /// Stream of bytes representing point coordinate values using variable length encoding format (defined in subclause 5.2)
104    glyph_scope: ReadScope<'a>,
105    /// Stream of bytes representing component flag values and associated composite glyph data
106    composite_scope: ReadScope<'a>,
107    /// Bitmap (a numGlyphs-long bit array) indicating explicit bounding boxes
108    bbox_bitmap_scope: ReadScope<'a>,
109    /// Stream of i16 values representing glyph bounding box data
110    bbox_scope: ReadScope<'a>,
111    /// Stream of u8 values representing a set of instructions for each corresponding glyph
112    instruction_scope: ReadScope<'a>,
113}
114
115bitflags! {
116    pub struct HmtxTableFlag: u8 {
117        const LSB_ABSENT = 0b01;
118        const LEFT_SIDE_BEARING_ABSENT = 0b10;
119    }
120}
121
122pub enum Woff2GlyfTable {}
123pub enum Woff2LocaTable {}
124pub enum Woff2HmtxTable {}
125
126pub struct BitSlice<'a> {
127    data: &'a [u8],
128}
129
130impl<'a> Woff2Font<'a> {
131    /// The "sfnt version" of the input font
132    pub fn flavor(&self) -> u32 {
133        self.woff_header.flavor
134    }
135
136    /// Decompress and return the extended metadata XML if present
137    pub fn extended_metadata(&self) -> Result<Option<String>, ParseError> {
138        let offset = usize::try_from(self.woff_header.meta_offset)?;
139        let length = usize::try_from(self.woff_header.meta_length)?;
140        if offset == 0 || length == 0 {
141            return Ok(None);
142        }
143
144        let compressed_metadata = self.scope.offset_length(offset, length)?;
145
146        let mut input = brotli_decompressor::Decompressor::new(
147            Cursor::new(compressed_metadata.data()),
148            BROTLI_DECODER_BUFFER_SIZE,
149        );
150        let mut metadata = String::new();
151        input
152            .read_to_string(&mut metadata)
153            .map_err(|_err| ParseError::CompressionError)?;
154
155        Ok(Some(metadata))
156    }
157
158    pub fn table_data_block_scope(&'a self) -> ReadScope<'a> {
159        ReadScope::new(&self.table_data_block)
160    }
161
162    fn read_table_directory(
163        ctxt: &mut ReadCtxt<'_>,
164        num_tables: usize,
165    ) -> Result<Vec<TableDirectoryEntry>, ParseError> {
166        let mut offset = 0;
167        let mut table_directory = Vec::with_capacity(num_tables);
168        for _i in 0..num_tables {
169            let entry = ctxt.read_dep::<TableDirectoryEntry>(offset)?;
170            offset += entry.length();
171            table_directory.push(entry);
172        }
173
174        Ok(table_directory)
175    }
176
177    pub fn find_table_entry(&self, tag: u32, index: usize) -> Option<&TableDirectoryEntry> {
178        if let Some(collection_directory) = &self.collection_directory {
179            collection_directory
180                .get(index)
181                .and_then(|font| font.table_entries(self).find(|entry| entry.tag == tag))
182        } else {
183            self.table_directory.iter().find(|entry| entry.tag == tag)
184        }
185    }
186
187    pub fn read_table(&self, tag: u32, index: usize) -> Result<Option<ReadBuf<'_>>, ParseError> {
188        self.find_table_entry(tag, index)
189            .map(|entry| entry.read_table(&self.table_data_block_scope()))
190            .transpose()
191    }
192
193    pub fn table_provider(&self, index: usize) -> Result<Woff2TableProvider, ReadWriteError> {
194        Woff2TableProvider::new(self, index)
195    }
196}
197
198impl<'b> ReadBinary for Woff2Font<'b> {
199    type HostType<'a> = Woff2Font<'a>;
200
201    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
202        let scope = ctxt.scope();
203        let woff_header = ctxt.read::<Woff2Header>()?;
204
205        let table_directory =
206            Self::read_table_directory(ctxt, usize::from(woff_header.num_tables))?;
207
208        let collection_directory = if woff_header.flavor == TTCF_MAGIC {
209            Some(ctxt.read::<collection::Directory>()?)
210        } else {
211            None
212        };
213
214        // Read compressed font table data
215        let compressed_data =
216            ctxt.read_slice(usize::try_from(woff_header.total_compressed_size)?)?;
217        let mut input = brotli_decompressor::Decompressor::new(
218            Cursor::new(compressed_data),
219            BROTLI_DECODER_BUFFER_SIZE,
220        );
221        let mut table_data_block = Vec::new();
222        input
223            .read_to_end(&mut table_data_block)
224            .map_err(|_err| ParseError::CompressionError)?;
225
226        Ok(Woff2Font {
227            scope,
228            woff_header,
229            table_directory,
230            collection_directory,
231            table_data_block,
232        })
233    }
234}
235
236impl FontTableProvider for Woff2TableProvider {
237    fn table_data(&self, tag: u32) -> Result<Option<Cow<'_, [u8]>>, ParseError> {
238        Ok(self.tables.get(&tag).map(|table| Cow::from(table.as_ref())))
239    }
240
241    fn has_table(&self, tag: u32) -> bool {
242        self.tables.contains_key(&tag)
243    }
244
245    fn table_tags(&self) -> Option<Vec<u32>> {
246        Some(self.tables.keys().copied().collect())
247    }
248}
249
250impl SfntVersion for Woff2TableProvider {
251    fn sfnt_version(&self) -> u32 {
252        self.flavor
253    }
254}
255
256impl ReadBinary for Woff2Header {
257    type HostType<'a> = Self;
258
259    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
260        let signature = ctxt.read_u32be()?;
261        match signature {
262            MAGIC => {
263                let flavor = ctxt.read_u32be()?;
264                let length = ctxt.read_u32be()?;
265                let num_tables = ctxt.read_u16be()?;
266                let reserved = ctxt.read_u16be()?;
267                // The header includes a reserved field; this MUST be set to zero. If this field is
268                // non-zero, a conforming user agent MUST reject the file as invalid.
269                ctxt.check(reserved == 0)?;
270                let total_sfnt_size = ctxt.read_u32be()?;
271                let total_compressed_size = ctxt.read_u32be()?;
272                // The WOFF majorVersion and minorVersion fields specify the version number for a
273                // given WOFF file, which can be based on the version number of the input font but
274                // is not required to be. These fields have no effect on font loading or usage
275                // behavior in user agents.
276                let _major_version = ctxt.read_u16be()?;
277                let _minor_version = ctxt.read_u16be()?;
278                let meta_offset = ctxt.read_u32be()?;
279                let meta_length = ctxt.read_u32be()?;
280                let meta_orig_length = ctxt.read_u32be()?;
281                let priv_offset = ctxt.read_u32be()?;
282                let priv_length = ctxt.read_u32be()?;
283
284                Ok(Woff2Header {
285                    flavor,
286                    length,
287                    num_tables,
288                    total_sfnt_size,
289                    total_compressed_size,
290                    _major_version,
291                    _minor_version,
292                    meta_offset,
293                    meta_length,
294                    meta_orig_length,
295                    priv_offset,
296                    priv_length,
297                })
298            }
299            _ => Err(ParseError::BadVersion),
300        }
301    }
302}
303
304impl ReadBinaryDep for TableDirectoryEntry {
305    type Args<'a> = usize;
306    type HostType<'a> = Self;
307
308    fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, offset: usize) -> Result<Self, ParseError> {
309        let flags = ctxt.read_u8()?;
310        let tag = if flags & BITS_0_TO_5 == 63 {
311            // Tag is the following 4 bytes
312            ctxt.read_u32be()
313        } else {
314            Ok(KNOWN_TABLE_TAGS[usize::from(flags & BITS_0_TO_5)])
315        }?;
316        let transformation_version = (flags & 0xC0) >> 6;
317        let orig_length = ctxt.read::<U32Base128>()?;
318
319        let transform_length = match (transformation_version, tag) {
320            (3, tag::GLYF) | (3, tag::LOCA) => None,
321            (_, tag::GLYF) | (_, tag::LOCA) | (1, tag::HMTX) => Some(ctxt.read::<U32Base128>()?),
322            (0, _) => None,
323            _ => Some(ctxt.read::<U32Base128>()?),
324        };
325
326        Ok(TableDirectoryEntry {
327            tag,
328            offset,
329            orig_length,
330            transform_length,
331        })
332    }
333}
334
335impl<'b> ReadBinary for TransformedGlyphTable<'b> {
336    type HostType<'a> = TransformedGlyphTable<'a>;
337
338    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
339        let _version = ctxt.read_u32be()?;
340        let num_glyphs = ctxt.read_u16be()?;
341        let index_format = ctxt.read_u16be()?;
342
343        let n_contour_stream_size = usize::try_from(ctxt.read_u32be()?)?;
344        let n_points_stream_size = usize::try_from(ctxt.read_u32be()?)?;
345        let flag_stream_size = usize::try_from(ctxt.read_u32be()?)?;
346        let glyph_stream_size = usize::try_from(ctxt.read_u32be()?)?;
347        let composite_stream_size = usize::try_from(ctxt.read_u32be()?)?;
348        let bbox_stream_size = usize::try_from(ctxt.read_u32be()?)?;
349        let instruction_stream_size = usize::try_from(ctxt.read_u32be()?)?;
350
351        // Build sub contexts for each of the streams, then iterate a glyph at a time pulling from
352        // those contexts as needed
353        let n_contour_scope = ReadScope::new(ctxt.read_slice(n_contour_stream_size)?);
354        let n_points_scope = ReadScope::new(ctxt.read_slice(n_points_stream_size)?);
355        let flag_scope = ReadScope::new(ctxt.read_slice(flag_stream_size)?);
356        let glyph_scope = ReadScope::new(ctxt.read_slice(glyph_stream_size)?);
357        let composite_scope = ReadScope::new(ctxt.read_slice(composite_stream_size)?);
358        // The total number of bytes in bboxBitmap is equal to 4 * floor((numGlyphs + 31) / 32).
359        // The bits are packed so that glyph number 0 corresponds to the most significant bit of
360        // the first byte, glyph number 7 corresponds to the least significant bit of the first
361        // byte, glyph number 8 corresponds to the most significant bit of the second byte, and so
362        // on. A bit=1 value indicates an explicitly set bounding box.
363        let bbox_bitmap_length = (4. * ((num_glyphs + 31) as f64 / 32.).floor()) as usize;
364        let bbox_bitmap_scope = ReadScope::new(ctxt.read_slice(bbox_bitmap_length)?);
365        let bbox_scope = ReadScope::new(ctxt.read_slice(bbox_stream_size - bbox_bitmap_length)?);
366        let instruction_scope = ReadScope::new(ctxt.read_slice(instruction_stream_size)?);
367
368        Ok(TransformedGlyphTable {
369            num_glyphs,
370            _index_format: index_format,
371            n_contour_scope,
372            n_points_scope,
373            flag_scope,
374            glyph_scope,
375            composite_scope,
376            bbox_bitmap_scope,
377            bbox_scope,
378            instruction_scope,
379        })
380    }
381}
382
383impl ReadBinaryDep for Woff2GlyfTable {
384    type Args<'a> = (&'a TableDirectoryEntry, &'a LocaTable<'a>);
385    type HostType<'a> = GlyfTable<'a>;
386
387    fn read_dep<'a>(
388        ctxt: &mut ReadCtxt<'a>,
389        (entry, loca): Self::Args<'a>,
390    ) -> Result<Self::HostType<'a>, ParseError> {
391        if entry.transform_length.is_some() {
392            let table = ctxt.read::<TransformedGlyphTable<'_>>()?;
393
394            // Read a glyph at a time and handle reconstructing each one
395            let num_glyphs = usize::from(table.num_glyphs);
396            let mut n_contour_ctxt = table.n_contour_scope.ctxt();
397            let mut n_points_ctxt = table.n_points_scope.ctxt();
398            let mut flags_ctxt = table.flag_scope.ctxt();
399            let mut glyphs_ctxt = table.glyph_scope.ctxt();
400            let mut instructions_ctxt = table.instruction_scope.ctxt();
401            let mut composite_ctxt = table.composite_scope.ctxt();
402            let bbox_bitmap = BitSlice::new(table.bbox_bitmap_scope.data());
403            let mut bbox_bitmap_ctxt = table.bbox_scope.ctxt();
404
405            let mut records = Vec::with_capacity(num_glyphs);
406            for i in 0..num_glyphs {
407                let number_of_contours = n_contour_ctxt.read_i16be()?;
408
409                let glyf_record = match number_of_contours {
410                    // Empty glyph
411                    0 => GlyfRecord::empty(),
412                    // Composite glyph
413                    -1 => {
414                        let glyphs = composite_ctxt.read::<CompositeGlyphs>()?;
415
416                        // Step 3a.
417                        let instruction_length = if glyphs.have_instructions {
418                            usize::from(glyphs_ctxt.read::<PackedU16>()?)
419                        } else {
420                            0
421                        };
422                        let instructions = instructions_ctxt.read_slice(instruction_length)?;
423
424                        // A composite glyph MUST have an explicitly supplied bounding box.
425                        // A decoder MUST check for presence of the bounding box info as part of
426                        // the composite glyph record and MUST NOT load a font file with the
427                        // composite bounding box data missing.
428                        match bbox_bitmap.get(i) {
429                            Some(true) => (),
430                            _ => return Err(ParseError::BadIndex),
431                        }
432
433                        // Read the bounding box
434                        let bounding_box = bbox_bitmap_ctxt.read::<BoundingBox>()?;
435
436                        GlyfRecord::Parsed(Glyph::Composite(CompositeGlyph {
437                            bounding_box,
438                            glyphs: glyphs.glyphs,
439                            instructions,
440                            phantom_points: None,
441                        }))
442                    }
443                    // Simple glyph
444                    num if num > 0 => {
445                        let mut data = Self::decode_simple_glyph(
446                            &mut n_points_ctxt,
447                            &mut flags_ctxt,
448                            &mut glyphs_ctxt,
449                            &mut instructions_ctxt,
450                            number_of_contours,
451                        )?;
452
453                        let bounding_box = match bbox_bitmap.get(i) {
454                            Some(true) => bbox_bitmap_ctxt.read::<BoundingBox>(),
455                            Some(false) => Ok(data.bounding_box()),
456                            _ => return Err(ParseError::BadIndex),
457                        }?;
458                        data.bounding_box = bounding_box;
459
460                        GlyfRecord::Parsed(Glyph::Simple(data))
461                    }
462                    _ => return Err(ParseError::BadValue),
463                };
464
465                records.push(glyf_record);
466            }
467
468            GlyfTable::new(records)
469        } else {
470            // glyf table has not been transformed
471            ctxt.read_dep::<GlyfTable<'_>>(loca)
472        }
473    }
474}
475
476impl ReadBinaryDep for Woff2LocaTable {
477    type Args<'a> = (&'a TableDirectoryEntry, usize, IndexToLocFormat);
478    type HostType<'a> = LocaTable<'a>;
479
480    fn read_dep<'a>(
481        ctxt: &mut ReadCtxt<'a>,
482        (loca_entry, num_glyphs, index_to_loc_format): Self::Args<'a>,
483    ) -> Result<Self::HostType<'a>, ParseError> {
484        if loca_entry.transform_length.is_some() {
485            Ok(LocaTable::empty())
486        } else {
487            ctxt.read_dep::<LocaTable<'_>>((num_glyphs, index_to_loc_format))
488        }
489    }
490}
491
492impl ReadBinaryDep for Woff2HmtxTable {
493    type Args<'a> = (&'a TableDirectoryEntry, &'a GlyfTable<'a>, usize, usize);
494    type HostType<'a> = HmtxTable<'a>;
495
496    /// Read hmtx table from WOFF2 file
497    ///
498    /// num_h_metrics is defined by the `hhea` table
499    fn read_dep<'a>(
500        ctxt: &mut ReadCtxt<'a>,
501        (hmtx_entry, glyf, num_glyphs, num_h_metrics): Self::Args<'a>,
502    ) -> Result<Self::HostType<'a>, ParseError> {
503        if hmtx_entry.transform_length.is_some() {
504            let flags = ctxt.read::<HmtxTableFlag>()?;
505            let advance_width_stream = ctxt.read_array::<U16Be>(num_h_metrics)?;
506
507            let lsb = if flags.lsb_is_present() {
508                // read the lsb stream
509                ReadArrayCow::Borrowed(ctxt.read_array::<I16Be>(num_h_metrics)?)
510            } else {
511                // Reconstitute lsb from glyf
512                //
513                // The transformation version "1" exploits the built-in redundancy of the TrueType
514                // glyphs where the outlines of the glyphs designed according to the TrueType
515                // recommendations would likely have their left side bearing values equal to xMin
516                // value of the glyph bounding box.
517                //
518                // If the hmtx table transform is both applicable and desired, the encoder MUST
519                // check that leftSideBearing values match the xMin values of the glyph bounding
520                // box for every glyph in a font (or check that leftSideBearing == 0 for an empty
521                // glyph)
522                ReadArrayCow::Owned(
523                    glyf.records()
524                        .iter()
525                        .map(|glyf_record| match glyf_record {
526                            GlyfRecord::Present { .. } => unreachable!(),
527                            GlyfRecord::Parsed(glyph) => {
528                                glyph.bounding_box().map(|bbox| bbox.x_min).unwrap_or(0)
529                            }
530                        })
531                        .collect(),
532                )
533            };
534
535            let length = num_glyphs
536                .checked_sub(num_h_metrics)
537                .ok_or(ParseError::BadIndex)?;
538            let left_side_bearings = if flags.left_side_bearing_is_present() {
539                ReadArrayCow::Borrowed(ctxt.read_array::<I16Be>(length)?)
540            } else {
541                // Reconstitute from glyf
542                ReadArrayCow::Owned(
543                    glyf.records()
544                        .iter()
545                        .map(|glyf_record| match glyf_record {
546                            GlyfRecord::Present { .. } => unreachable!(),
547                            GlyfRecord::Parsed(glyph) => {
548                                glyph.bounding_box().map(|bbox| bbox.x_min).unwrap_or(0)
549                            }
550                        })
551                        .collect(),
552                )
553            };
554
555            let h_metrics = lsb
556                .iter()
557                .zip(advance_width_stream.iter())
558                .map(|(lsb, advance_width)| LongHorMetric { advance_width, lsb })
559                .collect();
560
561            Ok(HmtxTable {
562                h_metrics: ReadArrayCow::Owned(h_metrics),
563                left_side_bearings,
564            })
565        } else {
566            ctxt.read_dep::<HmtxTable<'a>>((num_glyphs, num_h_metrics))
567        }
568    }
569}
570
571impl ReadFrom for WoffFlag {
572    type ReadType = U8;
573
574    fn read_from(flag: u8) -> Self {
575        WoffFlag::new(flag)
576    }
577}
578
579// Parse "255UInt16" Data Type
580// https://w3c.github.io/woff/woff2/#255UInt16-0
581impl ReadBinary for PackedU16 {
582    type HostType<'a> = u16;
583
584    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<u16, ParseError> {
585        match ctxt.read_u8()? {
586            253 => ctxt.read_u16be(),
587            254 => ctxt
588                .read_u8()
589                .map(|value| u16::from(value) + LOWEST_UCODE * 2),
590            255 => ctxt.read_u8().map(|value| u16::from(value) + LOWEST_UCODE),
591            code => Ok(u16::from(code)),
592        }
593        .map_err(ParseError::from)
594    }
595}
596
597// Parse "UIntBase128" Data Type
598// https://w3c.github.io/woff/woff2/#UIntBase128-0
599impl ReadBinary for U32Base128 {
600    type HostType<'a> = u32;
601
602    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<u32, ParseError> {
603        let mut accum = 0u32;
604
605        for i in 0..5 {
606            let byte = ctxt.read_u8()?;
607
608            // No leading 0's
609            if i == 0 && byte == 0x80 {
610                return Err(ParseError::BadValue);
611            }
612
613            // If any of the top 7 bits are set then << 7 would overflow
614            if accum & 0xFE000000 != 0 {
615                return Err(ParseError::BadValue);
616            }
617
618            // value = old value times 128 + (byte bitwise-and 127)
619            accum = (accum << 7) | u32::from(byte & 0x7F);
620
621            // Spin until most significant bit of data byte is false
622            if byte & 0x80 == 0 {
623                return Ok(accum);
624            }
625        }
626
627        // UIntBase128 sequence exceeds 5 bytes
628        Err(ParseError::BadValue)
629    }
630}
631
632impl ReadFrom for HmtxTableFlag {
633    type ReadType = U8;
634
635    fn read_from(flag: u8) -> Self {
636        HmtxTableFlag::from_bits_truncate(flag)
637    }
638}
639
640impl WoffFlag {
641    fn new(flag: u8) -> Self {
642        WoffFlag(flag)
643    }
644
645    fn bytes_to_read(&self) -> usize {
646        usize::from(self.xy_triplet().byte_count)
647    }
648
649    fn is_on_curve_point(&self) -> bool {
650        // WOFF2 says this about the MSB of flags:
651        // The most significant bit of a flag indicates whether the point is on- or off-curve point.
652        // The OpenType equivalent of this bit (Simple Glyph Flags) is defined as:
653        // Bit 0: If set, the point is on the curve; otherwise, it is off the curve.
654        // However it appears that in WOFF2 the bit is cleared to indicate that it is on-curve.
655        // I.e. opposite to OpenType. MicroType, which WOFF2 is based on adds:
656        // if the most significant bit is 0, then the point is on-curve.
657        self.0 & 0x80 == 0
658    }
659
660    fn xy_triplet(&self) -> &XYTriplet {
661        &COORD_LUT[usize::from(self.0 & 0x7F)]
662    }
663}
664
665impl From<WoffFlag> for SimpleGlyphFlag {
666    fn from(woff_flag: WoffFlag) -> Self {
667        if woff_flag.is_on_curve_point() {
668            SimpleGlyphFlag::ON_CURVE_POINT
669        } else {
670            SimpleGlyphFlag::empty()
671        }
672    }
673}
674
675impl Woff2GlyfTable {
676    fn compute_end_pts_of_contours(
677        n_points_ctxt: &mut ReadCtxt<'_>,
678        number_of_contours: i16,
679    ) -> Result<(Vec<u16>, u16), ParseError> {
680        // Read numberOfContours 255UInt16 values from the nPoints stream. Each of
681        // these is the number of points of that contour. Convert this into the
682        // endPtsOfContours[] array by computing the cumulative sum, then
683        // subtracting one.
684
685        // Also, the sum of all the values in the array is the total number of
686        // points in the glyph, nPoints.
687        let mut n_points = 0;
688        let end_pts_of_contours = (0..number_of_contours)
689            .map(|_i| {
690                n_points_ctxt.read::<PackedU16>().map(|n_contours| {
691                    n_points += n_contours;
692                    n_points - 1
693                })
694            })
695            .collect::<Result<Vec<_>, _>>()?;
696
697        Ok((end_pts_of_contours, n_points))
698    }
699
700    fn decode_coordinates(flag: WoffFlag, coordinates: ReadArray<'_, U8>) -> Point {
701        let xy_triplet = flag.xy_triplet();
702
703        let data = coordinates.iter().fold(0u32, |mut data, byte| {
704            data <<= 8;
705            data |= u32::from(byte);
706            data
707        });
708
709        // Extract x-bits and y-bits from the data value
710        Point(xy_triplet.dx(data), xy_triplet.dy(data))
711    }
712
713    fn decode_simple_glyph<'a>(
714        n_points_ctxt: &mut ReadCtxt<'_>,
715        flags_ctxt: &mut ReadCtxt<'_>,
716        glyphs_ctxt: &mut ReadCtxt<'_>,
717        instructions_ctxt: &mut ReadCtxt<'a>,
718        number_of_contours: i16,
719    ) -> Result<SimpleGlyph<'a>, ParseError> {
720        // Step 1. from spec section 5.1, Decoding of Simple Glyphs
721        let (end_pts_of_contours, n_points) =
722            Self::compute_end_pts_of_contours(n_points_ctxt, number_of_contours)?;
723
724        // Step 2.
725        let flags = flags_ctxt.read_array::<WoffFlag>(usize::from(n_points))?;
726
727        // Step 3.
728        let mut prev_point = Point::zero();
729        let mut points = Vec::with_capacity(flags.len());
730        for flag in flags.iter() {
731            let coordinates = glyphs_ctxt.read_array::<U8>(flag.bytes_to_read())?;
732            let point = Self::decode_coordinates(flag, coordinates);
733
734            // The x and y coordinates are stored as deltas against the previous point, with the
735            // first one being implicitly against (0, 0). Here we resolve these deltas into
736            // absolute (x, y) values.
737            prev_point = Point(prev_point.0 + point.0, prev_point.1 + point.1);
738            points.push((From::from(flag), prev_point));
739        }
740
741        // Step 4.
742        let instruction_length = usize::from(glyphs_ctxt.read::<PackedU16>()?);
743
744        // Step 5.
745        let instructions = instructions_ctxt.read_slice(instruction_length)?;
746
747        Ok(SimpleGlyph {
748            bounding_box: BoundingBox::empty(), // filled in later
749            end_pts_of_contours,
750            instructions,
751            coordinates: points,
752            phantom_points: None,
753        })
754    }
755}
756
757impl TableDirectoryEntry {
758    fn length(&self) -> usize {
759        self.transform_length.unwrap_or(self.orig_length) as usize
760    }
761
762    /// Read the contents of a table entry
763    pub fn read_table<'a>(&self, scope: &ReadScope<'a>) -> Result<ReadBuf<'a>, ParseError> {
764        let table_data = scope.offset_length(self.offset, self.length())?;
765
766        Ok(ReadBuf::from(table_data.data()))
767    }
768}
769
770impl HmtxTableFlag {
771    pub fn lsb_is_present(self) -> bool {
772        self & Self::LSB_ABSENT == Self::empty()
773    }
774
775    pub fn left_side_bearing_is_present(self) -> bool {
776        self & Self::LEFT_SIDE_BEARING_ABSENT == Self::empty()
777    }
778}
779
780impl<'a> BitSlice<'a> {
781    pub fn new(data: &'a [u8]) -> Self {
782        BitSlice { data }
783    }
784
785    pub fn get(&self, index: usize) -> Option<bool> {
786        if index >= self.len() {
787            return None;
788        }
789
790        // Find byte that holds the bit we're after
791        let byte_index = index / 8;
792        // The bits are packed so that glyph number 0 corresponds to the most significant bit of
793        // the first byte, glyph number 7 corresponds to the least significant bit of the first
794        // byte, glyph number 8 corresponds to the most significant bit of the second byte,
795        // and so on.
796        let shl = 8 - (index % 8) - 1;
797        let mask = 1 << shl;
798
799        Some(self.data[byte_index] & mask == mask)
800    }
801
802    pub fn len(&self) -> usize {
803        self.data.len() * 8
804    }
805}
806
807// The FontTableProvider implementation for WOFF2 provides some challenges because there's
808// dependencies between the tables. The implementation as it stands takes the somewhat brute force
809// approach of eager loading all the tables up front, which makes accessing them individually later
810// much easier.
811impl Woff2TableProvider {
812    fn new<'a>(woff: &Woff2Font<'a>, index: usize) -> Result<Self, ReadWriteError> {
813        let mut tables = HashMap::with_capacity(woff.table_directory.len());
814
815        // if hmtx is transformed then that means we have to parse glyf
816        // otherwise we only have to parse glyf if it's transformed
817        let hmtx_entry = woff.find_table_entry(tag::HMTX, index);
818        let glyf_entry = woff.find_table_entry(tag::GLYF, index);
819        let hmtx_is_transformed = hmtx_entry
820            .map(|entry| entry.transform_length.is_some())
821            .unwrap_or(false);
822        let glyf_is_transformed = glyf_entry
823            .map(|entry| entry.transform_length.is_some())
824            .unwrap_or(false);
825
826        if hmtx_is_transformed || glyf_is_transformed {
827            let glyf_entry = glyf_entry.ok_or(ParseError::MissingValue)?;
828            let glyf_table = glyf_entry.read_table(&woff.table_data_block_scope())?;
829            let mut head = read_table!(woff, tag::HEAD, HeadTable, index)?;
830            let maxp = read_table!(woff, tag::MAXP, MaxpTable, index)?;
831            let hhea = read_table!(woff, tag::HHEA, HheaTable, index)?;
832            let loca_entry = woff
833                .find_table_entry(tag::LOCA, index)
834                .ok_or(ParseError::MissingValue)?;
835            let loca = loca_entry.read_table(&woff.table_data_block_scope())?;
836            let loca = loca.scope().read_dep::<Woff2LocaTable>((
837                loca_entry,
838                usize::from(maxp.num_glyphs),
839                head.index_to_loc_format,
840            ))?;
841            let glyf = glyf_table
842                .scope()
843                .read_dep::<Woff2GlyfTable>((glyf_entry, &loca))?;
844
845            if hmtx_is_transformed {
846                let hmtx_entry = hmtx_entry.ok_or(ParseError::MissingValue)?;
847                let hmtx_table = hmtx_entry.read_table(&woff.table_data_block_scope())?;
848                let hmtx = hmtx_table.scope().read_dep::<Woff2HmtxTable>((
849                    hmtx_entry,
850                    &glyf,
851                    usize::from(maxp.num_glyphs),
852                    usize::from(hhea.num_h_metrics),
853                ))?;
854                let ((), data) = write::buffer::<_, HmtxTable<'_>>(&hmtx, ())?;
855                tables.insert(tag::HMTX, Box::from(data.into_inner()));
856            }
857
858            // Add head, glyf and loca
859            let (loca, data) = write::buffer::<_, GlyfTable<'_>>(glyf, head.index_to_loc_format)?;
860            tables.insert(tag::GLYF, Box::from(data.into_inner()));
861            match loca.offsets.last() {
862                Some(&last) if (last / 2) > u32::from(std::u16::MAX) => {
863                    head.index_to_loc_format = IndexToLocFormat::Long
864                }
865                _ => {}
866            }
867            let (_placeholder, data) = write::buffer::<_, HeadTable>(&head, ())?;
868            tables.insert(tag::HEAD, Box::from(data.into_inner()));
869            let ((), data) = write::buffer::<_, owned::LocaTable>(loca, head.index_to_loc_format)?;
870            tables.insert(tag::LOCA, Box::from(data.into_inner()));
871        }
872
873        // Add remaining tables
874        for table_entry in Self::table_directory(woff, index) {
875            let tag = table_entry.tag;
876            if tables.contains_key(&tag) {
877                // Skip tables that were inserted above
878                continue;
879            }
880            let data: Box<[u8]> = Box::from(
881                table_entry
882                    .read_table(&woff.table_data_block_scope())?
883                    .scope()
884                    .data(),
885            );
886            tables.insert(tag, data);
887        }
888
889        Ok(Woff2TableProvider {
890            flavor: woff.woff_header.flavor,
891            tables,
892        })
893    }
894
895    pub fn into_tables(self) -> HashMap<u32, Box<[u8]>> {
896        self.tables
897    }
898
899    fn table_directory<'a>(
900        woff: &'a Woff2Font<'a>,
901        index: usize,
902    ) -> impl Iterator<Item = &TableDirectoryEntry> {
903        if let Some(collection_directory) = &woff.collection_directory {
904            Either::Left(
905                collection_directory
906                    .get(index)
907                    .map(|font| font.table_entries(woff))
908                    .unwrap(), // NOTE(unwrap): It's assumed that index is determined valid in woff2_read_tables
909            )
910        } else {
911            Either::Right(woff.table_directory.iter())
912        }
913    }
914}
915
916#[cfg(test)]
917mod tests {
918    use super::*;
919
920    #[test]
921    fn test_compute_end_pts_of_contours() {
922        let data = [2u8, 4];
923        let mut ctxt = ReadScope::new(&data).ctxt();
924        let (end_pts_of_contours, n_points) =
925            Woff2GlyfTable::compute_end_pts_of_contours(&mut ctxt, data.len() as i16)
926                .expect("unable to decode simple glyph");
927        assert_eq!(end_pts_of_contours, vec![1, 5]);
928        assert_eq!(n_points, 6);
929    }
930
931    #[test]
932    fn test_xy_triplet_dx_dy() {
933        let triplet = XYTriplet {
934            byte_count: 2,
935            x_bits: 8,
936            y_bits: 8,
937            delta_x: 1,
938            delta_y: 257,
939            x_is_negative: true,
940            y_is_negative: false,
941        };
942        let data = 0x7AD2;
943
944        assert_eq!(triplet.dx(data), -(0x7A + 1));
945        assert_eq!(triplet.dy(data), 0xD2 + 257);
946    }
947
948    #[test]
949    fn test_bit_slice_len() {
950        let inner = vec![0b1000000, 0b00000001];
951        let bits = BitSlice::new(&inner);
952
953        assert_eq!(bits.len(), 16);
954    }
955
956    #[test]
957    fn test_bit_slice_get_out_of_bounds() {
958        let inner = vec![0b1000000, 0b00000001];
959        let bits = BitSlice::new(&inner);
960
961        assert_eq!(bits.get(16), None);
962    }
963
964    #[test]
965    fn test_bit_slice_start() {
966        let inner = vec![0b1000_0000, 0b0000_0000];
967        let bits = BitSlice::new(&inner);
968
969        assert_eq!(bits.get(0), Some(true));
970    }
971
972    #[test]
973    fn test_bit_slice_middle() {
974        let inner = vec![0b1111_1110, 0b1111_1111];
975        let bits = BitSlice::new(&inner);
976
977        assert_eq!(bits.get(7), Some(false));
978    }
979
980    #[test]
981    fn test_bit_slice_end() {
982        let inner = vec![0b0000_0000, 0b0000_0001];
983        let bits = BitSlice::new(&inner);
984
985        assert_eq!(bits.get(15), Some(true));
986    }
987
988    #[test]
989    fn test_read_packed_u16() {
990        assert_eq!(
991            ReadScope::new(&[255, 253]).read::<PackedU16>().unwrap(),
992            506
993        );
994        assert_eq!(ReadScope::new(&[254, 0]).read::<PackedU16>().unwrap(), 506);
995        assert_eq!(
996            ReadScope::new(&[253, 1, 250]).read::<PackedU16>().unwrap(),
997            506
998        );
999        assert_eq!(ReadScope::new(&[5u8]).read::<PackedU16>().unwrap(), 5);
1000        assert!(ReadScope::new(&[254u8]).read::<PackedU16>().is_err());
1001    }
1002
1003    #[test]
1004    fn test_read_u32base128() {
1005        assert_eq!(ReadScope::new(&[0x3F]).read::<U32Base128>().unwrap(), 63);
1006        assert_eq!(
1007            ReadScope::new(&[0x85, 0x07]).read::<U32Base128>().unwrap(),
1008            647
1009        );
1010        assert_eq!(
1011            ReadScope::new(&[0xFF, 0xFA, 0x00])
1012                .read::<U32Base128>()
1013                .unwrap(),
1014            2_096_384
1015        );
1016        assert_eq!(
1017            ReadScope::new(&[0x8F, 0xFF, 0xFF, 0xFF, 0x7F])
1018                .read::<U32Base128>()
1019                .unwrap(),
1020            0xFFFFFFFF
1021        );
1022    }
1023
1024    #[test]
1025    fn test_read_u32base128_err() {
1026        // Leading zeros
1027        assert!(ReadScope::new(&[0x80, 0x01]).read::<U32Base128>().is_err());
1028
1029        // Overflow
1030        assert!(ReadScope::new(&[0xFF, 0xFF, 0xFF, 0xFF, 0x7F])
1031            .read::<U32Base128>()
1032            .is_err());
1033
1034        // More than 5 bytes
1035        assert!(ReadScope::new(&[0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F])
1036            .read::<U32Base128>()
1037            .is_err());
1038    }
1039}