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.saturating_sub(1) {
567 if pos + 1 >= data.len() {
568 break;
569 }
570 let sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
571 pos += 2;
572 names.push(get_sid_string(sid, string_index));
573 }
574 }
575 1 => {
576 while names.len() < n_glyphs {
578 if pos + 2 >= data.len() {
579 break;
580 }
581 let first_sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
582 let n_left = data[pos + 2] as u16;
583 pos += 3;
584 let last_sid = u32::from(first_sid) + u32::from(n_left);
590 for sid in u32::from(first_sid)..=last_sid.min(u32::from(u16::MAX)) {
591 if names.len() >= n_glyphs {
592 break;
593 }
594 names.push(get_sid_string(sid as u16, string_index));
595 }
596 }
597 }
598 2 => {
599 while names.len() < n_glyphs {
601 if pos + 3 >= data.len() {
602 break;
603 }
604 let first_sid = u16::from_be_bytes([data[pos], data[pos + 1]]);
605 let n_left = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
606 pos += 4;
607 let last_sid = u32::from(first_sid) + u32::from(n_left);
613 for sid in u32::from(first_sid)..=last_sid.min(u32::from(u16::MAX)) {
614 if names.len() >= n_glyphs {
615 break;
616 }
617 names.push(get_sid_string(sid as u16, string_index));
618 }
619 }
620 }
621 _ => return Err(format!("Unknown charset format: {fmt}")),
622 }
623
624 Ok(names)
625}
626
627fn build_cid_to_gid(data: &[u8], offset: usize, n_glyphs: usize) -> Result<Vec<u16>, String> {
631 let mut gid_to_cid: Vec<u16> = vec![0]; if n_glyphs <= 1 || offset >= data.len() {
634 return Ok(Vec::new());
635 }
636 let fmt = data[offset];
637 let mut pos = offset + 1;
638 match fmt {
639 0 => {
640 for _ in 0..n_glyphs.saturating_sub(1) {
643 if pos + 1 >= data.len() {
644 break;
645 }
646 let cid = u16::from_be_bytes([data[pos], data[pos + 1]]);
647 pos += 2;
648 gid_to_cid.push(cid);
649 }
650 }
651 1 => {
652 while gid_to_cid.len() < n_glyphs {
653 if pos + 2 >= data.len() {
654 break;
655 }
656 let first = u16::from_be_bytes([data[pos], data[pos + 1]]);
657 let n_left = data[pos + 2] as u16;
658 pos += 3;
659 let last = u32::from(first) + u32::from(n_left);
661 for cid in u32::from(first)..=last.min(u32::from(u16::MAX)) {
662 if gid_to_cid.len() >= n_glyphs {
663 break;
664 }
665 gid_to_cid.push(cid as u16);
666 }
667 }
668 }
669 2 => {
670 while gid_to_cid.len() < n_glyphs {
671 if pos + 3 >= data.len() {
672 break;
673 }
674 let first = u16::from_be_bytes([data[pos], data[pos + 1]]);
675 let n_left = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
676 pos += 4;
677 let last = u32::from(first) + u32::from(n_left);
679 for cid in u32::from(first)..=last.min(u32::from(u16::MAX)) {
680 if gid_to_cid.len() >= n_glyphs {
681 break;
682 }
683 gid_to_cid.push(cid as u16);
684 }
685 }
686 }
687 _ => return Err(format!("Unknown charset format: {fmt}")),
688 }
689
690 let max_cid = gid_to_cid.iter().copied().max().unwrap_or(0) as usize;
692 let mut cid_to_gid = vec![0xFFFF_u16; max_cid + 1];
693 for (gid, &cid) in gid_to_cid.iter().enumerate() {
694 let cid_idx = cid as usize;
695 if cid_idx < cid_to_gid.len() {
696 cid_to_gid[cid_idx] = gid as u16;
697 }
698 }
699 Ok(cid_to_gid)
700}
701
702fn get_predefined_charset(
704 charset_id: i32,
705 n_glyphs: usize,
706 string_index: &[Vec<u8>],
707) -> Vec<String> {
708 let sids: &[u16] = match charset_id {
709 0 => &ISO_ADOBE_CHARSET,
710 1 => &EXPERT_CHARSET,
711 2 => &EXPERT_SUBSET_CHARSET,
712 _ => {
713 let mut names = vec![".notdef".to_string()];
714 for i in 1..n_glyphs {
715 names.push(format!(".gid{i}"));
716 }
717 return names;
718 }
719 };
720
721 let mut names = vec![".notdef".to_string()];
722 for &sid in sids.iter() {
723 if names.len() >= n_glyphs {
724 break;
725 }
726 names.push(get_sid_string(sid, string_index));
727 }
728 while names.len() < n_glyphs {
729 names.push(format!(".gid{}", names.len()));
730 }
731 names
732}
733
734fn parse_encoding(
740 data: &[u8],
741 offset: usize,
742 charset: &[String],
743 string_index: &[Vec<u8>],
744) -> Result<Vec<u16>, String> {
745 let mut encoding = vec![0u16; 256];
746
747 let name_to_gid: std::collections::HashMap<&str, u16> = charset
749 .iter()
750 .enumerate()
751 .map(|(gid, name)| (name.as_str(), gid as u16))
752 .collect();
753
754 if offset >= data.len() {
755 return Err("encoding: offset out of bounds".into());
756 }
757 let raw_format = data[offset];
758 let fmt = raw_format & 0x7F;
759 let has_supplement = (raw_format & 0x80) != 0;
760 let mut pos = offset + 1;
761
762 match fmt {
763 0 => {
764 if pos >= data.len() {
765 return Ok(encoding);
766 }
767 let n_codes = data[pos] as usize;
768 pos += 1;
769 for gid_minus_1 in 0..n_codes {
770 if pos >= data.len() {
771 break;
772 }
773 let code = data[pos] as usize;
774 pos += 1;
775 let gid = (gid_minus_1 + 1) as u16;
776 if code < 256 {
777 encoding[code] = gid;
778 }
779 }
780 }
781 1 => {
782 if pos >= data.len() {
783 return Ok(encoding);
784 }
785 let n_ranges = data[pos] as usize;
786 pos += 1;
787 let mut gid: u16 = 1;
788 for _ in 0..n_ranges {
789 if pos + 1 >= data.len() {
790 break;
791 }
792 let first_code = data[pos] as usize;
793 let n_left = data[pos + 1] as usize;
794 pos += 2;
795 for off in 0..=n_left {
796 let code = first_code + off;
797 if code < 256 {
798 encoding[code] = gid;
799 }
800 gid += 1;
801 }
802 }
803 }
804 _ => return Err(format!("Unknown encoding format: {fmt}")),
805 }
806
807 if has_supplement && pos < data.len() {
809 let n_sups = data[pos] as usize;
810 pos += 1;
811 for _ in 0..n_sups {
812 if pos + 2 >= data.len() {
813 break;
814 }
815 let code = data[pos] as usize;
816 let sid = u16::from_be_bytes([data[pos + 1], data[pos + 2]]);
817 pos += 3;
818 let name = get_sid_string(sid, string_index);
819 let gid = name_to_gid.get(name.as_str()).copied().unwrap_or(0);
820 if code < 256 {
821 encoding[code] = gid;
822 }
823 }
824 }
825
826 Ok(encoding)
827}
828
829fn get_predefined_encoding(
831 encoding_id: i32,
832 charset: &[String],
833 string_index: &[Vec<u8>],
834) -> Vec<u16> {
835 let mut encoding = vec![0u16; 256];
836
837 let enc_map: &[(u8, u16)] = match encoding_id {
838 0 => &STANDARD_ENCODING_MAP,
839 1 => &EXPERT_ENCODING_MAP,
840 _ => return encoding,
841 };
842
843 let name_to_gid: std::collections::HashMap<&str, u16> = charset
845 .iter()
846 .enumerate()
847 .map(|(gid, name)| (name.as_str(), gid as u16))
848 .collect();
849
850 for &(code, sid) in enc_map {
852 let name = get_sid_string(sid, string_index);
853 let gid = name_to_gid.get(name.as_str()).copied().unwrap_or(0);
854 encoding[code as usize] = gid;
855 }
856
857 encoding
858}
859
860fn parse_fd_select(data: &[u8], offset: usize, n_glyphs: usize) -> Result<Vec<u8>, String> {
866 if offset >= data.len() {
867 return Err("FDSelect: offset out of bounds".into());
868 }
869 let fmt = data[offset];
870 let mut pos = offset + 1;
871
872 match fmt {
873 0 => {
874 if pos + n_glyphs > data.len() {
876 return Err("FDSelect format 0: truncated data".into());
877 }
878 Ok(data[pos..pos + n_glyphs].to_vec())
879 }
880 3 => {
881 if pos + 1 >= data.len() {
883 return Err("FDSelect format 3: truncated".into());
884 }
885 let n_ranges = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
886 pos += 2;
887 let mut fd_select = vec![0u8; n_glyphs];
888
889 for i in 0..n_ranges {
890 if pos + 2 >= data.len() {
891 break;
892 }
893 let first_gid = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
894 let fd = data[pos + 2];
895 pos += 3;
896
897 let next_first = if i + 1 < n_ranges && pos + 1 < data.len() {
898 u16::from_be_bytes([data[pos], data[pos + 1]]) as usize
899 } else if pos + 1 < data.len() {
900 u16::from_be_bytes([data[pos], data[pos + 1]]) as usize
902 } else {
903 n_glyphs
904 };
905
906 for item in fd_select
907 .iter_mut()
908 .take(next_first.min(n_glyphs))
909 .skip(first_gid)
910 {
911 *item = fd;
912 }
913 }
914
915 Ok(fd_select)
916 }
917 _ => Err(format!("Unknown FDSelect format: {fmt}")),
918 }
919}
920
921#[rustfmt::skip]
926const STANDARD_STRINGS: [&str; 391] = [
927 ".notdef", "space", "exclam", "quotedbl", "numbersign",
929 "dollar", "percent", "ampersand", "quoteright", "parenleft",
930 "parenright", "asterisk", "plus", "comma", "hyphen",
932 "period", "slash", "zero", "one", "two",
933 "three", "four", "five", "six", "seven",
935 "eight", "nine", "colon", "semicolon", "less",
936 "equal", "greater", "question", "at", "A",
938 "B", "C", "D", "E", "F",
939 "G", "H", "I", "J", "K",
941 "L", "M", "N", "O", "P",
942 "Q", "R", "S", "T", "U",
944 "V", "W", "X", "Y", "Z",
945 "bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
947 "quoteleft", "a", "b", "c", "d",
948 "e", "f", "g", "h", "i",
950 "j", "k", "l", "m", "n",
951 "o", "p", "q", "r", "s",
953 "t", "u", "v", "w", "x",
954 "y", "z", "braceleft", "bar", "braceright",
956 "asciitilde", "exclamdown", "cent", "sterling", "fraction",
957 "yen", "florin", "section", "currency", "quotesingle",
959 "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
960 "fl", "endash", "dagger", "daggerdbl", "periodcentered",
962 "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
963 "guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
965 "acute", "circumflex", "tilde", "macron", "breve",
966 "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
968 "ogonek", "caron", "emdash", "AE", "ordfeminine",
969 "Lslash", "Oslash", "OE", "ordmasculine", "ae",
971 "dotlessi", "lslash", "oslash", "oe", "germandbls",
972 "onesuperior", "logicalnot", "mu", "trademark", "Eth",
974 "onehalf", "plusminus", "Thorn", "onequarter", "divide",
975 "brokenbar", "degree", "thorn", "threequarters", "twosuperior",
977 "registered", "minus", "eth", "multiply", "threesuperior",
978 "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
980 "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
981 "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
983 "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
984 "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
986 "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
987 "aacute", "acircumflex", "adieresis", "agrave", "aring",
989 "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
990 "egrave", "iacute", "icircumflex", "idieresis", "igrave",
992 "ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
993 "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
995 "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
996 "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
998 "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
999 "onedotenleader", "zerooldstyle",
1000 "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle",
1002 "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle",
1003 "nineoldstyle", "commasuperior",
1004 "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
1006 "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1007 "lsuperior",
1008 "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1010 "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
1011 "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1013 "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
1014 "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
1016 "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1017 "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
1019 "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
1020 "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
1022 "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall",
1023 "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
1025 "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall",
1026 "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
1028 "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
1029 "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior",
1031 "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior",
1032 "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior",
1034 "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall",
1035 "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
1037 "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1038 "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1040 "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall",
1041 "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall",
1043 "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall",
1044 "001.000", "001.001",
1045 "001.002", "001.003", "Black", "Bold", "Book",
1047 "Light", "Medium", "Regular", "Roman", "Semibold",
1048];
1049
1050#[rustfmt::skip]
1056const ISO_ADOBE_CHARSET: [u16; 228] = [
1057 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
1058 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1059 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
1060 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
1061 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
1062 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
1063 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
1064 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
1065 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
1066 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
1067 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
1068 221, 222, 223, 224, 225, 226, 227, 228,
1069];
1070
1071#[rustfmt::skip]
1073const EXPERT_CHARSET: [u16; 165] = [
1074 1, 229, 230, 231, 232, 233, 234, 235, 236, 237,
1075 238, 13, 14, 15, 99, 239, 240, 241, 242, 243,
1076 244, 245, 246, 247, 248, 27, 28, 249, 250, 251,
1077 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
1078 262, 263, 264, 265, 266, 109, 110, 267, 268, 269,
1079 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
1080 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
1081 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
1082 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
1083 310, 311, 312, 313, 314, 315, 316, 317, 318, 158,
1084 155, 163, 319, 320, 321, 322, 323, 324, 325, 326,
1085 150, 164, 169, 327, 328, 329, 330, 331, 332, 333,
1086 334, 335, 336, 337, 338, 339, 340, 341, 342, 343,
1087 344, 345, 346, 347, 348, 349, 350, 351, 352, 353,
1088 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
1089 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,
1090 374, 375, 376, 377, 378,
1091];
1092
1093#[rustfmt::skip]
1095const EXPERT_SUBSET_CHARSET: [u16; 86] = [
1096 1, 231, 232, 235, 236, 237, 238, 13, 14, 15,
1097 99, 239, 240, 241, 242, 243, 244, 245, 246, 247,
1098 248, 27, 28, 249, 250, 251, 253, 254, 255, 256,
1099 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
1100 109, 110, 267, 268, 269, 270, 272, 300, 301, 302,
1101 305, 314, 315, 158, 155, 163, 320, 321, 322, 323,
1102 324, 325, 326, 150, 164, 169, 327, 328, 329, 330,
1103 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
1104 341, 342, 343, 344, 345, 346,
1105];
1106
1107#[rustfmt::skip]
1113const STANDARD_ENCODING_MAP: [(u8, u16); 149] = [
1114 (32, 1), (33, 2), (34, 3), (35, 4), (36, 5), (37, 6), (38, 7), (39, 8),
1115 (40, 9), (41, 10), (42, 11), (43, 12), (44, 13), (45, 14), (46, 15), (47, 16),
1116 (48, 17), (49, 18), (50, 19), (51, 20), (52, 21), (53, 22), (54, 23), (55, 24),
1117 (56, 25), (57, 26), (58, 27), (59, 28), (60, 29), (61, 30), (62, 31), (63, 32),
1118 (64, 33), (65, 34), (66, 35), (67, 36), (68, 37), (69, 38), (70, 39), (71, 40),
1119 (72, 41), (73, 42), (74, 43), (75, 44), (76, 45), (77, 46), (78, 47), (79, 48),
1120 (80, 49), (81, 50), (82, 51), (83, 52), (84, 53), (85, 54), (86, 55), (87, 56),
1121 (88, 57), (89, 58), (90, 59), (91, 60), (92, 61), (93, 62), (94, 63), (95, 64),
1122 (96, 65), (97, 66), (98, 67), (99, 68), (100, 69), (101, 70), (102, 71),
1123 (103, 72), (104, 73), (105, 74), (106, 75), (107, 76), (108, 77), (109, 78),
1124 (110, 79), (111, 80), (112, 81), (113, 82), (114, 83), (115, 84), (116, 85),
1125 (117, 86), (118, 87), (119, 88), (120, 89), (121, 90), (122, 91), (123, 92),
1126 (124, 93), (125, 94), (126, 95),
1127 (161, 96), (162, 97), (163, 98), (164, 99), (165, 100), (166, 101),
1128 (167, 102), (168, 103), (169, 104), (170, 105), (171, 106), (172, 107),
1129 (173, 108), (174, 109), (175, 110), (177, 111), (178, 112), (179, 113),
1130 (180, 114), (182, 115), (183, 116), (184, 117), (185, 118), (186, 119),
1131 (187, 120), (188, 121), (189, 122), (191, 123), (193, 124), (194, 125),
1132 (195, 126), (196, 127), (197, 128), (198, 129), (199, 130), (200, 131),
1133 (202, 132), (203, 133), (205, 134), (206, 135), (207, 136), (208, 137),
1134 (225, 138), (227, 139), (232, 140), (233, 141), (234, 142), (235, 143),
1135 (241, 144), (245, 145), (248, 146), (249, 147), (250, 148), (251, 149),
1136];
1137
1138#[rustfmt::skip]
1140pub const EXPERT_ENCODING_MAP: [(u8, u16); 165] = [
1141 (32, 1), (33, 229), (34, 230), (36, 231), (37, 232), (38, 233), (39, 234),
1142 (40, 235), (41, 236), (42, 237), (43, 238), (44, 13), (45, 14), (46, 15),
1143 (47, 99), (48, 239), (49, 240), (50, 241), (51, 242), (52, 243), (53, 244),
1144 (54, 245), (55, 246), (56, 247), (57, 248), (58, 27), (59, 28), (60, 249),
1145 (61, 250), (62, 251), (63, 252), (64, 253), (65, 254), (66, 255), (67, 256),
1146 (68, 257), (69, 258), (70, 259), (71, 260), (72, 261), (73, 262), (74, 263),
1147 (75, 264), (76, 265), (77, 266), (78, 109), (79, 110), (80, 267), (81, 268),
1148 (82, 269), (83, 270), (84, 271), (85, 272), (86, 273), (87, 274), (88, 275),
1149 (89, 276), (90, 277), (91, 278), (92, 279), (93, 280), (94, 281), (95, 282),
1150 (96, 283), (97, 284), (98, 285), (99, 286), (100, 287), (101, 288), (102, 289),
1151 (103, 290), (104, 291), (105, 292), (106, 293), (107, 294), (108, 295),
1152 (109, 296), (110, 297), (111, 298), (112, 299), (113, 300), (114, 301),
1153 (115, 302), (116, 303), (117, 304), (118, 305), (119, 306), (120, 307),
1154 (121, 308), (122, 309), (123, 310), (124, 311), (125, 312), (126, 313),
1155 (161, 314), (162, 315), (163, 316), (164, 317), (165, 318), (166, 158),
1156 (167, 155), (168, 163), (169, 319), (170, 320), (171, 321), (172, 322),
1157 (173, 323), (174, 324), (175, 325), (176, 326), (177, 150), (178, 164),
1158 (179, 169), (180, 327), (181, 328), (182, 329), (183, 330), (184, 331),
1159 (185, 332), (186, 333), (187, 334), (188, 335), (189, 336), (190, 337),
1160 (191, 338), (192, 339), (193, 340), (194, 341), (195, 342), (196, 343),
1161 (197, 344), (198, 345), (199, 346), (200, 347), (201, 348), (202, 349),
1162 (203, 350), (204, 351), (205, 352), (206, 353), (207, 354), (208, 355),
1163 (209, 356), (210, 357), (211, 358), (212, 359), (213, 360), (214, 361),
1164 (215, 362), (216, 363), (217, 364), (218, 365), (219, 366), (220, 367),
1165 (221, 368), (222, 369), (223, 370), (224, 371), (225, 372), (226, 373),
1166 (227, 374), (228, 375), (229, 376), (230, 377), (231, 378),
1167];
1168
1169#[cfg(test)]
1170mod tests {
1171 use super::*;
1172
1173 #[test]
1174 fn test_sid_resolution() {
1175 let string_index = vec![b"CustomGlyph".to_vec()];
1176 assert_eq!(get_sid_string(0, &string_index), ".notdef");
1177 assert_eq!(get_sid_string(34, &string_index), "A");
1178 assert_eq!(get_sid_string(391, &string_index), "CustomGlyph");
1179 assert_eq!(get_sid_string(999, &string_index), ".sid999");
1180 }
1181
1182 #[test]
1183 fn test_dict_number_encoding() {
1184 let data = [139u8, 15]; let entries = parse_dict_data(&data);
1187 assert_eq!(entries.len(), 1);
1188 assert_eq!(entries[0].operands, vec![0.0]);
1189
1190 let data = [247u8, 0, 15]; let entries = parse_dict_data(&data);
1193 assert_eq!(entries[0].operands, vec![108.0]);
1194
1195 let data = [251u8, 0, 15]; let entries = parse_dict_data(&data);
1198 assert_eq!(entries[0].operands, vec![-108.0]);
1199 }
1200
1201 #[test]
1202 fn test_empty_index() {
1203 let data = [0u8, 0];
1205 let (items, off) = parse_index(&data, 0).unwrap();
1206 assert!(items.is_empty());
1207 assert_eq!(off, 2);
1208 }
1209
1210 #[test]
1211 fn test_predefined_charset_iso_adobe() {
1212 let names = get_predefined_charset(0, 5, &[]);
1213 assert_eq!(names[0], ".notdef");
1214 assert_eq!(names[1], "space");
1215 assert_eq!(names[2], "exclam");
1216 assert_eq!(names.len(), 5);
1217 }
1218}