woff2 0.3.0

WOFF2 decompression library
Documentation
use std::io::{Cursor, Write};

use bitvec::{order::Msb0, slice::BitSlice};
use bytes::{Buf, BufMut};
use safer_bytes::{error::Truncated, SafeBuf};
use thiserror::Error;

use crate::buffer_util::{pad_to_multiple_of_four, BufExt};

mod x_y_triplet;
use x_y_triplet::COORD_LUT;

#[derive(Error, Debug)]
pub enum GlyfDecoderError {
    #[error("Stream truncated")]
    Truncated,
    #[error("Composite glyph without bbox")]
    CompositeGlyphWithoutBbox,
    #[error("Extra Data")]
    ExtraData,
}

impl From<Truncated> for GlyfDecoderError {
    fn from(_: Truncated) -> Self {
        GlyfDecoderError::Truncated
    }
}

impl From<std::io::Error> for GlyfDecoderError {
    fn from(_: std::io::Error) -> Self {
        GlyfDecoderError::Truncated
    }
}

struct Woff2GlyfDecoder<'a, T> {
    num_glyphs: u16,
    n_contour_stream: Cursor<T>,
    n_points_stream: Cursor<T>,
    flag_stream: Cursor<T>,
    glyph_stream: Cursor<T>,
    composite_stream: Cursor<T>,
    bbox_bitmap: &'a BitSlice<u8, Msb0>,
    bbox_stream: Cursor<T>,
    instruction_stream: Cursor<T>,
    overlap_bitmap: Option<&'a BitSlice<u8, Msb0>>,
    index_format: u16,
}

fn bit_stream_byte_length(bit_stream_bit_length: u16) -> u16 {
    ((bit_stream_bit_length >> 5)
        + if bit_stream_bit_length % 32 != 0 {
            1
        } else {
            0
        })
        << 2
}

