Skip to main content

retex_engine/
nodeproc.rs

1//! LuaTeX's node processing before packaging and line breaking: the
2//! hyphenation, ligaturing and kerning passes (LuaTeX manual §5.5–§5.6).
3//!
4//! Characters are appended to lists as glyph nodes without any font
5//! processing. When an `\hbox` is packaged or a paragraph is broken into
6//! lines, LuaTeX first inserts discretionaries (explicit hyphens become
7//! "automatic" discretionaries; patterns insert "regular" ones), then
8//! builds ligatures, then inserts font kerns. Each step can be replaced by
9//! a Lua callback; without callbacks
10//! the built-in TeX-style processing below applies, using the font's
11//! lig/kern program.
12//!
13//! Observed behaviour reproduced here:
14//!
15//! * a single `\exhyphenchar` glyph immediately followed by another
16//!   glyph becomes an automatic discretionary (subtype 2) whose pre-break
17//!   and replacement texts are the hyphen, with `\exhyphenpenalty` as
18//!   current at processing time. Of a run of two or more hyphens (`--`,
19//!   `---`) only the last one becomes such a discretionary, and only when
20//!   the run follows a glyph with a non-zero hyphenation code and is
21//!   followed by a glyph, and at most once between two glues; a kern also
22//!   prevents it until the next glue. So `a--b` becomes `a-{-||-}b`, while
23//!   `16--19`, `1--a` and the second dash of `a--b--c` stay as they are
24//!   (observed with luatex);
25//! * ligatures form within the main list and within each list of a
26//!   discretionary; a glyph next to a discretionary moves into it when it
27//!   would form a ligature across it;
28//! * kerns between a glyph and a discretionary go into the discretionary's
29//!   lists (pre-break and replacement at the left, post-break and
30//!   replacement at the right);
31//! * boundary nodes are removed; glyphs lose their "character" subtype.
32
33use crate::Engine;
34use crate::fonts::LigOp;
35use crate::nodes::{GlyphData, NodeData, NodeRef, glyph_sub, id, kern_sub};
36use crate::primitives_gen::assign_int as ip;
37
38/// What a list is processed for (the callback group).
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum Context {
41    /// An `\hbox` (or local box) being packaged.
42    Hbox,
43    /// A paragraph before line breaking.
44    Paragraph,
45}
46
47impl Engine {
48    /// The hyphenation parameters of language `l`.
49    pub fn lang_params(&self, l: u32) -> retex_hyph::Settings {
50        self.language(l).map(|x| x.settings).unwrap_or_default()
51    }
52
53    /// A new glyph node in font `f` with the current language data
54    /// (LuaTeX's `new_char`).
55    pub fn new_char_node(&mut self, f: u32, c: u32) -> NodeRef {
56        let lang = self.cur_lang() as u16;
57        let left = self.eqtb.int(ip::LEFTHYPHENMIN).clamp(0, 63) as u8;
58        let right = self.eqtb.int(ip::RIGHTHYPHENMIN).clamp(0, 63) as u8;
59        let uchyph = self.eqtb.int(ip::UCHYPH) > 0;
60        self.new_node(
61            id::GLYPH,
62            glyph_sub::CHARACTER,
63            NodeData::Glyph(GlyphData {
64                chr: c,
65                font: f,
66                lang,
67                left,
68                right,
69                uchyph,
70                ..GlyphData::default()
71            }),
72        )
73    }
74
75    /// Runs the hyphenation, ligaturing and kerning passes over the list
76    /// starting at `first`; returns the new first node.
77    pub fn node_process_list(&mut self, first: NodeRef, _ctx: Context) -> NodeRef {
78        if first.is_null() {
79            return first;
80        }
81        self.fonts.sync_dense();
82        let head = self.nodes.new_temp();
83        self.nodes.link(head, first);
84        self.hyphenate_list(head);
85        self.ligature_list(head, true);
86        self.kern_list(head);
87        let first = self.nodes.next(head);
88        if !first.is_null() {
89            self.nodes.set_prev(first, NodeRef::NULL);
90        }
91        self.nodes.free(head);
92        first
93    }
94
95    // ----- hyphenation ---------------------------------------------------
96
97    /// The built-in `hyphenate` step: explicit hyphens become automatic
98    /// discretionaries; patterns (through the [`crate::typeset::Hyphenator`])
99    /// insert regular ones.
100    fn hyphenate_list(&mut self, head: NodeRef) {
101        let ex = self.eqtb.int(ip::EXHYPHENCHAR);
102        let pen = self.eqtb.int(ip::EXHYPHENPENALTY);
103        if ex > 0 {
104            let ex = ex as u32;
105            let is_hyphen = |e: &Engine, n: NodeRef| e.nodes.glyph(n).is_some_and(|g| g.chr == ex);
106            // Whether a run of several hyphens may still become a
107            // discretionary before the next glue.
108            let mut armed = true;
109            let mut prev = head;
110            let mut p = self.nodes.next(head);
111            while !p.is_null() {
112                if !is_hyphen(self, p) {
113                    match self.nodes.id(p) {
114                        id::GLUE => armed = true,
115                        id::KERN => armed = false,
116                        _ => {}
117                    }
118                    prev = p;
119                    p = self.nodes.next(p);
120                    continue;
121                }
122                // Find the run of hyphens.
123                let mut last = p;
124                let mut len = 1;
125                let mut q = self.nodes.next(p);
126                while !q.is_null() && is_hyphen(self, q) {
127                    last = q;
128                    len += 1;
129                    q = self.nodes.next(q);
130                }
131                let followed = !q.is_null() && self.nodes.is_glyph(q);
132                let convert =
133                    followed && (len == 1 || (armed && prev != head && self.has_hj_code(prev)));
134                if !convert {
135                    prev = last;
136                    p = q;
137                    continue;
138                }
139                if len > 1 {
140                    armed = false;
141                }
142                // Replace the last hyphen by an automatic discretionary.
143                let before = if len == 1 {
144                    prev
145                } else {
146                    self.nodes.prev(last)
147                };
148                self.nodes.set_next(last, NodeRef::NULL);
149                let d = self.nodes.new_disc(2, pen);
150                if let Some(a) = self.nodes.attr(last).cloned() {
151                    self.nodes.set_attr(d, Some(a));
152                }
153                let pre = self.nodes.copy_list(last);
154                self.nodes.set_prev(last, NodeRef::NULL);
155                self.nodes.set_disc_lists(d, (pre, NodeRef::NULL, last));
156                self.nodes.link(before, d);
157                self.nodes.link(d, q);
158                prev = d;
159                p = q;
160            }
161        }
162        if !self.languages.is_empty() {
163            self.pattern_hyphenate(head);
164        }
165    }
166
167    /// Whether `p` is a glyph whose character has a non-zero hyphenation
168    /// code in its language.
169    fn has_hj_code(&self, p: NodeRef) -> bool {
170        let Some(g) = self.nodes.glyph(p) else {
171            return false;
172        };
173        let lang = u32::from(g.lang);
174        let c = g.chr;
175        let code = match self.language(lang) {
176            Some(l) if l.has_hj_codes() => l.hj_code(c),
177            _ => self.eqtb.lc.get(c).max(0) as u32,
178        };
179        code != 0
180    }
181
182    /// Inserts pattern discretionaries into the words of the list
183    /// (LuaTeX's hyphenation pass, using `retex-hyph`).
184    ///
185    /// Observed: a word is hyphenated only if what precedes it lets a word
186    /// start and what follows it lets a word end. The list start and glue
187    /// let words start; once the list has had a glyph, kerns, penalties,
188    /// discretionaries, marks and `\vadjust`s stop that until the next
189    /// glue; other glyphs, boxes, rules, whatsits, math and direction
190    /// nodes leave it as it is. A word followed by a discretionary, a math
191    /// node or a mark is not hyphenated. With `\automatichyphenmode` > 0 automatic
192    /// discretionaries (from explicit hyphens) count as letting words
193    /// start and end.
194    fn pattern_hyphenate(&mut self, head: NodeRef) {
195        let hpen = self.eqtb.int(ip::HYPHENPENALTY);
196        let auto_ok = self.eqtb.int(ip::AUTOMATICHYPHENMODE) != 0;
197        let mut run: Vec<NodeRef> = Vec::new();
198        let mut word: Vec<NodeRef> = Vec::new();
199        let mut codes: Vec<u32> = Vec::new();
200        let mut pos: Vec<usize> = Vec::new();
201        let mut can_start = true;
202        let mut seen_glyph = false;
203        let mut p = self.nodes.next(head);
204        while !p.is_null() {
205            if self.nodes.is_glyph(p) && self.nodes.subtype(p) & glyph_sub::CHARACTER != 0 {
206                // A run of character glyphs.
207                run.clear();
208                let mut q = p;
209                while !q.is_null()
210                    && self.nodes.is_glyph(q)
211                    && self.nodes.subtype(q) & glyph_sub::CHARACTER != 0
212                {
213                    run.push(q);
214                    q = self.nodes.next(q);
215                }
216                let end_ok = q.is_null()
217                    || match self.nodes.id(q) {
218                        id::DISC => auto_ok && self.nodes.subtype(q) == 2,
219                        id::MATH | id::MARK => false,
220                        _ => true,
221                    };
222                let bounds = (can_start, end_ok);
223                self.hyphenate_run(&run, hpen, bounds, &mut word, &mut codes, &mut pos);
224                seen_glyph = true;
225                p = q;
226                continue;
227            }
228            match self.nodes.id(p) {
229                id::GLUE => can_start = true,
230                id::KERN | id::PENALTY | id::MARK | id::ADJUST => can_start &= !seen_glyph,
231                id::DISC if auto_ok && self.nodes.subtype(p) == 2 => can_start = true,
232                id::DISC => can_start &= !seen_glyph,
233                _ => {}
234            }
235            p = self.nodes.next(p);
236        }
237    }
238
239    /// Hyphenates the words in a run of character glyphs; `bounds` tells
240    /// whether words may start in the run and end at its end.
241    fn hyphenate_run(
242        &mut self,
243        run: &[NodeRef],
244        hpen: i32,
245        bounds: (bool, bool),
246        word: &mut Vec<NodeRef>,
247        codes: &mut Vec<u32>,
248        pos: &mut Vec<usize>,
249    ) {
250        if !bounds.0 {
251            return;
252        }
253        let mut i = 0;
254        while i < run.len() {
255            // A word: glyphs of one language with non-zero hj codes.
256            let Some(g0) = self.nodes.glyph(run[i]).cloned() else {
257                i += 1;
258                continue;
259            };
260            let lang = u32::from(g0.lang);
261            word.clear();
262            codes.clear();
263            while i < run.len() {
264                let Some(g) = self.nodes.glyph(run[i]) else {
265                    break;
266                };
267                if u32::from(g.lang) != lang {
268                    break;
269                }
270                let c = g.chr;
271                let code = match self.language(lang) {
272                    Some(l) if l.has_hj_codes() => l.hj_code(c),
273                    _ => self.eqtb.lc.get(c).max(0) as u32,
274                };
275                if code == 0 {
276                    if word.is_empty() {
277                        i += 1;
278                        continue;
279                    }
280                    break;
281                }
282                word.push(run[i]);
283                codes.push(code);
284                i += 1;
285            }
286            if word.len() < 2 {
287                continue;
288            }
289            if !bounds.1 && word.last() == run.last() {
290                continue;
291            }
292            let Some(first) = self.nodes.glyph(word[0]).cloned() else {
293                continue;
294            };
295            if first.font == 0 || self.font(first.font).hyphen_char < 0 {
296                continue;
297            }
298            if !first.uchyph && codes[0] != first.chr {
299                continue;
300            }
301            let Some(language) = self.language(lang) else {
302                continue;
303            };
304            pos.clear();
305            let _ =
306                language.hyphenate_into(codes, u32::from(first.left), u32::from(first.right), pos);
307            if pos.is_empty() {
308                continue;
309            }
310            let settings = language.settings;
311            pos.sort_unstable();
312            pos.dedup();
313            for &k in pos.iter().rev() {
314                if k == 0 || k >= word.len() {
315                    continue;
316                }
317                let left = word[k - 1];
318                let right = word[k];
319                let f = self.nodes.glyph(left).map_or(0, |g| g.font);
320                let d = self.nodes.new_disc(3, hpen);
321                if let Some(a) = self.nodes.attr(left).cloned() {
322                    self.nodes.set_attr(d, Some(a));
323                }
324                let mk = |e: &mut Engine, c: i32| {
325                    if c > 0 {
326                        let mut g = e.nodes.glyph(left).cloned().unwrap_or_default();
327                        g.chr = c as u32;
328                        g.font = f;
329                        g.components = NodeRef::NULL;
330                        e.nodes
331                            .alloc(id::GLYPH, glyph_sub::CHARACTER, NodeData::Glyph(g))
332                    } else {
333                        NodeRef::NULL
334                    }
335                };
336                let pre = mk(self, settings.pre_hyphen_char);
337                let post = mk(self, settings.post_hyphen_char);
338                self.nodes.set_disc_lists(d, (pre, post, NodeRef::NULL));
339                self.nodes.link(left, d);
340                self.nodes.link(d, right);
341            }
342        }
343    }
344
345    // ----- ligaturing ----------------------------------------------------
346
347    /// Whether `p` is a glyph of font `f` (any subtype except ghosts).
348    #[inline]
349    fn glyph_of_font(&self, p: NodeRef, f: u32) -> Option<u32> {
350        if p.is_null() {
351            return None;
352        }
353        match self.nodes.data(p) {
354            NodeData::Glyph(g) if g.font == f && self.nodes.subtype(p) & glyph_sub::GHOST == 0 => {
355                Some(g.chr)
356            }
357            _ => None,
358        }
359    }
360
361    /// The ligature step for (`l`, `r`) in font `f`, if it is a ligature.
362    #[inline]
363    fn lig_step(&self, f: u32, l: u32, r: u32) -> Option<(u8, u32)> {
364        match self.lig_kern_step(f, l, r) {
365            Some(LigOp::Lig { op, chr }) => Some((op, chr)),
366            _ => None,
367        }
368    }
369
370    /// The components of a glyph for a new ligature: the glyph itself, or
371    /// its components if it is already a ligature.
372    fn take_components(&mut self, p: NodeRef) -> NodeRef {
373        let is_lig = self.nodes.subtype(p) & glyph_sub::LIGATURE != 0;
374        if is_lig {
375            let c = self
376                .nodes
377                .glyph_mut(p)
378                .map(|g| std::mem::take(&mut g.components));
379            let c = c.unwrap_or(NodeRef::NULL);
380            self.nodes.free(p);
381            if c.is_null() {
382                return NodeRef::NULL;
383            }
384            c
385        } else {
386            self.nodes.set_next(p, NodeRef::NULL);
387            self.nodes.set_prev(p, NodeRef::NULL);
388            let s = self.nodes.subtype(p) & !glyph_sub::CHARACTER;
389            self.nodes.set_subtype(p, s);
390            p
391        }
392    }
393
394    /// Makes a ligature glyph like `model` with character `c` and the given
395    /// components.
396    fn new_ligature(&mut self, model: NodeRef, c: u32, comps: NodeRef) -> NodeRef {
397        let mut g = self.nodes.glyph(model).cloned().unwrap_or_default();
398        g.chr = c;
399        g.components = comps;
400        let a = self.nodes.attr(model).cloned();
401        let n = self
402            .nodes
403            .alloc(id::GLYPH, glyph_sub::LIGATURE, NodeData::Glyph(g));
404        self.nodes.set_attr(n, a);
405        n
406    }
407
408    /// Concatenates two node lists.
409    fn concat(&mut self, a: NodeRef, b: NodeRef) -> NodeRef {
410        if a.is_null() {
411            return b;
412        }
413        if !b.is_null() {
414            let t = self.nodes.tail_of(a);
415            self.nodes.link(t, b);
416        }
417        a
418    }
419
420    /// Replaces `old` (whose predecessor is `prev`) by `new` in a list.
421    fn replace_node(&mut self, prev: NodeRef, old_next: NodeRef, new: NodeRef) {
422        self.nodes.link(prev, new);
423        self.nodes.link(new, old_next);
424        if old_next.is_null() {
425            self.nodes.set_next(new, NodeRef::NULL);
426        }
427    }
428
429    /// Builds ligatures in the list after `head`. `main` is true for the
430    /// main list (boundary nodes are removed there).
431    fn ligature_list(&mut self, head: NodeRef, main: bool) {
432        let mut prev = head;
433        let mut p = self.nodes.next(head);
434        while !p.is_null() {
435            match self.nodes.id(p) {
436                id::BOUNDARY if main => {
437                    let n = self.nodes.next(p);
438                    self.nodes.set_next(prev, n);
439                    if !n.is_null() {
440                        self.nodes.set_prev(n, prev);
441                    }
442                    self.nodes.free(p);
443                    p = n;
444                }
445                id::GLYPH => {
446                    let (last_prev, last) = self.ligature_run(prev, p);
447                    // `last` is the final glyph of the run; it may be
448                    // followed by a discretionary.
449                    let n = self.nodes.next(last);
450                    if !n.is_null() && self.nodes.id(n) == id::DISC {
451                        let (np, nd) = self.lig_disc(last_prev, last, n);
452                        prev = np;
453                        p = nd;
454                    } else {
455                        prev = last;
456                        p = n;
457                    }
458                }
459                id::DISC => {
460                    self.lig_disc_lists(p);
461                    prev = p;
462                    p = self.nodes.next(p);
463                }
464                _ => {
465                    prev = p;
466                    p = self.nodes.next(p);
467                }
468            }
469        }
470    }
471
472    /// Ligatures the three lists of a discretionary independently.
473    fn lig_disc_lists(&mut self, d: NodeRef) {
474        let (pre, post, rep) = self.nodes.disc_lists(d);
475        let pre = self.ligature_sublist(pre);
476        let post = self.ligature_sublist(post);
477        let rep = self.ligature_sublist(rep);
478        self.nodes.set_disc_lists(d, (pre, post, rep));
479    }
480
481    /// Ligatures a detached list; returns its new head.
482    fn ligature_sublist(&mut self, l: NodeRef) -> NodeRef {
483        if l.is_null() {
484            return l;
485        }
486        let h = self.nodes.new_temp();
487        self.nodes.link(h, l);
488        self.ligature_list(h, false);
489        let r = self.nodes.next(h);
490        if !r.is_null() {
491            self.nodes.set_prev(r, NodeRef::NULL);
492        }
493        self.nodes.free(h);
494        r
495    }
496
497    /// Handles a discretionary `d` that follows the glyph `last` (whose
498    /// predecessor is found through `prev` links): moves neighbouring
499    /// glyphs into it when ligatures cross it, then ligatures its lists.
500    /// Returns (prev, next) for continuing the scan after `d`.
501    fn lig_disc(&mut self, before: NodeRef, last: NodeRef, d: NodeRef) -> (NodeRef, NodeRef) {
502        let f = self.nodes.glyph(last).map_or(0, |g| g.font);
503        if self.font(f).lig_kern.is_empty() {
504            self.lig_disc_lists(d);
505            return (d, self.nodes.next(d));
506        }
507        let lc = self.nodes.glyph(last).map_or(0, |g| g.chr);
508        let (pre, post, rep) = self.nodes.disc_lists(d);
509        let after = self.nodes.next(d);
510        let crosses =
511            |e: &Engine, l: NodeRef| e.glyph_of_font(l, f).and_then(|c| e.lig_step(f, lc, c));
512        let left = crosses(self, pre).is_some()
513            || (!rep.is_null() && crosses(self, rep).is_some())
514            || (rep.is_null() && crosses(self, after).is_some());
515        let (mut pre, mut post, mut rep) = (pre, post, rep);
516        if left {
517            // Move `last` into the discretionary.
518            self.nodes.link(before, d);
519            self.nodes.set_next(last, NodeRef::NULL);
520            self.nodes.set_prev(last, NodeRef::NULL);
521            let copy = self.nodes.copy_node(last);
522            pre = self.concat(copy, pre);
523            rep = self.concat(last, rep);
524        }
525        // Move following glyphs into the discretionary while a ligature
526        // crosses the right edge.
527        loop {
528            let n = self.nodes.next(d);
529            let Some(nc) = self.glyph_of_font(n, f) else {
530                break;
531            };
532            let ends = |e: &Engine, l: NodeRef| {
533                if l.is_null() {
534                    return false;
535                }
536                let t = e.nodes.tail_of(l);
537                e.glyph_of_font(t, f)
538                    .is_some_and(|c| e.lig_step(f, c, nc).is_some())
539            };
540            if !(ends(self, post) || ends(self, rep)) {
541                break;
542            }
543            let nn = self.nodes.next(n);
544            self.nodes.link(d, nn);
545            if nn.is_null() {
546                self.nodes.set_next(d, NodeRef::NULL);
547            }
548            self.nodes.set_next(n, NodeRef::NULL);
549            self.nodes.set_prev(n, NodeRef::NULL);
550            let copy = self.nodes.copy_node(n);
551            post = self.concat(post, copy);
552            rep = self.concat(rep, n);
553        }
554        self.nodes.set_disc_lists(d, (pre, post, rep));
555        self.lig_disc_lists(d);
556        (d, self.nodes.next(d))
557    }
558
559    /// Builds ligatures in the run of glyphs starting at `p` (with
560    /// predecessor `prev`); returns the last glyph of the processed run
561    /// and its predecessor.
562    fn ligature_run(&mut self, prev: NodeRef, p: NodeRef) -> (NodeRef, NodeRef) {
563        let f = self.nodes.glyph(p).map_or(0, |g| g.font);
564        if self.font(f).lig_kern.is_empty() {
565            // No ligatures in this font: skip the run.
566            let (mut qp, mut q) = (prev, p);
567            loop {
568                let n = self.nodes.next(q);
569                if self.glyph_of_font(n, f).is_none() {
570                    return (qp, q);
571                }
572                qp = q;
573                q = n;
574            }
575        }
576        let mut cur_prev = prev;
577        let mut cur = p;
578        let mut guard = 0u32;
579        loop {
580            let nxt = self.nodes.next(cur);
581            let (Some(l), Some(r)) = (self.glyph_of_font(cur, f), self.glyph_of_font(nxt, f))
582            else {
583                return (cur_prev, cur);
584            };
585            guard += 1;
586            if guard > 100_000 {
587                // An infinite ligature loop in a broken font.
588                return (cur_prev, cur);
589            }
590            let Some((op, c)) = self.lig_step(f, l, r) else {
591                cur_prev = cur;
592                cur = nxt;
593                continue;
594            };
595            let after = self.nodes.next(nxt);
596            match op {
597                0 => {
598                    // =: — both characters replaced.
599                    let a = self.take_components(cur);
600                    let b = self.take_components(nxt);
601                    let comps = self.concat(a, b);
602                    let model = if comps.is_null() { cur } else { comps };
603                    let lig = self.new_ligature(model, c, comps);
604                    self.replace_node(cur_prev, after, lig);
605                    cur = lig;
606                }
607                1 | 5 => {
608                    // =:| and =:|> — the left character is replaced.
609                    let a = self.take_components(cur);
610                    let model = if a.is_null() { nxt } else { a };
611                    let lig = self.new_ligature(model, c, a);
612                    self.replace_node(cur_prev, nxt, lig);
613                    if op == 5 {
614                        cur_prev = lig;
615                        cur = nxt;
616                    } else {
617                        cur = lig;
618                    }
619                }
620                2 | 6 => {
621                    // |=: and |=:> — the right character is replaced.
622                    let b = self.take_components(nxt);
623                    let model = if b.is_null() { cur } else { b };
624                    let lig = self.new_ligature(model, c, b);
625                    self.replace_node(cur, after, lig);
626                    if op == 6 {
627                        cur_prev = cur;
628                        cur = lig;
629                    }
630                }
631                _ => {
632                    // |=:|, |=:|>, |=:|>> — a character is inserted.
633                    let lig = self.new_ligature(cur, c, NodeRef::NULL);
634                    self.nodes.set_subtype(lig, 0);
635                    self.replace_node(cur, nxt, lig);
636                    match op {
637                        7 => {
638                            cur_prev = cur;
639                            cur = lig;
640                        }
641                        11 => {
642                            cur_prev = lig;
643                            cur = nxt;
644                        }
645                        _ => {}
646                    }
647                }
648            }
649        }
650    }
651
652    // ----- kerning -------------------------------------------------------
653
654    /// The font kern between (`l`, `r`) in font `f`.
655    #[inline]
656    fn kern_between(&self, f: u32, l: u32, r: u32) -> Option<i32> {
657        match self.lig_kern_step(f, l, r) {
658            Some(LigOp::Kern(k)) => Some(k),
659            _ => None,
660        }
661    }
662
663    /// A font kern node with the attributes of `model`.
664    fn font_kern(&mut self, k: i32, model: NodeRef) -> NodeRef {
665        let n = self.nodes.new_kern(k, kern_sub::FONT);
666        let a = self.nodes.attr(model).cloned();
667        self.nodes.set_attr(n, a);
668        n
669    }
670
671    /// Inserts font kerns into the list after `head` and clears the
672    /// character subtype of glyphs.
673    fn kern_list(&mut self, head: NodeRef) {
674        let mut p = self.nodes.next(head);
675        while !p.is_null() {
676            match self.nodes.id(p) {
677                id::GLYPH => {
678                    let s = self.nodes.subtype(p) & !glyph_sub::CHARACTER;
679                    self.nodes.set_subtype(p, s);
680                    let n = self.nodes.next(p);
681                    if n.is_null() {
682                        break;
683                    }
684                    let (f, l) = match self.nodes.glyph(p) {
685                        Some(g) => (g.font, g.chr),
686                        None => break,
687                    };
688                    if self.nodes.id(n) == id::GLYPH {
689                        if let Some(r) = self.glyph_of_font(n, f)
690                            && let Some(k) = self.kern_between(f, l, r)
691                        {
692                            let kn = self.font_kern(k, p);
693                            self.nodes.link(p, kn);
694                            self.nodes.link(kn, n);
695                        }
696                        p = n;
697                    } else if self.nodes.id(n) == id::DISC {
698                        self.kern_disc(p, n);
699                        p = self.nodes.next(n);
700                    } else {
701                        p = n;
702                    }
703                }
704                id::DISC => {
705                    self.kern_disc(NodeRef::NULL, p);
706                    p = self.nodes.next(p);
707                }
708                _ => p = self.nodes.next(p),
709            }
710        }
711    }
712
713    /// Kerns a discretionary with its neighbours (`before` may be null)
714    /// and kerns its lists.
715    fn kern_disc(&mut self, before: NodeRef, d: NodeRef) {
716        let (mut pre, mut post, mut rep) = self.nodes.disc_lists(d);
717        pre = self.kern_sublist(pre);
718        post = self.kern_sublist(post);
719        let rep_was_empty = rep.is_null();
720        rep = self.kern_sublist(rep);
721        let after = self.nodes.next(d);
722        let bg = if before.is_null() {
723            None
724        } else {
725            self.nodes.glyph(before).map(|g| (g.font, g.chr))
726        };
727        let ag = if after.is_null() || self.nodes.id(after) != id::GLYPH {
728            None
729        } else {
730            self.nodes.glyph(after).map(|g| (g.font, g.chr))
731        };
732        if let Some((f, l)) = bg {
733            if let Some(r) = self.glyph_of_font(pre, f)
734                && let Some(k) = self.kern_between(f, l, r)
735            {
736                let kn = self.font_kern(k, before);
737                self.nodes.link(kn, pre);
738                pre = kn;
739            }
740            if !rep_was_empty {
741                if let Some(r) = self.glyph_of_font(rep, f)
742                    && let Some(k) = self.kern_between(f, l, r)
743                {
744                    let kn = self.font_kern(k, before);
745                    self.nodes.link(kn, rep);
746                    rep = kn;
747                }
748            } else if let Some((af, ac)) = ag
749                && af == f
750                && let Some(k) = self.kern_between(f, l, ac)
751            {
752                rep = self.font_kern(k, before);
753            }
754        }
755        if let Some((f, r)) = ag {
756            if !post.is_null() {
757                let t = self.nodes.tail_of(post);
758                if let Some(l) = self.glyph_of_font(t, f)
759                    && let Some(k) = self.kern_between(f, l, r)
760                {
761                    let kn = self.font_kern(k, after);
762                    self.nodes.link(t, kn);
763                }
764            }
765            if !rep_was_empty {
766                let t = self.nodes.tail_of(rep);
767                if let Some(l) = self.glyph_of_font(t, f)
768                    && let Some(k) = self.kern_between(f, l, r)
769                {
770                    let kn = self.font_kern(k, after);
771                    self.nodes.link(t, kn);
772                }
773            }
774        }
775        self.nodes.set_disc_lists(d, (pre, post, rep));
776    }
777
778    /// Kerns a detached list; returns its new head.
779    fn kern_sublist(&mut self, l: NodeRef) -> NodeRef {
780        if l.is_null() {
781            return l;
782        }
783        let h = self.nodes.new_temp();
784        self.nodes.link(h, l);
785        self.kern_list(h);
786        let r = self.nodes.next(h);
787        if !r.is_null() {
788            self.nodes.set_prev(r, NodeRef::NULL);
789        }
790        self.nodes.free(h);
791        r
792    }
793}