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