1pub struct CffFont {
21 pub name: String,
23 pub font_matrix: [f64; 6],
25 pub font_bbox: [f64; 4],
27 pub char_strings: Vec<Vec<u8>>,
29 pub charset: Vec<String>,
31 pub encoding: Vec<u16>,
33 pub default_width_x: f64,
35 pub nominal_width_x: f64,
37 pub local_subrs: Vec<Vec<u8>>,
39 pub global_subrs: Vec<Vec<u8>>,
41 pub is_cid: bool,
43 pub fd_array: Vec<FdEntry>,
45 pub fd_select: Vec<u8>,
47 pub ros: Option<(String, String, i32)>,
49 pub cid_to_gid: Vec<u16>,
52}
53
54pub struct FdEntry {
56 pub default_width_x: f64,
58 pub nominal_width_x: f64,
60 pub local_subrs: Vec<Vec<u8>>,
62 pub font_matrix: Option<[f64; 6]>,
64}
65
66pub fn parse_cff(data: &[u8]) -> Result<Vec<CffFont>, String> {
68 if data.len() < 4 {
69 return Err("CFF data too short for header".into());
70 }
71
72 let major = data[0];
74 if major != 1 {
75 return Err(format!("Unsupported CFF major version: {major}"));
76 }
77 let hdr_size = data[2] as usize;
78 let mut offset = hdr_size;
79
80 let (name_index, off) = parse_index(data, offset)?;
82 offset = off;
83
84 let (top_dict_index, off) = parse_index(data, offset)?;
86 offset = off;
87
88 let (string_index, off) = parse_index(data, offset)?;
90 offset = off;
91
92 let (global_subr_index, _off) = parse_index(data, offset)?;
94
95 let mut fonts = Vec::new();
96 for font_idx in 0..name_index.len() {
97 let mut font = CffFont {
98 name: String::from_utf8_lossy(&name_index[font_idx]).into_owned(),
99 font_matrix: [0.001, 0.0, 0.0, 0.001, 0.0, 0.0],
100 font_bbox: [0.0; 4],
101 char_strings: Vec::new(),
102 charset: Vec::new(),
103 encoding: vec![0u16; 256],
104 default_width_x: 0.0,
105 nominal_width_x: 0.0,
106 local_subrs: Vec::new(),
107 global_subrs: global_subr_index.clone(),
108 is_cid: false,
109 fd_array: Vec::new(),
110 fd_select: Vec::new(),
111 ros: None,
112 cid_to_gid: Vec::new(),
113 };
114
115 let top_dict = if font_idx < top_dict_index.len() {
117 parse_dict_data(&top_dict_index[font_idx])
118 } else {
119 Vec::new()
120 };
121
122 if let Some(vals) = dict_get(&top_dict, DictOp::TwoByte(12, 7))
124 && vals.len() == 6
125 {
126 for (i, v) in vals.iter().enumerate() {
127 font.font_matrix[i] = *v;
128 }
129 }
130
131 if let Some(vals) = dict_get(&top_dict, DictOp::OneByte(5))
133 && vals.len() == 4
134 {
135 for (i, v) in vals.iter().enumerate() {
136 font.font_bbox[i] = *v;
137 }
138 }
139
140 if let Some(ros_ops) = dict_get(&top_dict, DictOp::TwoByte(12, 30)) {
142 font.is_cid = true;
143 if ros_ops.len() >= 3 {
144 let registry = get_sid_string(ros_ops[0] as u16, &string_index);
145 let ordering = get_sid_string(ros_ops[1] as u16, &string_index);
146 let supplement = ros_ops[2] as i32;
147 font.ros = Some((registry, ordering, supplement));
148 }
149 }
150
151 if let Some(vals) = dict_get(&top_dict, DictOp::OneByte(17))
153 && !vals.is_empty()
154 {
155 let cs_offset = vals[0] as usize;
156 if cs_offset > 0 && cs_offset < data.len() {
157 let (cs_items, _) = parse_index(data, cs_offset)?;
158 font.char_strings = cs_items;
159 }
160 }
161
162 let n_glyphs = font.char_strings.len();
163
164 let charset_val = dict_get(&top_dict, DictOp::OneByte(15))
166 .and_then(|v| v.first().copied())
167 .unwrap_or(0.0) as i32;
168 if charset_val <= 2 {
169 font.charset = get_predefined_charset(charset_val, n_glyphs, &string_index);
170 } else {
171 font.charset = parse_charset(data, charset_val as usize, n_glyphs, &string_index)?;
172 }
173
174 if font.is_cid && charset_val > 2 {
177 font.cid_to_gid = build_cid_to_gid(data, charset_val as usize, n_glyphs)?;
178 }
179
180 if !font.is_cid {
182 let enc_val = dict_get(&top_dict, DictOp::OneByte(16))
183 .and_then(|v| v.first().copied())
184 .unwrap_or(0.0) as i32;
185 if enc_val <= 1 {
186 font.encoding = get_predefined_encoding(enc_val, &font.charset, &string_index);
187 } else {
188 font.encoding =
189 parse_encoding(data, enc_val as usize, &font.charset, &string_index)?;
190 }
191 }
192
193 if let Some(priv_ops) = dict_get(&top_dict, DictOp::OneByte(18))
195 && priv_ops.len() >= 2
196 {
197 let priv_size = priv_ops[0] as usize;
198 let priv_offset = priv_ops[1] as usize;
199 if priv_size > 0 && priv_offset > 0 && priv_offset + priv_size <= data.len() {
200 let priv_data = &data[priv_offset..priv_offset + priv_size];
201 let priv_dict = parse_dict_data(priv_data);
202
203 if let Some(vals) = dict_get(&priv_dict, DictOp::OneByte(20))
205 && let Some(&v) = vals.first()
206 {
207 font.default_width_x = v;
208 }
209
210 if let Some(vals) = dict_get(&priv_dict, DictOp::OneByte(21))
212 && let Some(&v) = vals.first()
213 {
214 font.nominal_width_x = v;
215 }
216
217 if let Some(vals) = dict_get(&priv_dict, DictOp::OneByte(19))
219 && let Some(&v) = vals.first()
220 {
221 let subr_abs_offset = priv_offset + v as usize;
222 if subr_abs_offset < data.len() {
223 let (local_subrs, _) = parse_index(data, subr_abs_offset)?;
224 font.local_subrs = local_subrs;
225 }
226 }
227 }
228 }
229
230 if font.is_cid {
232 if let Some(vals) = dict_get(&top_dict, DictOp::TwoByte(12, 36))
234 && let Some(&v) = vals.first()
235 {
236 let fda_offset = v as usize;
237 if fda_offset < data.len() {
238 let (fd_dicts_raw, _) = parse_index(data, fda_offset)?;
239 for fd_raw in &fd_dicts_raw {
240 let fd_top = parse_dict_data(fd_raw);
241 let mut fd_entry = FdEntry {
242 default_width_x: 0.0,
243 nominal_width_x: 0.0,
244 local_subrs: Vec::new(),
245 font_matrix: None,
246 };
247
248 if let Some(fm_vals) = dict_get(&fd_top, DictOp::TwoByte(12, 7))
250 && fm_vals.len() == 6
251 {
252 fd_entry.font_matrix = Some([
253 fm_vals[0], fm_vals[1], fm_vals[2], fm_vals[3], fm_vals[4],
254 fm_vals[5],
255 ]);
256 }
257
258 if let Some(fd_priv_ops) = dict_get(&fd_top, DictOp::OneByte(18))
260 && fd_priv_ops.len() >= 2
261 {
262 let fd_priv_size = fd_priv_ops[0] as usize;
263 let fd_priv_offset = fd_priv_ops[1] as usize;
264 if fd_priv_size > 0
265 && fd_priv_offset > 0
266 && fd_priv_offset + fd_priv_size <= data.len()
267 {
268 let fd_priv_data =
269 &data[fd_priv_offset..fd_priv_offset + fd_priv_size];
270 let fd_priv_dict = parse_dict_data(fd_priv_data);
271
272 if let Some(vals) = dict_get(&fd_priv_dict, DictOp::OneByte(20))
273 && let Some(&v) = vals.first()
274 {
275 fd_entry.default_width_x = v;
276 }
277 if let Some(vals) = dict_get(&fd_priv_dict, DictOp::OneByte(21))
278 && let Some(&v) = vals.first()
279 {
280 fd_entry.nominal_width_x = v;
281 }
282
283 if let Some(vals) = dict_get(&fd_priv_dict, DictOp::OneByte(19))
285 && let Some(&v) = vals.first()
286 {
287 let subr_abs = fd_priv_offset + v as usize;
288 if subr_abs < data.len() {
289 let (fd_local, _) = parse_index(data, subr_abs)?;
290 fd_entry.local_subrs = fd_local;
291 }
292 }
293 }
294 }
295
296 font.fd_array.push(fd_entry);
297 }
298 }
299 }
300
301 if let Some(vals) = dict_get(&top_dict, DictOp::TwoByte(12, 37))
303 && let Some(&v) = vals.first()
304 {
305 let fds_offset = v as usize;
306 if fds_offset < data.len() {
307 font.fd_select = parse_fd_select(data, fds_offset, n_glyphs)?;
308 }
309 }
310 }
311
312 fonts.push(font);
313 }
314
315 Ok(fonts)
316}
317
318fn parse_index(data: &[u8], offset: usize) -> Result<(Vec<Vec<u8>>, usize), String> {
324 if offset + 2 > data.len() {
325 return Err("INDEX: truncated count".into());
326 }
327 let count = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
328 let mut pos = offset + 2;
329
330 if count == 0 {
331 return Ok((Vec::new(), pos));
332 }
333
334 if pos >= data.len() {
335 return Err("INDEX: truncated offSize".into());
336 }
337 let off_size = data[pos] as usize;
338 pos += 1;
339
340 if off_size == 0 || off_size > 4 {
341 return Err(format!("INDEX: invalid offSize {off_size}"));
342 }
343
344 let mut offsets = Vec::with_capacity(count + 1);
346 for _ in 0..=count {
347 if pos + off_size > data.len() {
348 return Err("INDEX: truncated offset".into());
349 }
350 let val = read_offset(data, pos, off_size);
351 offsets.push(val);
352 pos += off_size;
353 }
354
355 let data_start = pos - 1; let mut items = Vec::with_capacity(count);
358 for i in 0..count {
359 let start = data_start + offsets[i];
360 let end = data_start + offsets[i + 1];
361 if end > data.len() || start > end {
362 return Err("INDEX: data out of bounds".into());
363 }
364 items.push(data[start..end].to_vec());
365 }
366
367 let end_offset = data_start + offsets[count];
368 Ok((items, end_offset))
369}
370
371fn read_offset(data: &[u8], offset: usize, off_size: usize) -> usize {
373 match off_size {
374 1 => data[offset] as usize,
375 2 => u16::from_be_bytes([data[offset], data[offset + 1]]) as usize,
376 3 => {
377 ((data[offset] as usize) << 16)
378 | ((data[offset + 1] as usize) << 8)
379 | (data[offset + 2] as usize)
380 }
381 4 => u32::from_be_bytes([
382 data[offset],
383 data[offset + 1],
384 data[offset + 2],
385 data[offset + 3],
386 ]) as usize,
387 _ => 0,
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397enum DictOp {
398 OneByte(u8),
399 TwoByte(u8, u8),
400}
401
402struct DictEntry {
404 op: DictOp,
405 operands: Vec<f64>,
406}
407
408fn dict_get(dict: &[DictEntry], op: DictOp) -> Option<&[f64]> {
410 dict.iter()
411 .find(|e| e.op == op)
412 .map(|e| e.operands.as_slice())
413}
414
415fn parse_dict_data(data: &[u8]) -> Vec<DictEntry> {
417 let mut result = Vec::new();
418 let mut operands: Vec<f64> = Vec::new();
419 let mut i = 0;
420 let length = data.len();
421
422 while i < length {
423 let b0 = data[i];
424
425 if b0 <= 21 {
426 let op = if b0 == 12 {
428 i += 1;
429 if i >= length {
430 break;
431 }
432 DictOp::TwoByte(12, data[i])
433 } else {
434 DictOp::OneByte(b0)
435 };
436 result.push(DictEntry {
437 op,
438 operands: std::mem::take(&mut operands),
439 });
440 i += 1;
441 } else if b0 == 28 {
442 if i + 2 >= length {
444 break;
445 }
446 let val = i16::from_be_bytes([data[i + 1], data[i + 2]]);
447 operands.push(val as f64);
448 i += 3;
449 } else if b0 == 29 {
450 if i + 4 >= length {
452 break;
453 }
454 let val = i32::from_be_bytes([data[i + 1], data[i + 2], data[i + 3], data[i + 4]]);
455 operands.push(val as f64);
456 i += 5;
457 } else if b0 == 30 {
458 i += 1;
460 let mut chars = Vec::new();
461 while i < length {
462 let byte = data[i];
463 i += 1;
464 let n1 = (byte >> 4) & 0x0F;
465 let n2 = byte & 0x0F;
466
467 if !push_bcd_nibble(n1, &mut chars) {
468 break;
469 }
470 if !push_bcd_nibble(n2, &mut chars) {
471 break;
472 }
473 }
474 let s: String = chars.into_iter().collect();
475 operands.push(s.parse::<f64>().unwrap_or(0.0));
476 } else if (32..=246).contains(&b0) {
477 operands.push((b0 as i32 - 139) as f64);
478 i += 1;
479 } else if (247..=250).contains(&b0) {
480 if i + 1 >= length {
481 break;
482 }
483 let b1 = data[i + 1];
484 operands.push(((b0 as i32 - 247) * 256 + b1 as i32 + 108) as f64);
485 i += 2;
486 } else if (251..=254).contains(&b0) {
487 if i + 1 >= length {
488 break;
489 }
490 let b1 = data[i + 1];
491 operands.push((-(b0 as i32 - 251) * 256 - b1 as i32 - 108) as f64);
492 i += 2;
493 } else {
494 i += 1;
496 }
497 }
498
499 result
500}
501
502fn push_bcd_nibble(n: u8, chars: &mut Vec<char>) -> bool {
504 match n {
505 0..=9 => chars.push((b'0' + n) as char),
506 0x0A => chars.push('.'),
507 0x0B => chars.push('E'),
508 0x0C => {
509 chars.push('E');
510 chars.push('-');
511 }
512 0x0E => chars.push('-'),
513 0x0F => return false,
514 _ => {} }
516 true
517}
518
519pub fn get_sid_string(sid: u16, string_index: &[Vec<u8>]) -> String {
527 if (sid as usize) < STANDARD_STRINGS.len() {
528 return STANDARD_STRINGS[sid as usize].to_string();
529 }
530 let idx = sid as usize - STANDARD_STRINGS.len();
531 if idx < string_index.len() {
532 String::from_utf8_lossy(&string_index[idx]).into_owned()
533 } else {
534 format!(".sid{sid}")
535 }
536}
537
538fn parse_charset(
544 data: &[u8],
545 offset: usize,
546 n_glyphs: usize,
547 string_index: &[Vec<u8>],
548) -> Result<Vec<String>, String> {
549 let mut names = vec![".notdef".to_string()];
550 if n_glyphs <= 1 {
551 return Ok(names);
552 }
553
554 if offset >= data.len() {
555 return Err("charset: offset out of bounds".into());
556 }
557 let fmt = data[offset];
558 let mut pos = offset + 1;
559
560 match fmt {
561 0 => {
562 for _ in 0..n_glyphs - 1 {
564 if pos + 1 >= data.len() {
565 break;
566 }
567 let sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
568 pos += 2;
569 names.push(get_sid_string(sid, string_index));
570 }
571 }
572 1 => {
573 while names.len() < n_glyphs {
575 if pos + 2 >= data.len() {
576 break;
577 }
578 let first_sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
579 let n_left = data[pos + 2] as u16;
580 pos += 3;
581 for sid in first_sid..=first_sid + n_left {
582 if names.len() >= n_glyphs {
583 break;
584 }
585 names.push(get_sid_string(sid, string_index));
586 }
587 }
588 }
589 2 => {
590 while names.len() < n_glyphs {
592 if pos + 3 >= data.len() {
593 break;
594 }
595 let first_sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
596 let n_left = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
597 pos += 4;
598 for sid in first_sid..=first_sid + n_left {
599 if names.len() >= n_glyphs {
600 break;
601 }
602 names.push(get_sid_string(sid, string_index));
603 }
604 }
605 }
606 _ => return Err(format!("Unknown charset format: {fmt}")),
607 }
608
609 Ok(names)
610}
611
612fn build_cid_to_gid(data: &[u8], offset: usize, n_glyphs: usize) -> Result<Vec<u16>, String> {
616 let mut gid_to_cid: Vec<u16> = vec![0]; if n_glyphs <= 1 || offset >= data.len() {
619 return Ok(Vec::new());
620 }
621 let fmt = data[offset];
622 let mut pos = offset + 1;
623 match fmt {
624 0 => {
625 for _ in 0..n_glyphs - 1 {
626 if pos + 1 >= data.len() {
627 break;
628 }
629 let cid = u16::from_be_bytes([data[pos], data[pos + 1]]);
630 pos += 2;
631 gid_to_cid.push(cid);
632 }
633 }
634 1 => {
635 while gid_to_cid.len() < n_glyphs {
636 if pos + 2 >= data.len() {
637 break;
638 }
639 let first = u16::from_be_bytes([data[pos], data[pos + 1]]);
640 let n_left = data[pos + 2] as u16;
641 pos += 3;
642 for cid in first..=first + n_left {
643 if gid_to_cid.len() >= n_glyphs {
644 break;
645 }
646 gid_to_cid.push(cid);
647 }
648 }
649 }
650 2 => {
651 while gid_to_cid.len() < n_glyphs {
652 if pos + 3 >= data.len() {
653 break;
654 }
655 let first = u16::from_be_bytes([data[pos], data[pos + 1]]);
656 let n_left = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
657 pos += 4;
658 for cid in first..=first + n_left {
659 if gid_to_cid.len() >= n_glyphs {
660 break;
661 }
662 gid_to_cid.push(cid);
663 }
664 }
665 }
666 _ => return Err(format!("Unknown charset format: {fmt}")),
667 }
668
669 let max_cid = gid_to_cid.iter().copied().max().unwrap_or(0) as usize;
671 let mut cid_to_gid = vec![0xFFFF_u16; max_cid + 1];
672 for (gid, &cid) in gid_to_cid.iter().enumerate() {
673 let cid_idx = cid as usize;
674 if cid_idx < cid_to_gid.len() {
675 cid_to_gid[cid_idx] = gid as u16;
676 }
677 }
678 Ok(cid_to_gid)
679}
680
681fn get_predefined_charset(
683 charset_id: i32,
684 n_glyphs: usize,
685 string_index: &[Vec<u8>],
686) -> Vec<String> {
687 let sids: &[u16] = match charset_id {
688 0 => &ISO_ADOBE_CHARSET,
689 1 => &EXPERT_CHARSET,
690 2 => &EXPERT_SUBSET_CHARSET,
691 _ => {
692 let mut names = vec![".notdef".to_string()];
693 for i in 1..n_glyphs {
694 names.push(format!(".gid{i}"));
695 }
696 return names;
697 }
698 };
699
700 let mut names = vec![".notdef".to_string()];
701 for &sid in sids.iter() {
702 if names.len() >= n_glyphs {
703 break;
704 }
705 names.push(get_sid_string(sid, string_index));
706 }
707 while names.len() < n_glyphs {
708 names.push(format!(".gid{}", names.len()));
709 }
710 names
711}
712
713fn parse_encoding(
719 data: &[u8],
720 offset: usize,
721 charset: &[String],
722 string_index: &[Vec<u8>],
723) -> Result<Vec<u16>, String> {
724 let mut encoding = vec![0u16; 256];
725
726 let name_to_gid: std::collections::HashMap<&str, u16> = charset
728 .iter()
729 .enumerate()
730 .map(|(gid, name)| (name.as_str(), gid as u16))
731 .collect();
732
733 if offset >= data.len() {
734 return Err("encoding: offset out of bounds".into());
735 }
736 let raw_format = data[offset];
737 let fmt = raw_format & 0x7F;
738 let has_supplement = (raw_format & 0x80) != 0;
739 let mut pos = offset + 1;
740
741 match fmt {
742 0 => {
743 if pos >= data.len() {
744 return Ok(encoding);
745 }
746 let n_codes = data[pos] as usize;
747 pos += 1;
748 for gid_minus_1 in 0..n_codes {
749 if pos >= data.len() {
750 break;
751 }
752 let code = data[pos] as usize;
753 pos += 1;
754 let gid = (gid_minus_1 + 1) as u16;
755 if code < 256 {
756 encoding[code] = gid;
757 }
758 }
759 }
760 1 => {
761 if pos >= data.len() {
762 return Ok(encoding);
763 }
764 let n_ranges = data[pos] as usize;
765 pos += 1;
766 let mut gid: u16 = 1;
767 for _ in 0..n_ranges {
768 if pos + 1 >= data.len() {
769 break;
770 }
771 let first_code = data[pos] as usize;
772 let n_left = data[pos + 1] as usize;
773 pos += 2;
774 for off in 0..=n_left {
775 let code = first_code + off;
776 if code < 256 {
777 encoding[code] = gid;
778 }
779 gid += 1;
780 }
781 }
782 }
783 _ => return Err(format!("Unknown encoding format: {fmt}")),
784 }
785
786 if has_supplement && pos < data.len() {
788 let n_sups = data[pos] as usize;
789 pos += 1;
790 for _ in 0..n_sups {
791 if pos + 2 >= data.len() {
792 break;
793 }
794 let code = data[pos] as usize;
795 let sid = u16::from_be_bytes([data[pos + 1], data[pos + 2]]);
796 pos += 3;
797 let name = get_sid_string(sid, string_index);
798 let gid = name_to_gid.get(name.as_str()).copied().unwrap_or(0);
799 if code < 256 {
800 encoding[code] = gid;
801 }
802 }
803 }
804
805 Ok(encoding)
806}
807
808fn get_predefined_encoding(
810 encoding_id: i32,
811 charset: &[String],
812 string_index: &[Vec<u8>],
813) -> Vec<u16> {
814 let mut encoding = vec![0u16; 256];
815
816 let enc_map: &[(u8, u16)] = match encoding_id {
817 0 => &STANDARD_ENCODING_MAP,
818 1 => &EXPERT_ENCODING_MAP,
819 _ => return encoding,
820 };
821
822 let name_to_gid: std::collections::HashMap<&str, u16> = charset
824 .iter()
825 .enumerate()
826 .map(|(gid, name)| (name.as_str(), gid as u16))
827 .collect();
828
829 for &(code, sid) in enc_map {
831 let name = get_sid_string(sid, string_index);
832 let gid = name_to_gid.get(name.as_str()).copied().unwrap_or(0);
833 encoding[code as usize] = gid;
834 }
835
836 encoding
837}
838
839fn parse_fd_select(data: &[u8], offset: usize, n_glyphs: usize) -> Result<Vec<u8>, String> {
845 if offset >= data.len() {
846 return Err("FDSelect: offset out of bounds".into());
847 }
848 let fmt = data[offset];
849 let mut pos = offset + 1;
850
851 match fmt {
852 0 => {
853 if pos + n_glyphs > data.len() {
855 return Err("FDSelect format 0: truncated data".into());
856 }
857 Ok(data[pos..pos + n_glyphs].to_vec())
858 }
859 3 => {
860 if pos + 1 >= data.len() {
862 return Err("FDSelect format 3: truncated".into());
863 }
864 let n_ranges = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
865 pos += 2;
866 let mut fd_select = vec![0u8; n_glyphs];
867
868 for i in 0..n_ranges {
869 if pos + 2 >= data.len() {
870 break;
871 }
872 let first_gid = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
873 let fd = data[pos + 2];
874 pos += 3;
875
876 let next_first = if i + 1 < n_ranges && pos + 1 < data.len() {
877 u16::from_be_bytes([data[pos], data[pos + 1]]) as usize
878 } else if pos + 1 < data.len() {
879 u16::from_be_bytes([data[pos], data[pos + 1]]) as usize
881 } else {
882 n_glyphs
883 };
884
885 for item in fd_select
886 .iter_mut()
887 .take(next_first.min(n_glyphs))
888 .skip(first_gid)
889 {
890 *item = fd;
891 }
892 }
893
894 Ok(fd_select)
895 }
896 _ => Err(format!("Unknown FDSelect format: {fmt}")),
897 }
898}
899
900#[rustfmt::skip]
905const STANDARD_STRINGS: [&str; 391] = [
906 ".notdef", "space", "exclam", "quotedbl", "numbersign",
908 "dollar", "percent", "ampersand", "quoteright", "parenleft",
909 "parenright", "asterisk", "plus", "comma", "hyphen",
911 "period", "slash", "zero", "one", "two",
912 "three", "four", "five", "six", "seven",
914 "eight", "nine", "colon", "semicolon", "less",
915 "equal", "greater", "question", "at", "A",
917 "B", "C", "D", "E", "F",
918 "G", "H", "I", "J", "K",
920 "L", "M", "N", "O", "P",
921 "Q", "R", "S", "T", "U",
923 "V", "W", "X", "Y", "Z",
924 "bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
926 "quoteleft", "a", "b", "c", "d",
927 "e", "f", "g", "h", "i",
929 "j", "k", "l", "m", "n",
930 "o", "p", "q", "r", "s",
932 "t", "u", "v", "w", "x",
933 "y", "z", "braceleft", "bar", "braceright",
935 "asciitilde", "exclamdown", "cent", "sterling", "fraction",
936 "yen", "florin", "section", "currency", "quotesingle",
938 "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
939 "fl", "endash", "dagger", "daggerdbl", "periodcentered",
941 "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
942 "guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
944 "acute", "circumflex", "tilde", "macron", "breve",
945 "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
947 "ogonek", "caron", "emdash", "AE", "ordfeminine",
948 "Lslash", "Oslash", "OE", "ordmasculine", "ae",
950 "dotlessi", "lslash", "oslash", "oe", "germandbls",
951 "onesuperior", "logicalnot", "mu", "trademark", "Eth",
953 "onehalf", "plusminus", "Thorn", "onequarter", "divide",
954 "brokenbar", "degree", "thorn", "threequarters", "twosuperior",
956 "registered", "minus", "eth", "multiply", "threesuperior",
957 "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
959 "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
960 "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
962 "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
963 "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
965 "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
966 "aacute", "acircumflex", "adieresis", "agrave", "aring",
968 "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
969 "egrave", "iacute", "icircumflex", "idieresis", "igrave",
971 "ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
972 "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
974 "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
975 "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
977 "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
978 "onedotenleader", "zerooldstyle",
979 "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle",
981 "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle",
982 "nineoldstyle", "commasuperior",
983 "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
985 "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
986 "lsuperior",
987 "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
989 "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
990 "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
992 "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
993 "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
995 "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
996 "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
998 "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
999 "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
1001 "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall",
1002 "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
1004 "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall",
1005 "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
1007 "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
1008 "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior",
1010 "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior",
1011 "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior",
1013 "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall",
1014 "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
1016 "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1017 "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1019 "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall",
1020 "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall",
1022 "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall",
1023 "001.000", "001.001",
1024 "001.002", "001.003", "Black", "Bold", "Book",
1026 "Light", "Medium", "Regular", "Roman", "Semibold",
1027];
1028
1029#[rustfmt::skip]
1035const ISO_ADOBE_CHARSET: [u16; 228] = [
1036 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
1037 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1038 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
1039 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
1040 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
1041 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
1042 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
1043 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
1044 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
1045 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
1046 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
1047 221, 222, 223, 224, 225, 226, 227, 228,
1048];
1049
1050#[rustfmt::skip]
1052const EXPERT_CHARSET: [u16; 165] = [
1053 1, 229, 230, 231, 232, 233, 234, 235, 236, 237,
1054 238, 13, 14, 15, 99, 239, 240, 241, 242, 243,
1055 244, 245, 246, 247, 248, 27, 28, 249, 250, 251,
1056 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
1057 262, 263, 264, 265, 266, 109, 110, 267, 268, 269,
1058 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
1059 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
1060 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
1061 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
1062 310, 311, 312, 313, 314, 315, 316, 317, 318, 158,
1063 155, 163, 319, 320, 321, 322, 323, 324, 325, 326,
1064 150, 164, 169, 327, 328, 329, 330, 331, 332, 333,
1065 334, 335, 336, 337, 338, 339, 340, 341, 342, 343,
1066 344, 345, 346, 347, 348, 349, 350, 351, 352, 353,
1067 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
1068 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,
1069 374, 375, 376, 377, 378,
1070];
1071
1072#[rustfmt::skip]
1074const EXPERT_SUBSET_CHARSET: [u16; 86] = [
1075 1, 231, 232, 235, 236, 237, 238, 13, 14, 15,
1076 99, 239, 240, 241, 242, 243, 244, 245, 246, 247,
1077 248, 27, 28, 249, 250, 251, 253, 254, 255, 256,
1078 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
1079 109, 110, 267, 268, 269, 270, 272, 300, 301, 302,
1080 305, 314, 315, 158, 155, 163, 320, 321, 322, 323,
1081 324, 325, 326, 150, 164, 169, 327, 328, 329, 330,
1082 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
1083 341, 342, 343, 344, 345, 346,
1084];
1085
1086#[rustfmt::skip]
1092const STANDARD_ENCODING_MAP: [(u8, u16); 149] = [
1093 (32, 1), (33, 2), (34, 3), (35, 4), (36, 5), (37, 6), (38, 7), (39, 8),
1094 (40, 9), (41, 10), (42, 11), (43, 12), (44, 13), (45, 14), (46, 15), (47, 16),
1095 (48, 17), (49, 18), (50, 19), (51, 20), (52, 21), (53, 22), (54, 23), (55, 24),
1096 (56, 25), (57, 26), (58, 27), (59, 28), (60, 29), (61, 30), (62, 31), (63, 32),
1097 (64, 33), (65, 34), (66, 35), (67, 36), (68, 37), (69, 38), (70, 39), (71, 40),
1098 (72, 41), (73, 42), (74, 43), (75, 44), (76, 45), (77, 46), (78, 47), (79, 48),
1099 (80, 49), (81, 50), (82, 51), (83, 52), (84, 53), (85, 54), (86, 55), (87, 56),
1100 (88, 57), (89, 58), (90, 59), (91, 60), (92, 61), (93, 62), (94, 63), (95, 64),
1101 (96, 65), (97, 66), (98, 67), (99, 68), (100, 69), (101, 70), (102, 71),
1102 (103, 72), (104, 73), (105, 74), (106, 75), (107, 76), (108, 77), (109, 78),
1103 (110, 79), (111, 80), (112, 81), (113, 82), (114, 83), (115, 84), (116, 85),
1104 (117, 86), (118, 87), (119, 88), (120, 89), (121, 90), (122, 91), (123, 92),
1105 (124, 93), (125, 94), (126, 95),
1106 (161, 96), (162, 97), (163, 98), (164, 99), (165, 100), (166, 101),
1107 (167, 102), (168, 103), (169, 104), (170, 105), (171, 106), (172, 107),
1108 (173, 108), (174, 109), (175, 110), (177, 111), (178, 112), (179, 113),
1109 (180, 114), (182, 115), (183, 116), (184, 117), (185, 118), (186, 119),
1110 (187, 120), (188, 121), (189, 122), (191, 123), (193, 124), (194, 125),
1111 (195, 126), (196, 127), (197, 128), (198, 129), (199, 130), (200, 131),
1112 (202, 132), (203, 133), (205, 134), (206, 135), (207, 136), (208, 137),
1113 (225, 138), (227, 139), (232, 140), (233, 141), (234, 142), (235, 143),
1114 (241, 144), (245, 145), (248, 146), (249, 147), (250, 148), (251, 149),
1115];
1116
1117#[rustfmt::skip]
1119pub const EXPERT_ENCODING_MAP: [(u8, u16); 165] = [
1120 (32, 1), (33, 229), (34, 230), (36, 231), (37, 232), (38, 233), (39, 234),
1121 (40, 235), (41, 236), (42, 237), (43, 238), (44, 13), (45, 14), (46, 15),
1122 (47, 99), (48, 239), (49, 240), (50, 241), (51, 242), (52, 243), (53, 244),
1123 (54, 245), (55, 246), (56, 247), (57, 248), (58, 27), (59, 28), (60, 249),
1124 (61, 250), (62, 251), (63, 252), (64, 253), (65, 254), (66, 255), (67, 256),
1125 (68, 257), (69, 258), (70, 259), (71, 260), (72, 261), (73, 262), (74, 263),
1126 (75, 264), (76, 265), (77, 266), (78, 109), (79, 110), (80, 267), (81, 268),
1127 (82, 269), (83, 270), (84, 271), (85, 272), (86, 273), (87, 274), (88, 275),
1128 (89, 276), (90, 277), (91, 278), (92, 279), (93, 280), (94, 281), (95, 282),
1129 (96, 283), (97, 284), (98, 285), (99, 286), (100, 287), (101, 288), (102, 289),
1130 (103, 290), (104, 291), (105, 292), (106, 293), (107, 294), (108, 295),
1131 (109, 296), (110, 297), (111, 298), (112, 299), (113, 300), (114, 301),
1132 (115, 302), (116, 303), (117, 304), (118, 305), (119, 306), (120, 307),
1133 (121, 308), (122, 309), (123, 310), (124, 311), (125, 312), (126, 313),
1134 (161, 314), (162, 315), (163, 316), (164, 317), (165, 318), (166, 158),
1135 (167, 155), (168, 163), (169, 319), (170, 320), (171, 321), (172, 322),
1136 (173, 323), (174, 324), (175, 325), (176, 326), (177, 150), (178, 164),
1137 (179, 169), (180, 327), (181, 328), (182, 329), (183, 330), (184, 331),
1138 (185, 332), (186, 333), (187, 334), (188, 335), (189, 336), (190, 337),
1139 (191, 338), (192, 339), (193, 340), (194, 341), (195, 342), (196, 343),
1140 (197, 344), (198, 345), (199, 346), (200, 347), (201, 348), (202, 349),
1141 (203, 350), (204, 351), (205, 352), (206, 353), (207, 354), (208, 355),
1142 (209, 356), (210, 357), (211, 358), (212, 359), (213, 360), (214, 361),
1143 (215, 362), (216, 363), (217, 364), (218, 365), (219, 366), (220, 367),
1144 (221, 368), (222, 369), (223, 370), (224, 371), (225, 372), (226, 373),
1145 (227, 374), (228, 375), (229, 376), (230, 377), (231, 378),
1146];
1147
1148#[cfg(test)]
1149mod tests {
1150 use super::*;
1151
1152 #[test]
1153 fn test_sid_resolution() {
1154 let string_index = vec![b"CustomGlyph".to_vec()];
1155 assert_eq!(get_sid_string(0, &string_index), ".notdef");
1156 assert_eq!(get_sid_string(34, &string_index), "A");
1157 assert_eq!(get_sid_string(391, &string_index), "CustomGlyph");
1158 assert_eq!(get_sid_string(999, &string_index), ".sid999");
1159 }
1160
1161 #[test]
1162 fn test_dict_number_encoding() {
1163 let data = [139u8, 15]; let entries = parse_dict_data(&data);
1166 assert_eq!(entries.len(), 1);
1167 assert_eq!(entries[0].operands, vec![0.0]);
1168
1169 let data = [247u8, 0, 15]; let entries = parse_dict_data(&data);
1172 assert_eq!(entries[0].operands, vec![108.0]);
1173
1174 let data = [251u8, 0, 15]; let entries = parse_dict_data(&data);
1177 assert_eq!(entries[0].operands, vec![-108.0]);
1178 }
1179
1180 #[test]
1181 fn test_empty_index() {
1182 let data = [0u8, 0];
1184 let (items, off) = parse_index(&data, 0).unwrap();
1185 assert!(items.is_empty());
1186 assert_eq!(off, 2);
1187 }
1188
1189 #[test]
1190 fn test_predefined_charset_iso_adobe() {
1191 let names = get_predefined_charset(0, 5, &[]);
1192 assert_eq!(names[0], ".notdef");
1193 assert_eq!(names[1], "space");
1194 assert_eq!(names[2], "exclam");
1195 assert_eq!(names.len(), 5);
1196 }
1197}