use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use pith_digest::{Error, Result};
use crate::lex::{Lexer, Tok};
#[derive(Clone, Debug)]
pub struct CMap {
map: BTreeMap<u64, String>,
lens: Vec<usize>,
wmode: u8,
}
fn key(code: u64, len: usize) -> u64 {
((len as u64) << 56) | (code & ((1u64 << 56) - 1))
}
pub(crate) fn code_of_pub(bytes: &[u8]) -> u64 {
let mut v = 0u64;
for &b in bytes {
v = (v << 8) | u64::from(b);
}
v
}
fn utf16be(raw: &[u8]) -> Result<String> {
if raw.len() % 2 != 0 {
return Err(Error::BadValue("ToUnicode destination bytes"));
}
let units: Vec<u16> = raw
.chunks_exact(2)
.map(|p| u16::from_be_bytes([p[0], p[1]]))
.collect();
Ok(char::decode_utf16(units)
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect())
}
impl CMap {
pub fn parse(data: &[u8]) -> Result<CMap> {
let mut lx = Lexer::new(data);
let mut map = BTreeMap::new();
let mut spaces: Vec<(u64, u64, usize)> = Vec::new();
let mut wmode: u8 = 0;
let mut stack: Vec<Tok> = Vec::new();
while let Some(t) = lx.next()? {
match t {
Tok::Str(_) | Tok::Num(_) | Tok::Name(_) | Tok::ArrOpen | Tok::ArrClose => {
if matches!(t, Tok::ArrOpen) {
let mut arr: Vec<Vec<u8>> = Vec::new();
loop {
match lx.next()? {
Some(Tok::ArrClose) => break,
Some(Tok::Str(s)) => arr.push(s.into_owned()),
Some(_) => return Err(Error::BadValue("bfrange array")),
None => {
return Err(Error::Truncated {
what: "bfrange array",
needed: 1,
found: 0,
});
}
}
}
stack.push(Tok::ArrOpen);
for s in arr {
stack.push(Tok::Str(alloc::borrow::Cow::Owned(s)));
}
stack.push(Tok::ArrClose);
} else {
stack.push(t);
}
if stack.len() > 4096 {
return Err(Error::BadValue("cmap operand stack"));
}
}
Tok::DictOpen | Tok::DictClose => {}
Tok::Kw(k) => {
match &*k {
b"begincodespacerange" => {
let n = pop_count(&mut stack)?;
for _ in 0..n {
let (lo, hi) = take2(&mut lx, "codespacerange")?;
if lo.len() != hi.len() || lo.len() > 7 {
return Err(Error::BadValue("codespacerange"));
}
spaces.push((code_of_pub(&lo), code_of_pub(&hi), lo.len()));
}
}
b"beginbfchar" => {
let n = pop_count(&mut stack)?;
for _ in 0..n {
let (src, dst) = take2(&mut lx, "bfchar")?;
add_bfchar(&mut map, &src, &dst)?;
}
}
b"beginbfrange" => {
let n = pop_count(&mut stack)?;
for _ in 0..n {
read_bfrange(&mut lx, &mut map)?;
}
}
b"begincidchar" | b"begincidrange" | b"beginnotdefchar"
| b"beginnotdefrange" => {
let n = pop_count(&mut stack)?;
skip_cid_pairs(&mut lx, n)?;
}
b"def" => {
if stack.len() >= 2 {
if let (Tok::Name(name), Tok::Num(v)) =
(&stack[stack.len() - 2], &stack[stack.len() - 1])
{
if name.as_ref() == b"WMode"
&& crate::lex::is_int(*v)
&& *v >= 0.0
&& *v <= 1.0
{
wmode = *v as u8;
}
}
}
stack.clear();
}
_ => stack.clear(),
}
}
}
}
if map.is_empty() && spaces.is_empty() {
return Err(Error::BadValue("CMap has no mappings"));
}
let mut lens: Vec<usize> = spaces.iter().map(|&(_, _, l)| l).collect();
for &k in map.keys() {
let l = (k >> 56) as usize;
if !lens.contains(&l) {
lens.push(l);
}
}
lens.sort_unstable_by(|a, b| b.cmp(a));
Ok(CMap { map, lens, wmode })
}
pub fn is_vertical(&self) -> bool {
self.wmode == 1
}
pub(crate) fn get(&self, code: u64, len: usize) -> Option<&str> {
self.map.get(&key(code, len)).map(String::as_str)
}
pub(crate) fn lens(&self) -> &[usize] {
&self.lens
}
pub fn lookup(&self, bytes: &[u8]) -> (Option<&str>, usize) {
for &l in &self.lens {
if l <= bytes.len() {
let c = code_of_pub(&bytes[..l]);
if let Some(s) = self.map.get(&key(c, l)) {
return (Some(s.as_str()), l);
}
}
}
(None, *self.lens.last().unwrap_or(&1))
}
}
fn pop_count(stack: &mut Vec<Tok>) -> Result<i64> {
match stack.pop() {
Some(Tok::Num(v)) => crate::lex::f64_as_i64(v)
.filter(|&n| (0..=1_000_000).contains(&n))
.ok_or(Error::BadValue("cmap section count")),
_ => Err(Error::BadValue("cmap section count")),
}
}
fn take2(lx: &mut Lexer, what: &'static str) -> Result<(Vec<u8>, Vec<u8>)> {
let a = take_str(lx, what)?;
let b = take_str(lx, what)?;
Ok((a, b))
}
fn take_str(lx: &mut Lexer, what: &'static str) -> Result<Vec<u8>> {
match lx.next()? {
Some(Tok::Str(s)) => Ok(s.into_owned()),
Some(Tok::Name(n)) => Ok(n.into_owned()),
_ => Err(Error::BadValue(what)),
}
}
fn add_bfchar(map: &mut BTreeMap<u64, String>, src: &[u8], dst: &[u8]) -> Result<()> {
if src.is_empty() || src.len() > 7 {
return Err(Error::BadValue("bfchar source"));
}
if dst.is_empty() {
return Err(Error::BadValue("bfchar destination"));
}
map.insert(key(code_of_pub(src), src.len()), utf16be(dst)?);
Ok(())
}
fn read_bfrange(lx: &mut Lexer, map: &mut BTreeMap<u64, String>) -> Result<()> {
let lo = take_str(lx, "bfrange lo")?;
let hi = take_str(lx, "bfrange hi")?;
if lo.len() != hi.len() || lo.is_empty() || lo.len() > 7 {
return Err(Error::BadValue("bfrange source"));
}
let lo_v = code_of_pub(&lo);
let hi_v = code_of_pub(&hi);
if hi_v < lo_v {
return Err(Error::BadValue("bfrange range"));
}
let span = hi_v - lo_v;
if span > 65536 {
return Err(Error::TooLarge {
what: "bfrange span",
limit: 65536,
});
}
match lx.next()? {
Some(Tok::Str(dst0)) => {
let units = utf16_units(&dst0)?;
for i in 0..=span {
let dst = inc_units(&units, i);
let s: String = char::decode_utf16(dst)
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect();
map.insert(key(lo_v + i, lo.len()), s);
}
Ok(())
}
Some(Tok::ArrOpen) => {
let mut i = 0u64;
loop {
match lx.next()? {
Some(Tok::ArrClose) => break,
Some(Tok::Str(d)) => {
if i > span {
return Err(Error::BadValue("bfrange array length"));
}
map.insert(key(lo_v + i, lo.len()), utf16be(&d)?);
i += 1;
}
Some(_) => return Err(Error::BadValue("bfrange array")),
None => {
return Err(Error::Truncated {
what: "bfrange array",
needed: 1,
found: 0,
});
}
}
}
if i != span + 1 {
return Err(Error::BadValue("bfrange array length"));
}
Ok(())
}
_ => Err(Error::BadValue("bfrange destination")),
}
}
fn utf16_units(raw: &[u8]) -> Result<Vec<u16>> {
if raw.len() % 2 != 0 || raw.is_empty() {
return Err(Error::BadValue("bfrange destination bytes"));
}
Ok(raw
.chunks_exact(2)
.map(|p| u16::from_be_bytes([p[0], p[1]]))
.collect())
}
fn inc_units(dst: &[u16], i: u64) -> Vec<u16> {
let mut out = dst.to_vec();
let mut carry = i;
for u in out.iter_mut().rev() {
if carry == 0 {
break;
}
let v = u64::from(*u) + carry;
*u = (v & 0xFFFF) as u16;
carry = v >> 16;
}
out
}
fn skip_cid_pairs(lx: &mut Lexer, n: i64) -> Result<()> {
for _ in 0..n {
let _ = lx.next()?.ok_or(Error::Truncated {
what: "cid section",
needed: 1,
found: 0,
})?;
match lx.next()? {
Some(Tok::ArrOpen) => loop {
match lx.next()? {
Some(Tok::ArrClose) => break,
Some(_) => {}
None => {
return Err(Error::Truncated {
what: "cidrange array",
needed: 1,
found: 0,
});
}
}
},
Some(_) => {}
None => {
return Err(Error::Truncated {
what: "cid section",
needed: 1,
found: 0,
});
}
}
}
Ok(())
}