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 glyphs.

#![allow(clippy::type_complexity)]

pub mod charset;
pub mod charstring;
pub mod encodings;

use tarrasque::{Extract, ExtractError, ExtractResult, Stream, View, extract};

use crate::dict::{Private, Top};
use crate::error::{CffError, UNSUPPORTED};
use crate::index::{Index};
use self::charset::{Charset};
use self::charstring::{Charstring};
use self::encodings::{Encodings};

/// Parses the private `DICT` and the subroutines for the supplied top-level `DICT`.
#[inline]
fn parse_private<'s>(
    bytes: &'s [u8], top: &Top<'s>
) -> Result<(Private<'s>, Option<Index<'s, &'s [u8]>>), CffError<'s>> {
    if let Some((size, offset)) = top.private {
        let stream = Stream(bytes);

        let start = offset as usize;
        let end = start + size as usize;
        let private: Private<'s> = stream.substream(start..end)?.extract(())?;

        let subroutines = if let Some(subroutines) = private.subroutines {
            Some(stream.substream(start + subroutines as usize..)?.extract(())?)
        } else {
            None
        };

        Ok((private, subroutines))
    } else {
        Ok((Stream(&[]).extract(())?, None))
    }
}

extract! {
    /// A `Format 0` mapping from glyphs to groups of glyphs.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Format0<'s>(num_glyphs: usize) {
        /// The group indices in this glyph mapping.
        pub indices: &'s [u8] = [num_glyphs as usize],
    }
}

impl<'s> Format0<'s> {
    /// Returns the top level `DICT` index for the glyph at the supplied index.
    #[inline]
    pub fn find_index(&self, index: usize) -> Option<usize> {
        self.indices.get(index).cloned().map(usize::from)
    }
}

extract! {
    /// A contiguous range of glyph indices for a `Format 3` mapping from
    /// glyphs to groups of glyphs.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Range[3] {
        /// The first glyph index in this glyph index range.
        pub first: u16 = [],
        /// The group index for the glyphs in this range.
        pub index: u8 = [],
    }
}

extract! {
    /// A `Format 3` mapping from glyphs to groups of glyphs.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Format3<'s> {
        /// The number of index ranges in this glyph mapping.
        pub num_ranges: u16 = [],
        /// The glyph index ranges in this glyph mapping.
        pub ranges: View<'s, Range, ()> = [(num_ranges as usize, ())],
        /// The number of glyphs in the font.
        pub sentinel: u16 = [],
    }
}

impl<'s> Format3<'s> {
    /// Returns the top level `DICT` index for the glyph at the supplied index.
    #[inline]
    pub fn find_index(&self, index: usize) -> Option<usize> {
        let mut ranges = self.ranges.iter().peekable();

        while let (Some(current), next) = (ranges.next(), ranges.peek()) {
            if index < next.map_or(self.sentinel as usize, |r| r.first as usize) {
                return Some(current.index as usize);
            }
        }

        None
    }
}

/// A mapping from glyphs to groups of glyphs.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Select<'s> {
    /// A `Format 0` glyph mapping.
    Format0(Format0<'s>),
    /// A `Format 3` glyph mapping.
    Format3(Format3<'s>),
}

impl<'s> Select<'s> {
    /// Returns the top level `DICT` index for the glyph at the supplied index.
    #[inline]
    pub fn find_index(&self, index: usize) -> Option<usize> {
        match self {
            Select::Format0(select) => select.find_index(index),
            Select::Format3(select) => select.find_index(index),
        }
    }
}

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

/// A set of `DICT`s and subroutines for a group of glyphs for a font.
pub type Group<'s> = (Top<'s>, Private<'s>, Option<Index<'s, &'s [u8]>>);

/// A collection of glyphs for a font.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Glyphs<'s> {
    /// The bytes in the table.
    pub bytes: &'s [u8],
    /// The top level `DICT` for the table.
    pub top: Top<'s>,
    /// The private `DICT` for the font.
    pub private: Private<'s>,
    /// The subroutines for the font.
    pub subroutines: (Index<'s, &'s [u8]>, Option<Index<'s, &'s [u8]>>),
    /// The glyph encodings for the font.
    pub encodings: Encodings<'s>,
    /// The glyph charset for the font.
    pub charset: Charset<'s>,
    /// The glyph charstrings for the font.
    pub charstrings: Index<'s, &'s [u8]>,
    /// The top-level `DICT`s for the groups of glyphs in the font and the
    /// mapping from the glyphs to those `DICT`s.
    pub groups: Option<(Select<'s>, Index<'s, Top<'s>>)>,
}

impl<'s> Glyphs<'s> {
    /// Parses the glyphs in the supplied CFF table for the supplied top-level `DICT`.
    #[inline]
    pub fn parse(
        bytes: &'s [u8], top: Top<'s>, global: Index<'s, &'s [u8]>
    ) -> Result<Self, CffError<'s>> {
        let stream = Stream(bytes);

        let charstrings = if let Some(charstrings) = top.charstrings {
            let mut substream = stream.substream(charstrings as usize..)?;
            substream.extract::<Index<'s, &'s [u8]>, _>(())?
        } else {
            return Err(CffError::Missing);
        };

        let groups = if let Some(cid) = top.cid {
            let mut substream = stream.substream(cid.fd_select as usize..)?;
            let select: Select<'s> = substream.extract(charstrings.len())?;

            let mut substream = stream.substream(cid.fd_array as usize..)?;
            let tops: Index<'s, Top<'s>> = substream.extract(())?;

            Some((select, tops))
        } else {
            None
        };

        let (private, local) = parse_private(bytes, &top)?;

        let mut substream = stream.substream(top.encoding as usize..)?;
        let encodings = substream.extract(top.encoding)?;

        let mut substream = stream.substream(top.charset as usize..)?;
        let charset = substream.extract((top.charset, charstrings.len()))?;

        Ok(Glyphs {
            bytes,
            top,
            private,
            subroutines: (global, local),
            encodings,
            charset,
            charstrings,
            groups,
        })
    }

    /// Returns the `DICT`s and subroutines for the group of glyphs at the
    /// supplied index in this collection of glyphs.
    #[inline]
    fn parse_group(
        &self, index: usize
    ) -> Result<Option<Group<'s>>, CffError<'s>> {
        if let Some((select, tops)) = self.groups {
            let index = select.find_index(index).ok_or(CffError::Missing)?;
            let top = tops.parse_object(index).ok_or(CffError::Missing)??;
            let (private, subroutines) = parse_private(self.bytes, &top)?;
            Ok(Some((top, private, subroutines)))
        } else {
            Ok(Some((self.top, self.private, self.subroutines.1)))
        }
    }

    /// Returns the type 2 charstring for the glyph at the supplied index in
    /// this collection of glyphs.
    #[inline]
    pub fn parse_charstring(
        &self, index: usize
    ) -> Option<Result<(Charstring<'s>, Group<'s>), CffError<'s>>> {
        let group = match self.parse_group(index) {
            Ok(Some(group)) => group,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        self.charstrings.get(index).map(|b| {
            Ok((Charstring::new(self.subroutines.0, group.2, b), group))
        })
    }
}