1use std::collections::{HashMap, HashSet};
41use std::sync::Arc;
42
43use retex_engine::Engine;
44use retex_engine::nodes::{GlyphData, NodeData, NodeRef, glyph_sub, id, kern_sub};
45use retex_font::{LoadedFont, ShapedItem};
46
47use crate::luaotfload::loaded_font;
48
49const PROTECTED: u16 = 256;
51
52fn is_raw_glyph(e: &Engine, p: NodeRef, font: u32) -> bool {
54 let n = e.nodes.get(p);
55 matches!(&n.data, NodeData::Glyph(g) if g.font == font && n.subtype < PROTECTED)
56}
57
58fn glyph_list(e: &Engine, head: NodeRef, font: u32) -> Option<Vec<NodeRef>> {
61 let mut v = Vec::new();
62 let mut p = head;
63 while !p.is_null() {
64 if !is_raw_glyph(e, p, font) {
65 return None;
66 }
67 v.push(p);
68 p = e.nodes.next(p);
69 }
70 Some(v)
71}
72
73fn disc_lists(e: &Engine, d: NodeRef) -> [NodeRef; 3] {
74 match &e.nodes.get(d).data {
75 NodeData::Disc {
76 pre, post, replace, ..
77 } => [*pre, *post, *replace],
78 _ => [NodeRef::NULL; 3],
79 }
80}
81
82fn set_disc_lists(e: &mut Engine, d: NodeRef, lists: [NodeRef; 3]) {
83 if let NodeData::Disc {
84 pre, post, replace, ..
85 } = &mut e.nodes.get_mut(d).data
86 {
87 [*pre, *post, *replace] = lists;
88 }
89}
90
91const CONTEXT_LEN: usize = 8;
93
94fn context(e: &Engine, mut p: NodeRef, font: u32, forward: bool) -> Vec<u32> {
100 let mut out = Vec::new();
101 while !p.is_null() && out.len() < CONTEXT_LEN {
102 match &e.nodes.get(p).data {
103 NodeData::Glue { .. } => out.push(0x20),
104 NodeData::Glyph(g) if g.font == font => out.push(g.chr),
105 _ => break,
106 }
107 p = if forward {
108 e.nodes.next(p)
109 } else {
110 e.nodes.prev(p)
111 };
112 }
113 if !forward {
114 out.reverse();
115 }
116 out
117}
118
119fn chr(e: &Engine, p: NodeRef) -> u32 {
120 match &e.nodes.get(p).data {
121 NodeData::Glyph(g) => g.chr,
122 _ => 0,
123 }
124}
125
126struct DiscRun {
129 segs: Vec<Vec<NodeRef>>,
130 discs: Vec<NodeRef>,
131 lists: Vec<[Vec<NodeRef>; 3]>,
133}
134
135pub fn process_list(e: &mut Engine, head: NodeRef) -> NodeRef {
138 let mut head = head;
139 let mut marks: HashMap<u32, bool> = HashMap::new();
140 let mut base_rtl = false;
143 let mut dirs: Vec<bool> = Vec::new();
144 let mut p = head;
145 while !p.is_null() {
146 let node = e.nodes.get(p);
147 match &node.data {
148 NodeData::Glyph(g) if node.subtype < PROTECTED => {
149 let font = g.font;
150 let Some(lf) = loaded_font(e, font) else {
151 p = e.nodes.next(p);
152 continue;
153 };
154 let mut run = DiscRun {
155 segs: vec![vec![p]],
156 discs: Vec::new(),
157 lists: Vec::new(),
158 };
159 let mut q = e.nodes.next(p);
160 while !q.is_null() {
161 if is_raw_glyph(e, q, font) {
162 run.segs.last_mut().expect("segment").push(q);
163 } else if e.nodes.id(q) == id::DISC {
164 let [pre, post, rep] = disc_lists(e, q);
165 let (Some(a), Some(b), Some(c)) = (
166 glyph_list(e, pre, font),
167 glyph_list(e, post, font),
168 glyph_list(e, rep, font),
169 ) else {
170 break;
171 };
172 run.discs.push(q);
173 run.lists.push([a, b, c]);
174 run.segs.push(Vec::new());
175 } else {
176 break;
177 }
178 q = e.nodes.next(q);
179 }
180 let first = p;
181 let marks = *marks.entry(font).or_insert_with(|| {
182 let mut chars = Vec::new();
183 font_chars(e, head, font, &mut chars);
184 lf.shaper.has_marks(&lf.file, &lf.tex, &chars)
185 });
186 let rtl = dirs.last().copied().unwrap_or(base_rtl);
187 let ctx = if lf.shaper.uses_context {
188 (
189 context(e, e.nodes.prev(first), font, false),
190 context(e, q, font, true),
191 marks,
192 rtl,
193 )
194 } else {
195 (Vec::new(), Vec::new(), marks, rtl)
196 };
197 let new_first = if run.discs.is_empty() {
198 shape_plain(e, &lf, &run.segs[0], &ctx)
199 } else {
200 shape_disc_run(e, &lf, &run, &ctx)
201 .unwrap_or_else(|| shape_separately(e, &lf, &run, ctx.2, ctx.3))
202 };
203 if first == head {
204 head = new_first;
205 }
206 p = q;
207 }
208 NodeData::LocalPar { dir, .. } => {
209 base_rtl = *dir == 1;
210 p = e.nodes.next(p);
211 }
212 NodeData::Dir { dir, .. } => {
213 if node.subtype == 0 {
214 dirs.push(*dir == 1);
215 } else {
216 dirs.pop();
217 }
218 p = e.nodes.next(p);
219 }
220 NodeData::Disc { .. } => {
221 let lists = disc_lists(e, p);
222 let new = lists.map(|l| if l.is_null() { l } else { process_list(e, l) });
223 set_disc_lists(e, p, new);
224 p = e.nodes.next(p);
225 }
226 _ => p = e.nodes.next(p),
227 }
228 }
229 head
230}
231
232fn build(
241 e: &mut Engine,
242 items: &[ShapedItem],
243 src: &[NodeRef],
244 reused: &mut HashSet<NodeRef>,
245 base: bool,
246) -> Vec<NodeRef> {
247 let mut out = Vec::with_capacity(items.len());
248 let Some(&last_src) = src.last() else {
249 return out;
250 };
251 let source = |k: usize| src.get(k).copied().unwrap_or(last_src);
252 for it in items {
253 match it {
254 ShapedItem::Glyph(g) => {
255 let s = source(g.cluster);
256 let mut data = match &e.nodes.get(s).data {
257 NodeData::Glyph(d) => d.clone(),
258 _ => GlyphData::default(),
259 };
260 let attr = e.nodes.get(s).attr.clone();
261 data.chr = g.code;
262 data.xoffset = g.x_offset;
263 data.yoffset = g.y_offset;
264 data.components = NodeRef::NULL;
265 let mut subtype = if base {
269 PROTECTED
270 } else {
271 glyph_sub::CHARACTER | PROTECTED
272 };
273 if !g.components.is_empty() {
274 let mut ch = NodeRef::NULL;
275 let mut tail = NodeRef::NULL;
276 for (k, &c) in g.components.iter().enumerate() {
277 let n = e.nodes.copy_node(source(g.cluster + k));
278 if let NodeData::Glyph(gd) = &mut e.nodes.get_mut(n).data {
279 gd.chr = c;
280 }
281 if ch.is_null() {
282 ch = n;
283 } else {
284 e.nodes.link(tail, n);
285 }
286 tail = n;
287 }
288 data.components = ch;
289 subtype = if base {
293 PROTECTED | glyph_sub::LIGATURE
294 } else {
295 PROTECTED
296 };
297 }
298 let n = if data.components.is_null() && reused.insert(s) {
299 let node = e.nodes.get_mut(s);
300 node.subtype = subtype;
301 node.data = NodeData::Glyph(data);
302 s
303 } else {
304 let n = e.nodes.alloc(id::GLYPH, subtype, NodeData::Glyph(data));
305 e.nodes.get_mut(n).attr = attr;
306 n
307 };
308 out.push(n);
309 }
310 ShapedItem::Kern(k) => {
311 let attr = e.nodes.get(last_src).attr.clone();
312 let n = e.nodes.new_kern(*k, kern_sub::FONT);
313 e.nodes.sync_as_copied(n);
315 e.nodes.get_mut(n).attr = attr;
316 out.push(n);
317 }
318 }
319 }
320 for i in 1..out.len() {
322 if e.nodes.id(out[i]) == id::KERN {
323 let a = e.nodes.get(out[i - 1]).attr.clone();
324 e.nodes.get_mut(out[i]).attr = a;
325 }
326 }
327 out
328}
329
330fn link_all(e: &mut Engine, nodes: &[NodeRef]) -> NodeRef {
332 for w in nodes.windows(2) {
333 e.nodes.link(w[0], w[1]);
334 }
335 if let Some(&f) = nodes.first() {
336 e.nodes.get_mut(f).prev = NodeRef::NULL;
337 }
338 if let Some(&l) = nodes.last() {
339 e.nodes.get_mut(l).next = NodeRef::NULL;
340 }
341 nodes.first().copied().unwrap_or(NodeRef::NULL)
342}
343
344fn free_glyphs(e: &mut Engine, nodes: &[NodeRef], reused: &HashSet<NodeRef>) {
347 for &n in nodes {
348 if !reused.contains(&n) {
349 e.nodes.get_mut(n).next = NodeRef::NULL;
350 e.nodes.flush_list(n);
351 }
352 }
353}
354
355fn splice(e: &mut Engine, prev: NodeRef, next: NodeRef, new: &[NodeRef]) -> NodeRef {
360 let mut cur = prev;
361 for &n in new {
362 if cur.is_null() {
363 e.nodes.get_mut(n).prev = NodeRef::NULL;
364 } else {
365 e.nodes.link(cur, n);
366 }
367 cur = n;
368 }
369 if cur.is_null() {
370 return next;
371 }
372 e.nodes.link(cur, next);
373 if !next.is_null() {
374 e.nodes.get_mut(next).prev = cur;
375 }
376 new.first().copied().unwrap_or(next)
377}
378
379type Context = (Vec<u32>, Vec<u32>, bool, bool);
386
387fn shape_text(
391 lf: &LoadedFont,
392 before: &[u32],
393 text: &[u32],
394 after: &[u32],
395 marks: bool,
396 rtl: bool,
397) -> Vec<retex_font::ShapedItem> {
398 let harf = lf.request.mode == retex_font::Mode::Harf;
399 let use_hb = if harf {
400 rtl || retex_font::harf::needs_harfbuzz(text)
401 } else {
402 lf.request.mode == retex_font::Mode::Node && retex_font::harf::joining_script(text)
403 };
404 if use_hb && let Some(h) = lf.harf() {
405 return h.shape(&lf.tex, text, rtl);
406 }
407 let mut items = lf
408 .shaper
409 .shape_marked(&lf.file, &lf.tex, before, text, after, marks);
410 if rtl {
411 mirror_mark_offsets(lf, &mut items);
412 }
413 items
414}
415
416fn mirror_mark_offsets(lf: &LoadedFont, items: &mut [retex_font::ShapedItem]) {
422 use retex_font::ShapedItem;
423 let mut since_base = 0i32;
424 for it in items.iter_mut() {
425 match it {
426 ShapedItem::Glyph(g) if g.x_offset == 0 && g.y_offset == 0 => {
427 since_base = g
428 .glyph
429 .map(|gid| lf.tex.glyph_metrics(gid).width)
430 .unwrap_or(0);
431 }
432 ShapedItem::Glyph(g) => {
433 if g.x_offset != 0 {
434 g.x_offset = -(g.x_offset + since_base);
435 }
436 since_base += g
437 .glyph
438 .map(|gid| lf.tex.glyph_metrics(gid).width)
439 .unwrap_or(0);
440 }
441 ShapedItem::Kern(k) => since_base += *k,
442 }
443 }
444}
445
446fn font_chars(e: &Engine, head: NodeRef, font: u32, out: &mut Vec<u32>) {
449 let mut p = head;
450 while !p.is_null() {
451 match &e.nodes.get(p).data {
452 NodeData::Glyph(g) if g.font == font => out.push(chr(e, p)),
453 NodeData::Disc { .. } => {
454 for l in disc_lists(e, p) {
455 font_chars(e, l, font, out);
456 }
457 }
458 _ => {}
459 }
460 p = e.nodes.next(p);
461 }
462}
463
464fn shape_plain(e: &mut Engine, lf: &Arc<LoadedFont>, run: &[NodeRef], ctx: &Context) -> NodeRef {
465 let chars: Vec<u32> = run.iter().map(|&n| chr(e, n)).collect();
466 let items = shape_text(lf, &ctx.0, &chars, &ctx.1, ctx.2, ctx.3);
467 let unchanged = items.len() == run.len()
468 && items.iter().zip(&chars).all(|(it, &c)| {
469 matches!(it, ShapedItem::Glyph(g) if g.code == c && g.components.is_empty()
470 && g.x_offset == 0 && g.y_offset == 0)
471 });
472 let first = run[0];
473 let last = *run.last().expect("non-empty run");
474 let base = lf.request.mode == retex_font::Mode::Base;
475 if unchanged {
476 for &n in run {
477 let node = e.nodes.get_mut(n);
478 if base {
479 node.subtype &= !glyph_sub::CHARACTER;
480 }
481 node.subtype |= PROTECTED;
482 }
483 return first;
484 }
485 let mut reused = HashSet::new();
486 let (prev, next) = (e.nodes.get(first).prev, e.nodes.next(last));
487 let new = build(e, &items, run, &mut reused, base);
488 let new_first = splice(e, prev, next, &new);
489 free_glyphs(e, run, &reused);
490 new_first
491}
492
493fn n_chars(it: &ShapedItem) -> usize {
494 match it {
495 ShapedItem::Glyph(g) => g.components.len().max(1),
496 ShapedItem::Kern(_) => 0,
497 }
498}
499
500fn shape_disc_run(
504 e: &mut Engine,
505 lf: &Arc<LoadedFont>,
506 run: &DiscRun,
507 ctx: &Context,
508) -> Option<NodeRef> {
509 let k = run.discs.len();
510 let base = lf.request.mode == retex_font::Mode::Base;
511 let harf = lf.request.mode == retex_font::request::Mode::Harf;
512 let first = run.segs[0][0];
514 let last = run
515 .segs
516 .last()
517 .and_then(|s| s.last().copied())
518 .unwrap_or(*run.discs.last().expect("a discretionary"));
519 let (prev, next) = (e.nodes.get(first).prev, e.nodes.next(last));
520 if !harf && run.segs[1..k].iter().any(Vec::is_empty) {
521 return None;
522 }
523 let mut usrc: Vec<NodeRef> = Vec::new();
525 let mut starts = Vec::with_capacity(k);
526 for i in 0..k {
527 usrc.extend(&run.segs[i]);
528 starts.push(usrc.len());
529 usrc.extend(&run.lists[i][2]);
530 }
531 usrc.extend(&run.segs[k]);
532 let uchars: Vec<u32> = usrc.iter().map(|&n| chr(e, n)).collect();
533 if lf.shaper.changes_text(&lf.tex, &uchars, ctx.2) {
534 return None;
535 }
536 let shape = |src: &[NodeRef]| {
537 let chars: Vec<u32> = src.iter().map(|&n| chr(e, n)).collect();
538 shape_text(lf, &[], &chars, &[], ctx.2, ctx.3)
539 };
540 let u = shape_text(lf, &ctx.0, &uchars, &ctx.1, ctx.2, ctx.3);
541 let n = u.len();
542 let is_kern = |i: usize| matches!(u[i], ShapedItem::Kern(_));
543 struct Piece {
544 pre: (Vec<ShapedItem>, Vec<NodeRef>),
545 post: (Vec<ShapedItem>, Vec<NodeRef>),
546 lo: usize,
547 hi: usize,
548 rep_lo: usize,
551 disc: NodeRef,
553 merged: Vec<NodeRef>,
554 }
555 let glyph_id = |it: &ShapedItem| match it {
556 ShapedItem::Glyph(g) => g.glyph,
557 ShapedItem::Kern(_) => None,
558 };
559 let mut regions: Vec<(usize, usize, usize, usize)> = Vec::with_capacity(k);
561 for (&a, lists) in starts.iter().zip(&run.lists) {
562 let b = a + lists[2].len();
563 let mut lo = 0;
566 while lo < n
567 && match &u[lo] {
568 ShapedItem::Glyph(g) => g.cluster + n_chars(&u[lo]) <= a,
569 ShapedItem::Kern(_) => true,
570 }
571 {
572 lo += 1;
573 }
574 while lo > 0 && is_kern(lo - 1) {
575 lo -= 1;
576 }
577 let mut s = 0;
578 while s < n
579 && match &u[n - 1 - s] {
580 ShapedItem::Glyph(g) => g.cluster >= b,
581 ShapedItem::Kern(_) => true,
582 }
583 {
584 s += 1;
585 }
586 while s > 0 && is_kern(n - s) {
587 s -= 1;
588 }
589 let mut hi = n - s;
590 if harf {
591 while lo > 0 && (is_kern(lo - 1) || (lo < n && is_kern(lo))) {
593 lo -= 1;
594 }
595 while hi < n && (is_kern(hi) || (hi > 0 && is_kern(hi - 1))) {
596 hi += 1;
597 }
598 }
599 if hi < lo {
600 return None;
601 }
602 regions.push((lo, hi, a, b));
603 }
604 let mut groups: Vec<(usize, usize)> = Vec::new();
607 for i in 0..k {
608 match groups.last_mut() {
609 Some(g) if regions[i].0 < regions[g.1].1 => {
610 if !harf {
611 return None;
612 }
613 g.1 = i;
614 }
615 _ => groups.push((i, i)),
616 }
617 }
618 let mut pieces: Vec<Piece> = Vec::with_capacity(groups.len());
619 for &(first, last) in &groups {
620 let lo = (first..=last).map(|i| regions[i].0).min().unwrap_or(0);
621 let hi = (first..=last).map(|i| regions[i].1).max().unwrap_or(0);
622 let (a, b) = (regions[first].2, regions[last].3);
623 let (pre, post) = (&run.lists[first][0], &run.lists[last][1]);
624 let ca = u[..lo]
626 .iter()
627 .filter_map(|it| match it {
628 ShapedItem::Glyph(g) => Some(g.cluster + n_chars(it)),
629 ShapedItem::Kern(_) => None,
630 })
631 .max()
632 .unwrap_or(0)
633 .min(a);
634 let cb = u[hi..]
635 .iter()
636 .filter_map(|it| match it {
637 ShapedItem::Glyph(g) => Some(g.cluster),
638 ShapedItem::Kern(_) => None,
639 })
640 .min()
641 .unwrap_or(usrc.len())
642 .max(b);
643 let mut psrc = usrc[ca..a].to_vec();
646 psrc.extend(pre);
647 let mut qsrc = post.clone();
648 qsrc.extend(&usrc[b..cb]);
649 let mut pre_items = shape(&psrc);
650 let mut post_items = shape(&qsrc);
651 let sub = e.nodes.get(run.discs[first]).subtype;
657 let left_kerns = !(base && matches!(sub, 2 | 3));
658 let mut rep_lo = lo;
659 if base && sub == 2 {
660 while rep_lo < hi && is_kern(rep_lo) {
661 rep_lo += 1;
662 }
663 }
664 if !harf {
665 if left_kerns
666 && let (Some(l), Some(r)) = (
667 lo.checked_sub(1).and_then(|i| glyph_id(&u[i])),
668 pre_items.first().and_then(glyph_id),
669 )
670 {
671 let k = lf.shaper.pair_kern(&lf.file, &lf.tex, l, r);
672 if k != 0 {
673 pre_items.insert(0, ShapedItem::Kern(k));
674 }
675 }
676 if let (Some(l), Some(r)) = (
677 post_items.last().and_then(glyph_id),
678 u.get(hi).and_then(glyph_id),
679 ) {
680 let k = lf.shaper.pair_kern(&lf.file, &lf.tex, l, r);
681 if k != 0 {
682 post_items.push(ShapedItem::Kern(k));
683 }
684 }
685 }
686 pieces.push(Piece {
687 pre: (pre_items, psrc),
688 post: (post_items, qsrc),
689 lo,
690 hi,
691 rep_lo,
692 disc: run.discs[first],
693 merged: run.discs[first + 1..=last].to_vec(),
694 });
695 }
696 let mut out: Vec<NodeRef> = Vec::new();
698 let mut pos = 0usize;
699 let mut reused = HashSet::new();
700 for pc in &pieces {
701 out.extend(build(e, &u[pos..pc.lo], &usrc, &mut reused, base));
702 let pre = build(e, &pc.pre.0, &pc.pre.1, &mut reused, base);
703 let post = build(e, &pc.post.0, &pc.post.1, &mut reused, base);
704 let rep = build(e, &u[pc.rep_lo..pc.hi], &usrc, &mut reused, base);
705 let d = pc.disc;
706 let lists = [link_all(e, &pre), link_all(e, &post), link_all(e, &rep)];
707 set_disc_lists(e, d, lists);
708 out.push(d);
709 pos = pc.hi;
710 }
711 out.extend(build(e, &u[pos..], &usrc, &mut reused, base));
712 let new_first = splice(e, prev, next, &out);
714 for seg in &run.segs {
715 free_glyphs(e, seg, &reused);
716 }
717 for l in &run.lists {
718 for part in l {
719 free_glyphs(e, part, &reused);
720 }
721 }
722 for &d in pieces.iter().flat_map(|pc| &pc.merged) {
724 set_disc_lists(e, d, [NodeRef::NULL; 3]);
725 e.nodes.get_mut(d).next = NodeRef::NULL;
726 e.nodes.flush_list(d);
727 }
728 Some(new_first)
729}
730
731fn shape_separately(
733 e: &mut Engine,
734 lf: &Arc<LoadedFont>,
735 run: &DiscRun,
736 marks: bool,
737 rtl: bool,
738) -> NodeRef {
739 let mut first = NodeRef::NULL;
740 for (i, seg) in run.segs.iter().enumerate() {
741 if !seg.is_empty() {
742 let f = shape_plain(e, lf, seg, &(Vec::new(), Vec::new(), marks, rtl));
743 if i == 0 {
744 first = f;
745 }
746 }
747 if let Some(&d) = run.discs.get(i) {
748 let lists = disc_lists(e, d);
749 let new = lists.map(|l| if l.is_null() { l } else { process_list(e, l) });
750 set_disc_lists(e, d, new);
751 }
752 }
753 first
754}