use crate::cff::charset::Charset;
use crate::cff::strings::{glyph_name_to_codepoint, Strings};
use crate::parser::{read_u16, read_u8};
use crate::Error;
#[derive(Debug, Clone)]
pub(crate) enum Encoding<'a> {
Standard,
Expert,
Format0 {
codes: &'a [u8],
},
#[allow(dead_code)]
Format1 {
runs: &'a [u8],
},
}
impl<'a> Encoding<'a> {
pub(crate) fn parse(bytes: &'a [u8], top_off: i32) -> Result<Self, Error> {
match top_off {
0 => Ok(Self::Standard),
1 => Ok(Self::Expert),
n if n < 0 => Err(Error::Cff("negative encoding offset")),
n => {
let off = n as usize;
if off >= bytes.len() {
return Err(Error::UnexpectedEof);
}
let format_byte = read_u8(bytes, off)?;
let format = format_byte & 0x7f;
let after = off + 1;
match format {
0 => {
let n_codes = read_u8(bytes, after)? as usize;
let payload = bytes
.get(after + 1..after + 1 + n_codes)
.ok_or(Error::UnexpectedEof)?;
Ok(Self::Format0 { codes: payload })
}
1 => {
let n_ranges = read_u8(bytes, after)? as usize;
let runs = bytes
.get(after + 1..after + 1 + n_ranges * 2)
.ok_or(Error::UnexpectedEof)?;
Ok(Self::Format1 { runs })
}
_ => Err(Error::Cff("unknown Encoding format")),
}
}
}
}
pub(crate) fn lookup(
&self,
code: u8,
charset: &Charset<'_>,
strings: &Strings<'_>,
) -> Option<u16> {
match self {
Self::Standard | Self::Expert => {
let _ = (code, charset, strings);
None
}
Self::Format0 { codes } => {
for (i, &c) in codes.iter().enumerate() {
if c == code {
return Some(i as u16 + 1);
}
}
None
}
Self::Format1 { runs } => {
let mut gid: u16 = 1;
let mut off = 0;
while off + 1 < runs.len() {
let first = runs[off];
let n_left = runs[off + 1];
off += 2;
let last = first.saturating_add(n_left);
if code >= first && code <= last {
return Some(gid + (code - first) as u16);
}
gid = gid.saturating_add(n_left as u16 + 1);
}
None
}
}
}
}
#[allow(dead_code)]
fn _unused() {
let _ = (
read_u16 as fn(&[u8], usize) -> _,
glyph_name_to_codepoint as fn(&str) -> _,
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cff::charset::Charset;
use crate::cff::index::Index;
#[test]
fn format0_lookup() {
let mut table = vec![0u8; 4]; table.push(0); table.push(2); table.push(65);
table.push(66);
let enc = Encoding::parse(&table, 4).unwrap();
let charset = Charset::IsoAdobe;
let custom = Index::parse(&[0u8, 0], 0).unwrap();
let strings = Strings::new(custom);
assert_eq!(enc.lookup(65, &charset, &strings), Some(1));
assert_eq!(enc.lookup(66, &charset, &strings), Some(2));
assert_eq!(enc.lookup(67, &charset, &strings), None);
}
#[test]
fn format1_run_lookup() {
let mut table = vec![0u8; 2];
table.push(1); table.push(1); table.push(65);
table.push(2);
let enc = Encoding::parse(&table, 2).unwrap();
let charset = Charset::IsoAdobe;
let custom = Index::parse(&[0u8, 0], 0).unwrap();
let strings = Strings::new(custom);
assert_eq!(enc.lookup(65, &charset, &strings), Some(1));
assert_eq!(enc.lookup(66, &charset, &strings), Some(2));
assert_eq!(enc.lookup(67, &charset, &strings), Some(3));
assert_eq!(enc.lookup(68, &charset, &strings), None);
}
}