1use std::collections::HashMap;
9
10use crate::Engine;
11use crate::cmd::Cmd;
12use crate::eqtb::{Loc, Meaning, Val};
13use crate::hash::CsId;
14use crate::primitives_gen as pg;
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub struct CharMetrics {
19 pub width: i32,
21 pub height: i32,
23 pub depth: i32,
25 pub italic: i32,
27 pub index: u32,
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum LigOp {
34 Kern(i32),
36 Lig {
40 op: u8,
42 chr: u32,
44 },
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct LigKernStep {
50 pub next: u32,
53 pub op: LigOp,
55}
56
57#[derive(Default, Clone, Copy)]
60pub struct FxHasher(u64);
61
62impl std::hash::Hasher for FxHasher {
63 #[inline]
64 fn write(&mut self, bytes: &[u8]) {
65 for &b in bytes {
66 self.write_u64(u64::from(b));
67 }
68 }
69
70 #[inline]
71 fn write_u32(&mut self, i: u32) {
72 self.write_u64(u64::from(i));
73 }
74
75 #[inline]
76 fn write_u64(&mut self, i: u64) {
77 self.0 = (self.0.rotate_left(5) ^ i).wrapping_mul(0x517c_c1b7_2722_0a95);
78 }
79
80 #[inline]
81 fn finish(&self) -> u64 {
82 self.0
83 }
84}
85
86pub type LigKernMap = HashMap<u32, Box<[LigKernStep]>, std::hash::BuildHasherDefault<FxHasher>>;
88
89pub const LEFT_BOUNDARY: u32 = 0x11_0000;
91
92#[derive(Clone, Debug, PartialEq, Eq)]
94pub enum FontKind {
95 Null,
97 Tfm,
99 OpenType,
101}
102
103#[derive(Clone)]
106pub struct FontData(pub std::rc::Rc<dyn std::any::Any>);
107
108impl std::fmt::Debug for FontData {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 f.write_str("FontData(..)")
111 }
112}
113
114#[derive(Debug)]
117pub struct TfmData {
118 pub tfm: std::rc::Rc<retex_font::tfm::Tfm>,
120 pub raw: Vec<u8>,
122}
123
124pub fn tfm_font_data(tfm: retex_font::tfm::Tfm, raw: Vec<u8>) -> FontData {
126 FontData(std::rc::Rc::new(TfmData {
127 tfm: std::rc::Rc::new(tfm),
128 raw,
129 }))
130}
131
132#[derive(Clone, Debug)]
134pub struct FontInfo {
135 pub name: Vec<u8>,
137 pub size: i32,
139 pub design_size: i32,
141 pub checksum: u32,
144 pub params: Vec<i32>,
146 pub chars: HashMap<u32, CharMetrics, crate::hash::FastHash>,
148 pub hyphen_char: i32,
150 pub skew_char: i32,
152 pub kind: FontKind,
154 pub handle: u64,
157 pub char_codes: [HashMap<u32, i32>; 4],
159 pub ignore_ligatures: i32,
161 pub data: Option<FontData>,
163 pub lig_kern: LigKernMap,
167 pub right_boundary: Option<u32>,
169 pub expansion: Option<crate::expansion::FontExpansion>,
171 pub math_constants: Option<std::rc::Rc<MathConstants>>,
175 pub math_chars: Option<std::rc::Rc<HashMap<u32, MathCharInfo>>>,
178 pub size_spec: i32,
181 pub vf: Option<std::rc::Rc<VirtualFont>>,
184}
185
186#[derive(Debug, Default)]
189pub struct VirtualFont {
190 pub fonts: Vec<u32>,
193 pub packets: HashMap<u32, Box<[retex_font::vf::Command]>, crate::hash::FastHash>,
195}
196
197const VF_MAX_DEPTH: u32 = 8;
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub struct ExtPart {
203 pub code: u32,
205 pub start: i32,
207 pub end: i32,
209 pub advance: i32,
211 pub extender: bool,
213}
214
215#[derive(Debug, Clone, Default, PartialEq, Eq)]
217pub struct MathCharInfo {
218 pub next: Option<u32>,
220 pub top_accent: Option<i32>,
222 pub vert: Option<std::rc::Rc<[ExtPart]>>,
224 pub horiz: Option<std::rc::Rc<[ExtPart]>>,
226}
227
228pub type MathConstants = HashMap<String, i32>;
230
231impl Default for FontInfo {
232 fn default() -> Self {
233 FontInfo::null()
234 }
235}
236
237impl FontInfo {
238 pub fn null() -> Self {
240 FontInfo {
241 name: b"nullfont".to_vec(),
242 size: 0,
243 design_size: 0,
244 checksum: 0,
245 params: vec![0; 8],
246 chars: HashMap::default(),
247 hyphen_char: '-' as i32,
248 skew_char: -1,
249 kind: FontKind::Null,
250 handle: 0,
251 char_codes: Default::default(),
252 ignore_ligatures: 0,
253 lig_kern: LigKernMap::default(),
254 right_boundary: None,
255 math_constants: None,
256 math_chars: None,
257 size_spec: i32::MIN,
258 data: None,
259 expansion: None,
260 vf: None,
261 }
262 }
263}
264
265#[derive(Clone, Debug)]
267pub struct FontRequest {
268 pub name: Vec<u8>,
270 pub size: i32,
273}
274
275pub trait FontLoader {
277 fn load(&mut self, req: &FontRequest) -> Option<FontInfo>;
279}
280
281#[derive(Default)]
283pub struct FontTable {
284 pub fonts: Vec<FontInfo>,
286 pub id_cs: Vec<CsId>,
288 pub loader: Option<Box<dyn FontLoader>>,
290 pub dense: Vec<DenseMetrics>,
293}
294
295const DENSE_CHARS: usize = 0x800;
297
298#[derive(Clone, Debug, Default)]
300pub struct DenseMetrics {
301 metrics: Box<[CharMetrics]>,
302 present: Box<[u64]>,
303 lk: Box<[(u32, u32)]>,
306 steps: Box<[LigKernStep]>,
307 pairs: Box<[u16]>,
310}
311
312impl FontTable {
313 pub fn sync_dense(&mut self) {
317 while self.dense.len() < self.fonts.len() {
318 let f = &self.fonts[self.dense.len()];
319 let mut metrics = vec![CharMetrics::default(); DENSE_CHARS].into_boxed_slice();
320 let mut present = vec![0u64; DENSE_CHARS / 64].into_boxed_slice();
321 for (&c, m) in &f.chars {
322 if (c as usize) < DENSE_CHARS {
323 metrics[c as usize] = *m;
324 present[c as usize / 64] |= 1 << (c % 64);
325 }
326 }
327 let mut lk = Vec::new();
328 let mut steps: Vec<LigKernStep> = Vec::new();
329 let mut pairs = Vec::new();
330 if !f.lig_kern.is_empty() {
331 lk = vec![(0u32, 0u32); 256];
332 pairs = vec![0u16; 128 * 128];
333 for (c, e) in lk.iter_mut().enumerate() {
334 if let Some(p) = f.lig_kern.get(&(c as u32)) {
335 *e = (steps.len() as u32, p.len() as u32);
336 for st in p.iter() {
337 if c < 128 && st.next < 128 && steps.len() < 0xFFFF {
338 pairs[c * 128 + st.next as usize] = steps.len() as u16 + 1;
339 }
340 steps.push(*st);
341 }
342 }
343 }
344 }
345 self.dense.push(DenseMetrics {
346 metrics,
347 present,
348 lk: lk.into_boxed_slice(),
349 steps: steps.into_boxed_slice(),
350 pairs: pairs.into_boxed_slice(),
351 });
352 }
353 }
354
355 pub fn invalidate_dense(&mut self, f: u32) {
357 self.dense.truncate(f as usize);
358 }
359
360 #[inline]
362 pub fn char_metrics(&self, f: u32, c: u32) -> Option<&CharMetrics> {
363 if let Some(d) = self.dense.get(f as usize)
364 && (c as usize) < DENSE_CHARS
365 {
366 return if d.present[c as usize / 64] & (1 << (c % 64)) != 0 {
367 Some(&d.metrics[c as usize])
368 } else {
369 None
370 };
371 }
372 self.fonts.get(f as usize)?.chars.get(&c)
373 }
374}
375
376impl std::fmt::Debug for FontTable {
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 f.debug_struct("FontTable")
379 .field("fonts", &self.fonts.len())
380 .finish_non_exhaustive()
381 }
382}
383
384pub const MAX_FONT_DIMEN: i32 = 1 << 20;
386
387impl Engine {
388 pub fn font(&self, f: u32) -> &FontInfo {
390 self.fonts
391 .fonts
392 .get(f as usize)
393 .unwrap_or(&self.fonts.fonts[0])
394 }
395
396 pub fn font_exists(&self, f: i32) -> bool {
398 f >= 0 && (f as usize) < self.fonts.fonts.len()
399 }
400
401 pub fn font_id_cs(&self, f: u32) -> CsId {
403 self.fonts
404 .id_cs
405 .get(f as usize)
406 .copied()
407 .unwrap_or(self.cs.frozen_null_font)
408 }
409
410 pub fn quad(&self, f: u32) -> i32 {
412 self.font(f).params.get(6).copied().unwrap_or(0)
413 }
414
415 pub fn x_height(&self, f: u32) -> i32 {
417 self.font(f).params.get(5).copied().unwrap_or(0)
418 }
419
420 pub fn font_has_char(&self, f: u32, c: u32) -> bool {
422 self.font(f).chars.contains_key(&c)
423 }
424
425 pub fn font_char_dimen(&self, f: u32, c: u32, which: u32) -> i32 {
427 match self.font(f).chars.get(&c) {
428 Some(m) => match which {
429 0 => m.width,
430 1 => m.height,
431 2 => m.depth,
432 _ => m.italic,
433 },
434 None => 0,
435 }
436 }
437
438 pub fn print_font_name_and_size(&mut self, f: u32) {
441 let (name, size, dsize) = {
442 let fi = self.font(f);
443 (fi.name.clone(), fi.size, fi.design_size)
444 };
445 self.printer.print_bytes(&name);
446 if size != dsize {
447 self.printer.print_str(" at ");
448 self.printer.print_scaled(size);
449 self.printer.print_str("pt");
450 }
451 }
452
453 pub fn scan_font_ident(&mut self) {
455 self.get_nonblank_noncall();
456 let f: i32 = match self.cur_cmd {
457 Cmd::DefFont => self.eqtb.cur_font.val as i32,
458 Cmd::SetFont => self.cur_chr as i32,
459 Cmd::DefFamily => {
460 let size = self.cur_chr;
461 self.scan_math_family();
462 let fam = self.cur_val as u32;
463 self.eqtb.math_font[(size * crate::eqtb::NUM_FAMILIES as u32 + fam) as usize].val
464 as i32
465 }
466 _ => {
467 self.print_err("Missing font identifier");
468 self.help(&[
469 "I was looking for a control sequence whose",
470 "current meaning has been defined by \\font.",
471 ]);
472 self.back_error();
473 0
474 }
475 };
476 self.cur_val = f;
477 }
478
479 pub fn find_font_dimen(&mut self, _writing: bool) -> i32 {
486 self.scan_int();
487 let n = self.cur_val;
488 self.scan_font_ident();
489 let f = self.cur_val as usize;
490 let ok = n > 0 && n <= MAX_FONT_DIMEN;
491 if ok {
492 let params = &mut self.fonts.fonts[f].params;
493 if n as usize >= params.len() {
494 params.resize(n as usize + 1, 0);
495 }
496 }
497 if !ok {
498 self.print_err("Font ");
499 let cs = self.font_id_cs(f as u32);
500 let name = self.hash.name(cs).to_vec();
501 self.print_esc_bytes(&name);
502 self.printer.print_str(" has only ");
503 self.printer
504 .print_int((self.fonts.fonts[f].params.len() as i64 - 1).max(0));
505 self.printer.print_str(" fontdimen parameters");
506 self.help(&[
507 "To increase the number of font parameters, you must",
508 "use \\fontdimen immediately after the \\font is loaded.",
509 ]);
510 self.error();
511 self.font_dimen_loc = None;
512 return 0;
513 }
514 self.font_dimen_loc = Some((f as u32, n as u32));
515 self.fonts.fonts[f].params[n as usize]
516 }
517
518 pub fn set_font_dimen(&mut self, loc: Option<(u32, u32)>, v: i32) {
520 if let Some((f, n)) = loc {
521 self.fonts.fonts[f as usize].params[n as usize] = v;
522 }
523 }
524
525 pub fn font_int_param(&mut self, f: u32, n: u32) -> i32 {
527 match n {
528 0 => self.font(f).hyphen_char,
529 1 => self.font(f).skew_char,
530 2..=5 => {
531 self.scan_char_num();
532 let c = self.cur_val as u32;
533 if n < 5 && self.fonts.char_metrics(f, c).is_none() {
536 return 0;
537 }
538 self.font(f).char_codes[(n - 2) as usize]
539 .get(&c)
540 .copied()
541 .unwrap_or(if n == 4 { 1000 } else { 0 })
542 }
543 _ => self.font(f).ignore_ligatures,
544 }
545 }
546
547 pub fn set_font_int_param(&mut self, f: u32, n: u32, v: i32) {
549 if let Some(fi) = self.fonts.fonts.get_mut(f as usize) {
550 match n {
551 0 => fi.hyphen_char = v,
552 1 => fi.skew_char = v,
553 _ => fi.ignore_ligatures = v,
554 }
555 }
556 }
557
558 pub fn set_font_char_code(&mut self, f: u32, n: u32, c: u32, v: i32) {
561 if n < 5 && self.fonts.char_metrics(f, c).is_none() {
562 return;
563 }
564 if let Some(fi) = self.fonts.fonts.get_mut(f as usize) {
565 fi.char_codes[(n - 2) as usize].insert(c, v);
566 }
567 }
568
569 pub fn new_font(&mut self, global: bool) {
571 if self.job_name.is_none() {
572 self.open_log_file();
573 }
574 self.get_r_token();
575 let u = self.cur_cs;
576 self.define(
577 global,
578 Loc::Cs(u),
579 Val::Meaning(Meaning::prim(Cmd::SetFont, 0)),
580 );
581 self.scan_optional_equals();
582 let name = self.scan_file_name();
583 self.files.name_in_progress = true;
585 let size: i32 = if self.scan_keyword("at") {
586 self.scan_normal_dimen();
587 let s = self.cur_val;
588 if s <= 0 || s >= 0o1000000000 {
589 self.print_err("Improper `at' size (");
590 self.printer.print_scaled(s);
591 self.printer.print_str("pt), replaced by 10pt");
592 self.help(&[
593 "I can only handle fonts at positive sizes that are",
594 "less than 2048pt, so I've changed what you said to 10pt.",
595 ]);
596 self.error();
597 10 * retex_arith::UNITY
598 } else {
599 s
600 }
601 } else if self.scan_keyword("scaled") {
602 self.scan_int();
603 let s = -self.cur_val;
604 if self.cur_val <= 0 || self.cur_val > 32768 {
605 self.print_err("Illegal magnification has been changed to 1000");
606 self.help(&["The magnification ratio must be between 1 and 32768."]);
607 self.int_error(self.cur_val);
608 -1000
609 } else {
610 s
611 }
612 } else {
613 -1000
614 };
615 self.files.name_in_progress = false;
616 let existing = self
620 .fonts
621 .fonts
622 .iter()
623 .enumerate()
624 .skip(1)
625 .find(|(_, fi)| fi.name == name && fi.size_spec == size)
626 .map(|(i, _)| i);
627 let f = match existing {
628 Some(i) => i as u32,
629 None => match self.load_font(&name, size) {
630 Some(f) => f,
631 None => {
632 self.print_err("Font ");
633 self.sprint_cs(u);
634 self.printer.print_char('=' as u32);
635 self.printer.print_bytes(&name);
636 if size >= 0 {
637 self.printer.print_str(" at ");
638 self.printer.print_scaled(size);
639 self.printer.print_str("pt");
640 } else if size != -1000 {
641 self.printer.print_str(" scaled ");
642 self.printer.print_int(i64::from(-size));
643 }
644 self.printer
645 .print_str(" not loadable: Metric (TFM) file not found");
646 self.help(&[
647 "I wasn't able to read the size data for this font,",
648 "so I will ignore the font specification.",
649 "[Wizards can fix TFM files using TFtoPL/PLtoTF.]",
650 "You might try inserting a different font spec;",
651 "e.g., type `I\\font<same font id>=<substitute font name>'.",
652 ]);
653 self.error();
654 0
655 }
656 },
657 };
658 if f != 0 && self.fonts.fonts[f as usize].size_spec == i32::MIN {
659 self.fonts.fonts[f as usize].size_spec = size;
660 }
661 if f != 0 {
664 let name = self.hash.name(u).to_vec();
665 let id = self.hash.new_frozen(&name);
666 self.eqtb.ensure_cs(id);
667 self.eqtb.cs[id.0 as usize].val = Meaning::prim(Cmd::SetFont, f);
668 self.fonts.id_cs[f as usize] = id;
669 }
670 self.define(
671 global,
672 Loc::Cs(u),
673 Val::Meaning(Meaning::prim(Cmd::SetFont, f)),
674 );
675 }
676
677 pub fn load_font(&mut self, name: &[u8], size: i32) -> Option<u32> {
680 if let Some(h) = self.lua.clone()
681 && h.has_callback("define_font")
682 {
683 return h.define_font(self, name, size);
684 }
685 let req = FontRequest {
686 name: name.to_vec(),
687 size,
688 };
689 if self.fonts.loader.is_none() {
690 return self.load_tfm_font(name, size, name);
691 }
692 let mut loader = self.fonts.loader.take()?;
693 let info = loader.load(&req);
694 self.fonts.loader = Some(loader);
695 let mut info = info?;
696 info.hyphen_char = self.eqtb.int(pg::assign_int::DEFAULTHYPHENCHAR);
697 info.skew_char = self.eqtb.int(pg::assign_int::DEFAULTSKEWCHAR);
698 Some(self.register_font(info))
699 }
700
701 pub fn register_font(&mut self, mut info: FontInfo) -> u32 {
703 if info.hyphen_char == i32::MIN {
704 info.hyphen_char = self.eqtb.int(pg::assign_int::DEFAULTHYPHENCHAR);
705 }
706 if info.skew_char == i32::MIN {
707 info.skew_char = self.eqtb.int(pg::assign_int::DEFAULTSKEWCHAR);
708 }
709 self.fonts.fonts.push(info);
710 self.fonts.id_cs.push(self.cs.frozen_null_font);
711 self.fonts.fonts.len() as u32 - 1
712 }
713
714 pub fn load_tfm(&mut self, name: &[u8], size: i32) -> Option<FontInfo> {
717 let name_s = String::from_utf8_lossy(name).into_owned();
718 let path = self
719 .kpse
720 .as_ref()?
721 .borrow_mut()
722 .find_file(&name_s, retex_kpath::FileFormat::Tfm)?;
723 let data = self.read_file_recorded(&path)?;
724 let tfm = retex_font::tfm::Tfm::parse(&data).ok()?;
725 let scaled = tfm.scaled(size);
726 let mut params = vec![0; scaled.params.len() + 1];
727 params[1..].copy_from_slice(&scaled.params);
728 let mut chars =
729 HashMap::with_capacity_and_hasher(scaled.chars.len(), crate::hash::FastHash::default());
730 for (c, m) in &scaled.chars {
731 chars.insert(
732 u32::from(*c),
733 CharMetrics {
734 width: m[0],
735 height: m[1],
736 depth: m[2],
737 italic: m[3],
738 index: u32::from(*c),
739 },
740 );
741 }
742 Some(FontInfo {
743 name: name.to_vec(),
744 size: scaled.size,
745 design_size: tfm.design_size,
746 checksum: tfm.checksum,
747 params,
748 chars,
749 hyphen_char: i32::MIN,
750 skew_char: i32::MIN,
751 kind: FontKind::Tfm,
752 handle: 0,
753 char_codes: Default::default(),
754 ignore_ligatures: 0,
755 lig_kern: lig_kern_table(&tfm, &scaled.kerns),
756 right_boundary: tfm.bchar.map(u32::from),
757 math_constants: None,
758 math_chars: None,
759 size_spec: i32::MIN,
760 data: Some(tfm_font_data(tfm, data)),
761 expansion: None,
762 vf: None,
763 })
764 }
765
766 pub fn load_tfm_font(&mut self, tfm_name: &[u8], size: i32, font_name: &[u8]) -> Option<u32> {
772 self.load_tfm_font_at_depth(tfm_name, size, font_name, 0)
773 }
774
775 fn load_tfm_font_at_depth(
776 &mut self,
777 tfm_name: &[u8],
778 size: i32,
779 font_name: &[u8],
780 depth: u32,
781 ) -> Option<u32> {
782 let mut info = self.load_tfm(tfm_name, size)?;
783 info.name = font_name.to_vec();
784 let size = info.size;
785 let f = self.register_font(info);
786 if depth < VF_MAX_DEPTH {
787 self.attach_virtual_font(f, tfm_name, size, depth);
788 }
789 Some(f)
790 }
791
792 fn attach_virtual_font(&mut self, f: u32, name: &[u8], size: i32, depth: u32) {
795 let name_s = String::from_utf8_lossy(name).into_owned();
796 let Some(path) = self.kpse.as_ref().and_then(|k| {
797 k.borrow_mut()
798 .find_file(&name_s, retex_kpath::FileFormat::Vf)
799 }) else {
800 return;
801 };
802 let Some(data) = self.read_file_recorded(&path) else {
803 return;
804 };
805 let Ok(vf) = retex_font::vf::Vf::parse(&data) else {
806 return;
807 };
808 let mut fonts = Vec::with_capacity(vf.fonts.len());
809 for (i, lf) in vf.fonts.iter().enumerate() {
810 let lsize = vf.font_size(i, size);
811 let lname = lf.name.as_bytes();
812 let existing = (1..self.fonts.fonts.len()).find(|&g| {
813 let fi = &self.fonts.fonts[g];
814 fi.kind == FontKind::Tfm && fi.size == lsize && fi.name == lname
815 });
816 let id = match existing {
817 Some(g) => g as u32,
818 None => self
819 .load_tfm_font_at_depth(lname, lsize, lname, depth + 1)
820 .unwrap_or(0),
821 };
822 fonts.push(id);
823 }
824 let mut packets =
825 HashMap::with_capacity_and_hasher(vf.packets.len(), crate::hash::FastHash::default());
826 for &c in vf.packets.keys() {
827 if let Some(cmds) = vf.commands(c, size) {
828 packets.insert(c, cmds.into_boxed_slice());
829 }
830 }
831 self.fonts.fonts[f as usize].vf = Some(std::rc::Rc::new(VirtualFont { fonts, packets }));
832 }
833
834 #[inline]
836 pub fn lig_kern_step(&self, f: u32, left: u32, right: u32) -> Option<LigOp> {
837 if let Some(d) = self.fonts.dense.get(f as usize)
838 && left < 256
839 {
840 if d.lk.is_empty() {
841 return None;
842 }
843 if left < 128 && right < 128 {
844 let i = d.pairs[(left * 128 + right) as usize];
845 return if i == 0 {
846 None
847 } else {
848 Some(d.steps[i as usize - 1].op)
849 };
850 }
851 let (start, len) = d.lk[left as usize];
852 return d.steps[start as usize..(start + len) as usize]
853 .iter()
854 .find(|s| s.next == right)
855 .map(|s| s.op);
856 }
857 let fi = self.font(f);
858 if fi.lig_kern.is_empty() {
859 return None;
860 }
861 fi.lig_kern
862 .get(&left)?
863 .iter()
864 .find(|s| s.next == right)
865 .map(|s| s.op)
866 }
867
868 pub fn math_char(&self, f: u32, c: u32) -> Option<&MathCharInfo> {
870 self.font(f).math_chars.as_ref()?.get(&c)
871 }
872
873 pub fn tfm_of(&self, f: u32) -> Option<std::rc::Rc<retex_font::tfm::Tfm>> {
875 let d = self.font(f).data.as_ref()?;
876 d.0.downcast_ref::<TfmData>().map(|t| t.tfm.clone())
877 }
878
879 pub fn letterspace_font(&mut self, global: bool) {
881 self.get_r_token();
882 let u = self.cur_cs;
883 self.scan_font_ident();
884 let f = self.cur_val as u32;
885 self.scan_int();
886 let _amount = self.cur_val;
887 self.define(
889 global,
890 Loc::Cs(u),
891 Val::Meaning(Meaning::prim(Cmd::SetFont, f)),
892 );
893 }
894
895 pub fn copy_font(&mut self, global: bool) {
897 self.get_r_token();
898 let u = self.cur_cs;
899 self.scan_font_ident();
900 let f = self.cur_val as usize;
901 let copy = self.fonts.fonts[f].clone();
902 self.fonts.fonts.push(copy);
903 self.fonts.id_cs.push(self.cs.frozen_null_font);
904 let nf = self.fonts.fonts.len() as u32 - 1;
905 self.define(
906 global,
907 Loc::Cs(u),
908 Val::Meaning(Meaning::prim(Cmd::SetFont, nf)),
909 );
910 }
911}
912
913pub(crate) fn lig_kern_table(tfm: &retex_font::tfm::Tfm, kerns: &[i32]) -> LigKernMap {
916 let nl = tfm.lig_kern.len();
917 let read = |start: usize| -> Vec<LigKernStep> {
918 let mut steps: Vec<LigKernStep> = Vec::new();
919 let mut k = start;
920 for _ in 0..nl {
921 let Some(w) = tfm.lig_kern.get(k) else { break };
922 if w.skip <= 128 {
923 let next = u32::from(w.next);
924 if !steps.iter().any(|s| s.next == next) {
925 let op = if w.op >= 128 {
926 let ki = 256 * (usize::from(w.op) - 128) + usize::from(w.rem);
927 LigOp::Kern(kerns.get(ki).copied().unwrap_or(0))
928 } else {
929 LigOp::Lig {
930 op: w.op,
931 chr: u32::from(w.rem),
932 }
933 };
934 steps.push(LigKernStep { next, op });
935 }
936 }
937 if w.skip >= 128 {
938 break;
939 }
940 k += usize::from(w.skip) + 1;
941 }
942 steps
943 };
944 let mut map = LigKernMap::default();
945 for c in u32::from(tfm.bc)..=u32::from(tfm.ec) {
946 if let Some(start) = tfm.lig_kern_start(c) {
947 let p = read(start);
948 if !p.is_empty() {
949 map.insert(c, p.into_boxed_slice());
950 }
951 }
952 }
953 if let Some(l) = tfm.bchar_label {
954 let p = read(l);
955 if !p.is_empty() {
956 map.insert(LEFT_BOUNDARY, p.into_boxed_slice());
957 }
958 }
959 map
960}