mod predefined;
pub use predefined::{cid_to_unicode, predefined, CidToUnicode};
use crate::document::decoded_stream_data_with;
use crate::hash::FastMap;
use crate::lexer::{decode_hex, decode_hex_fixed, Lexer, RawToken, Token};
use crate::object::{Dict, Object, Stream};
use crate::source::AsyncObjectSource;
use std::sync::Arc;
#[derive(Clone, Copy)]
struct Codespace {
len: u8,
lo: [u8; 4],
hi: [u8; 4],
}
impl Codespace {
fn contains(&self, code: &[u8]) -> bool {
code.iter()
.zip(self.lo.iter().zip(&self.hi))
.all(|(&b, (&lo, &hi))| lo <= b && b <= hi)
}
}
#[derive(Clone, Copy)]
struct CidRange {
len: u8,
lo: u32,
hi: u32,
cid: u32,
}
pub struct CidCmap {
wmode: u8,
codespaces: Vec<Codespace>,
singles: FastMap<(u8, u32), u32>,
ranges: Vec<CidRange>,
notdefs: Vec<CidRange>,
parent: Option<Arc<CidCmap>>,
}
fn code_value(bytes: &[u8]) -> u32 {
bytes.iter().fold(0u32, |acc, &b| (acc << 8) | u32::from(b))
}
fn covering_range(ranges: &[CidRange], code: u32, len: u8) -> Option<&CidRange> {
let idx = ranges.partition_point(|r| (r.len, r.lo) <= (len, code));
let r = ranges.get(idx.checked_sub(1)?)?;
(r.len == len && code <= r.hi).then_some(r)
}
impl CidCmap {
pub fn identity(vertical: bool) -> CidCmap {
CidCmap {
wmode: u8::from(vertical),
codespaces: vec![Codespace {
len: 2,
lo: [0; 4],
hi: [0xFF; 4],
}],
singles: FastMap::default(),
ranges: vec![CidRange {
len: 2,
lo: 0,
hi: 0xFFFF,
cid: 0,
}],
notdefs: Vec::new(),
parent: None,
}
}
pub fn parse(data: &[u8]) -> CidCmap {
CidCmap::parse_with(data, None, &mut |_| None)
}
pub fn parse_with(
data: &[u8],
parent: Option<Arc<CidCmap>>,
resolve: &mut dyn FnMut(&str) -> Option<Arc<CidCmap>>,
) -> CidCmap {
let mut out = CidCmap {
wmode: 0,
codespaces: Vec::new(),
singles: FastMap::default(),
ranges: Vec::new(),
notdefs: Vec::new(),
parent,
};
let mut lx = Lexer::new(data);
let mut pending_name: Option<String> = None;
let mut wmode_pending = false;
loop {
match next_or_skip(&mut lx, data.len()) {
None => break,
Some(RawToken::Keyword(kw)) => {
match kw {
b"begincodespacerange" => out.parse_codespaces(&mut lx, data.len()),
b"begincidchar" => out.parse_cidchars(&mut lx, data.len(), false),
b"begincidrange" => out.parse_cidranges(&mut lx, data.len(), false),
b"beginnotdefchar" => out.parse_cidchars(&mut lx, data.len(), true),
b"beginnotdefrange" => out.parse_cidranges(&mut lx, data.len(), true),
b"usecmap" => {
if let (None, Some(name)) = (&out.parent, pending_name.take()) {
out.parent = resolve(&name);
}
}
_ => {}
}
pending_name = None;
wmode_pending = false;
}
Some(RawToken::Owned(Token::Name(n))) => {
wmode_pending = n.0 == "WMode";
pending_name = (!wmode_pending).then_some(n.0);
}
Some(RawToken::Owned(Token::Int(i))) => {
if wmode_pending {
out.wmode = u8::from(i == 1);
}
wmode_pending = false;
}
Some(_) => {
pending_name = None;
wmode_pending = false;
}
}
}
out.finish();
out
}
fn finish(&mut self) {
if let Some(parent) = &self.parent {
self.codespaces.extend_from_slice(&parent.codespaces);
}
self.codespaces.sort_by_key(|c| c.len);
self.ranges.sort_by_key(|r| (r.len, r.lo));
self.notdefs.sort_by_key(|r| (r.len, r.lo));
}
pub fn is_empty(&self) -> bool {
self.singles.is_empty() && self.ranges.is_empty() && self.parent.is_none()
}
pub fn vertical(&self) -> bool {
self.wmode == 1
}
pub fn parent(&self) -> Option<&Arc<CidCmap>> {
self.parent.as_ref()
}
pub fn single_byte(&self, b: u8) -> bool {
self.codespaces
.iter()
.any(|c| c.len == 1 && c.contains(&[b]))
}
pub fn code_at(&self, bytes: &[u8], pos: usize) -> (u32, u8) {
let rest = &bytes[pos..];
if self.codespaces.is_empty() {
let n = rest.len().min(2);
return (code_value(&rest[..n]), n as u8);
}
for cs in &self.codespaces {
let n = usize::from(cs.len);
if rest.len() >= n && cs.contains(&rest[..n]) {
return (code_value(&rest[..n]), cs.len);
}
}
for cs in &self.codespaces {
if cs.lo[0] <= rest[0] && rest[0] <= cs.hi[0] {
let n = usize::from(cs.len).min(rest.len());
return (code_value(&rest[..n]), n as u8);
}
}
(u32::from(rest[0]), 1)
}
pub fn cid(&self, code: u32, len: u8) -> Option<u32> {
self.mapped(code, len).or_else(|| self.notdef(code, len))
}
fn mapped(&self, code: u32, len: u8) -> Option<u32> {
if let Some(&cid) = self.singles.get(&(len, code)) {
return Some(cid);
}
if let Some(r) = covering_range(&self.ranges, code, len) {
return Some(r.cid.saturating_add(code - r.lo));
}
self.parent.as_ref()?.mapped(code, len)
}
fn notdef(&self, code: u32, len: u8) -> Option<u32> {
covering_range(&self.notdefs, code, len)
.map(|r| r.cid)
.or_else(|| self.parent.as_ref()?.notdef(code, len))
}
fn mappings(&self, push: &mut impl FnMut(u8, u32, u32, u32)) {
let mut singles: Vec<(u8, u32, u32)> = self
.singles
.iter()
.map(|(&(len, code), &cid)| (len, code, cid))
.collect();
singles.sort_unstable();
let mut singles = singles.into_iter().peekable();
let mut ranges = self.ranges.iter().peekable();
loop {
let single_first = match (singles.peek(), ranges.peek()) {
(None, None) => break,
(Some(_), None) => true,
(None, Some(_)) => false,
(Some(&(len, code, _)), Some(r)) => (len, code) <= (r.len, r.lo),
};
if single_first {
let (len, code, cid) = singles.next().unwrap();
push(len, code, code, cid);
} else {
let r = ranges.next().unwrap();
push(r.len, r.lo, r.hi, r.cid);
}
}
if let Some(parent) = &self.parent {
parent.mappings(push);
}
}
fn parse_codespaces(&mut self, lx: &mut Lexer<'_>, len: usize) {
loop {
let lo = match next_or_skip(lx, len) {
Some(RawToken::Hex(span)) => span,
Some(_) | None => return, };
let Some(RawToken::Hex(hi)) = next_or_skip(lx, len) else {
return;
};
let (Some((lo, lo_len)), Some((hi, hi_len))) =
(decode_hex_fixed::<4>(lo), decode_hex_fixed::<4>(hi))
else {
continue;
};
if lo_len == 0 || hi_len != lo_len {
continue;
}
self.codespaces.push(Codespace {
len: lo_len as u8,
lo,
hi,
});
}
}
fn parse_cidchars(&mut self, lx: &mut Lexer<'_>, len: usize, notdef: bool) {
loop {
let code = match next_or_skip(lx, len) {
Some(RawToken::Hex(span)) => span,
Some(_) | None => return,
};
let Some(RawToken::Owned(Token::Int(cid))) = next_or_skip(lx, len) else {
return;
};
let Some((code, code_len)) = decode_hex_fixed::<4>(code) else {
continue;
};
if code_len == 0 {
continue;
}
let (value, width) = (code_value(&code[..code_len]), code_len as u8);
let cid = cid.max(0) as u32;
if notdef {
self.notdefs.push(CidRange {
len: width,
lo: value,
hi: value,
cid,
});
} else {
self.singles.insert((width, value), cid);
}
}
}
fn parse_cidranges(&mut self, lx: &mut Lexer<'_>, len: usize, notdef: bool) {
loop {
let lo = match next_or_skip(lx, len) {
Some(RawToken::Hex(span)) => span,
Some(_) | None => return,
};
let Some(RawToken::Hex(hi)) = next_or_skip(lx, len) else {
return;
};
let Some(RawToken::Owned(Token::Int(cid))) = next_or_skip(lx, len) else {
return;
};
let Some((lo, lo_len)) = decode_hex_fixed::<4>(lo) else {
continue;
};
if lo_len == 0 {
continue;
}
let hi_v = match decode_hex_fixed::<4>(hi) {
Some((hi, hi_len)) => code_value(&hi[..hi_len]),
None => code_value(&decode_hex(hi)),
};
let lo_v = code_value(&lo[..lo_len]);
if hi_v < lo_v {
continue;
}
let range = CidRange {
len: lo_len as u8,
lo: lo_v,
hi: hi_v,
cid: cid.max(0) as u32,
};
if notdef {
self.notdefs.push(range);
} else {
self.ranges.push(range);
}
}
}
}
pub struct Type0Encoding {
pub cmap: Option<Arc<CidCmap>>,
pub vertical: bool,
pub known: bool,
}
async fn rv<S: AsyncObjectSource>(src: &S, dict: &Dict, key: &str) -> Option<Object> {
let obj = dict.get(key)?;
let resolved = src.resolve(obj).await.ok()?;
(!resolved.is_null()).then_some(resolved)
}
pub async fn type0_encoding<S: AsyncObjectSource>(src: &S, font: &Dict) -> Type0Encoding {
let identity = |vertical: bool, known: bool| Type0Encoding {
cmap: None,
vertical,
known,
};
let Some(enc) = rv(src, font, "Encoding").await else {
return identity(false, false);
};
match enc {
Object::Name(n) if n.0 == "Identity-H" => identity(false, true),
Object::Name(n) if n.0 == "Identity-V" => identity(true, true),
Object::Name(n) => match predefined(&n.0) {
Some(cmap) => Type0Encoding {
vertical: cmap.vertical(),
cmap: Some(cmap),
known: true,
},
None => identity(n.0.ends_with("-V"), false),
},
Object::Stream(stream) => match embedded_cmap(src, &stream).await {
Some(cmap) => Type0Encoding {
vertical: cmap.vertical(),
cmap: Some(cmap),
known: true,
},
None => identity(false, false),
},
_ => identity(false, false),
}
}
async fn embedded_cmap<S: AsyncObjectSource>(src: &S, stream: &Stream) -> Option<Arc<CidCmap>> {
let mut layers: Vec<(Vec<u8>, Option<i64>)> = Vec::new();
let mut parent: Option<Arc<CidCmap>> = None;
let mut current = stream.clone();
for _ in 0..4 {
let data = decoded_stream_data_with(src, ¤t).await.ok()?;
let wmode = rv(src, ¤t.dict, "WMode")
.await
.and_then(|o| o.as_int());
layers.push((data, wmode));
match rv(src, ¤t.dict, "UseCMap").await {
Some(Object::Name(n)) => {
parent = predefined(&n.0);
break;
}
Some(Object::Stream(s)) => current = s,
_ => break,
}
}
for (data, wmode) in layers.into_iter().rev() {
let mut resolve = |n: &str| predefined(n);
let mut cmap = CidCmap::parse_with(&data, parent.take(), &mut resolve);
if let Some(w) = wmode {
cmap.wmode = u8::from(w == 1);
}
parent = Some(Arc::new(cmap));
}
parent.filter(|c| !c.is_empty())
}
fn next_or_skip<'a>(lx: &mut Lexer<'a>, len: usize) -> Option<RawToken<'a>> {
loop {
let before = lx.pos();
match lx.next_raw_token() {
Ok(RawToken::Owned(Token::Eof)) => return None,
Ok(t) => return Some(t),
Err(_) => {
if lx.pos() <= before {
if before + 1 >= len {
return None;
}
lx.seek(before + 1);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const RKSJ_CODESPACES: &str = "4 begincodespacerange\n\
<00> <80>\n\
<8140> <9FFC>\n\
<A0> <DF>\n\
<E040> <FCFC>\n\
endcodespacerange\n";
fn rksj() -> CidCmap {
let data = format!(
"{RKSJ_CODESPACES}\
1 beginnotdefrange\n<00> <1f> 231\nendnotdefrange\n\
3 begincidrange\n\
<20> <7d> 231\n\
<8140> <817e> 633\n\
<e040> <e07e> 100\n\
endcidrange\n\
1 begincidchar\n<a1> 9000\nendcidchar\n"
);
CidCmap::parse(data.as_bytes())
}
#[test]
fn rksj_codespaces_split_mixed_widths() {
let c = rksj();
let bytes = [0x41, 0x81, 0x40, 0xA1];
assert_eq!(c.code_at(&bytes, 0), (0x41, 1));
assert_eq!(c.code_at(&bytes, 1), (0x8140, 2));
assert_eq!(c.code_at(&bytes, 3), (0xA1, 1));
}
#[test]
fn cidrange_arithmetic_offsets_within_the_range() {
let c = rksj();
assert_eq!(c.cid(0x20, 1), Some(231));
assert_eq!(c.cid(0x7d, 1), Some(324));
assert_eq!(c.cid(0x8140, 2), Some(633));
assert_eq!(c.cid(0x8163, 2), Some(633 + 0x23));
assert_eq!(c.cid(0xe041, 2), Some(101));
assert_eq!(c.cid(0x82FF, 2), None);
}
#[test]
fn cidchar_singletons_map() {
let c = rksj();
assert_eq!(c.cid(0xA1, 1), Some(9000));
}
#[test]
fn notdef_ranges_lose_to_real_mappings() {
let data = format!(
"{RKSJ_CODESPACES}\
1 beginnotdefrange\n<00> <1f> 231\nendnotdefrange\n\
1 begincidrange\n<10> <11> 5\nendcidrange\n"
);
let c = CidCmap::parse(data.as_bytes());
assert_eq!(c.cid(0x10, 1), Some(5)); assert_eq!(c.cid(0x12, 1), Some(231)); assert_eq!(c.cid(0x20, 1), None);
}
#[test]
fn a_one_byte_code_and_a_two_byte_code_with_equal_values_stay_apart() {
let data = "2 begincodespacerange <00> <20> <4000> <41FF> endcodespacerange\n\
2 begincidrange <20> <20> 7 <0020> <0020> 9 endcidrange";
let c = CidCmap::parse(data.as_bytes());
assert_eq!(c.cid(0x20, 1), Some(7));
assert_eq!(c.cid(0x20, 2), Some(9));
}
#[test]
fn usecmap_layers_child_over_parent() {
let parent = Arc::new(CidCmap::parse(
format!(
"{RKSJ_CODESPACES}\
2 begincidrange <8140> <817e> 633 <20> <7d> 231 endcidrange"
)
.as_bytes(),
));
let mut resolve = |name: &str| (name == "90ms-RKSJ-H").then(|| Arc::clone(&parent));
let child = CidCmap::parse_with(
b"/90ms-RKSJ-H usecmap\n\
/WMode 1 def\n\
1 begincidrange <8141> <8142> 7887 endcidrange",
None,
&mut resolve,
);
assert!(child.vertical());
assert_eq!(child.cid(0x8141, 2), Some(7887)); assert_eq!(child.cid(0x8140, 2), Some(633)); assert_eq!(child.cid(0x21, 1), Some(232)); assert_eq!(child.code_at(&[0x81, 0x40], 0), (0x8140, 2)); assert_eq!(child.parent().map(|p| p.cid(0x8141, 2)), Some(Some(634)));
}
#[test]
fn wmode_reads_and_defaults_horizontal() {
assert!(!CidCmap::parse(b"/WMode 0 def").vertical());
assert!(CidCmap::parse(b"/WMode 1 def").vertical());
assert!(!CidCmap::parse(b"").vertical());
assert!(CidCmap::identity(true).vertical());
}
#[test]
fn identity_maps_code_to_cid() {
let c = CidCmap::identity(false);
assert_eq!(c.code_at(&[0x12, 0x34], 0), (0x1234, 2));
assert_eq!(c.cid(0x1234, 2), Some(0x1234));
assert!(!c.single_byte(0x20));
}
#[test]
fn word_spacing_evidence_is_a_one_byte_codespace() {
assert!(rksj().single_byte(0x20));
assert!(!rksj().single_byte(0x81));
}
#[test]
fn splitting_always_consumes_at_least_one_byte() {
let cmaps = [rksj(), CidCmap::identity(false), CidCmap::parse(b"")];
for c in &cmaps {
for bytes in [&[0x81][..], &[0xFF][..], &[0x00, 0x81][..]] {
let mut pos = 0;
let mut codes = 0;
while pos < bytes.len() {
let (_, n) = c.code_at(bytes, pos);
assert!(n >= 1);
pos += usize::from(n).min(bytes.len() - pos);
codes += 1;
}
assert!(codes >= 1);
}
}
assert_eq!(rksj().code_at(&[0x81], 0), (0x81, 1));
}
#[test]
fn a_truncated_section_keeps_what_parsed_so_far() {
let c = CidCmap::parse(b"2 begincidrange <20> <7d> 231 <8140> <81");
assert_eq!(c.cid(0x20, 1), Some(231));
assert_eq!(c.cid(0x8140, 2), None);
let garbage = CidCmap::parse(b"\xFF\xFE ) ] >> begincidchar <41> 12 endcidchar");
assert_eq!(garbage.cid(0x41, 1), Some(12));
assert!(CidCmap::parse(b"").is_empty());
}
}