mod charstring;
mod charstring_parser;
mod decrypt;
mod operator;
mod standard;
pub(crate) mod stream;
use crate::font::type1::charstring::{parse_char_string, parse_char_string_width};
use crate::font::type1::decrypt::{decrypt, decrypt_byte};
use crate::font::type1::standard::STANDARD;
use crate::font::type1::stream::Stream;
use crate::font::{Arc, Map, Matrix, OutlineBuilder};
use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::iter::Copied;
use core::slice::Iter;
use core::str::FromStr;
use log::error;
#[derive(Debug)]
pub(crate) struct Parameters {
font_matrix: Matrix,
encoding_type: EncodingType,
subroutines: Map<u32, Vec<u8>>,
charstrings: Map<String, Vec<u8>>,
charstring_names: Vec<String>,
charstring_indices: Map<String, u16>,
pub(crate) weight_vector: Option<Vec<f32>>,
}
impl Default for Parameters {
fn default() -> Self {
Self {
font_matrix: Matrix::default(),
encoding_type: EncodingType::Standard,
subroutines: Map::new(),
charstrings: Map::new(),
charstring_names: Vec::new(),
charstring_indices: Map::new(),
weight_vector: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Table {
params: Arc<Parameters>,
}
impl Table {
pub fn parse(data: &[u8]) -> Option<Self> {
let data = if data.starts_with(&[0x80, 0x01]) {
extract_pfb_segments(data)?
} else if data.starts_with(b"%!") {
Cow::Borrowed(data)
} else {
error!("type1 font wasn't recognized!");
return None;
};
let mut s = Stream::new(data.as_ref());
let mut params = Parameters::default();
while let Some(token) = s.next_token() {
match token {
b"/FontInfo" => s.skip_dict(),
b"/FontName" => s.skip_token(),
b"/PaintType" => s.skip_token(),
b"/FontType" => s.skip_token(),
b"/FontBBox" => s.skip_token(),
b"/UniqueID" => s.skip_token(),
b"/Metrics" => s.skip_dict(),
b"/StrokeWidth" => s.skip_token(),
b"/FontMatrix" => {
let matrix = s.read_font_matrix()?;
params.font_matrix = Matrix {
sx: matrix[0],
kx: matrix[2],
ky: matrix[1],
sy: matrix[3],
tx: matrix[4],
ty: matrix[5],
};
}
b"/WeightVector" => {
if let Some(wv) = s.read_float_array() {
params.weight_vector = Some(wv);
}
}
b"/Encoding" => params.encoding_type = s.read_encoding()?,
b"eexec" => {
let decrypted = decrypt(s.tail()?, true)?;
Self::parse_eexec(&decrypted, &mut params)?;
}
_ => {}
}
}
if params.charstrings.is_empty() {
return None;
}
Some(Self {
params: Arc::new(params),
})
}
fn parse_eexec(data: &[u8], params: &mut Parameters) -> Option<()> {
let mut s = Stream::new(data);
let mut len_iv = 4;
let mut use_decryption = true;
while let Some(token) = s.next_token() {
match token {
b"/Subrs" => {
params.subroutines = s
.parse_subroutines(len_iv, use_decryption)
.unwrap_or_default();
}
b"/CharStrings" => {
if let Some((chars, names)) = s.parse_charstrings(len_iv, use_decryption) {
params.charstring_indices = names
.iter()
.enumerate()
.map(|(i, n)| (n.clone(), i as u16))
.collect();
params.charstrings = chars;
params.charstring_names = names;
}
}
b"/lenIV" => {
len_iv = s.next_int()?;
if len_iv < 0 {
use_decryption = false;
len_iv = 0;
}
}
b"/WeightVector" => {
if let Some(wv) = s.read_float_array() {
params.weight_vector = Some(wv);
}
}
_ => {}
}
}
Some(())
}
pub fn is_multiple_master(&self) -> bool {
self.params.weight_vector.is_some()
}
pub fn matrix(&self) -> Matrix {
self.params.font_matrix
}
pub fn outline(&self, string: &str, builder: &mut dyn OutlineBuilder) -> Option<()> {
let data = self.params.charstrings.get(string)?;
parse_char_string(data, &self.params, builder).ok()?;
Some(())
}
pub fn glyph_width(&self, string: &str) -> Option<f32> {
let data = self.params.charstrings.get(string)?;
parse_char_string_width(data, &self.params).ok()
}
pub fn code_to_string(&self, code_point: u8) -> Option<&str> {
self.params.encoding_type.encode(code_point)
}
pub fn charstring_names(&self) -> &[String] {
&self.params.charstring_names
}
pub fn charstring_index(&self, name: &str) -> Option<u16> {
self.params.charstring_indices.get(name).copied()
}
}
fn extract_pfb_segments(pfb: &[u8]) -> Option<Cow<'static, [u8]>> {
const START_MARKER: u8 = 0x80;
const ASCII_MARKER: u8 = 0x01;
const BINARY_MARKER: u8 = 0x02;
const EOF_MARKER: u8 = 0x03;
const PFB_HEADER_LENGTH: usize = 18;
if pfb.len() < PFB_HEADER_LENGTH {
return None;
}
let mut stream = Stream::new(pfb);
let mut type_list = Vec::new();
let mut barr_list = Vec::new();
let mut total = 0;
loop {
let r = stream.read_byte();
if r.is_none() && total > 0 {
break; }
let r = r?;
if r != START_MARKER {
return None;
}
let record_type = stream.read_byte()?;
if record_type == EOF_MARKER {
break;
}
if record_type != ASCII_MARKER && record_type != BINARY_MARKER {
return None;
}
let size_bytes = stream.read_bytes(4)?;
let mut size = size_bytes[0] as usize;
size += (size_bytes[1] as usize) << 8;
size += (size_bytes[2] as usize) << 16;
size += (size_bytes[3] as usize) << 24;
let Some(ar) = stream.read_bytes(size) else {
break;
};
total += size;
type_list.push(record_type);
barr_list.push(ar);
}
let mut pfbdata = Vec::with_capacity(total);
let mut cleartomark_segment = None;
for i in 0..type_list.len() {
if type_list[i] != ASCII_MARKER {
continue;
}
let ar = barr_list[i];
if i == type_list.len() - 1
&& ar.len() < 600
&& let Ok(s) = core::str::from_utf8(ar)
&& s.contains("cleartomark")
{
cleartomark_segment = Some(ar);
continue;
}
pfbdata.extend_from_slice(ar);
}
for i in 0..type_list.len() {
if type_list[i] != BINARY_MARKER {
continue;
}
let ar = barr_list[i];
pfbdata.extend_from_slice(ar);
}
if let Some(segment) = cleartomark_segment {
pfbdata.extend_from_slice(segment);
}
Some(Cow::Owned(pfbdata))
}
const ND: &[u8] = b"ND";
const ND_ALT: &[u8] = b"|-";
const RD: &[u8] = b"RD";
const RD_ALT: &[u8] = b"-|";
const NP: &[u8] = b"NP";
const NP_ALT: &[u8] = b"|";
impl<'a> Stream<'a> {
fn next_int(&mut self) -> Option<i64> {
parse_int(core::str::from_utf8(self.next_token()?).ok()?)
}
#[allow(clippy::type_complexity)]
fn parse_charstrings(
&mut self,
len_iv: i64,
use_decryption: bool,
) -> Option<(Map<String, Vec<u8>>, Vec<String>)> {
let mut charstrings = Map::new();
let mut names = Vec::new();
let mut first_glyph_name = None;
let mut int_token = None;
while let Some(token) = self.next_token() {
if token == b"end" {
return Some((charstrings, names));
}
if token.starts_with(b"/") {
first_glyph_name = Some(token);
} else if token
.iter()
.all(|b| matches!(*b, b'#') || b.is_ascii_digit())
{
int_token = parse_int(core::str::from_utf8(token).ok()?);
} else if token == RD || token == RD_ALT {
break;
}
}
let (first_glyph_name, int_token) = (first_glyph_name?, int_token?);
let mut is_first = true;
loop {
let bin_len;
let mut glyph_name;
if is_first {
is_first = false;
bin_len = int_token;
glyph_name = first_glyph_name;
if glyph_name.starts_with(b"/") {
glyph_name = &glyph_name[1..];
}
self.read_byte();
} else {
let tok = self.next_token()?;
if tok == b"end" {
break;
}
if tok.starts_with(b"/") {
glyph_name = &tok[1..];
} else {
glyph_name = tok;
}
let Some(len) = self.next_int() else {
break;
};
bin_len = len;
let tok = self.next_token()?;
if tok == RD || tok == RD_ALT {
self.read_byte();
} else {
error!("invalid charstring in start, expected RD");
return None;
}
}
let encrypted_bytes = self.read_bytes(bin_len as usize)?;
let decrypted_bytes = decrypt_charstring(encrypted_bytes, len_iv, use_decryption)?;
let name = core::str::from_utf8(glyph_name).ok()?.to_string();
names.push(name.clone());
charstrings.insert(name, decrypted_bytes);
let tok = self.next_token()?;
if tok == ND || tok == ND_ALT {
} else if tok.starts_with(b"/") {
self.move_back(tok.len());
} else {
error!("invalid charstring in end, expected ND, found {tok:?}");
if charstrings.is_empty() {
return None;
} else {
break;
}
}
}
Some((charstrings, names))
}
fn parse_subroutines(
&mut self,
len_iv: i64,
use_decryption: bool,
) -> Option<Map<u32, Vec<u8>>> {
let mut subroutines = Map::new();
let num_subrs = parse_int(core::str::from_utf8(self.next_token()?).ok()?)?;
if num_subrs < 1 {
return Some(subroutines);
}
if !self.skip_until_before(b"dup", |b| matches!(b, ND | ND_ALT | b"noaccess")) {
return Some(subroutines);
}
while let Some(token) = self.next_token() {
if matches!(token, ND | ND_ALT) {
break;
}
if token == b"noaccess" {
if self.next_token() == Some(b"def") {
break;
} else {
error!("invalid sequence noaccess");
return None;
}
}
if token != b"dup" {
error!("expected dup, got token {:?} instead", &token);
return None;
}
let subr_idx = self.next_int()?;
let bin_len = self.next_int()?;
let tok = self.next_token()?;
if tok != RD && tok != RD_ALT {
error!("invalid subroutine start token {tok:?}");
return None;
} else {
self.read_byte();
}
let encrypted_bytes = self.read_bytes(bin_len as usize)?;
subroutines.insert(
subr_idx as u32,
decrypt_charstring(encrypted_bytes, len_iv, use_decryption)?,
);
let mut tok = self.next_token()?;
if tok == NP || tok == NP_ALT {
} else if tok == b"noaccess" {
tok = self.next_token()?;
if tok == b"def" {
break;
}
if tok == b"put" {
} else {
error!("invalid subroutine end {tok:?}");
return None;
}
} else {
error!("invalid subroutine end token {tok:?}");
return None;
}
}
Some(subroutines)
}
fn peek_token(&mut self) -> Option<&'a [u8]> {
self.clone().next_token()
}
fn next_token(&mut self) -> Option<&'a [u8]> {
let skip_token = |st: &mut Stream<'_>| -> usize {
let mut count = 1;
while let Some(ch) = st.read_bytes(1) {
if is_whitespace(ch[0]) || is_self_delim_after_token(ch[0]) {
st.move_back(1);
break;
}
count += 1;
}
count
};
self.skip_whitespaces();
while let Some(ch) = self.clone().read_bytes(1) {
let tail = self.tail()?;
self.read_bytes(1);
match ch[0] {
b'%' => self.skip_line_comment(),
b'(' => return Some(b"("),
b'<' => {
if let Some(ch2) = self.read_bytes(1) {
if ch2[0] == b'>' {
return Some(b"( )");
} else if ch2[0] == b'<' {
return Some(b"<<");
} else {
return Some(b"<");
}
}
}
b'>' => {
if let Some(ch2) = self.read_bytes(1) {
if ch2[0] == b'>' {
return Some(b">>");
} else {
self.move_back(1);
return Some(b">");
}
}
}
b'[' => {
return Some(b"[");
}
b']' => {
return Some(b"]");
}
b'{' => {
return Some(b"{");
}
b'}' => {
return Some(b"}");
}
b'/' => {
if let Some(ch2) = self.read_bytes(1) {
if is_whitespace(ch2[0]) || is_self_delim_after_token(ch2[0]) {
let token = b"/";
if is_self_delim_after_token(ch2[0]) {
self.move_back(1);
}
return Some(token);
} else {
let count = skip_token(self);
return Some(&tail[0..(count + 1)]);
}
}
}
_ => {
let count = skip_token(self);
return Some(&tail[0..count]);
}
}
self.skip_whitespaces();
}
None
}
fn read_float_array(&mut self) -> Option<Vec<f32>> {
let mut entries = Vec::new();
if self.next_token()? != b"[" {
return None;
}
while let Some(token) = self.next_token() {
if token == b"]" {
break;
}
if let Ok(s) = core::str::from_utf8(token)
&& let Ok(v) = f32::from_str(s)
{
entries.push(v);
}
}
Some(entries)
}
fn read_font_matrix(&mut self) -> Option<[f32; 6]> {
let mut entries = [0.0_f32; 6];
let mut idx = 0;
self.skip_token();
while let Some(token) = self.next_token() {
entries[idx] = f32::from_str(core::str::from_utf8(token).ok()?).ok()?;
idx += 1;
if idx == 5 {
break;
}
}
self.skip_token();
Some(entries)
}
fn read_encoding(&mut self) -> Option<EncodingType> {
let mut map = Map::new();
let t1 = self.next_token()?;
let t2 = self.next_token()?;
if t1 == b"StandardEncoding" && t2 == b"def" {
return Some(EncodingType::Standard);
}
if !self.skip_until_before(b"dup", |b| matches!(b, b"def" | b"readonly")) {
return Some(EncodingType::Custom(Arc::new(map)));
}
while let Some(token) = self.next_token() {
if matches!(token, b"def" | b"readonly") {
break;
}
if token != b"dup" {
error!("Unexpected token {token:?}");
return None;
}
let next = self.next_token();
let code = parse_int(core::str::from_utf8(next?).ok()?)?;
let glyph_name = core::str::from_utf8(&self.next_token()?[1..])
.ok()?
.to_string();
if self.next_token()? != b"put" {
error!("Unexpected token {token:?}");
return None;
}
map.insert(u8::try_from(code).ok()?, glyph_name);
}
Some(EncodingType::Custom(Arc::new(map)))
}
fn skip_dict(&mut self) {
self.skip_until(b"begin", |b| matches!(b, b"end"));
}
fn skip_token(&mut self) {
self.next_token();
}
fn skip_line_comment(&mut self) {
while let Some(ch) = self.read_byte() {
if matches!(ch, b'\n' | b'\r') {
break;
}
}
}
fn skip_until(&mut self, find: &[u8], stop: impl Fn(&[u8]) -> bool) -> bool {
while let Some(token) = self.next_token() {
if token == find {
return true;
}
if stop(token) {
break;
}
}
false
}
fn skip_whitespaces(&mut self) {
while let Some(ch) = self.peek_byte() {
if is_whitespace(ch) {
self.read_byte();
} else {
break;
}
}
}
fn skip_until_before(&mut self, find: &[u8], stop: impl Fn(&[u8]) -> bool) -> bool {
while let Some(token) = self.peek_token() {
if token == find {
return true;
}
if self.next_token().is_none() {
return false;
}
if stop(token) {
break;
}
}
false
}
}
fn decrypt_charstring(data: &[u8], len_iv: i64, use_decryption: bool) -> Option<Vec<u8>> {
let mut r = 4330;
let mut cb: Copied<Iter<'_, u8>> = data.iter().copied();
let mut decrypted = vec![];
for _ in 0..len_iv {
let _ = decrypt_byte(cb.next()?, &mut r, use_decryption);
}
for byte in cb {
decrypted.push(decrypt_byte(byte, &mut r, use_decryption));
}
Some(decrypted)
}
fn is_whitespace(c: u8) -> bool {
if c <= 32 {
return matches!(c, b' ' | b'\n' | b'\r' | b'\t' | 0x00 | 0x0C);
}
false
}
fn is_self_delim_after_token(c: u8) -> bool {
matches!(
c,
b'(' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%' | b')'
)
}
#[derive(Debug, Clone)]
pub(crate) enum EncodingType {
Standard,
Custom(Arc<Map<u8, String>>),
}
impl EncodingType {
pub(crate) fn encode(&self, code: u8) -> Option<&str> {
match self {
Self::Standard => STANDARD.get(&code).copied(),
Self::Custom(c) => c.get(&code).map(|s| s.as_str()),
}
}
}
fn parse_int(str: &str) -> Option<i64> {
if let Some(hash_idx) = str.find('#') {
if hash_idx == 1 || hash_idx == 2 {
let radix_str = &str[0..hash_idx];
let number_str = &str[hash_idx + 1..];
let radix = radix_str.parse::<u32>().ok()?;
if (2..=36).contains(&radix) {
i64::from_str_radix(number_str, radix).ok()
} else {
None
}
} else {
str.parse::<i64>().ok()
}
} else {
str.parse::<i64>().ok()
}
}
#[cfg(test)]
mod tests {
use crate::font::type1::stream::Stream;
macro_rules! assert_token {
($content:expr, $token:expr) => {
assert_eq!($content.next_token(), Some(&$token[..]))
};
}
#[test]
fn lexing_1() {
let mut content = Stream::new(b"/FontInfo ");
assert_token!(content, b"/FontInfo");
}
#[test]
fn lexing_2() {
let mut content = Stream::new(b"/version (01) readonly def");
assert_token!(content, b"/version");
assert_token!(content, b"(");
assert_token!(content, b"01");
assert_token!(content, b")");
assert_token!(content, b"readonly");
assert_token!(content, b"def");
}
}