1use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
11pub struct CMap {
12 codespace: Vec<(usize, u32, u32)>,
14 single: HashMap<u32, u32>,
15 ranges: Vec<(u32, u32, u32)>,
17 pub identity: bool,
18 pub unicode_codes: bool,
20 pub vertical: bool,
21}
22
23impl CMap {
24 pub fn identity_h() -> CMap {
25 CMap {
26 codespace: vec![(2, 0, 0xFFFF)],
27 single: HashMap::new(),
28 ranges: Vec::new(),
29 identity: true,
30 unicode_codes: false,
31 vertical: false,
32 }
33 }
34
35 pub fn predefined(name: &[u8]) -> CMap {
37 let s = String::from_utf8_lossy(name);
38 let vertical = s.ends_with("-V");
39 let mut c = CMap::identity_h();
40 c.vertical = vertical;
41 if s.starts_with("Identity") {
42 return c;
43 }
44 c.identity = false;
47 c.unicode_codes = s.contains("UCS2") || s.contains("UTF16");
48 c
49 }
50
51 pub fn parse(data: &[u8]) -> CMap {
53 let mut c = CMap {
54 codespace: Vec::new(),
55 single: HashMap::new(),
56 ranges: Vec::new(),
57 identity: false,
58 unicode_codes: false,
59 vertical: false,
60 };
61 let toks = tokenize(data);
62 let mut i = 0;
63 while i < toks.len() {
64 match &toks[i] {
65 Tok::Kw(k) if k == "begincodespacerange" => {
66 i += 1;
67 while i + 1 < toks.len() {
68 match (&toks[i], &toks[i + 1]) {
69 (Tok::Hex(lo), Tok::Hex(hi)) => {
70 let n = lo.len().clamp(1, 4);
71 c.codespace.push((n, be(lo), be(hi)));
72 i += 2;
73 }
74 _ => break,
75 }
76 }
77 }
78 Tok::Kw(k) if k == "begincidrange" => {
79 i += 1;
80 while i + 2 < toks.len() {
81 match (&toks[i], &toks[i + 1], &toks[i + 2]) {
82 (Tok::Hex(lo), Tok::Hex(hi), Tok::Num(cid)) => {
83 c.ranges.push((be(lo), be(hi), *cid as u32));
84 if c.codespace.is_empty() {
85 c.codespace.push((lo.len().clamp(1, 4), 0, u32::MAX));
86 }
87 i += 3;
88 }
89 _ => break,
90 }
91 }
92 }
93 Tok::Kw(k) if k == "begincidchar" => {
94 i += 1;
95 while i + 1 < toks.len() {
96 match (&toks[i], &toks[i + 1]) {
97 (Tok::Hex(code), Tok::Num(cid)) => {
98 c.single.insert(be(code), *cid as u32);
99 if c.codespace.is_empty() {
100 c.codespace.push((code.len().clamp(1, 4), 0, u32::MAX));
101 }
102 i += 2;
103 }
104 _ => break,
105 }
106 }
107 }
108 Tok::Kw(k) if k == "usecmap" => {
109 if let Some(Tok::Name(n)) = toks.get(i.wrapping_sub(1)) {
111 if n.starts_with("Identity") {
112 c.identity = c.single.is_empty() && c.ranges.is_empty();
113 if c.codespace.is_empty() {
114 c.codespace.push((2, 0, 0xFFFF));
115 }
116 }
117 }
118 i += 1;
119 }
120 Tok::Kw(k) if k == "def" => {
121 if let (Some(Tok::Name(n)), Some(Tok::Num(v))) =
122 (toks.get(i.wrapping_sub(2)), toks.get(i.wrapping_sub(1)))
123 {
124 if n == "WMode" && *v == 1.0 {
125 c.vertical = true;
126 }
127 }
128 i += 1;
129 }
130 _ => i += 1,
131 }
132 }
133 if c.codespace.is_empty() {
134 c.codespace.push((2, 0, 0xFFFF));
135 }
136 c.codespace.sort_by_key(|r| r.0);
139 c
140 }
141
142 pub fn split(&self, bytes: &[u8]) -> Vec<(u32, usize)> {
144 let mut out = Vec::with_capacity(bytes.len() / 2 + 1);
145 let mut i = 0;
146 while i < bytes.len() {
147 let mut taken = None;
148 for &(n, lo, hi) in &self.codespace {
149 if i + n > bytes.len() {
150 continue;
151 }
152 let code = bytes[i..i + n]
153 .iter()
154 .fold(0u32, |a, &b| (a << 8) | u32::from(b));
155 if code >= lo && code <= hi {
156 taken = Some((code, n));
157 break;
158 }
159 }
160 let (code, n) = taken.unwrap_or_else(|| {
161 let n = self
163 .codespace
164 .first()
165 .map(|r| r.0)
166 .unwrap_or(1)
167 .min(bytes.len() - i);
168 let code = bytes[i..i + n]
169 .iter()
170 .fold(0u32, |a, &b| (a << 8) | u32::from(b));
171 (code, n)
172 });
173 out.push((code, n));
174 i += n;
175 }
176 out
177 }
178
179 pub fn cid(&self, code: u32) -> u32 {
180 if self.identity {
181 return code;
182 }
183 if let Some(c) = self.single.get(&code) {
184 return *c;
185 }
186 for &(lo, hi, cid) in &self.ranges {
187 if code >= lo && code <= hi {
188 return cid + (code - lo);
189 }
190 }
191 if self.single.is_empty() && self.ranges.is_empty() {
192 code
193 } else {
194 0
195 }
196 }
197}
198
199fn be(bytes: &[u8]) -> u32 {
200 bytes
201 .iter()
202 .take(4)
203 .fold(0u32, |a, &b| (a << 8) | u32::from(b))
204}
205
206enum Tok {
207 Hex(Vec<u8>),
208 Num(f64),
209 Name(String),
210 Kw(String),
211}
212
213fn tokenize(data: &[u8]) -> Vec<Tok> {
214 let mut out = Vec::new();
215 let mut i = 0;
216 while i < data.len() {
217 let b = data[i];
218 match b {
219 b'%' => {
220 while i < data.len() && data[i] != b'\n' && data[i] != b'\r' {
221 i += 1;
222 }
223 }
224 b'<' => {
225 if data.get(i + 1) == Some(&b'<') {
226 i += 2;
227 continue;
228 }
229 let end = data[i..]
230 .iter()
231 .position(|&c| c == b'>')
232 .map(|p| i + p)
233 .unwrap_or(data.len());
234 let mut bytes = Vec::new();
235 let mut hi: Option<u8> = None;
236 for &c in &data[i + 1..end] {
237 let v = match c {
238 b'0'..=b'9' => c - b'0',
239 b'a'..=b'f' => c - b'a' + 10,
240 b'A'..=b'F' => c - b'A' + 10,
241 _ => continue,
242 };
243 match hi.take() {
244 None => hi = Some(v),
245 Some(h) => bytes.push((h << 4) | v),
246 }
247 }
248 if let Some(h) = hi {
249 bytes.push(h << 4);
250 }
251 out.push(Tok::Hex(bytes));
252 i = end + 1;
253 }
254 b'/' => {
255 let start = i + 1;
256 let mut j = start;
257 while j < data.len()
258 && !data[j].is_ascii_whitespace()
259 && !b"/<>[](){}%".contains(&data[j])
260 {
261 j += 1;
262 }
263 out.push(Tok::Name(
264 String::from_utf8_lossy(&data[start..j]).into_owned(),
265 ));
266 i = j;
267 }
268 b'[' | b']' | b'{' | b'}' | b'>' | b'(' | b')' => i += 1,
269 c if c.is_ascii_whitespace() => i += 1,
270 _ => {
271 let start = i;
272 while i < data.len()
273 && !data[i].is_ascii_whitespace()
274 && !b"/<>[](){}%".contains(&data[i])
275 {
276 i += 1;
277 }
278 let s = String::from_utf8_lossy(&data[start..i]).into_owned();
279 match s.parse::<f64>() {
280 Ok(v) => out.push(Tok::Num(v)),
281 Err(_) => out.push(Tok::Kw(s)),
282 }
283 }
284 }
285 }
286 out
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292
293 #[test]
294 fn embedded_cmap_ranges_and_codespaces() {
295 let src = b"%!PS\n/CIDInit /ProcSet findresource begin 12 dict begin begincmap\n1 begincodespacerange <00> <80> <8140> <9ffc> endcodespacerange\n2 begincidrange <20> <7e> 1 <8140> <817e> 633 endcidrange\n1 begincidchar <80> 97 endcidchar\nendcmap";
296 let c = CMap::parse(src);
297 assert_eq!(
298 c.split(b"\x41\x81\x41\x80"),
299 vec![(0x41, 1), (0x8141, 2), (0x80, 1)]
300 );
301 assert_eq!(c.cid(0x41), 1 + 0x21);
302 assert_eq!(c.cid(0x8141), 634);
303 assert_eq!(c.cid(0x80), 97);
304 }
305
306 #[test]
307 fn identity_is_two_byte() {
308 let c = CMap::predefined(b"Identity-H");
309 assert_eq!(c.split(b"\x00\x41\x12\x34"), vec![(0x41, 2), (0x1234, 2)]);
310 assert_eq!(c.cid(0x1234), 0x1234);
311 assert!(CMap::predefined(b"UniGB-UCS2-H").unicode_codes);
312 }
313}