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