cff 0.5.0

A zero-allocation CFF parser.
// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! CFF glyph charsets.

use tarrasque::*;

use crate::error::{UNSUPPORTED};

extract! {
    /// A `Format 0` glyph charset.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Format0<'s>(num_glyphs: usize) {
        /// The string IDs for the glyphs in this glyph charset.
        pub glyphs: View<'s, u16, ()> = [(num_glyphs - 1, ())],
    }
}

impl<'s> Format0<'s> {
    /// Returns the string ID for the glyph with the supplied index.
    #[inline]
    pub fn find_id(&self, index: usize) -> usize {
        if index != 0 {
            self.glyphs.extract(index - 1).map(usize::from).unwrap_or(0)
        } else {
            0
        }
    }
}

extract! {
    /// A contiguous range of glyphs for a `Format 1` glyph charset.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Range1[3] {
        /// The string ID for the first glyph in this glyph range.
        pub first: u16 = [],
        /// The number of glyphs in this glyph range.
        pub num_glyphs: u8 = [],
    }
}

/// Extracts a view of ranges from the supplied stream.
#[inline]
fn extract_range1s<'s>(
    stream: &mut Stream<'s>, mut num_glyphs: usize
) -> ExtractResult<'s, View<'s, Range1, ()>> {
    let mut count = 0;
    let mut index = 0;

    while num_glyphs != 0 {
        if index + 3 > stream.len() {
            return Err(ExtractError::Insufficient(3));
        }

        num_glyphs -= stream[index + 2] as usize + 1;
        count += 1;
        index += 3;
    }

    stream.extract((count, ()))
}

extract! {
    /// A `Format 1` glyph charset.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Format1<'s>(num_glyphs: usize) {
        /// The glyph ranges in this glyph charset.
        pub ranges: View<'s, Range1, ()> = extract_range1s(stream, num_glyphs - 1)?,
    }
}

impl<'s> Format1<'s> {
    /// Returns the string ID for the glyph with the supplied index.
    #[inline]
    pub fn find_id(&self, mut index: usize) -> usize {
        if index != 0 {
            index -= 1;
        } else {
            return 0;
        }

        for range in self.ranges.iter() {
            if index < range.num_glyphs as usize + 1 {
                return range.first as usize + index;
            }

            index -= range.num_glyphs as usize + 1;
        }

        0
    }
}

extract! {
    /// A contiguous range of glyphs for a `Format 2` glyph charset.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Range2[4] {
        /// The string ID for the first glyph in this glyph range.
        pub first: u16 = [],
        /// The number of glyphs in this glyph range.
        pub num_glyphs: u16 = [],
    }
}

/// Extracts a view of ranges from the supplied stream.
#[inline]
fn extract_range2s<'s>(
    stream: &mut Stream<'s>, mut num_glyphs: usize
) -> ExtractResult<'s, View<'s, Range2, ()>> {
    let mut count = 0;
    let mut index = 0;

    while num_glyphs != 0 {
        if index + 4 > stream.len() {
            return Err(ExtractError::Insufficient(4));
        }

        num_glyphs -= be_u16(&stream[index + 2..]) as usize + 1;
        count += 1;
        index += 4;
    }

    stream.extract((count, ()))
}

extract! {
    /// A `Format 2` glyph charset.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Format2<'s>(num_glyphs: usize) {
        /// The glyph ranges in this glyph charset.
        pub ranges: View<'s, Range2, ()> = extract_range2s(stream, num_glyphs - 1)?,
    }
}

impl<'s> Format2<'s> {
    /// Returns the string ID for the glyph with the supplied index.
    #[inline]
    pub fn find_id(&self, mut index: usize) -> usize {
        if index != 0 {
            index -= 1;
        } else {
            return 0;
        }

        for range in self.ranges.iter() {
            if index < range.num_glyphs as usize + 1 {
                return range.first as usize + index;
            }

            index -= range.num_glyphs as usize + 1;
        }

        0
    }
}

/// A glyph charset.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Charset<'s> {
    /// A `Format 0` glyph charset.
    Format0(Format0<'s>),
    /// A `Format 1` glyph charset.
    Format1(Format1<'s>),
    /// A `Format 2` glyph charset.
    Format2(Format2<'s>),
}

impl<'s> Charset<'s> {
    /// Returns the string ID for the glyph with the supplied index.
    #[inline]
    pub fn find_id(&self, index: usize) -> usize {
        match self {
            Charset::Format0(charset) => charset.find_id(index),
            Charset::Format1(charset) => charset.find_id(index),
            Charset::Format2(charset) => charset.find_id(index),
        }
    }
}

impl<'s> Extract<'s, (i32, usize)> for Charset<'s> {
    #[inline]
    fn extract(
        stream: &mut Stream<'s>, (charset, num_glyphs): (i32, usize)
    ) -> ExtractResult<'s, Self> {
        match charset {
            0 ..= 2 => Err(ExtractError::Code(UNSUPPORTED)),
            _ => match stream.extract::<u8, _>(())? {
                0 => Ok(Charset::Format0(stream.extract(num_glyphs)?)),
                1 => Ok(Charset::Format1(stream.extract(num_glyphs)?)),
                2 => Ok(Charset::Format2(stream.extract(num_glyphs)?)),
                _ => Err(ExtractError::Code(UNSUPPORTED)),
            },
        }
    }
}