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>>,
185}
186
187#[derive(Debug, Default)]
190pub struct VirtualFont {
191 pub fonts: Vec<u32>,
194 pub packets: HashMap<u32, Box<[PacketCommand]>, crate::hash::FastHash>,
196}
197
198#[derive(Debug, Clone, PartialEq)]
202pub enum PacketCommand {
203 Font(usize),
205 FontId(u32),
207 Char(u32),
209 Rule {
212 height: i32,
214 width: i32,
216 },
217 Right(i32),
219 Down(i32),
221 Push,
223 Pop,
225 Special(Vec<u8>),
227 Pdf(u8, Vec<u8>),
229 Lua(i32),
234 Node(crate::nodes::NodeRef),
237}
238
239impl From<&retex_font::vf::Command> for PacketCommand {
240 fn from(c: &retex_font::vf::Command) -> Self {
241 use retex_font::vf::Command;
242 match c {
243 Command::Font(i) => PacketCommand::Font(*i),
244 Command::Char(c) => PacketCommand::Char(*c),
245 Command::Rule { height, width } => PacketCommand::Rule {
246 height: *height,
247 width: *width,
248 },
249 Command::Right(x) => PacketCommand::Right(*x),
250 Command::Down(y) => PacketCommand::Down(*y),
251 Command::Push => PacketCommand::Push,
252 Command::Pop => PacketCommand::Pop,
253 Command::Special(s) => PacketCommand::Special(s.clone()),
254 }
255 }
256}
257
258const VF_MAX_DEPTH: u32 = 8;
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub struct ExtPart {
264 pub code: u32,
266 pub start: i32,
268 pub end: i32,
270 pub advance: i32,
272 pub extender: bool,
274}
275
276#[derive(Debug, Clone, Default, PartialEq, Eq)]
278pub struct MathCharInfo {
279 pub next: Option<u32>,
281 pub top_accent: Option<i32>,
283 pub vert: Option<std::rc::Rc<[ExtPart]>>,
285 pub horiz: Option<std::rc::Rc<[ExtPart]>>,
287}
288
289pub type MathConstants = HashMap<String, i32>;
291
292impl Default for FontInfo {
293 fn default() -> Self {
294 FontInfo::null()
295 }
296}
297
298impl FontInfo {
299 pub fn null() -> Self {
301 FontInfo {
302 name: b"nullfont".to_vec(),
303 size: 0,
304 design_size: 0,
305 checksum: 0,
306 params: vec![0; 8],
307 chars: HashMap::default(),
308 hyphen_char: '-' as i32,
309 skew_char: -1,
310 kind: FontKind::Null,
311 handle: 0,
312 char_codes: Default::default(),
313 ignore_ligatures: 0,
314 lig_kern: LigKernMap::default(),
315 right_boundary: None,
316 math_constants: None,
317 math_chars: None,
318 size_spec: i32::MIN,
319 data: None,
320 expansion: None,
321 vf: None,
322 }
323 }
324}
325
326#[derive(Clone, Debug)]
328pub struct FontRequest {
329 pub name: Vec<u8>,
331 pub size: i32,
334}
335
336pub trait FontLoader {
338 fn load(&mut self, req: &FontRequest) -> Option<FontInfo>;
340}
341
342#[derive(Default)]
344pub struct FontTable {
345 pub fonts: Vec<FontInfo>,
347 pub id_cs: Vec<CsId>,
349 pub loader: Option<Box<dyn FontLoader>>,
351 pub dense: Vec<DenseMetrics>,
354}
355
356const DENSE_CHARS: usize = 0x800;
358
359#[derive(Clone, Debug, Default)]
361pub struct DenseMetrics {
362 metrics: Box<[CharMetrics]>,
363 present: Box<[u64]>,
364 lk: Box<[(u32, u32)]>,
367 steps: Box<[LigKernStep]>,
368 pairs: Box<[u16]>,
371}
372
373impl FontTable {
374 pub fn sync_dense(&mut self) {
378 while self.dense.len() < self.fonts.len() {
379 let f = &self.fonts[self.dense.len()];
380 let mut metrics = vec![CharMetrics::default(); DENSE_CHARS].into_boxed_slice();
381 let mut present = vec![0u64; DENSE_CHARS / 64].into_boxed_slice();
382 for (&c, m) in &f.chars {
383 if (c as usize) < DENSE_CHARS {
384 metrics[c as usize] = *m;
385 present[c as usize / 64] |= 1 << (c % 64);
386 }
387 }
388 let mut lk = Vec::new();
389 let mut steps: Vec<LigKernStep> = Vec::new();
390 let mut pairs = Vec::new();
391 if !f.lig_kern.is_empty() {
392 lk = vec![(0u32, 0u32); 256];
393 pairs = vec![0u16; 128 * 128];
394 for (c, e) in lk.iter_mut().enumerate() {
395 if let Some(p) = f.lig_kern.get(&(c as u32)) {
396 *e = (steps.len() as u32, p.len() as u32);
397 for st in p.iter() {
398 if c < 128 && st.next < 128 && steps.len() < 0xFFFF {
399 pairs[c * 128 + st.next as usize] = steps.len() as u16 + 1;
400 }
401 steps.push(*st);
402 }
403 }
404 }
405 }
406 self.dense.push(DenseMetrics {
407 metrics,
408 present,
409 lk: lk.into_boxed_slice(),
410 steps: steps.into_boxed_slice(),
411 pairs: pairs.into_boxed_slice(),
412 });
413 }
414 }
415
416 pub fn invalidate_dense(&mut self, f: u32) {
418 self.dense.truncate(f as usize);
419 }
420
421 #[inline]
423 pub fn char_metrics(&self, f: u32, c: u32) -> Option<&CharMetrics> {
424 if let Some(d) = self.dense.get(f as usize)
425 && (c as usize) < DENSE_CHARS
426 {
427 return if d.present[c as usize / 64] & (1 << (c % 64)) != 0 {
428 Some(&d.metrics[c as usize])
429 } else {
430 None
431 };
432 }
433 self.fonts.get(f as usize)?.chars.get(&c)
434 }
435}
436
437impl std::fmt::Debug for FontTable {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 f.debug_struct("FontTable")
440 .field("fonts", &self.fonts.len())
441 .finish_non_exhaustive()
442 }
443}
444
445pub const MAX_FONT_DIMEN: i32 = 1 << 20;
447
448impl Engine {
449 pub fn font(&self, f: u32) -> &FontInfo {
451 self.fonts
452 .fonts
453 .get(f as usize)
454 .unwrap_or(&self.fonts.fonts[0])
455 }
456
457 pub fn font_exists(&self, f: i32) -> bool {
459 f >= 0 && (f as usize) < self.fonts.fonts.len()
460 }
461
462 pub fn font_id_cs(&self, f: u32) -> CsId {
464 self.fonts
465 .id_cs
466 .get(f as usize)
467 .copied()
468 .unwrap_or(self.cs.frozen_null_font)
469 }
470
471 pub fn quad(&self, f: u32) -> i32 {
473 self.font(f).params.get(6).copied().unwrap_or(0)
474 }
475
476 pub fn x_height(&self, f: u32) -> i32 {
478 self.font(f).params.get(5).copied().unwrap_or(0)
479 }
480
481 pub fn font_has_char(&self, f: u32, c: u32) -> bool {
483 self.font(f).chars.contains_key(&c)
484 }
485
486 pub fn font_char_dimen(&self, f: u32, c: u32, which: u32) -> i32 {
488 match self.font(f).chars.get(&c) {
489 Some(m) => match which {
490 0 => m.width,
491 1 => m.height,
492 2 => m.depth,
493 _ => m.italic,
494 },
495 None => 0,
496 }
497 }
498
499 pub fn print_font_name_and_size(&mut self, f: u32) {
502 let (name, size, dsize) = {
503 let fi = self.font(f);
504 (fi.name.clone(), fi.size, fi.design_size)
505 };
506 self.printer.print_bytes(&name);
507 if size != dsize {
508 self.printer.print_str(" at ");
509 self.printer.print_scaled(size);
510 self.printer.print_str("pt");
511 }
512 }
513
514 pub fn scan_font_ident(&mut self) {
516 self.get_nonblank_noncall();
517 let f: i32 = match self.cur_cmd {
518 Cmd::DefFont => self.eqtb.cur_font.val as i32,
519 Cmd::SetFont => self.cur_chr as i32,
520 Cmd::DefFamily => {
521 let size = self.cur_chr;
522 self.scan_math_family();
523 let fam = self.cur_val as u32;
524 self.eqtb.math_font[(size * crate::eqtb::NUM_FAMILIES as u32 + fam) as usize].val
525 as i32
526 }
527 _ => {
528 self.print_err("Missing font identifier");
529 self.help(&[
530 "I was looking for a control sequence whose",
531 "current meaning has been defined by \\font.",
532 ]);
533 self.back_error();
534 0
535 }
536 };
537 self.cur_val = f;
538 }
539
540 pub fn find_font_dimen(&mut self, _writing: bool) -> i32 {
547 self.scan_int();
548 let n = self.cur_val;
549 self.scan_font_ident();
550 let f = self.cur_val as usize;
551 let ok = n > 0 && n <= MAX_FONT_DIMEN;
552 if ok {
553 let params = &mut self.fonts.fonts[f].params;
554 if n as usize >= params.len() {
555 params.resize(n as usize + 1, 0);
556 }
557 }
558 if !ok {
559 self.print_err("Font ");
560 let cs = self.font_id_cs(f as u32);
561 let name = self.hash.name(cs).to_vec();
562 self.print_esc_bytes(&name);
563 self.printer.print_str(" has only ");
564 self.printer
565 .print_int((self.fonts.fonts[f].params.len() as i64 - 1).max(0));
566 self.printer.print_str(" fontdimen parameters");
567 self.help(&[
568 "To increase the number of font parameters, you must",
569 "use \\fontdimen immediately after the \\font is loaded.",
570 ]);
571 self.error();
572 self.font_dimen_loc = None;
573 return 0;
574 }
575 self.font_dimen_loc = Some((f as u32, n as u32));
576 self.fonts.fonts[f].params[n as usize]
577 }
578
579 pub fn set_font_dimen(&mut self, loc: Option<(u32, u32)>, v: i32) {
581 if let Some((f, n)) = loc {
582 self.fonts.fonts[f as usize].params[n as usize] = v;
583 }
584 }
585
586 pub fn font_int_param(&mut self, f: u32, n: u32) -> i32 {
588 match n {
589 0 => self.font(f).hyphen_char,
590 1 => self.font(f).skew_char,
591 2..=5 => {
592 self.scan_char_num();
593 let c = self.cur_val as u32;
594 if n < 5 && self.fonts.char_metrics(f, c).is_none() {
597 return 0;
598 }
599 if n == 5 {
600 if self.fonts.char_metrics(f, c).is_none() {
604 return -1;
605 }
606 let tag = self
607 .tfm_of(f)
608 .and_then(|t| t.char_info(c).map(|ci| ci.tag))
609 .unwrap_or(0);
610 return match tag {
611 1 => 1,
612 2 => 2,
613 3 => 4,
614 _ => 0,
615 };
616 }
617 self.font(f).char_codes[(n - 2) as usize]
618 .get(&c)
619 .copied()
620 .unwrap_or(if n == 4 { 1000 } else { 0 })
621 }
622 _ => self.font(f).ignore_ligatures,
623 }
624 }
625
626 pub fn set_font_int_param(&mut self, f: u32, n: u32, v: i32) {
628 if let Some(fi) = self.fonts.fonts.get_mut(f as usize) {
629 match n {
630 0 => fi.hyphen_char = v,
631 1 => fi.skew_char = v,
632 _ => fi.ignore_ligatures = v,
633 }
634 }
635 }
636
637 pub fn set_font_char_code(&mut self, f: u32, n: u32, c: u32, v: i32) {
640 if n < 5 && self.fonts.char_metrics(f, c).is_none() {
641 return;
642 }
643 if let Some(fi) = self.fonts.fonts.get_mut(f as usize) {
644 fi.char_codes[(n - 2) as usize].insert(c, v);
645 }
646 }
647
648 pub fn new_font(&mut self, global: bool) {
650 if self.job_name.is_none() {
651 self.open_log_file();
652 }
653 self.get_r_token();
654 let u = self.cur_cs;
655 self.define(
656 global,
657 Loc::Cs(u),
658 Val::Meaning(Meaning::prim(Cmd::SetFont, 0)),
659 );
660 self.scan_optional_equals();
661 let name = self.scan_file_name();
662 self.files.name_in_progress = true;
664 let size: i32 = if self.scan_keyword("at") {
665 self.scan_normal_dimen();
666 let s = self.cur_val;
667 if s <= 0 || s >= 0o1000000000 {
668 self.print_err("Improper `at' size (");
669 self.printer.print_scaled(s);
670 self.printer.print_str("pt), replaced by 10pt");
671 self.help(&[
672 "I can only handle fonts at positive sizes that are",
673 "less than 2048pt, so I've changed what you said to 10pt.",
674 ]);
675 self.error();
676 10 * retex_arith::UNITY
677 } else {
678 s
679 }
680 } else if self.scan_keyword("scaled") {
681 self.scan_int();
682 let s = -self.cur_val;
683 if self.cur_val <= 0 || self.cur_val > 32768 {
684 self.print_err("Illegal magnification has been changed to 1000");
685 self.help(&["The magnification ratio must be between 1 and 32768."]);
686 self.int_error(self.cur_val);
687 -1000
688 } else {
689 s
690 }
691 } else {
692 -1000
693 };
694 self.files.name_in_progress = false;
695 let f = {
699 match self.load_font(&name, size) {
700 Some(f) => f,
701 None if self
702 .eqtb
703 .int(crate::primitives_gen::assign_int::SUPPRESSFONTNOTFOUNDERROR)
704 > 0 =>
705 {
706 0
707 }
708 None => {
709 self.print_err("Font ");
710 self.sprint_cs(u);
711 self.printer.print_char('=' as u32);
712 self.printer.print_bytes(&name);
713 if size >= 0 {
714 self.printer.print_str(" at ");
716 if size % retex_arith::UNITY == 0 {
717 self.printer.print_int(i64::from(size / retex_arith::UNITY));
718 } else {
719 self.printer.print_scaled(size);
720 }
721 self.printer.print_str("pt");
722 } else if size != -1000 {
723 self.printer.print_str(" scaled ");
724 self.printer.print_int(i64::from(-size));
725 }
726 self.printer
727 .print_str(" not loadable: metric data not found or bad");
728 self.help(&[
729 "I wasn't able to read the size data for this font,",
730 "so I will ignore the font specification.",
731 "[Wizards can fix TFM files using TFtoPL/PLtoTF.]",
732 "You might try inserting a different font spec;",
733 "e.g., type `I\\font<same font id>=<substitute font name>'.",
734 ]);
735 self.error();
736 0
737 }
738 }
739 };
740 if f != 0 && self.fonts.fonts[f as usize].size_spec == i32::MIN {
741 self.fonts.fonts[f as usize].size_spec = size;
742 }
743 if f != 0 {
746 let name = self.hash.name(u).to_vec();
747 let id = self.hash.new_frozen(&name);
748 self.eqtb.ensure_cs(id);
749 self.eqtb.cs[id.0 as usize].val = Meaning::prim(Cmd::SetFont, f);
750 self.fonts.id_cs[f as usize] = id;
751 }
752 self.define(
753 global,
754 Loc::Cs(u),
755 Val::Meaning(Meaning::prim(Cmd::SetFont, f)),
756 );
757 }
758
759 pub fn load_font(&mut self, name: &[u8], size: i32) -> Option<u32> {
762 if let Some(h) = self.lua.clone()
763 && h.has_callback("define_font")
764 {
765 return h.define_font(self, name, size);
766 }
767 let req = FontRequest {
768 name: name.to_vec(),
769 size,
770 };
771 if self.fonts.loader.is_none() {
772 return self.load_tfm_font(name, size, name);
773 }
774 let mut loader = self.fonts.loader.take()?;
775 let info = loader.load(&req);
776 self.fonts.loader = Some(loader);
777 let mut info = info?;
778 info.hyphen_char = self.eqtb.int(pg::assign_int::DEFAULTHYPHENCHAR);
779 info.skew_char = self.eqtb.int(pg::assign_int::DEFAULTSKEWCHAR);
780 Some(self.register_font(info))
781 }
782
783 pub fn register_font(&mut self, mut info: FontInfo) -> u32 {
785 if info.hyphen_char == i32::MIN {
786 info.hyphen_char = self.eqtb.int(pg::assign_int::DEFAULTHYPHENCHAR);
787 }
788 if info.skew_char == i32::MIN {
789 info.skew_char = self.eqtb.int(pg::assign_int::DEFAULTSKEWCHAR);
790 }
791 self.fonts.fonts.push(info);
792 self.fonts.id_cs.push(self.cs.frozen_null_font);
793 self.fonts.fonts.len() as u32 - 1
794 }
795
796 pub fn check_lua_loaded_font(&mut self, f: u32) {
799 let info = &self.fonts.fonts[f as usize];
800 if info.chars.is_empty() {
801 let msg = format!(
802 "lua-loaded font '{f}' with name '{}' has no characters",
803 String::from_utf8_lossy(&info.name)
804 );
805 self.luatex_warning("font", &msg);
806 }
807 }
808
809 pub fn load_tfm(&mut self, name: &[u8], size: i32) -> Option<FontInfo> {
812 let name_s = String::from_utf8_lossy(name).into_owned();
813 let path = self
814 .kpse
815 .as_ref()?
816 .borrow_mut()
817 .find_file(&name_s, retex_kpath::FileFormat::Tfm)?;
818 let data = self.read_file_recorded(&path)?;
819 let tfm = retex_font::tfm::Tfm::parse(&data).ok()?;
820 let scaled = tfm.scaled(size);
821 let mut params = vec![0; scaled.params.len() + 1];
822 params[1..].copy_from_slice(&scaled.params);
823 let mut chars =
824 HashMap::with_capacity_and_hasher(scaled.chars.len(), crate::hash::FastHash::default());
825 for (c, m) in &scaled.chars {
826 chars.insert(
827 u32::from(*c),
828 CharMetrics {
829 width: m[0],
830 height: m[1],
831 depth: m[2],
832 italic: m[3],
833 index: u32::from(*c),
834 },
835 );
836 }
837 Some(FontInfo {
838 name: name.to_vec(),
839 size: scaled.size,
840 design_size: tfm.design_size,
841 checksum: tfm.checksum,
842 params,
843 chars,
844 hyphen_char: i32::MIN,
845 skew_char: i32::MIN,
846 kind: FontKind::Tfm,
847 handle: 0,
848 char_codes: Default::default(),
849 ignore_ligatures: 0,
850 lig_kern: lig_kern_table(&tfm, &scaled.kerns),
851 right_boundary: tfm.bchar.map(u32::from),
852 math_constants: None,
853 math_chars: None,
854 size_spec: i32::MIN,
855 data: Some(tfm_font_data(tfm, data)),
856 expansion: None,
857 vf: None,
858 })
859 }
860
861 pub fn load_tfm_font(&mut self, tfm_name: &[u8], size: i32, font_name: &[u8]) -> Option<u32> {
867 self.load_tfm_font_at_depth(tfm_name, size, font_name, 0)
868 }
869
870 fn load_tfm_font_at_depth(
871 &mut self,
872 tfm_name: &[u8],
873 size: i32,
874 font_name: &[u8],
875 depth: u32,
876 ) -> Option<u32> {
877 let mut info = self.load_tfm(tfm_name, size)?;
878 info.name = font_name.to_vec();
879 let size = info.size;
880 let f = self.register_font(info);
881 if depth < VF_MAX_DEPTH {
882 self.attach_virtual_font(f, tfm_name, size, depth);
883 }
884 Some(f)
885 }
886
887 fn attach_virtual_font(&mut self, f: u32, name: &[u8], size: i32, depth: u32) {
890 let name_s = String::from_utf8_lossy(name).into_owned();
891 let Some(path) = self.kpse.as_ref().and_then(|k| {
892 k.borrow_mut()
893 .find_file(&name_s, retex_kpath::FileFormat::Vf)
894 }) else {
895 return;
896 };
897 let Some(data) = self.read_file_recorded(&path) else {
898 return;
899 };
900 let Ok(vf) = retex_font::vf::Vf::parse(&data) else {
901 return;
902 };
903 let mut fonts = Vec::with_capacity(vf.fonts.len());
904 for (i, lf) in vf.fonts.iter().enumerate() {
905 let lsize = vf.font_size(i, size);
906 let lname = lf.name.as_bytes();
907 let existing = (1..self.fonts.fonts.len()).find(|&g| {
908 let fi = &self.fonts.fonts[g];
909 fi.kind == FontKind::Tfm && fi.size == lsize && fi.name == lname
910 });
911 let id = match existing {
912 Some(g) => g as u32,
913 None => self
914 .load_tfm_font_at_depth(lname, lsize, lname, depth + 1)
915 .unwrap_or(0),
916 };
917 fonts.push(id);
918 }
919 let mut packets =
920 HashMap::with_capacity_and_hasher(vf.packets.len(), crate::hash::FastHash::default());
921 for &c in vf.packets.keys() {
922 if let Some(cmds) = vf.commands(c, size) {
923 let cmds = cmds
926 .iter()
927 .filter(|c| !matches!(c, retex_font::vf::Command::Special(_)))
928 .map(PacketCommand::from);
929 packets.insert(c, cmds.collect());
930 }
931 }
932 self.fonts.fonts[f as usize].vf = Some(std::rc::Rc::new(VirtualFont { fonts, packets }));
933 }
934
935 #[inline]
937 pub fn lig_kern_step(&self, f: u32, left: u32, right: u32) -> Option<LigOp> {
938 if let Some(d) = self.fonts.dense.get(f as usize)
939 && left < 256
940 {
941 if d.lk.is_empty() {
942 return None;
943 }
944 if left < 128 && right < 128 {
945 let i = d.pairs[(left * 128 + right) as usize];
946 return if i == 0 {
947 None
948 } else {
949 Some(d.steps[i as usize - 1].op)
950 };
951 }
952 let (start, len) = d.lk[left as usize];
953 return d.steps[start as usize..(start + len) as usize]
954 .iter()
955 .find(|s| s.next == right)
956 .map(|s| s.op);
957 }
958 let fi = self.font(f);
959 if fi.lig_kern.is_empty() {
960 return None;
961 }
962 fi.lig_kern
963 .get(&left)?
964 .iter()
965 .find(|s| s.next == right)
966 .map(|s| s.op)
967 }
968
969 #[cold]
974 pub fn base_mode_kern(&self, f: u32, left: u32, right: u32) -> Option<LigOp> {
975 let fi = self.font(f);
976 if fi.kind != FontKind::OpenType || fi.math_constants.is_some() {
978 return None;
979 }
980 let lf = fi
981 .data
982 .as_ref()?
983 .0
984 .downcast_ref::<std::sync::Arc<retex_font::loader::LoadedFont>>()?;
985 if lf.request.mode != retex_font::request::Mode::Base {
986 return None;
987 }
988 let l = u16::try_from(fi.chars.get(&left)?.index).ok()?;
989 let r = u16::try_from(fi.chars.get(&right)?.index).ok()?;
990 let k = lf.shaper.pair_kern(&lf.file, &lf.tex, l, r);
991 (k != 0).then_some(LigOp::Kern(k))
992 }
993
994 pub fn math_char(&self, f: u32, c: u32) -> Option<&MathCharInfo> {
996 self.font(f).math_chars.as_ref()?.get(&c)
997 }
998
999 pub fn tfm_of(&self, f: u32) -> Option<std::rc::Rc<retex_font::tfm::Tfm>> {
1001 let d = self.font(f).data.as_ref()?;
1002 d.0.downcast_ref::<TfmData>().map(|t| t.tfm.clone())
1003 }
1004
1005 pub fn letterspace_font(&mut self, global: bool) {
1007 self.get_r_token();
1008 let u = self.cur_cs;
1009 self.scan_font_ident();
1010 let f = self.cur_val as u32;
1011 self.scan_int();
1012 let _amount = self.cur_val;
1013 self.define(
1015 global,
1016 Loc::Cs(u),
1017 Val::Meaning(Meaning::prim(Cmd::SetFont, f)),
1018 );
1019 }
1020
1021 pub fn copy_font(&mut self, global: bool) {
1023 self.get_r_token();
1024 let u = self.cur_cs;
1025 self.scan_font_ident();
1026 let f = self.cur_val as usize;
1027 let copy = self.fonts.fonts[f].clone();
1028 self.fonts.fonts.push(copy);
1029 self.fonts.id_cs.push(self.cs.frozen_null_font);
1030 let nf = self.fonts.fonts.len() as u32 - 1;
1031 self.define(
1032 global,
1033 Loc::Cs(u),
1034 Val::Meaning(Meaning::prim(Cmd::SetFont, nf)),
1035 );
1036 }
1037}
1038
1039pub(crate) fn lig_kern_table(tfm: &retex_font::tfm::Tfm, kerns: &[i32]) -> LigKernMap {
1042 let nl = tfm.lig_kern.len();
1043 let read = |start: usize| -> Vec<LigKernStep> {
1044 let mut steps: Vec<LigKernStep> = Vec::new();
1045 let mut k = start;
1046 for _ in 0..nl {
1047 let Some(w) = tfm.lig_kern.get(k) else { break };
1048 if w.skip <= 128 {
1049 let next = u32::from(w.next);
1050 if !steps.iter().any(|s| s.next == next) {
1051 let op = if w.op >= 128 {
1052 let ki = 256 * (usize::from(w.op) - 128) + usize::from(w.rem);
1053 LigOp::Kern(kerns.get(ki).copied().unwrap_or(0))
1054 } else {
1055 LigOp::Lig {
1056 op: w.op,
1057 chr: u32::from(w.rem),
1058 }
1059 };
1060 steps.push(LigKernStep { next, op });
1061 }
1062 }
1063 if w.skip >= 128 {
1064 break;
1065 }
1066 k += usize::from(w.skip) + 1;
1067 }
1068 steps
1069 };
1070 let mut map = LigKernMap::default();
1071 for c in u32::from(tfm.bc)..=u32::from(tfm.ec) {
1072 if let Some(start) = tfm.lig_kern_start(c) {
1073 let p = read(start);
1074 if !p.is_empty() {
1075 map.insert(c, p.into_boxed_slice());
1076 }
1077 }
1078 }
1079 if let Some(l) = tfm.bchar_label {
1080 let p = read(l);
1081 if !p.is_empty() {
1082 map.insert(LEFT_BOUNDARY, p.into_boxed_slice());
1083 }
1084 }
1085 map
1086}