pub mod charset;
pub mod charstring;
pub mod dict;
pub mod encoding;
pub mod header;
pub mod index;
pub mod private;
pub mod strings;
pub mod subrs;
use crate::outline::CubicOutline;
use crate::Error;
use self::charset::Charset;
use self::charstring::Interpreter;
use self::dict::{Dict, Operator};
use self::encoding::Encoding;
use self::header::CffHeader;
use self::index::Index;
use self::private::PrivateDict;
use self::strings::Strings;
#[derive(Debug, Clone)]
pub struct Cff<'a> {
bytes: &'a [u8],
name: &'a [u8],
strings: Strings<'a>,
global_subrs: Index<'a>,
charstrings: Index<'a>,
charset: Charset<'a>,
encoding: Encoding<'a>,
private: PrivateDict<'a>,
}
impl<'a> Cff<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
let header = CffHeader::parse(bytes)?;
let mut cursor = header.size as usize;
let name_index = Index::parse(bytes, cursor)?;
cursor = name_index.end;
if name_index.count == 0 {
return Err(Error::Cff("empty Name INDEX"));
}
let name = name_index.entry(0)?;
let top_index = Index::parse(bytes, cursor)?;
cursor = top_index.end;
if top_index.count != name_index.count {
return Err(Error::Cff("Top DICT INDEX count mismatch"));
}
let top_bytes = top_index.entry(0)?;
let top_dict = Dict::parse(top_bytes)?;
let string_index = Index::parse(bytes, cursor)?;
cursor = string_index.end;
let strings = Strings::new(string_index);
let global_subrs = Index::parse(bytes, cursor)?;
let cs_off = top_dict
.get_int(Operator::CharStrings)
.ok_or(Error::Cff("Top DICT missing CharStrings offset"))?;
if cs_off < 0 {
return Err(Error::Cff("negative CharStrings offset"));
}
let charstrings = Index::parse(bytes, cs_off as usize)?;
let charset_off = top_dict.get_int(Operator::Charset).unwrap_or(0);
let charset = Charset::parse(bytes, charset_off, charstrings.count)?;
let encoding_off = top_dict.get_int(Operator::Encoding).unwrap_or(0);
let encoding = Encoding::parse(bytes, encoding_off)?;
let private_arr = top_dict
.get_array(Operator::Private)
.ok_or(Error::Cff("Top DICT missing Private"))?;
if private_arr.len() != 2 {
return Err(Error::Cff("Private operand must be [size, offset]"));
}
let priv_size = private_arr[0].as_int().ok_or(Error::Cff("Private size"))?;
let priv_off = private_arr[1]
.as_int()
.ok_or(Error::Cff("Private offset"))?;
if priv_size < 0 || priv_off < 0 {
return Err(Error::Cff("negative Private size/offset"));
}
let private = PrivateDict::parse(bytes, priv_off as usize, priv_size as usize)?;
Ok(Self {
bytes,
name,
strings,
global_subrs,
charstrings,
charset,
encoding,
private,
})
}
pub fn glyph_count(&self) -> u16 {
self.charstrings.count.min(u16::MAX as u32) as u16
}
pub fn ps_name(&self) -> &'a [u8] {
self.name
}
pub fn encoding_lookup(&self, codepoint: u8) -> Option<u16> {
self.encoding
.lookup(codepoint, &self.charset, &self.strings)
}
pub fn glyph_outline(&self, gid: u16) -> Result<CubicOutline, Error> {
let gid_u = gid as u32;
if gid_u >= self.charstrings.count {
return Err(Error::GlyphOutOfRange(gid));
}
let cs = self.charstrings.entry(gid_u)?;
let mut interp = Interpreter::new(
&self.global_subrs,
self.private.local_subrs.as_ref(),
self.private.nominal_width_x,
self.private.default_width_x,
);
interp.run(cs)?;
let mut outline = interp.into_outline();
outline.recompute_bounds();
Ok(outline)
}
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
pub(crate) fn strings(&self) -> &Strings<'a> {
&self.strings
}
pub(crate) fn charset(&self) -> &Charset<'a> {
&self.charset
}
}