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.

//! A zero-allocation CFF parser.
//!
//! # Example
//!
//! ```
//! # extern crate cff;
//! extern crate sfnt;
//!
//! use std::fs::{File};
//! use std::io::{Read};
//!
//! use cff::{Cff};
//! use cff::glyphs::charstring::{Operation, Point};
//!
//! use sfnt::{Sfnt};
//!
//! fn main() {
//!     // Read the font file into memory.
//!     let mut file = File::open("tests/resources/SourceSansPro-It.otf").unwrap();
//!     let mut bytes = vec![];
//!     file.read_to_end(&mut bytes).unwrap();
//!
//!     // Parse the font file and find the CFF table in the font file.
//!     let sfnt = Sfnt::parse(&bytes).unwrap();
//!     let (_, bytes) = sfnt.find(b"CFF ").unwrap();
//!
//!     // Parse the CFF table.
//!     let cff = Cff::parse(&bytes).unwrap();
//!
//!     // Parse the glyphs for the first font.
//!     let glyphs = cff.parse_glyphs(0).unwrap().unwrap();
//!
//!     // Find the charstring for the ".notdef" glyph.
//!     let (charstring, _) = glyphs.parse_charstring(0).unwrap().unwrap();
//!
//!     // Parse and collect the operations in the charstring.
//!     let operations = charstring.operations().collect::<Result<Vec<_>, _>>().unwrap();
//!
//!     assert_eq!(&operations[..6], &[
//!         Operation::Width(92),
//!         Operation::HStem(0, 56),
//!         Operation::HStem(604, 660),
//!         Operation::VStem(36, 622),
//!         Operation::MoveTo(Point { x: 36, y: 0 }),
//!         Operation::LineTo(Point { x: 492, y: 0 }),
//!     ]);
//! }
//! ```

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs)]

#![no_std]

pub mod dict;
pub mod error;
pub mod glyphs;
pub mod index;

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

use crate::dict::{Top};
use crate::error::{OFFSET_SIZE};
use crate::glyphs::{Glyphs};
use crate::index::{Index, Strings};
pub use crate::error::{CffError};

/// A number which indicates the byte length of offsets.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct OffsetSize(pub u8);

impl<'s> Extract<'s, ()> for OffsetSize {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        let size: u8 = stream.extract(())?;
        if size >= 1 && size <= 4 {
            Ok(OffsetSize(size))
        } else {
            Err(ExtractError::Code(OFFSET_SIZE))
        }
    }
}

extract! {
    /// A table header.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Header[4] {
        /// The major CFF table version number.
        pub major_version: u8 = [],
        /// The minor CFF table version number.
        pub minor_version: u8 = [],
        /// The byte length of this header.
        pub header_size: u8 = [],
        /// The byte length of the absolute offsets in the table.
        pub offset_size: OffsetSize = [],
    }
}

/// A table.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Cff<'s> {
    /// The bytes in this table.
    pub bytes: &'s [u8],
    /// The header of this table.
    pub header: Header,
    /// The names of the fonts in this table.
    pub names: Index<'s, &'s str>,
    /// The top level `DICT`s for the fonts in this table.
    pub tops: Index<'s, Top<'s>>,
    /// The shared strings for the fonts in this table.
    pub strings: Strings<'s>,
    /// The shared subroutines for the fonts in this table.
    pub subroutines: Index<'s, &'s [u8]>,
}

impl<'s> Cff<'s> {
    /// Parses the supplied CFF table.
    #[inline]
    pub fn parse(bytes: &'s [u8]) -> Result<Self, CffError<'s>> {
        let mut stream = Stream(bytes);
        let header: Header = stream.extract(())?;
        let names: Index<&str> = stream.extract(())?;
        let tops: Index<Top> = stream.extract(())?;
        let strings: Strings = stream.extract(())?;
        let subroutines: Index<&[u8]> = stream.extract(())?;
        Ok(Cff { bytes, header, names, tops, strings, subroutines })
    }

    /// Returns the glyphs for the font at the supplied index in this table.
    #[inline]
    pub fn parse_glyphs(
        &self, index: usize
    ) -> Option<Result<Glyphs<'s>, CffError<'s>>> {
        self.tops.parse_object(index).map(|t| {
            Glyphs::parse(&self.bytes, t?, self.subroutines)
        })
    }
}