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