use crate::parser::{read_u16, read_u8};
use crate::Error;
#[derive(Debug, Clone)]
pub(crate) enum Charset<'a> {
IsoAdobe,
Format0 { bytes: &'a [u8], num_glyphs: u32 },
Format1 { bytes: &'a [u8], num_glyphs: u32 },
Format2 { bytes: &'a [u8], num_glyphs: u32 },
}
impl<'a> Charset<'a> {
pub(crate) fn parse(bytes: &'a [u8], top_off: i32, num_glyphs: u32) -> Result<Self, Error> {
match top_off {
0 => Ok(Self::IsoAdobe),
1 | 2 => Err(Error::Cff(
"predefined Expert charset not implemented in round 1",
)),
n if n < 0 => Err(Error::Cff("negative charset offset")),
n => {
let off = n as usize;
if off >= bytes.len() {
return Err(Error::UnexpectedEof);
}
let format = read_u8(bytes, off)?;
let payload = &bytes[off + 1..];
match format {
0 => Ok(Self::Format0 {
bytes: payload,
num_glyphs,
}),
1 => Ok(Self::Format1 {
bytes: payload,
num_glyphs,
}),
2 => Ok(Self::Format2 {
bytes: payload,
num_glyphs,
}),
_ => Err(Error::Cff("unknown charset format")),
}
}
}
}
pub(crate) fn sid_of(&self, gid: u16) -> Option<u16> {
if gid == 0 {
return Some(0); }
match self {
Self::IsoAdobe => {
if (gid as usize) < 229 {
Some(gid)
} else {
None
}
}
Self::Format0 { bytes, num_glyphs } => {
if (gid as u32) >= *num_glyphs {
return None;
}
let off = (gid as usize - 1) * 2;
read_u16(bytes, off).ok()
}
Self::Format1 { bytes, num_glyphs } => walk_runs(bytes, *num_glyphs, gid, 1),
Self::Format2 { bytes, num_glyphs } => walk_runs(bytes, *num_glyphs, gid, 2),
}
}
}
fn walk_runs(bytes: &[u8], num_glyphs: u32, target_gid: u16, n_left_size: usize) -> Option<u16> {
let mut gid: u32 = 1;
let mut off: usize = 0;
while gid < num_glyphs {
let first = read_u16(bytes, off).ok()?;
off += 2;
let n_left: u32 = match n_left_size {
1 => read_u8(bytes, off).ok()? as u32,
2 => read_u16(bytes, off).ok()? as u32,
_ => unreachable!(),
};
off += n_left_size;
let run_end = gid + n_left;
if (target_gid as u32) >= gid && (target_gid as u32) <= run_end {
let in_run = (target_gid as u32) - gid;
let sid = first as u32 + in_run;
if sid > u16::MAX as u32 {
return None;
}
return Some(sid as u16);
}
gid = run_end + 1;
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iso_adobe_identity() {
let cs = Charset::IsoAdobe;
assert_eq!(cs.sid_of(0), Some(0));
assert_eq!(cs.sid_of(1), Some(1));
assert_eq!(cs.sid_of(228), Some(228));
assert_eq!(cs.sid_of(229), None);
}
#[test]
fn format0_walk() {
let payload = vec![0x00, 100, 0x00, 200, 0x01, 0x2C];
let cs = Charset::Format0 {
bytes: &payload,
num_glyphs: 4,
};
assert_eq!(cs.sid_of(1), Some(100));
assert_eq!(cs.sid_of(2), Some(200));
assert_eq!(cs.sid_of(3), Some(300));
assert_eq!(cs.sid_of(4), None);
}
#[test]
fn format1_walk() {
let payload = vec![0x00, 50, 0x02, 0x00, 70, 0x01];
let cs = Charset::Format1 {
bytes: &payload,
num_glyphs: 6,
};
assert_eq!(cs.sid_of(1), Some(50));
assert_eq!(cs.sid_of(2), Some(51));
assert_eq!(cs.sid_of(3), Some(52));
assert_eq!(cs.sid_of(4), Some(70));
assert_eq!(cs.sid_of(5), Some(71));
}
#[test]
fn parse_via_offset_dispatch_format0() {
let mut table = vec![0u8, 0, 0, 0]; table.push(0); table.extend_from_slice(&[0, 100, 0, 200]); let cs = Charset::parse(&table, 4, 3).expect("parse");
assert!(matches!(cs, Charset::Format0 { .. }));
assert_eq!(cs.sid_of(1), Some(100));
assert_eq!(cs.sid_of(2), Some(200));
}
}