impl<'a> Woff2GlyfDecoder<'a, &'a [u8]> {
    fn has_read_all(&self) -> bool {
        let _n_contour_stream_remaining = self.n_contour_stream.remaining();
        let _n_points_stream_reminaing = self.n_points_stream.remaining();

        self.n_contour_stream.remaining() == 0
            && self.n_points_stream.remaining() == 0
            && self.flag_stream.remaining() == 0
            && self.glyph_stream.remaining() == 0
            && self.composite_stream.remaining() == 0
            && self.bbox_stream.remaining() == 0
            && self.instruction_stream.remaining() == 0
    }

    fn new(transformed_glyf_table: &'a [u8]) -> Result<Self, GlyfDecoderError> {
        let mut table_buf = Cursor::new(transformed_glyf_table);

        const GLYF_HEADER_SIZE: usize = 36;
        if table_buf.remaining() < GLYF_HEADER_SIZE {
            return Err(GlyfDecoderError::Truncated);
        }
        let _ = table_buf.get_u16();
        let option_flags = table_buf.get_u16();
        let num_glyphs = table_buf.get_u16();
        let bitmap_stream_length = bit_stream_byte_length(num_glyphs);
        let index_format = table_buf.get_u16();
        let n_contour_stream_size = table_buf.get_u32();
        let n_points_stream_size = table_buf.get_u32();
        let flag_stream_size = table_buf.get_u32();
        let glyph_stream_size = table_buf.get_u32();
        let composite_stream_size = table_buf.get_u32();
        let bbox_bitmap_size = bitmap_stream_length;
        let bbox_stream_size = table_buf.get_u32() - bbox_bitmap_size as u32;
        let instruction_stream_size = table_buf.get_u32();
        assert_eq!(table_buf.position() as usize, GLYF_HEADER_SIZE);
        let has_overlap_bit_stream = (option_flags & 0x01) == 0x01;
        let overlap_simple_bit_stream_size = if has_overlap_bit_stream {
            bit_stream_byte_length(num_glyphs)
        } else {
            0
        };

        let n_contour_stream_start: usize = table_buf.position().try_into().unwrap();
        let n_points_stream_start = n_contour_stream_start + n_contour_stream_size as usize;
        let flag_stream_start = n_points_stream_start + n_points_stream_size as usize;
        let glyph_stream_start = flag_stream_start + flag_stream_size as usize;
        let composite_stream_start = glyph_stream_start + glyph_stream_size as usize;
        let bbox_bitmap_start = composite_stream_start + composite_stream_size as usize;
        let bbox_stream_start = bbox_bitmap_start + bbox_bitmap_size as usize;
        let instruction_stream_start = bbox_stream_start + bbox_stream_size as usize;
        let overlap_bit_stream_start = instruction_stream_start + instruction_stream_size as usize;
        let overlap_bit_stream_end =
            overlap_bit_stream_start + overlap_simple_bit_stream_size as usize;
        if transformed_glyf_table.len() < overlap_bit_stream_end {
            return Err(GlyfDecoderError::Truncated);
        }
        let n_contour_stream =
            Cursor::new(&transformed_glyf_table[n_contour_stream_start..n_points_stream_start]);
        let n_points_stream =
            Cursor::new(&transformed_glyf_table[n_points_stream_start..flag_stream_start]);
        let flag_stream =
            Cursor::new(&transformed_glyf_table[flag_stream_start..glyph_stream_start]);
        let glyph_stream =
            Cursor::new(&transformed_glyf_table[glyph_stream_start..composite_stream_start]);
        let composite_stream =
            Cursor::new(&transformed_glyf_table[composite_stream_start..bbox_bitmap_start]);
        let bbox_bitmap = BitSlice::<_, Msb0>::from_slice(
            &transformed_glyf_table[bbox_bitmap_start..bbox_stream_start],
        );
        let bbox_stream =
            Cursor::new(&transformed_glyf_table[bbox_stream_start..instruction_stream_start]);
        let instruction_stream = Cursor::new(
            &transformed_glyf_table[instruction_stream_start..overlap_bit_stream_start],
        );
        let overlap_bitmap = if has_overlap_bit_stream {
            Some(BitSlice::<_, Msb0>::from_slice(
                &transformed_glyf_table[overlap_bit_stream_start
                    ..overlap_bit_stream_start + overlap_simple_bit_stream_size as usize],
            ))
        } else {
            None
        };

        Ok(Self {
            num_glyphs,
            n_contour_stream,
            n_points_stream,
            flag_stream,
            glyph_stream,
            composite_stream,
            bbox_bitmap,
            bbox_stream,
            instruction_stream,
            overlap_bitmap,
            index_format,
        })
    }

    fn parse_simple_glyph(
        &mut self,
        number_of_contours: i16,
        glyph_index: u16,
        output_buffer: &mut Vec<u8>,
    ) -> Result<(), GlyfDecoderError> {
        let mut end_points_of_contours_stream: Vec<u8> = Vec::new();
        let mut instructions_stream: Vec<u8> = Vec::new();
        let mut flags_stream: Vec<u8> = Vec::new();
        let mut x_coordinates_stream: Vec<u8> = Vec::new();
        let mut y_coordinates_stream: Vec<u8> = Vec::new();

        let mut running_total_points: u16 = 0;

        let overlap_simple_flag = match self.overlap_bitmap {
            Some(ob) if ob[glyph_index as usize] => 0x40,
            _ => 0x00,
        };

        let mut x_min = 0i16;
        let mut y_min = 0i16;
        let mut x_max = 0i16;
        let mut y_max = 0i16;
        let mut extents_set: bool = false;
        let mut x = 0i16;
        let mut y = 0i16;

        for _contour_index in 0..number_of_contours {
            let number_of_points = self.n_points_stream.try_get_255_u16()?;
            running_total_points += number_of_points;
            end_points_of_contours_stream.put_u16(running_total_points - 1);
            for _point_index in 0..number_of_points {
                let flags = self.flag_stream.try_get_u8()?;
                let triplet = &COORD_LUT[(flags & 0x7f) as usize];
                let data = match triplet.byte_count {
                    1 => self.glyph_stream.try_get_u8()? as u32,
                    2 => self.glyph_stream.try_get_u16()? as u32,
                    3 => {
                        ((self.glyph_stream.try_get_u8()? as u32) << 16)
                            | (self.glyph_stream.try_get_u16()? as u32)
                    }
                    4 => self.glyph_stream.try_get_u32()?,
                    _ => panic!(),
                };
                let dx = triplet.dx(data);
                let dy = triplet.dy(data);
                x += dx;
                y += dy;
                if extents_set {
                    x_min = x_min.min(x);
                    y_min = y_min.min(y);
                    x_max = x_max.max(x);
                    y_max = y_max.max(y);
                } else {
                    x_min = x;
                    x_max = x;
                    y_min = y;
                    y_max = y;
                    extents_set = true;
                }

                let point_is_on_curve = (flags & 0x80) == 0x00;
                let on_curve_flag = if point_is_on_curve { 0x01 } else { 0x00 };
                let (x_short_vector_flag, x_is_same_flag) = match dx {
                    0 => (0x00, 0x10),
                    1..=255 => {
                        x_coordinates_stream.put_u8(u8::try_from(dx).unwrap());
                        (0x02, 0x10)
                    }
                    -255..=-1 => {
                        x_coordinates_stream.put_u8(u8::try_from(-dx).unwrap());
                        (0x02, 0x00)
                    }
                    _ => {
                        x_coordinates_stream.put_i16(dx);
                        (0x00, 0x00)
                    }
                };
                let (y_short_vector_flag, y_is_same_flag) = match dy {
                    0 => (0x00, 0x20),
                    1..=255 => {
                        y_coordinates_stream.put_u8(u8::try_from(dy).unwrap());
                        (0x04, 0x20)
                    }
                    -255..=-1 => {
                        y_coordinates_stream.put_u8(u8::try_from(-dy).unwrap());
                        (0x04, 0x00)
                    }
                    _ => {
                        y_coordinates_stream.put_i16(dy);
                        (0x00, 0x00)
                    }
                };

                flags_stream.put_u8(
                    on_curve_flag
                        | x_short_vector_flag
                        | y_short_vector_flag
                        | x_is_same_flag
                        | y_is_same_flag
                        | overlap_simple_flag,
                );
            }
        }

        let instruction_length = self.glyph_stream.try_get_255_u16()?;
        self.instruction_stream
            .try_copy_to_buf(&mut instructions_stream, instruction_length as usize)?;

        if self.bbox_bitmap[glyph_index as usize] {
            x_min = self.bbox_stream.try_get_i16()?;
            y_min = self.bbox_stream.try_get_i16()?;
            x_max = self.bbox_stream.try_get_i16()?;
            y_max = self.bbox_stream.try_get_i16()?;
        }

        output_buffer.put_i16(number_of_contours);
        output_buffer.put_i16(x_min);
        output_buffer.put_i16(y_min);
        output_buffer.put_i16(x_max);
        output_buffer.put_i16(y_max);
        output_buffer.write_all(&end_points_of_contours_stream)?;
        output_buffer.put_u16(instruction_length);
        output_buffer.write_all(&instructions_stream)?;
        output_buffer.write_all(&flags_stream)?;
        output_buffer.write_all(&x_coordinates_stream)?;
        output_buffer.write_all(&y_coordinates_stream)?;

        Ok(())
    }

    fn parse_composite_glyph(
        &mut self,
        glyph_index: u16,
        output_buffer: &mut Vec<u8>,
    ) -> Result<(), GlyfDecoderError> {
        output_buffer.put_i16(-1);
        if self.bbox_bitmap[glyph_index as usize] {
            output_buffer.put_i16(self.bbox_stream.try_get_i16()?);
            output_buffer.put_i16(self.bbox_stream.try_get_i16()?);
            output_buffer.put_i16(self.bbox_stream.try_get_i16()?);
            output_buffer.put_i16(self.bbox_stream.try_get_i16()?);
        } else {
            Err(GlyfDecoderError::CompositeGlyphWithoutBbox)?
        }

        let mut have_instructions = false;
        loop {
            let flag_word = self.composite_stream.try_get_u16()?;
            let mut num_bytes = 4usize;

            if flag_word & 0x0001 == 0x0001 {
                num_bytes += 2;
            }
            if flag_word & 0x0008 == 0x0008 {
                num_bytes += 2;
            } else if flag_word & 0x0040 == 0x0040 {
                num_bytes += 4;
            } else if flag_word & 0x0080 == 0x0080 {
                num_bytes += 8;
            }

            output_buffer.put_u16(flag_word);

            self.composite_stream
                .try_copy_to_buf(output_buffer, num_bytes)?;

            if flag_word & 0x0100 == 0x0100 {
                have_instructions = true;
            }

            if flag_word & 0x0020 == 0 {
                break;
            }
        }

        if have_instructions {
            let instruction_length = self.glyph_stream.try_get_255_u16()?;
            output_buffer.put_u16(instruction_length);
            self.instruction_stream
                .try_copy_to_buf(output_buffer, instruction_length as usize)?;
        }

        Ok(())
    }

    fn parse_next_glyph(
        &mut self,
        glyph_index: u16,
        output_vector: &mut Vec<u8>,
    ) -> Result<(), GlyfDecoderError> {
        let number_of_contours = self.n_contour_stream.try_get_i16()?;
        match number_of_contours {
            0 => Ok(()),
            num if num > 0 => {
                self.parse_simple_glyph(number_of_contours, glyph_index, output_vector)
            }
            _ => self.parse_composite_glyph(glyph_index, output_vector),
        }
    }

    fn parse_all_glyphs(&mut self) -> Result<(Vec<u8>, Vec<u8>), GlyfDecoderError> {
        let loca_use_u32 = self.index_format > 0;
        let loca_capacity = (self.num_glyphs + 1) as usize * if loca_use_u32 { 4 } else { 2 };
        let mut output_glyf_table: Vec<u8> = Vec::new();
        let mut output_loca_table: Vec<u8> = Vec::with_capacity(loca_capacity);
        for glyph_index in 0..self.num_glyphs {
            if loca_use_u32 {
                output_loca_table.put_u32(output_glyf_table.len().try_into().unwrap());
            } else {
                output_loca_table.put_u16((output_glyf_table.len() / 2).try_into().unwrap());
            }
            self.parse_next_glyph(glyph_index, &mut output_glyf_table)?;
            pad_to_multiple_of_four(&mut output_glyf_table);
        }
        if loca_use_u32 {
            output_loca_table.put_u32(output_glyf_table.len().try_into().unwrap());
        } else {
            if output_glyf_table.len() % 2 == 1 {
                output_glyf_table.put_u8(0);
            }
            output_loca_table.put_u16((output_glyf_table.len() / 2).try_into().unwrap());
        }
        Ok((output_glyf_table, output_loca_table))
    }
}

pub fn decode_glyf_table<'a>(glyf_table: &'a [u8]) -> Result<(Vec<u8>, Vec<u8>), GlyfDecoderError> {
    let mut decoder = Woff2GlyfDecoder::new(glyf_table)?;
    let res = decoder.parse_all_glyphs()?;
    if decoder.has_read_all() {
        Ok(res)
    } else {
        Err(GlyfDecoderError::ExtraData)
    }
}