1use std::collections::{HashMap, HashSet};
30use std::sync::Arc;
31
32use retex_engine::Engine;
33use retex_engine::nodes::{GlyphData, NodeData, NodeRef, glyph_sub, id, kern_sub};
34use retex_font::{LoadedFont, ShapedItem};
35
36use crate::luaotfload::loaded_font;
37
38const PROTECTED: u16 = 256;
40
41fn is_raw_glyph(e: &Engine, p: NodeRef, font: u32) -> bool {
43 let n = e.nodes.get(p);
44 matches!(&n.data, NodeData::Glyph(g) if g.font == font && n.subtype < PROTECTED)
45}
46
47fn glyph_list(e: &Engine, head: NodeRef, font: u32) -> Option<Vec<NodeRef>> {
50 let mut v = Vec::new();
51 let mut p = head;
52 while !p.is_null() {
53 if !is_raw_glyph(e, p, font) {
54 return None;
55 }
56 v.push(p);
57 p = e.nodes.next(p);
58 }
59 Some(v)
60}
61
62fn disc_lists(e: &Engine, d: NodeRef) -> [NodeRef; 3] {
63 match &e.nodes.get(d).data {
64 NodeData::Disc {
65 pre, post, replace, ..
66 } => [*pre, *post, *replace],
67 _ => [NodeRef::NULL; 3],
68 }
69}
70
71fn set_disc_lists(e: &mut Engine, d: NodeRef, lists: [NodeRef; 3]) {
72 if let NodeData::Disc {
73 pre, post, replace, ..
74 } = &mut e.nodes.get_mut(d).data
75 {
76 [*pre, *post, *replace] = lists;
77 }
78}
79
80const CONTEXT_LEN: usize = 8;
82
83fn context(e: &Engine, mut p: NodeRef, font: u32, forward: bool) -> Vec<u32> {
89 let mut out = Vec::new();
90 while !p.is_null() && out.len() < CONTEXT_LEN {
91 match &e.nodes.get(p).data {
92 NodeData::Glue { .. } => out.push(0x20),
93 NodeData::Glyph(g) if g.font == font => out.push(g.chr),
94 _ => break,
95 }
96 p = if forward {
97 e.nodes.next(p)
98 } else {
99 e.nodes.prev(p)
100 };
101 }
102 if !forward {
103 out.reverse();
104 }
105 out
106}
107
108fn chr(e: &Engine, p: NodeRef) -> u32 {
109 match &e.nodes.get(p).data {
110 NodeData::Glyph(g) => g.chr,
111 _ => 0,
112 }
113}
114
115struct DiscRun {
118 segs: Vec<Vec<NodeRef>>,
119 discs: Vec<NodeRef>,
120 lists: Vec<[Vec<NodeRef>; 3]>,
122}
123
124pub fn process_list(e: &mut Engine, head: NodeRef) -> NodeRef {
127 let mut head = head;
128 let mut marks: HashMap<u32, bool> = HashMap::new();
129 let mut p = head;
130 while !p.is_null() {
131 let node = e.nodes.get(p);
132 match &node.data {
133 NodeData::Glyph(g) if node.subtype < PROTECTED => {
134 let font = g.font;
135 let Some(lf) = loaded_font(e, font) else {
136 p = e.nodes.next(p);
137 continue;
138 };
139 let mut run = DiscRun {
140 segs: vec![vec![p]],
141 discs: Vec::new(),
142 lists: Vec::new(),
143 };
144 let mut q = e.nodes.next(p);
145 while !q.is_null() {
146 if is_raw_glyph(e, q, font) {
147 run.segs.last_mut().expect("segment").push(q);
148 } else if e.nodes.id(q) == id::DISC {
149 let [pre, post, rep] = disc_lists(e, q);
150 let (Some(a), Some(b), Some(c)) = (
151 glyph_list(e, pre, font),
152 glyph_list(e, post, font),
153 glyph_list(e, rep, font),
154 ) else {
155 break;
156 };
157 run.discs.push(q);
158 run.lists.push([a, b, c]);
159 run.segs.push(Vec::new());
160 } else {
161 break;
162 }
163 q = e.nodes.next(q);
164 }
165 let first = p;
166 let marks = *marks.entry(font).or_insert_with(|| {
167 let mut chars = Vec::new();
168 font_chars(e, head, font, &mut chars);
169 lf.shaper.has_marks(&lf.file, &lf.tex, &chars)
170 });
171 let ctx = if lf.shaper.uses_context {
172 (
173 context(e, e.nodes.prev(first), font, false),
174 context(e, q, font, true),
175 marks,
176 )
177 } else {
178 (Vec::new(), Vec::new(), marks)
179 };
180 let new_first = if run.discs.is_empty() {
181 shape_plain(e, &lf, &run.segs[0], &ctx)
182 } else {
183 shape_disc_run(e, &lf, &run, &ctx)
184 .unwrap_or_else(|| shape_separately(e, &lf, &run, ctx.2))
185 };
186 if first == head {
187 head = new_first;
188 }
189 p = q;
190 }
191 NodeData::Disc { .. } => {
192 let lists = disc_lists(e, p);
193 let new = lists.map(|l| if l.is_null() { l } else { process_list(e, l) });
194 set_disc_lists(e, p, new);
195 p = e.nodes.next(p);
196 }
197 _ => p = e.nodes.next(p),
198 }
199 }
200 head
201}
202
203fn build(
212 e: &mut Engine,
213 items: &[ShapedItem],
214 src: &[NodeRef],
215 reused: &mut HashSet<NodeRef>,
216) -> Vec<NodeRef> {
217 let mut out = Vec::with_capacity(items.len());
218 let Some(&last_src) = src.last() else {
219 return out;
220 };
221 let source = |k: usize| src.get(k).copied().unwrap_or(last_src);
222 for it in items {
223 match it {
224 ShapedItem::Glyph(g) => {
225 let s = source(g.cluster);
226 let mut data = match &e.nodes.get(s).data {
227 NodeData::Glyph(d) => d.clone(),
228 _ => GlyphData::default(),
229 };
230 let attr = e.nodes.get(s).attr.clone();
231 data.chr = g.code;
232 data.xoffset = g.x_offset;
233 data.yoffset = g.y_offset;
234 data.components = NodeRef::NULL;
235 let mut subtype = glyph_sub::CHARACTER | PROTECTED;
236 if !g.components.is_empty() {
237 let mut ch = NodeRef::NULL;
238 let mut tail = NodeRef::NULL;
239 for (k, &c) in g.components.iter().enumerate() {
240 let n = e.nodes.copy_node(source(g.cluster + k));
241 if let NodeData::Glyph(gd) = &mut e.nodes.get_mut(n).data {
242 gd.chr = c;
243 }
244 if ch.is_null() {
245 ch = n;
246 } else {
247 e.nodes.link(tail, n);
248 }
249 tail = n;
250 }
251 data.components = ch;
252 subtype = PROTECTED;
256 }
257 let n = if data.components.is_null() && reused.insert(s) {
258 let node = e.nodes.get_mut(s);
259 node.subtype = subtype;
260 node.data = NodeData::Glyph(data);
261 s
262 } else {
263 let n = e.nodes.alloc(id::GLYPH, subtype, NodeData::Glyph(data));
264 e.nodes.get_mut(n).attr = attr;
265 n
266 };
267 out.push(n);
268 }
269 ShapedItem::Kern(k) => {
270 let attr = e.nodes.get(last_src).attr.clone();
271 let n = e.nodes.new_kern(*k, kern_sub::FONT);
272 e.nodes.sync_as_copied(n);
274 e.nodes.get_mut(n).attr = attr;
275 out.push(n);
276 }
277 }
278 }
279 for i in 1..out.len() {
281 if e.nodes.id(out[i]) == id::KERN {
282 let a = e.nodes.get(out[i - 1]).attr.clone();
283 e.nodes.get_mut(out[i]).attr = a;
284 }
285 }
286 out
287}
288
289fn link_all(e: &mut Engine, nodes: &[NodeRef]) -> NodeRef {
291 for w in nodes.windows(2) {
292 e.nodes.link(w[0], w[1]);
293 }
294 if let Some(&f) = nodes.first() {
295 e.nodes.get_mut(f).prev = NodeRef::NULL;
296 }
297 if let Some(&l) = nodes.last() {
298 e.nodes.get_mut(l).next = NodeRef::NULL;
299 }
300 nodes.first().copied().unwrap_or(NodeRef::NULL)
301}
302
303fn free_glyphs(e: &mut Engine, nodes: &[NodeRef], reused: &HashSet<NodeRef>) {
306 for &n in nodes {
307 if !reused.contains(&n) {
308 e.nodes.get_mut(n).next = NodeRef::NULL;
309 e.nodes.flush_list(n);
310 }
311 }
312}
313
314fn splice(e: &mut Engine, first: NodeRef, last: NodeRef, new: &[NodeRef]) -> NodeRef {
317 let prev = e.nodes.get(first).prev;
318 let next = e.nodes.next(last);
319 let mut cur = prev;
320 for &n in new {
321 if cur.is_null() {
322 e.nodes.get_mut(n).prev = NodeRef::NULL;
323 } else {
324 e.nodes.link(cur, n);
325 }
326 cur = n;
327 }
328 if cur.is_null() {
329 return next;
330 }
331 e.nodes.link(cur, next);
332 if !next.is_null() {
333 e.nodes.get_mut(next).prev = cur;
334 }
335 new.first().copied().unwrap_or(next)
336}
337
338type Context = (Vec<u32>, Vec<u32>, bool);
343
344fn font_chars(e: &Engine, head: NodeRef, font: u32, out: &mut Vec<u32>) {
347 let mut p = head;
348 while !p.is_null() {
349 match &e.nodes.get(p).data {
350 NodeData::Glyph(g) if g.font == font => out.push(chr(e, p)),
351 NodeData::Disc { .. } => {
352 for l in disc_lists(e, p) {
353 font_chars(e, l, font, out);
354 }
355 }
356 _ => {}
357 }
358 p = e.nodes.next(p);
359 }
360}
361
362fn shape_plain(e: &mut Engine, lf: &Arc<LoadedFont>, run: &[NodeRef], ctx: &Context) -> NodeRef {
363 let chars: Vec<u32> = run.iter().map(|&n| chr(e, n)).collect();
364 let items = lf
365 .shaper
366 .shape_marked(&lf.file, &lf.tex, &ctx.0, &chars, &ctx.1, ctx.2);
367 let unchanged = items.len() == run.len()
368 && items.iter().zip(&chars).all(|(it, &c)| {
369 matches!(it, ShapedItem::Glyph(g) if g.code == c && g.components.is_empty()
370 && g.x_offset == 0 && g.y_offset == 0)
371 });
372 let first = run[0];
373 let last = *run.last().expect("non-empty run");
374 if unchanged {
375 for &n in run {
376 e.nodes.get_mut(n).subtype |= PROTECTED;
377 }
378 return first;
379 }
380 let mut reused = HashSet::new();
381 let new = build(e, &items, run, &mut reused);
382 let new_first = splice(e, first, last, &new);
383 free_glyphs(e, run, &reused);
384 new_first
385}
386
387fn n_chars(it: &ShapedItem) -> usize {
388 match it {
389 ShapedItem::Glyph(g) => g.components.len().max(1),
390 ShapedItem::Kern(_) => 0,
391 }
392}
393
394fn shape_disc_run(
398 e: &mut Engine,
399 lf: &Arc<LoadedFont>,
400 run: &DiscRun,
401 ctx: &Context,
402) -> Option<NodeRef> {
403 let k = run.discs.len();
404 if run.segs[1..k].iter().any(Vec::is_empty) {
405 return None;
406 }
407 let mut usrc: Vec<NodeRef> = Vec::new();
409 let mut starts = Vec::with_capacity(k);
410 for i in 0..k {
411 usrc.extend(&run.segs[i]);
412 starts.push(usrc.len());
413 usrc.extend(&run.lists[i][2]);
414 }
415 usrc.extend(&run.segs[k]);
416 let uchars: Vec<u32> = usrc.iter().map(|&n| chr(e, n)).collect();
417 if lf.shaper.changes_text(&lf.tex, &uchars, ctx.2) {
418 return None;
419 }
420 let shape = |src: &[NodeRef]| {
421 let chars: Vec<u32> = src.iter().map(|&n| chr(e, n)).collect();
422 lf.shaper
423 .shape_marked(&lf.file, &lf.tex, &[], &chars, &[], ctx.2)
424 };
425 let u = lf
426 .shaper
427 .shape_marked(&lf.file, &lf.tex, &ctx.0, &uchars, &ctx.1, ctx.2);
428 let n = u.len();
429 let is_kern = |i: usize| matches!(u[i], ShapedItem::Kern(_));
430 struct Piece {
431 pre: (Vec<ShapedItem>, Vec<NodeRef>),
432 post: (Vec<ShapedItem>, Vec<NodeRef>),
433 lo: usize,
434 hi: usize,
435 }
436 let mut pieces: Vec<Piece> = Vec::with_capacity(k);
437 let mut prev_hi = 0usize;
438 let glyph_id = |it: &ShapedItem| match it {
439 ShapedItem::Glyph(g) => g.glyph,
440 ShapedItem::Kern(_) => None,
441 };
442 for (&a, lists) in starts.iter().zip(&run.lists) {
443 let [pre, post, rep] = lists;
444 let b = a + rep.len();
445 let mut lo = 0;
448 while lo < n
449 && match &u[lo] {
450 ShapedItem::Glyph(g) => g.cluster + n_chars(&u[lo]) <= a,
451 ShapedItem::Kern(_) => true,
452 }
453 {
454 lo += 1;
455 }
456 while lo > 0 && is_kern(lo - 1) {
457 lo -= 1;
458 }
459 let mut s = 0;
460 while s < n
461 && match &u[n - 1 - s] {
462 ShapedItem::Glyph(g) => g.cluster >= b,
463 ShapedItem::Kern(_) => true,
464 }
465 {
466 s += 1;
467 }
468 while s > 0 && is_kern(n - s) {
469 s -= 1;
470 }
471 let hi = n - s;
472 if lo < prev_hi || hi < lo {
473 return None;
474 }
475 let ca = u[..lo]
477 .iter()
478 .filter_map(|it| match it {
479 ShapedItem::Glyph(g) => Some(g.cluster + n_chars(it)),
480 ShapedItem::Kern(_) => None,
481 })
482 .max()
483 .unwrap_or(0)
484 .min(a);
485 let cb = u[hi..]
486 .iter()
487 .filter_map(|it| match it {
488 ShapedItem::Glyph(g) => Some(g.cluster),
489 ShapedItem::Kern(_) => None,
490 })
491 .min()
492 .unwrap_or(usrc.len())
493 .max(b);
494 let mut psrc = usrc[ca..a].to_vec();
497 psrc.extend(pre);
498 let mut qsrc = post.clone();
499 qsrc.extend(&usrc[b..cb]);
500 let mut pre_items = shape(&psrc);
501 let mut post_items = shape(&qsrc);
502 if let (Some(l), Some(r)) = (
503 lo.checked_sub(1).and_then(|i| glyph_id(&u[i])),
504 pre_items.first().and_then(glyph_id),
505 ) {
506 let k = lf.shaper.pair_kern(&lf.file, &lf.tex, l, r);
507 if k != 0 {
508 pre_items.insert(0, ShapedItem::Kern(k));
509 }
510 }
511 if let (Some(l), Some(r)) = (
512 post_items.last().and_then(glyph_id),
513 u.get(hi).and_then(glyph_id),
514 ) {
515 let k = lf.shaper.pair_kern(&lf.file, &lf.tex, l, r);
516 if k != 0 {
517 post_items.push(ShapedItem::Kern(k));
518 }
519 }
520 pieces.push(Piece {
521 pre: (pre_items, psrc),
522 post: (post_items, qsrc),
523 lo,
524 hi,
525 });
526 prev_hi = hi;
527 }
528 let mut out: Vec<NodeRef> = Vec::new();
530 let mut pos = 0usize;
531 let mut reused = HashSet::new();
532 for (i, pc) in pieces.iter().enumerate() {
533 out.extend(build(e, &u[pos..pc.lo], &usrc, &mut reused));
534 let pre = build(e, &pc.pre.0, &pc.pre.1, &mut reused);
535 let post = build(e, &pc.post.0, &pc.post.1, &mut reused);
536 let rep = build(e, &u[pc.lo..pc.hi], &usrc, &mut reused);
537 let d = run.discs[i];
538 let lists = [link_all(e, &pre), link_all(e, &post), link_all(e, &rep)];
539 set_disc_lists(e, d, lists);
540 out.push(d);
541 pos = pc.hi;
542 }
543 out.extend(build(e, &u[pos..], &usrc, &mut reused));
544 let first = run.segs[0][0];
546 let last = run
547 .segs
548 .last()
549 .and_then(|s| s.last().copied())
550 .unwrap_or(*run.discs.last().expect("a discretionary"));
551 let new_first = splice(e, first, last, &out);
552 for seg in &run.segs {
553 free_glyphs(e, seg, &reused);
554 }
555 for l in &run.lists {
556 for part in l {
557 free_glyphs(e, part, &reused);
558 }
559 }
560 Some(new_first)
561}
562
563fn shape_separately(e: &mut Engine, lf: &Arc<LoadedFont>, run: &DiscRun, marks: bool) -> NodeRef {
565 let mut first = NodeRef::NULL;
566 for (i, seg) in run.segs.iter().enumerate() {
567 if !seg.is_empty() {
568 let f = shape_plain(e, lf, seg, &(Vec::new(), Vec::new(), marks));
569 if i == 0 {
570 first = f;
571 }
572 }
573 if let Some(&d) = run.discs.get(i) {
574 let lists = disc_lists(e, d);
575 let new = lists.map(|l| if l.is_null() { l } else { process_list(e, l) });
576 set_disc_lists(e, d, new);
577 }
578 }
579 first
580}