texpackaging.c /size: 153 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    We're essentially done with the parts of \TEX\ that are concerned with the input (|get_next|)
10    and the output (|ship_out|). So it's time to get heavily into the remaining part, which does
11    the real work of typesetting.
12
13    After lists are constructed, \TEX\ wraps them up and puts them into boxes. Two major
14    subroutines are given the responsibility for this task: |hpack| applies to horizontal lists
15    (hlists) and |vpack| applies to vertical lists (vlists). The main duty of |hpack| and |vpack|
16    is to compute the dimensions of the resulting boxes, and to adjust the glue if one of those
17    dimensions is pre-specified. The computed sizes normally enclose all of the material inside the
18    new box; but some items may stick out if negative glue is used, if the box is overfull, or if a
19    |\vbox| includes other boxes that have been shifted left.
20
21    The subroutine call |hpack(p, w, m)| returns a pointer to an |hlist_node| for a box containing
22    the hlist that starts at |p|. Parameter |w| specifies a width; and parameter |m| is either
23    |exactly| or |additional|. Thus, |hpack(p, w, exactly)| produces a box whose width is exactly
24    |w|, while |hpack(p, w, additional)| yields a box whose width is the natural width plus |w|. It
25    is convenient to define a macro called |natural| to cover the most common case, so that we can
26    say |hpack(p, natural)| to get a box that has the natural width of list |p|.
27
28    Similarly, |vpack(p, w, m)| returns a pointer to a |vlist_node| for a box containing the vlist
29    that starts at |p|. In this case |w| represents a height instead of a width; the parameter |m|
30    is interpreted as in |hpack|.
31
32    The parameters to |hpack| and |vpack| correspond to \TEX's primitives like |\hbox to 300pt|,
33    |\hbox spread 10pt|; note that |\hbox| with no dimension following it is equivalent to |\hbox
34    spread 0pt|. The |scan_spec| subroutine scans such constructions in the user's input, including
35    the mandatory left brace that follows them, and it puts the specification onto |save_stack| so
36    that the desired box can later be obtained by executing the following code:
37
38    \starttyping
39    save_state.save_ptr := save_state.save_ptr-1;
40    hpack(p, saved_value(0), saved_level(0));
41    \stoptyping
42
43     Scan a box specification and left brace:
44 */
45
46/*tex
47    The next version is the (current) end point of successive improvements. After some keys were
48    added it became important to avoid redundant checking and pushing back mismatched keys. The
49    older (maybe more readable) variants using |scan_keyword| can be found in the archives (zip
50    and git) instead of as comments here.
51*/
52
53/*tex
54
55    When scanning, special care is necessary to ensure that the special |save_stack| codes are
56    placed just below the new group code, because scanning can change |save_stack| when |\csname|
57    appears. This coincides with the text on |dir| and |attr| keywords, as these are exaclty the
58    uses of |\hbox|, |\vbox|, and |\vtop| in the input stream (the others are |\vcenter|, |\valign|,
59    and  |\halign|).
60
61    Scan a box specification and left brace comes next. Again, the more verbose, but already
62    rather optimized intermediate variants are in the archives. Improving scanners like this happen
63    stepwise in order to maintain compatibility (although \unknown\ we now quit earlier in a
64    mismatch so we're not exact compatible when an forward looking error happens.
65
66 */
67
68/* todo: options bitset */
69
70typedef enum saved_box_entries {
71    saved_box_slot_entry        = 0,
72    saved_box_context_entry     = 0,
73    saved_box_packing_entry     = 0,
74    saved_box_amount_entry      = 1,
75    saved_box_direction_entry   = 1,
76    saved_box_dir_pointer_entry = 1,
77    saved_box_attr_list_entry   = 2,
78    saved_box_pack_entry        = 2,
79    saved_box_orientation_entry = 2,
80    saved_box_anchor_entry      = 3,
81    saved_box_geometry_entry    = 3,
82    saved_box_xoffset_entry     = 3,
83    saved_box_yoffset_entry     = 4,
84    saved_box_xmove_entry       = 4,
85    saved_box_ymove_entry       = 4,
86    saved_box_shift_entry       = 5,
87    saved_box_source_entry      = 5,
88    saved_box_target_entry      = 5,
89    saved_box_class_entry       = 6,
90    saved_box_state_entry       = 6,
91    saved_box_axis_entry        = 6, 
92    saved_box_retain_entry      = 7,  
93    saved_box_callback_entry    = 7, // from elsewhere 
94    saved_box_option_entry      = 7,
95    saved_box_n_of_records      = 8,
96} saved_box_entries;
97
98typedef enum saved_box_options { 
99    saved_box_reverse_option   = 0x01,
100    saved_box_container_option = 0x02,
101    saved_box_limit_option     = 0x04,
102} saved_box_options;
103
104inline static void saved_box_initialize(void)
105{
106    saved_type(0) = saved_record_0;
107    saved_type(1) = saved_record_1;
108    saved_type(2) = saved_record_2;
109    saved_type(3) = saved_record_3;
110    saved_type(4) = saved_record_4;
111    saved_type(5) = saved_record_5;
112    saved_type(6) = saved_record_6;
113    saved_type(7) = saved_record_7;
114    saved_record(0) = box_save_type;
115    saved_record(1) = box_save_type;
116    saved_record(2) = box_save_type;
117    saved_record(3) = box_save_type;
118    saved_record(4) = box_save_type;
119    saved_record(5) = box_save_type;
120    saved_record(6) = box_save_type;
121    saved_record(7) = box_save_type;
122}
123
124# define saved_box_slot        saved_value_1(saved_box_slot_entry)
125# define saved_box_context     saved_value_2(saved_box_context_entry)
126# define saved_box_packing     saved_value_3(saved_box_packing_entry)
127# define saved_box_amount      saved_value_1(saved_box_amount_entry)
128# define saved_box_direction   saved_value_2(saved_box_direction_entry)
129# define saved_box_dir_pointer saved_value_3(saved_box_dir_pointer_entry)
130# define saved_box_attr_list   saved_value_1(saved_box_attr_list_entry)
131# define saved_box_pack        saved_value_2(saved_box_pack_entry)
132# define saved_box_orientation saved_value_3(saved_box_orientation_entry)
133# define saved_box_anchor      saved_value_1(saved_box_anchor_entry)
134# define saved_box_geometry    saved_value_2(saved_box_geometry_entry)
135# define saved_box_xoffset     saved_value_3(saved_box_xoffset_entry)
136# define saved_box_yoffset     saved_value_1(saved_box_yoffset_entry)
137# define saved_box_xmove       saved_value_2(saved_box_xmove_entry)
138# define saved_box_ymove       saved_value_3(saved_box_ymove_entry)
139# define saved_box_shift       saved_value_1(saved_box_shift_entry)
140# define saved_box_source      saved_value_2(saved_box_source_entry)
141# define saved_box_target      saved_value_3(saved_box_target_entry)
142# define saved_box_class       saved_value_1(saved_box_class_entry)
143# define saved_box_state       saved_value_2(saved_box_state_entry)
144# define saved_box_axis        saved_value_3(saved_box_axis_entry)
145# define saved_box_retain      saved_value_1(saved_box_retain_entry)
146# define saved_box_callback    saved_value_2(saved_box_callback_entry)
147# define saved_box_option      saved_value_3(saved_box_option_entry)
148
149void tex_show_packaging_group(const char *package)
150{
151    tex_print_str_esc(package);
152    tex_print_str(saved_box_packing == packing_exactly ? " to " : " spread ");
153    tex_print_dimension(saved_box_amount, pt_unit);
154}
155
156int tex_show_packaging_record(void)
157{
158    tex_print_str("box, ");
159    switch (saved_type(0)) { 
160        case saved_record_0:
161            tex_print_format("slot %i, context %i, packing %i", save_value_1(0), saved_value_2(0), saved_value_3(0));
162            break;
163        case saved_record_1:
164            tex_print_format("amount %p, direction %i, pointer %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
165            break;
166        case saved_record_2:
167            tex_print_format("attrlist %i, pack %i, orientation %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
168            break;
169        case saved_record_3:
170            tex_print_format("anchor %i, geometry %i, xoffset %p", saved_value_1(0), saved_value_2(0), saved_value_3(0));
171            break;
172        case saved_record_4:
173            tex_print_format("yoffset %p, xmove %p, ymove %p", saved_value_1(0), saved_value_2(0), saved_value_3(0));
174            break;
175        case saved_record_5:       
176            tex_print_format("shift %p, source %i, target %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
177            break;
178        case saved_record_6:
179            tex_print_format("class %i, state %i, axis %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
180            break;
181        case saved_record_7:
182            tex_print_format("retain %i, callback %i, option %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
183            break;
184        default: 
185            return 0;
186    }
187    return 1;
188}
189
190int tex_get_packaging_context(void) { return saved_box_context; }
191int tex_get_packaging_shift  (void) { return saved_box_shift; }
192
193static void tex_aux_scan_full_spec(halfword context, quarterword c, quarterword spec_direction, int just_pack, scaled shift, halfword slot, halfword callback)
194{
195    quarterword spec_packing = packing_additional;
196    int spec_amount = 0;
197    halfword attrlist = null;
198    halfword orientation = 0;
199    scaled xoffset = 0;
200    scaled yoffset = 0;
201    scaled xmove = 0;
202    scaled ymove = 0;
203    halfword source = 0;
204    halfword target = 0;
205    halfword anchor = 0;
206    halfword geometry = 0;
207    halfword axis = 0;
208    halfword state = 0;
209    halfword retain = 0;
210    halfword mainclass = unset_noad_class;
211    halfword options = 0;
212    int brace = 0;
213    while (1) {
214        /*tex Maybe |migrate <int>| makes sense here. */
215        switch (tex_scan_character("tascdoxyrlTASCDOXYRL", 1, 1, 1)) {
216            case 0:
217                goto DONE;
218            case 't': case 'T':
219                switch (tex_scan_character("aoAO", 0, 0, 0)) {
220                    case 'a': case 'A':
221                        if (tex_scan_mandate_keyword("target", 2)) {
222                            target = tex_scan_integer(1, NULL);
223                        }
224                        break;
225                    case 'o': case 'O':
226                        spec_packing = packing_exactly;
227                        spec_amount = tex_scan_dimension(0, 0, 0, 0, NULL);
228                        break;
229                    default:
230                        tex_aux_show_keyword_error("target|to");
231                        goto DONE;
232                }
233                break;
234            case 'a': case 'A':
235                switch (tex_scan_character("dntxDNTX", 0, 0, 0)) {
236                    case 'd': case 'D':
237                        if (tex_scan_mandate_keyword("adapt", 2)) {
238                            spec_packing = packing_adapted;
239                            spec_amount = tex_scan_limited_scale(0);
240                        }
241                        break;
242                    case 't': case 'T':
243                        if (tex_scan_mandate_keyword("attr", 2)) {
244                            attrlist = tex_scan_attribute(attrlist);
245                        }
246                        break;
247                    case 'n': case 'N':
248                        if (tex_scan_mandate_keyword("anchor", 2)) {
249                            switch (tex_scan_character("sS", 0, 0, 0)) {
250                                case 's': case 'S':
251                                    anchor = tex_scan_anchors(0);
252                                    break;
253                                default:
254                                    anchor = tex_scan_anchor(0);
255                                    break;
256                            }
257                        }
258                        break;
259                    case 'x': case 'X':
260                        if (tex_scan_mandate_keyword("axis", 2)) {
261                            axis |= tex_scan_box_axis();
262                        }
263                        break;
264                    default:
265                        tex_aux_show_keyword_error("adapt|attr|anchor|axis");
266                        goto DONE;
267                }
268                break;
269            case 's': case 'S':
270                switch (tex_scan_character("hpoHPO", 0, 0, 0)) {
271                    case 'h': case 'H':
272                        /*tex
273                            This is a bonus because we decoupled the shift amount from the context,
274                            where it can be somewhat confusing as that is a hybrid amount, kind, or
275                            flag field. The keyword overloads an already given |move_cmd|.
276                        */
277                        if (tex_scan_mandate_keyword("shift", 2)) {
278                            shift = tex_scan_dimension(0, 0, 0, 0, NULL);
279                        }
280                        break;
281                    case 'p': case 'P':
282                        if (tex_scan_mandate_keyword("spread", 2)) {
283                            spec_packing = packing_additional;
284                            spec_amount = tex_scan_dimension(0, 0, 0, 0, NULL);
285                        }
286                        break;
287                    case 'o': case 'O':
288                        if (tex_scan_mandate_keyword("source", 2)) {
289                            source = tex_scan_integer(1, NULL);
290                        }
291                        break;
292                    default:
293                        tex_aux_show_keyword_error("shift|spread|source");
294                        goto DONE;
295                }
296                break;
297            case 'd': case 'D':
298                switch (tex_scan_character("eiEI", 0, 0, 0)) {
299                    case 'i': case 'I':
300                        /*tex 
301                            For the sake of third party code that assumes \LUATEX's old directives 
302                            we can support |dir TRT| and |dirTLT|; only these. But \unknown\ that 
303                            would also mean we have to deal with |\textdir| and such. 
304                        */
305# if (0) 
306                        if (tex_scan_mandate_keyword("direction", 2)) {
307                            spec_direction = tex_scan_direction(0);
308                        }
309# else 
310                        if (tex_scan_keyword("rection")) {
311                            spec_direction = tex_scan_direction(0);
312                        } else if (tex_scan_character("rR", 0, 0, 0)) {
313                            /*tex This is undocumented. */
314                            if (tex_scan_character("tT", 0, 1, 0)) {
315                                switch (tex_scan_character("lLrR", 0, 0, 0)) {
316                                    case 'l': case 'L': 
317                                        spec_direction = 0;
318                                        break;
319                                    case 'r': case 'R': 
320                                        spec_direction = 1;
321                                        break;
322                                    default: 
323                                        goto BADDIR;
324                                }
325                                if (! tex_scan_character("tT", 0, 0, 0)) {
326                                    goto BADDIR;
327                                }
328                            } else { 
329                              BADDIR:
330                                tex_aux_show_keyword_error("tlt|trt");
331                                goto DONE;
332                            }
333                        } else {
334                            tex_aux_show_keyword_error("direction|dir");
335                            goto DONE;
336                        }
337# endif 
338                        break;
339                    case 'e': case 'E':
340                        if (tex_scan_mandate_keyword("delay", 2)) {
341                            state |= package_u_leader_delayed;
342                        }
343                        break;
344                    default:
345                        tex_aux_show_keyword_error("direction|delay");
346                        goto DONE;
347                }
348                break;
349            case 'o': case 'O':
350                if (tex_scan_mandate_keyword("orientation", 1)) {
351                    orientation = tex_scan_orientation(0);
352                }
353                break;
354            case 'x': case 'X':
355                switch (tex_scan_character("omOM", 0, 0, 0)) {
356                    case 'o': case 'O' :
357                        if (tex_scan_mandate_keyword("xoffset", 2)) {
358                            xoffset = tex_scan_dimension(0, 0, 0, 0, NULL);
359                        }
360                        break;
361                    case 'm': case 'M' :
362                        if (tex_scan_mandate_keyword("xmove", 2)) {
363                            xmove = tex_scan_dimension(0, 0, 0, 0, NULL);
364                        }
365                        break;
366                    default:
367                        tex_aux_show_keyword_error("xoffset|xmove");
368                        goto DONE;
369                }
370                break;
371            case 'y': case 'Y':
372                switch (tex_scan_character("omOM", 0, 0, 0)) {
373                    case 'o': case 'O' :
374                        if (tex_scan_mandate_keyword("yoffset", 2)) {
375                             yoffset = tex_scan_dimension(0, 0, 0, 0, NULL);
376                        }
377                        break;
378                    case 'm': case 'M' :
379                        if (tex_scan_mandate_keyword("ymove", 2)) {
380                             ymove = tex_scan_dimension(0, 0, 0, 0, NULL);
381                        }
382                        break;
383                    default:
384                        tex_aux_show_keyword_error("yoffset|ymove");
385                        goto DONE;
386                }
387                break;
388            case 'r': case 'R':
389                if (tex_scan_character("eE", 0, 0, 0)) {
390                    switch (tex_scan_character("vVtT", 0, 0, 0)) {
391                        case 'v': case 'V' :
392                            if (tex_scan_mandate_keyword("reverse", 3)) {
393                                options |= saved_box_reverse_option;
394                            }
395                            break;
396                        case 't': case 'T' :
397                            if (tex_scan_mandate_keyword("retain", 3)) {
398                                retain = tex_scan_integer(0, NULL);
399                            }
400                            break;
401                        default:
402                            tex_aux_show_keyword_error("reverse|retain");
403                            goto DONE;
404                    }
405                }
406                break;
407            case 'c': case 'C':
408                switch (tex_scan_character("olOL", 0, 0, 0)) {
409                    case 'o': case 'O' :
410                        if (tex_scan_mandate_keyword("container", 2)) {
411                            options |= saved_box_container_option;
412                        }
413                        break;
414                    case 'l': case 'L' :
415                        if (tex_scan_mandate_keyword("class", 2)) {
416                            mainclass = tex_scan_math_class_number(0);
417                        }
418                        break;
419                    default:
420                        tex_aux_show_keyword_error("container|class");
421                        goto DONE;
422                }
423                break;
424            case 'l': case 'L':
425                if (tex_scan_mandate_keyword("limit", 1)) {
426                    options |= saved_box_limit_option;
427                }
428                break;
429            case '{':
430                brace = 1;
431                goto DONE;
432            default:
433                goto DONE;
434        }
435    }
436  DONE:
437    if (anchor || source || target) {
438        geometry |= anchor_geometry;
439    }
440    if (orientation || xmove || ymove) {
441        geometry |= orientation_geometry;
442    }
443    if (xoffset || yoffset) {
444        geometry |= offset_geometry;
445    }
446    /*tex
447        We either build one triggered by the |attr| key or we never set it in which case we use the
448        default. As we will use it anyway, we also bump the reference, which also makes sure that
449        it will stay.
450    */
451    if (! attrlist) {
452        /* this alse sets the reference when not yet set */
453        attrlist = tex_current_attribute_list();
454    }
455    /*tex Now we're referenced. We need to preserve this over the group. */
456    add_attribute_reference(attrlist);
457    /* */
458    saved_box_initialize();
459    saved_box_slot = slot;
460    saved_box_context = context; 
461    /*tex Traditionally these two are packed into one record: */
462    saved_box_packing = spec_packing;
463    saved_box_amount = spec_amount;
464    /*tex Adjust |text_dir_ptr| for |scan_spec|: */
465    saved_box_direction = spec_direction;
466    if (spec_direction != direction_unknown) {
467        saved_box_dir_pointer = lmt_dir_state.text_dir_ptr;
468        lmt_dir_state.text_dir_ptr = tex_new_dir(normal_dir_subtype, spec_direction);
469    } else {
470        saved_box_dir_pointer = null;
471    }
472    saved_box_attr_list = attrlist;
473    saved_box_pack = just_pack;
474    saved_box_orientation = orientation;
475    saved_box_anchor = anchor;
476    saved_box_geometry = geometry;
477    saved_box_xoffset = xoffset;
478    saved_box_yoffset = yoffset;
479    saved_box_xmove = xmove;
480    saved_box_ymove = ymove;
481    saved_box_source = source;
482    saved_box_target = target;
483    saved_box_state = state;
484    saved_box_option = options;
485    saved_box_shift = shift;
486    saved_box_axis = axis;
487    saved_box_class = mainclass;
488    saved_box_retain = retain;
489    saved_box_callback = callback;
490    lmt_save_state.save_stack_data.ptr += saved_box_n_of_records;
491    tex_new_save_level(c);
492    if (! brace) {
493        tex_scan_left_brace();
494    }
495    update_tex_par_direction(spec_direction);
496    update_tex_text_direction(spec_direction);
497}
498
499/*tex
500
501    To figure out the glue setting, |hpack| and |vpack| determine how much stretchability and
502    shrinkability are present, considering all four orders of infinity. The highest order of
503    infinity that has a nonzero coefficient is then used as if no other orders were present.
504
505    For example, suppose that the given list contains six glue nodes with the respective
506    stretchabilities |3pt|, |8fill|, |5fil|, |6pt|, |-3fil|, |-8fill|. Then the total is essentially
507    |2fil|; and if a total additional space of 6pt is to be achieved by stretching, the actual
508    amounts of stretch will be |0pt|, |0pt|, |15pt|, |0pt|, |-9pt|, and |0pt|, since only |fi| glue
509    will be considered. (The |fill| glue is therefore not really stretching infinitely with respect
510    to |fil|; nobody would actually want that to happen.)
511
512    The arrays |total_stretch| and |total_shrink| are used to determine how much glue of each kind
513    is present. A global variable |last_badness| is used to implement |\badness|.
514
515*/
516
517packaging_state_info lmt_packaging_state = {
518    .total_stretch          = { 0 },
519    .total_shrink           = { 0 },  /*tex glue found by |hpack| or |vpack| */
520    .last_badness           = 0,      /*tex badness of the most recently packaged box */
521    .last_overshoot         = 0,      /*tex overshoot of the most recently packaged box */
522    .post_adjust_tail       = null,   /*tex tail of adjustment list */
523    .pre_adjust_tail        = null,
524    .post_migrate_tail      = null,   /*tex tail of migration list */
525    .pre_migrate_tail       = null,
526    .last_leftmost_char     = null,
527    .last_rightmost_char    = null,
528    .pack_begin_line        = 0,
529 /* .active_height          = { 0 }, */
530    .best_height_plus_depth = 0,
531    .previous_char_ptr      = null,
532    .font_expansion_ratio   = 0,
533    .padding                = 0,
534    .page_discards_tail     = null,
535    .page_discards_head     = null,
536    .split_discards_head    = null,
537};
538
539/*tex
540
541    This state collects the glue found by |hpack| or |vpack|: |total_stretch| and |total_shrink|
542    and the badness of the most recently packaged box |last_badness|.
543
544    If the variable |adjust_tail| is non-null, the |hpack| routine also removes all occurrences of
545    |insert_node|, |mark_node|, and |adjust_node| items and appends the resulting material onto the
546    list that ends at location |adjust_tail|.
547
548    Tail of adjustment list is stored in |adjust_tail|. Materials in |\vadjust| used with |pre|
549    keyword will be appended to |pre_adjust_tail| instead of |adjust_tail|.
550
551    The optimizers use |last_leftmost_char| and last_rightmost_char|.
552
553    In order to provide a decent indication of where an overfull or underfull box originated, we
554    use a global variable |pack_begin_line| that is set nonzero only when |hpack| is being called
555    by the paragraph builder or the alignment finishing routine.
556
557    The source file line where the current paragraph or alignment began; a negative value denotes
558    alignment |pack_begin_line|.
559
560    Pointers to the prev and next char of an implicit kern are kept in |next_char_p| and
561    prev_char_p|.
562
563    The kern stretch and shrink code was (or had become) rather weird ... the width field is set,
564    and then used in a second calculation, repeatedly, so why is that \unknown\ maybe some some
565    weird left-over \unknown\ anyway, the values are so small that in practice they are not
566    significant at all when the backend sees them because a few hundred sp positive or negative are
567    just noise there (so adjustlevel 3 has hardly any consequence for the result but is more
568    efficient).
569
570    In the end I simplified the code because in practice these kerns can between glyphs burried in
571    discretionary nodes. Also, we don't enable it by default so let's just stick to the leftmost
572    character as reference. We can assume the same font anyway.
573
574*/
575
576scaled tex_char_stretch(halfword p) /* todo: move this to texfont.c and make it more efficient */
577{
578    if (! tex_has_glyph_option(p, glyph_option_no_expansion)) {
579        halfword f = glyph_font(p);
580        halfword m = font_max_stretch(f);
581        if (m > 0) {
582            halfword c = glyph_character(p);
583            scaled e = tex_char_ef_from_font(f, c);
584            if (e > 0) {
585                scaled dw = tex_calculated_glyph_width(p, m) - tex_char_width_from_glyph(p);
586                if (dw > 0) {
587                    return tex_round_xn_over_d(dw, e, scaling_factor);
588                }
589            }
590        }
591    }
592    return 0;
593}
594
595scaled tex_char_shrink(halfword p) /* todo: move this to texfont.c and make it more efficient */
596{
597    if (! tex_has_glyph_option(p, glyph_option_no_expansion)) {
598        halfword f = glyph_font(p);
599        halfword m = font_max_shrink(f);
600        if (m > 0) {
601            halfword c = glyph_character(p);
602            scaled e = tex_char_cf_from_font(f, c);
603            if (e > 0) {
604                scaled dw = tex_char_width_from_glyph(p) - tex_calculated_glyph_width(p, -m);
605                if (dw > 0) {
606                    return tex_round_xn_over_d(dw, e, scaling_factor);
607                }
608            }
609        }
610    }
611    return 0;
612}
613
614scaled tex_kern_stretch(halfword p)
615{
616    scaled w = kern_amount(p);
617    if (w)  {
618        halfword l = lmt_packaging_state.previous_char_ptr;
619        if (l && node_type(l) == glyph_node && ! tex_has_glyph_option(l, glyph_option_no_expansion)) {
620            scaled m = font_max_stretch(glyph_font(l));
621            if (m > 0) {
622                scaled e = tex_char_ef_from_font(glyph_font(l), glyph_character(l));
623                if (e > 0) {
624                    scaled dw = w - tex_round_xn_over_d(w, scaling_factor + m, scaling_factor);
625                    if (dw > 0) {
626                        return tex_round_xn_over_d(dw, e, scaling_factor);
627                    }
628                }
629            }
630        }
631    }
632    return 0;
633}
634
635scaled tex_kern_shrink(halfword p)
636{
637    scaled w = kern_amount(p) ;
638    if (w)  {
639        halfword l = lmt_packaging_state.previous_char_ptr;
640        if (l && node_type(l) == glyph_node && ! tex_has_glyph_option(l, glyph_option_no_expansion)) {
641            halfword m = font_max_shrink(glyph_font(l));
642            if (m > 0) {
643                scaled e = tex_char_cf_from_font(glyph_font(l), glyph_character(l));
644                if (e > 0) {
645                    scaled dw = tex_round_xn_over_d(w, scaling_factor - m, scaling_factor) - w;
646                    if (dw > 0) {
647                        return tex_round_xn_over_d(dw, e, scaling_factor);
648                    }
649                }
650            }
651        }
652    }
653    return 0;
654}
655
656static void tex_aux_set_kern_expansion(halfword p, halfword ex_ratio)
657{
658    scaled w = kern_amount(p) ;
659    if (w )  {
660        halfword l = lmt_packaging_state.previous_char_ptr;
661        if (l && node_type(l) == glyph_node && ! tex_has_glyph_option(l, glyph_option_no_expansion)) {
662            halfword f = glyph_font(l);
663            halfword c = glyph_character(l);
664            if (ex_ratio > 0) {
665                scaled e = tex_char_ef_from_font(f, c);
666                if (e > 0) { 
667                    halfword m = font_max_stretch(f);
668                    if (m > 0) {
669                        e = tex_ext_xn_over_d(ex_ratio * e, m, scaling_factor_squared);
670                        kern_expansion(p) = tex_fix_expand_value(f, e) * scaling_factor;
671                    }
672                }
673            } else if (ex_ratio < 0) {
674                scaled e = tex_char_cf_from_font(f, c);
675                if (e > 0) { 
676                    halfword m = font_max_shrink(f);
677                    if (m > 0) {
678                        e = tex_ext_xn_over_d(ex_ratio * e, m, scaling_factor_squared);
679                        kern_expansion(p) = tex_fix_expand_value(f, e) * scaling_factor;
680                    }
681                }
682            }
683        }
684    }
685}
686
687static void tex_aux_set_glyph_expansion(halfword p, int ex_ratio)
688{
689    switch (node_type(p)) {
690        case glyph_node:
691            if (! tex_has_glyph_option(p, glyph_option_no_expansion)) {
692                if (ex_ratio > 0) {
693                    halfword f = glyph_font(p);
694                    halfword c = glyph_character(p);
695                    scaled e = tex_char_ef_from_font(f, c);
696                    if (e > 0) { 
697                        halfword m = font_max_stretch(f);
698                        if (m > 0) {
699                            e = tex_ext_xn_over_d(ex_ratio * e, m, scaling_factor_squared);
700                            glyph_expansion(p) = tex_fix_expand_value(f, e) * scaling_factor;
701                        }
702                    }
703                } else if (ex_ratio < 0) {
704                    halfword f = glyph_font(p);
705                    halfword c = glyph_character(p);
706                    scaled e = tex_char_cf_from_font(f, c);
707                    if (e > 0) { 
708                        halfword m = font_max_shrink(f);
709                        if (m > 0) {
710                            e = tex_ext_xn_over_d(ex_ratio * e, m, scaling_factor_squared);
711                            glyph_expansion(p) = tex_fix_expand_value(f, e) * scaling_factor;
712                        }
713                    }
714                }
715            }
716            break;
717        case disc_node:
718            {
719                halfword r = disc_pre_break_head(p);
720                while (r) {
721                    if (node_type(r) == glyph_node) {
722                        tex_aux_set_glyph_expansion(r, ex_ratio);
723                    }
724                    r = node_next(r);
725                }
726                r = disc_post_break_head(p);
727                while (r) {
728                    if (node_type(r) == glyph_node) {
729                        tex_aux_set_glyph_expansion(r, ex_ratio);
730                    }
731                    r = node_next(r);
732                }
733                r = disc_no_break_head(p);
734                while (r) {
735                    if (node_type(r) == glyph_node) {
736                        tex_aux_set_glyph_expansion(r, ex_ratio);
737                    }
738                    r = node_next(r);
739                }
740                break;
741            }
742        default:
743            tex_normal_error("font expansion", "invalid node type");
744            break;
745    }
746}
747
748scaled tex_left_marginkern(halfword p)
749{
750    while (p && node_type(p) == glue_node) {
751        p = node_next(p);
752    }
753    if (p && node_type(p) == kern_node && node_subtype(p) == left_margin_kern_subtype) {
754        return kern_amount(p);
755    } else  {
756        return 0;
757    }
758}
759
760scaled tex_right_marginkern(halfword p)
761{
762    if (p) {
763        p = tex_tail_of_node_list(p);
764        /*tex
765            There can be a leftskip, rightskip, penalty and yes, also a disc node with a nesting
766            node that points to glue spec ... and we don't want to analyze that messy lot.
767        */
768        while (p) {
769            switch(node_type(p)) {
770                case glue_node:
771                    /*tex We backtrack over glue. */
772                    p = node_prev(p);
773                    break;
774                case kern_node:
775                    if (node_subtype(p) == right_margin_kern_subtype) {
776                        return kern_amount(p);
777                    } else {
778                        return 0;
779                    }
780                case disc_node:
781                    /*tex
782                        Officially we should look in the replace but currently protrusion doesn't
783                        work anyway with |foo\discretionary {} {} {bar-} | (no following char) so we
784                        don't need it now.
785                    */
786                    p = node_prev(p);
787                    if (p && node_type(p) == kern_node && node_subtype(p) == right_margin_kern_subtype) {
788                        return kern_amount(p);
789                    } else {
790                        return 0;
791                    }
792                default:
793                    return 0;
794            }
795        }
796    }
797    return 0;
798}
799
800/*tex
801
802    Character protrusion is something we inherited from \PDFTEX\ and the next helper calculates
803    the extend. Is this |last_*_char| logic still valid?
804
805*/
806
807scaled tex_char_protrusion(halfword p, int side)
808{
809    if (side == left_margin_kern_subtype) {
810        lmt_packaging_state.last_leftmost_char = null;
811    } else {
812        lmt_packaging_state.last_rightmost_char = null;
813    }
814    if (! p || node_type(p) != glyph_node || tex_has_glyph_option(p, glyph_option_no_protrusion)) {
815        return 0;
816    } else if (side == left_margin_kern_subtype) {
817        lmt_packaging_state.last_leftmost_char = p;
818        return tex_char_left_protrusion_from_glyph(p);
819    } else {
820        lmt_packaging_state.last_rightmost_char = p;
821        return tex_char_right_protrusion_from_glyph(p);
822    }
823}
824
825/*tex
826
827    Here we prepare for |hpack|, which is place where we do font substituting when font expansion
828    is being used.
829
830*/
831
832int tex_ignore_math_skip(halfword p)
833{
834    if (math_skip_mode_par == math_skip_only_when_skip) {
835        if (node_subtype(p) == end_inline_math) {
836            if (tex_math_skip_boundary(node_next(p))) {
837                return 0;
838            }
839        } else {
840            if (tex_math_skip_boundary(node_prev(p))) {
841                return 0;
842            }
843        }
844    } else if (math_skip_mode_par == math_skip_only_when_no_skip) {
845        if (node_subtype(p) == end_inline_math) {
846            if (! tex_math_skip_boundary(node_next(p))) {
847                return 0;
848            }
849        } else {
850            if (! tex_math_skip_boundary(node_prev(p))) {
851                return 0;
852            }
853        }
854    } else {
855        return 0;
856    }
857    tex_reset_math_glue_to_zero(p);
858    return 1;
859}
860
861# define fix_int(val,min,max) (val < min ? min : (val > max ? max : val))
862
863inline static halfword tex_aux_used_order(halfword *total)
864{
865    if (total[filll_glue_order]) {
866        return filll_glue_order;
867    } else if (total[fill_glue_order]) {
868        return fill_glue_order;
869    } else if (total[fil_glue_order]) {
870        return fil_glue_order;
871    } else if (total[fi_glue_order]) {
872        return fi_glue_order;
873    } else {
874        return normal_glue_order;
875    }
876}
877
878/*tex
879
880    The original code mentions: \quotation {Transfer node |p| to the adjustment list. Although node
881    |q| is not necessarily the immediate predecessor of node |p|, it always points to some node in
882    the list preceding |p|. Thus, we can delete nodes by moving |q| when necessary. The algorithm
883    takes linear time, and the extra computation does not intrude on the inner loop unless it is
884    necessary to make a deletion.}. The trick used is the following:
885
886    \starttyping
887    q = r + list_offset;
888    node_next(q) = p;
889    ....
890            while (node_next(q) != p) {
891                q = node_next(q);
892            }
893    \stoptyping
894
895    This list offset points to the memory slot in the node and it happens that the next pointer
896    takes the same subfield as the normal next pointer (these are actually offsets in an array of
897    memorywords). This kind of neat trickery is needed because there are only forward linked lists,
898    but we can do it differently and thereby also use the normal list pointer. We need a bit more
899    checking but in the end we have a better abstraction.
900
901*/
902
903/*tex 
904    For now we have this here but maybe it makes sense to move the adjust migration to the adjust 
905    module (tracing and such). It's a bit fyzzy code anyway. 
906*/
907
908inline static void tex_aux_promote_pre_migrated(halfword r, halfword p)
909{
910    halfword pa = box_pre_adjusted(p);
911    halfword pm = box_pre_migrated(p);
912    if (pa) {
913        if (lmt_packaging_state.pre_adjust_tail) {
914            lmt_packaging_state.pre_adjust_tail = tex_append_adjust_list(pre_adjust_head, lmt_packaging_state.pre_adjust_tail, pa, "promote");
915        } else if (box_pre_adjusted(r)) {
916            tex_couple_nodes(box_pre_adjusted(r), pa);
917        } else {
918            box_pre_adjusted(r) = pa;
919        }
920        box_pre_adjusted(p) = null;
921    }
922    if (pm) {
923        if (lmt_packaging_state.pre_migrate_tail) {
924            tex_couple_nodes(lmt_packaging_state.pre_migrate_tail, pm);
925            lmt_packaging_state.pre_migrate_tail = tex_tail_of_node_list(pm);
926        } else {
927            /* here we prepend pm to rm */
928            halfword rm = box_pre_migrated(r);
929            if (rm) {
930                tex_couple_nodes(pm, rm);
931            }
932            box_pre_migrated(r) = pm;
933        }
934        box_pre_migrated(p) = null;
935    }
936}
937
938inline static void tex_aux_promote_post_migrated(halfword r, halfword p)
939{
940    halfword pa = box_post_adjusted(p);
941    halfword pm = box_post_migrated(p);
942    if (pa) {
943        if (lmt_packaging_state.post_adjust_tail) {
944            lmt_packaging_state.post_adjust_tail = tex_append_adjust_list(post_adjust_head, lmt_packaging_state.post_adjust_tail, pa, "promote");
945        } else if (box_post_adjusted(r)) {
946            tex_couple_nodes(box_post_adjusted(r), pa);
947        } else {
948            box_post_adjusted(r) = pa;
949        }
950        box_post_adjusted(p) = null;
951    }
952    if (pm) {
953        if (lmt_packaging_state.post_migrate_tail) {
954            tex_couple_nodes(lmt_packaging_state.post_migrate_tail, pm);
955            lmt_packaging_state.post_migrate_tail = tex_tail_of_node_list(pm);
956        } else {
957            /* here we append pm to rm */
958            halfword rm = box_post_migrated(r);
959            if (rm) {
960                tex_couple_nodes(tex_tail_of_node_list(rm), pm);
961            } else {
962                box_post_migrated(r) = pm;
963            }
964        }
965        box_post_migrated(p) = null;
966    }
967}
968
969inline static halfword tex_aux_post_migrate(halfword r, halfword p)
970{
971    halfword n = p;
972    halfword nn = node_next(p);
973    halfword pm = box_post_migrated(r);
974    if (p == box_list(r)) {
975        box_list(r) = nn;
976        if (nn) {
977            node_prev(nn) = null;
978        }
979    } else {
980        tex_couple_nodes(node_prev(p), nn);
981    }
982    if (pm) {
983        tex_couple_nodes(tex_tail_of_node_list(pm), n);
984    } else {
985        box_post_migrated(r) = n;
986    }
987    node_next(n) = null;
988    p = nn;
989    return p;
990}
991
992inline static halfword tex_aux_normal_migrate(halfword r, halfword p)
993{
994    halfword n = p;
995    halfword nn = node_next(p);
996    if (p == box_list(r)) {
997        box_list(r) = nn;
998        if (nn) {
999            node_prev(nn) = null;
1000        }
1001    } else {
1002        tex_couple_nodes(node_prev(p), nn);
1003    }
1004    tex_couple_nodes(lmt_packaging_state.post_migrate_tail, n);
1005    lmt_packaging_state.post_migrate_tail = n;
1006    node_next(n) = null;
1007    p = nn;
1008    return p;
1009}
1010
1011static void tex_aux_append_diagnostic_rule(halfword box, halfword rule)
1012{
1013    halfword n = box_list(box);
1014    if (n) {
1015        halfword t = tex_tail_of_node_list(n);
1016        halfword c = t; 
1017        while (c && node_type(c) == glue_node) {
1018            switch (node_subtype(c)) { 
1019                case par_fill_right_skip_glue:
1020                case par_init_right_skip_glue:
1021                case right_skip_glue:
1022                case right_hang_skip_glue:
1023                    c = node_prev(c); 
1024                    break;
1025                default:
1026                    goto DONE; 
1027            }
1028        }
1029      DONE:
1030        if (c) { 
1031            n = node_next(c);
1032            if (n) { 
1033                tex_couple_nodes(rule, n);
1034            }
1035        } else { 
1036            c = t; 
1037        }
1038        tex_couple_nodes(c, rule);
1039    } else { 
1040        box_list(box) = rule;
1041    }
1042}
1043
1044void tex_repack(halfword p, scaled w, int m)
1045{
1046    if (p) {
1047        halfword tmp; 
1048        switch (node_type(p)) { 
1049            case hlist_node:
1050                tmp = tex_hpack(box_list(p), w, m, box_dir(p), holding_none_option, box_limit_none);
1051                break;
1052            case vlist_node: 
1053                tmp = tex_vpack(box_list(p), w, m > packing_additional ? packing_additional : m, max_dimension, box_dir(p), holding_none_option, NULL);
1054                break;
1055            default: 
1056                return;
1057        }
1058        box_width(p) = box_width(tmp);
1059        box_height(p) = box_height(tmp);
1060        box_depth(p) = box_depth(tmp);
1061        box_glue_set(p) = box_glue_set(tmp);
1062        box_glue_order(p) = box_glue_order(tmp);
1063        box_glue_sign(p) = box_glue_sign(tmp);
1064        box_list(tmp) = null;
1065        tex_flush_node(tmp);
1066    }
1067}
1068
1069// Not ok. For now we accept some drift and assume it averages out. Just   
1070// for fun we could actually store it in the glue set field afterwards. 
1071// 
1072// { 
1073//     halfword drift = scaledround(wd) - ws;
1074//     if (drift < 0) {
1075//         d -= (double) drift; 
1076//         wd -= (double) drift; 
1077//     }
1078// } 
1079
1080/*tex 
1081    When |limitate| equals the node type we indeed limitate. Otherwise we freeze. 
1082*/
1083
1084void tex_freeze(halfword p, int recurse, int limitate, halfword factor) 
1085{
1086    if (p) {
1087        switch (node_type(p)) { 
1088            case hlist_node:
1089                {
1090                    halfword c = box_list(p);
1091                    double set = (double) box_glue_set(p);
1092                    halfword order = box_glue_order(p);
1093                    halfword sign = box_glue_sign(p);
1094                    if (factor > 0) { 
1095                        set *= factor * 0.001;
1096                    } else if (factor < 0) {
1097                        double max = - factor * 0.001; 
1098                        if (set > max) {
1099                            set = max;
1100                        }
1101                    }
1102                    while (c) {
1103                        switch (node_type(c)) {
1104                            case glue_node:
1105                                switch (sign) {
1106                                    case stretching_glue_sign:
1107                                        if (glue_stretch_order(c) == order) {
1108                                            glue_amount(c) += limitate == hlist_node ? glue_stretch(c) : scaledround(glue_stretch(c) * set);
1109                                        }
1110                                        break;
1111                                    case shrinking_glue_sign:
1112                                        if (glue_shrink_order(c) == order) {
1113                                            glue_amount(c) -= scaledround(glue_shrink(c) * set);
1114                                        }
1115                                        break;
1116                                }
1117                                glue_stretch(c) = 0;
1118                                glue_shrink(c) = 0;
1119                                glue_stretch_order(c) = 0;
1120                                glue_shrink_order(c) = 0;
1121                                break;
1122                            case hlist_node:
1123                            case vlist_node:
1124                                if (recurse) {
1125                                    tex_freeze(c, recurse, limitate, factor);
1126                                }
1127                                break;
1128                            case math_node:
1129                                switch (sign) {
1130                                    case stretching_glue_sign:
1131                                        if (math_stretch_order(c) == order) {
1132                                            math_amount(c) += limitate == hlist_node ? math_stretch(c) : scaledround(math_stretch(c) * set);
1133                                        }
1134                                        break;
1135                                    case shrinking_glue_sign:
1136                                        if (math_shrink_order(c) == order) {
1137                                            math_amount(c) += scaledround(math_shrink(c) * set);
1138                                        }
1139                                        break;
1140                                }
1141                                math_stretch(c) = 0;
1142                                math_shrink(c) = 0;
1143                                math_stretch_order(c) = 0;
1144                                math_shrink_order(c) = 0;
1145                                break;
1146                            default: 
1147                                break;
1148                        }
1149                        c = node_next(c);
1150                    }
1151                    box_glue_set(p) = 0;
1152                    box_glue_order(p) = 0;
1153                    box_glue_sign(p) = 0;
1154                }
1155                break;
1156            case vlist_node: 
1157                {
1158                    halfword c = box_list(p);
1159                    double set = (double) box_glue_set(p);
1160                    halfword order = box_glue_order(p);
1161                    halfword sign = box_glue_sign(p);
1162                    if (factor > 0) { 
1163                        set *= factor * 0.001;
1164                    } else if (factor < 0) {
1165                        double max = - factor * 0.001; 
1166                        if (set > max) {
1167                            set = max;
1168                        }
1169                    }
1170                    while (c) {
1171//printf("%i / %i\n",node_type(c),node_subtype(c));
1172                        switch (node_type(c)) {
1173                            case glue_node:
1174                                switch (sign) {
1175                                    case stretching_glue_sign:
1176                                        if (glue_stretch_order(c) == order) {
1177//printf("+ %i : %f + %f * %f -> %f\n",node_subtype(c),glue_amount(c)/65536.0,glue_stretch(c)/65536.0,set,scaledround(glue_stretch(c) * set)/65536.0);
1178                                                glue_amount(c) += limitate == vlist_node ? glue_stretch(c) : scaledround(glue_stretch(c) * set);
1179                                        }
1180                                        break;
1181                                    case shrinking_glue_sign:
1182                                        if (glue_shrink_order(c) == order) {
1183//printf("- %i : %f + %f * %f -> %f\n",node_subtype(c),glue_amount(c)/65536.0,glue_shrink(c)/65536.0,set,scaledround(glue_shrink(c) * set)/65536.0);
1184                                            glue_amount(c) -= scaledround(glue_shrink(c) * set);
1185                                        }
1186                                        break;
1187                                }
1188                                glue_stretch(c) = 0;
1189                                glue_shrink(c) = 0;
1190                                glue_stretch_order(c) = 0;
1191                                glue_shrink_order(c) = 0;
1192                                break;
1193                            case hlist_node:
1194                            case vlist_node:
1195                                if (recurse) {
1196                                    tex_freeze(c, recurse, limitate, factor);
1197                                }
1198                                break;
1199                            default: 
1200                                break;
1201                        }
1202                        c = node_next(c);
1203                    }
1204                    box_glue_set(p) = 0;
1205                    box_glue_order(p) = 0;
1206                    box_glue_sign(p) = 0;
1207                }
1208                break;
1209            default: 
1210                return;
1211        }
1212    }
1213}
1214
1215void tex_limit(halfword p) 
1216{
1217    /* 
1218        For now we only handle stretch. This is not watertight because when we max some, the others
1219        actually can becoem relative smaller. Of course we can also freeze and then recalculate. 
1220    */
1221    if (p) { 
1222        halfword c = box_list(p);
1223        double set = (double) box_glue_set(p);
1224        halfword order = box_glue_order(p);
1225        halfword sign = box_glue_sign(p);
1226        int limit = set > 1 && order == normal_glue_order; 
1227        if (limit) {
1228            double nonfrozen = 0;
1229            double frozen = 0;
1230            while (c) {
1231                if (node_type(c) == glue_node) {
1232                    switch (sign) {
1233                        case stretching_glue_sign:
1234                            if (glue_stretch_order(c) == order) {
1235                                if (tex_has_glue_option(c, glue_option_limit)) {
1236                                    frozen += glue_stretch(c);
1237                                } else {
1238                                    nonfrozen += glue_stretch(c);
1239                                }
1240                            }
1241                            break;
1242                        case shrinking_glue_sign:
1243                            if (glue_shrink_order(c) == order) {
1244                                if (tex_has_glue_option(c, glue_option_limit)) {
1245                                    frozen -= glue_shrink(c);
1246                                } else { 
1247                                    nonfrozen -= glue_shrink(c);
1248                                }
1249                            }
1250                            break;
1251                    }
1252                }
1253                c = node_next(c);
1254            }
1255            if (frozen) { 
1256                set = (set * (frozen + nonfrozen) - frozen) / nonfrozen; 
1257            } else { 
1258                limit = 0;
1259            }
1260        }
1261        c = box_list(p);
1262        while (c) {
1263            if (node_type(c) == glue_node) {
1264                switch (sign) {
1265                    case stretching_glue_sign:
1266                        if (glue_stretch_order(c) == order) {
1267                            if (limit && tex_has_glue_option(c, glue_option_limit)) {
1268                                glue_amount(c) += glue_stretch(c);
1269                            } else {
1270                                glue_amount(c) += scaledround(glue_stretch(c) * set);
1271                            }
1272                        }
1273                        break;
1274                    case shrinking_glue_sign:
1275                        if (glue_shrink_order(c) == order) {
1276                            if (limit && tex_has_glue_option(c, glue_option_limit)) {
1277                                glue_amount(c) -= glue_shrink(c);
1278                            } else { 
1279                                glue_amount(c) -= scaledround(glue_shrink(c) * set);
1280                            }
1281                        }
1282                        break;
1283                }
1284                glue_stretch(c) = 0;
1285                glue_shrink(c) = 0;
1286                glue_stretch_order(c) = 0;
1287                glue_shrink_order(c) = 0;
1288            }
1289            c = node_next(c);
1290        }
1291        box_glue_set(p) = 0;
1292        box_glue_order(p) = 0;
1293        box_glue_sign(p) = 0;
1294    }
1295}
1296
1297scaled tex_stretch(halfword p)
1298{
1299    scaled stretch = 0;
1300    if (p) {
1301        switch (node_type(p)) { 
1302            case hlist_node:
1303            case vlist_node:
1304                if (box_glue_sign(p) == stretching_glue_sign) { 
1305                    halfword c = box_list(p);
1306                    halfword order = box_glue_order(p);
1307                    while (c) {
1308                        switch (node_type(c)) {
1309                            case glue_node:
1310                                if (glue_stretch_order(c) == order) {
1311                                    stretch += glue_stretch(c);
1312                                }
1313                                break;
1314                            case math_node:
1315                                if (math_stretch_order(c) == order) {
1316                                    stretch += math_stretch(c);
1317                                }
1318                                break;
1319                        }
1320                        c = node_next(c);
1321                    }
1322                }
1323                break;
1324        }
1325    }
1326    return stretch;
1327}
1328
1329scaled tex_shrink(halfword p)
1330{
1331    scaled shrink = 0;
1332    if (p) {
1333        switch (node_type(p)) { 
1334            case hlist_node:
1335            case vlist_node:
1336                if (box_glue_sign(p) == shrinking_glue_sign) { 
1337                    halfword c = box_list(p);
1338                    halfword order = box_glue_order(p);
1339                    while (c) {
1340                        switch (node_type(c)) {
1341                            case glue_node:
1342                                if (glue_shrink_order(c) == order) {
1343                                    shrink += glue_shrink(c);
1344                                }
1345                                break;
1346                            case math_node:
1347                                if (math_shrink_order(c) == order) {
1348                                    shrink += math_shrink(c);
1349                                }
1350                                break;
1351                        }
1352                        c = node_next(c);
1353                    }
1354                }
1355                break;
1356        }
1357    }
1358    return shrink;
1359}
1360
1361halfword tex_hpack(halfword p, scaled w, int m, singleword pack_direction, int retain, int limit)
1362{
1363    /*tex trails behind |p| */
1364    halfword q = null;
1365    /*tex height */
1366    scaled h = 0;
1367    /*tex depth */
1368    scaled d = 0;
1369    /*tex natural width */
1370    scaled x = 0;
1371    /*tex the current direction */
1372    singleword hpack_dir = pack_direction == direction_unknown ?(singleword) text_direction_par : pack_direction;
1373    int disc_level = 0;
1374    int has_uleader = 0;
1375    halfword pack_interrupt[8];
1376    scaled font_stretch = 0;
1377    scaled font_shrink = 0;
1378    int adjust_spacing = adjust_spacing_off;
1379    int has_limit = box_limit_mode_hlist ? 1 : (limit == box_limit_line && box_limit_mode_line ? 1 : -1);
1380    /*tex the box node that will be returned */
1381    halfword r = tex_new_node(hlist_node, unknown_list);
1382    box_dir(r) = hpack_dir;
1383    lmt_packaging_state.last_badness = 0;
1384    lmt_packaging_state.last_overshoot = 0;
1385    switch(m) { 
1386        case packing_linebreak:
1387            m = packing_expanded;
1388            /* fall through, later we'll come back here: */
1389        case packing_substitute:
1390            adjust_spacing = tex_checked_font_adjust(
1391                lmt_linebreak_state.adjust_spacing,
1392                lmt_linebreak_state.adjust_spacing_step,
1393                lmt_linebreak_state.adjust_spacing_shrink,
1394                lmt_linebreak_state.adjust_spacing_stretch
1395            );
1396            break;
1397        default:
1398            adjust_spacing = tex_checked_font_adjust(
1399                adjust_spacing_par,
1400                adjust_spacing_step_par,
1401                adjust_spacing_shrink_par,
1402                adjust_spacing_stretch_par
1403            );
1404            break;
1405    }
1406    box_list(r) = p;
1407    switch (m) { 
1408        case packing_expanded:
1409            /*tex Why not always: */
1410            lmt_packaging_state.previous_char_ptr = null;
1411            break;
1412        case packing_adapted:
1413            if (w > scaling_factor) { 
1414                w = scaling_factor;
1415            } else if (w  < -scaling_factor) { 
1416                w = -scaling_factor;
1417            }
1418    }
1419    for (int i = normal_glue_order; i <= filll_glue_order; i++) {
1420        lmt_packaging_state.total_stretch[i] = 0;
1421        lmt_packaging_state.total_shrink[i] = 0;
1422    }
1423    /*tex
1424
1425        Examine node |p| in the hlist, taking account of its effect on the dimensions of the new
1426        box, or moving it to the adjustment list; then advance |p| to the next node. For disc
1427        node we enter a level so we don't use recursion.
1428
1429        In other engines there is an optimization for glyph runs but here we use just one switch
1430        for everything. The performance hit is neglectable. So the comment \quotation {Incorporate
1431        character dimensions into the dimensions of the hbox that will contain~it, then move to
1432        the next node.} no longer applies. In \LUATEX\ ligature building, kerning and hyphenation
1433        are decoupled so comments about inner loop and performance no longer make sense here.
1434
1435    */
1436    while (p) {
1437        switch (node_type(p)) {
1438            case glyph_node:
1439                {
1440                    scaledwhd whd;
1441                    if (adjust_spacing) {
1442                        switch (m) {
1443                            case packing_expanded:
1444                                {
1445                                    lmt_packaging_state.previous_char_ptr = p;
1446                                    font_stretch += tex_char_stretch(p);
1447                                    font_shrink += tex_char_shrink(p);
1448                                    break;
1449                                }
1450                            case packing_substitute:
1451                                lmt_packaging_state.previous_char_ptr = p;
1452                                if (lmt_packaging_state.font_expansion_ratio != 0) {
1453                                    tex_aux_set_glyph_expansion(p, lmt_packaging_state.font_expansion_ratio);
1454                                }
1455                                break;
1456                        }
1457                    }
1458                    whd = tex_glyph_dimensions_ex(p);
1459                    x += whd.wd;
1460                    if (whd.ht > h) {
1461                        h = whd.ht;
1462                    }
1463                    if (whd.dp > d) {
1464                        d = whd.dp;
1465                    }
1466                    break;
1467                }
1468            case hlist_node:
1469            case vlist_node:
1470                {
1471                    /*tex
1472
1473                        Incorporate box dimensions into the dimensions of the hbox that will contain
1474                        it.
1475
1476                    */
1477                    halfword s = box_shift_amount(p);
1478                    scaledwhd whd = tex_pack_dimensions(p);
1479                    x += whd.wd;
1480                    if (whd.ht - s > h) {
1481                        h = whd.ht - s;
1482                    }
1483                    if (whd.dp + s > d) {
1484                        d = whd.dp + s;
1485                    }
1486                    tex_aux_promote_pre_migrated(r, p);
1487                    tex_aux_promote_post_migrated(r, p);
1488                    break;
1489                }
1490            case unset_node:
1491                x += box_width(p);
1492                if (box_height(p) > h) {
1493                    h = box_height(p);
1494                }
1495                if (box_depth(p) > d) {
1496                    d = box_depth(p);
1497                }
1498             // tex_aux_promote_pre_migrated(r, p);
1499             // tex_aux_promote_post_migrated(r, p);
1500                break;
1501            case rule_node:
1502                /*tex
1503
1504                    The code here implicitly uses the fact that running dimensions are indicated
1505                    by |null_flag|, which will be ignored in the calculations because it is a
1506                    highly negative number.
1507
1508                */
1509                x += rule_width(p);
1510                if (rule_height(p) > h) {
1511                    h = rule_height(p);
1512                }
1513                if (rule_depth(p) > d) {
1514                    d = rule_depth(p);
1515                }
1516                break;
1517            case glue_node:
1518                /*tex Incorporate glue into the horizontal totals. Can this overflow? */
1519                {
1520                    switch (m) { 
1521                        case packing_adapted:
1522                            if (w < 0) {
1523                                if (glue_shrink_order(p) == normal_glue_order) {                                   
1524                                    glue_amount(p) -= scaledround(-0.001 * w * (double) glue_shrink(p));
1525                                }
1526                            } else if (w > 0) {
1527                                if (glue_stretch_order(p) == normal_glue_order) {
1528                                    glue_amount(p) += scaledround( 0.001 * w * (double) glue_stretch(p));
1529                                }
1530                            }
1531                            x += glue_amount(p);
1532                            glue_shrink_order(p) = normal_glue_order;
1533                            glue_shrink(p) = 0;
1534                            glue_stretch_order(p) = normal_glue_order;
1535                            glue_stretch(p) = 0;
1536                            break;
1537                        default:
1538                            {
1539                                halfword o;
1540                                x += glue_amount(p);
1541                                o = glue_stretch_order(p);
1542                                lmt_packaging_state.total_stretch[o] += glue_stretch(p);
1543                                o = glue_shrink_order(p);
1544                                lmt_packaging_state.total_shrink[o] += glue_shrink(p);
1545                            }
1546                    }
1547                    if (is_leader(p)) {
1548                        halfword gl = glue_leader_ptr(p);
1549                        scaled ht = 0;
1550                        scaled dp = 0;
1551                        switch (node_type(gl)) {
1552                            case hlist_node:
1553                            case vlist_node:
1554                                ht = box_height(gl);
1555                                dp = box_depth(gl);
1556                                break;
1557                            case rule_node:
1558                                ht = rule_height(gl);
1559                                dp = rule_depth(gl);
1560                                break;
1561                        }
1562                        if (ht > h) {
1563                            h = ht;
1564                        }
1565                        if (dp > d) {
1566                            d = dp;
1567                        }
1568                        if (node_subtype(p) == u_leaders) {
1569                            has_uleader = 1;
1570                        }
1571                    }
1572                    if (! has_limit && tex_has_glue_option(p, glue_option_limit)) {
1573                        has_limit = 1;
1574                    }
1575                    break;
1576                }
1577            case kern_node:
1578                if (adjust_spacing == adjust_spacing_full && node_subtype(p) == font_kern_subtype) {
1579                    switch (m) {
1580                        case packing_expanded:
1581                            {
1582                                font_stretch += tex_kern_stretch(p);
1583                                font_shrink += tex_kern_shrink(p);
1584                                break;
1585                            }
1586                        case packing_substitute:
1587                            if (lmt_packaging_state.font_expansion_ratio != 0) {
1588                                tex_aux_set_kern_expansion(p, lmt_packaging_state.font_expansion_ratio);
1589                            }
1590                            break;
1591                    }
1592                }
1593                x += tex_kern_dimension_ex(p);
1594                break;
1595            case disc_node:
1596                if (adjust_spacing) {
1597                    switch (m) {
1598                        case packing_expanded:
1599                            /*tex
1600                                Won't give this issues with complex discretionaries as we don't
1601                                do the |packing_expand| here? I need to look into this!
1602                            */
1603                            break;
1604                        case packing_substitute:
1605                            if (lmt_packaging_state.font_expansion_ratio != 0) {
1606                                tex_aux_set_glyph_expansion(p, lmt_packaging_state.font_expansion_ratio);
1607                            }
1608                            break;
1609                    }
1610                }
1611                if (disc_no_break_head(p)) {
1612                    pack_interrupt[disc_level] = node_next(p);
1613                    ++disc_level;
1614                    p = disc_no_break(p);
1615                }
1616                break;
1617            case math_node:
1618                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
1619                    x += math_surround(p);
1620                } else {
1621                    halfword o;
1622                    x += math_amount(p);
1623                    o = math_stretch_order(p);
1624                    lmt_packaging_state.total_stretch[o] += math_stretch(p);
1625                    o = math_shrink_order(p);
1626                    lmt_packaging_state.total_shrink[o] += math_shrink(p);
1627                }
1628                break;
1629            case dir_node:
1630                break;
1631            case insert_node:
1632                if (retain_inserts(retain)) {
1633                    break;
1634                } else if (lmt_packaging_state.post_migrate_tail) {
1635                    p = tex_aux_normal_migrate(r, p);
1636                    /*tex Here |q| stays as it is and we're already at next. */
1637                    continue;
1638                } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_insert)) {
1639                    halfword l = insert_list(p);
1640                    p = tex_aux_post_migrate(r, p);
1641                    while (l) {
1642                        l = node_type(l) == insert_node ? tex_aux_post_migrate(r, l) : node_next(l);
1643                    }
1644                    /*tex Here |q| stays as it is and we're already at next. */
1645                    continue;
1646                } else {
1647                    /*tex Nothing done, so we move on. */
1648                    break;
1649                }
1650            case mark_node:
1651                if (retain_marks(retain)) {
1652                    break;
1653                } else if (lmt_packaging_state.post_migrate_tail) {
1654                    p = tex_aux_normal_migrate(r, p);
1655                    /*tex Here |q| stays as it is and we're already at next. */
1656                    continue;
1657                } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_mark)) {
1658                    p = tex_aux_post_migrate(r, p);
1659                    /*tex Here |q| stays as it is and we're already at next. */
1660                    continue;
1661                } else {
1662                    /*tex Nothing done, so we move on. */
1663                    break;
1664                }
1665            case adjust_node:
1666                /*tex
1667                    We could combine this with migration code but adjust content actually is taken into account
1668                    as part of the flow (dimensions, penalties, etc).
1669                */
1670                if (adjust_list(p) && ! retain_adjusts(retain)) {
1671                    halfword next = node_next(p);
1672                    halfword current = p;
1673                    /*tex Remove from list: */
1674                    if (p == box_list(r)) {
1675                        box_list(r) = next;
1676                        if (next) {
1677                            node_prev(next) = null;
1678                        }
1679                    } else {
1680                        tex_couple_nodes(node_prev(p), next);
1681                    }
1682                    if (lmt_packaging_state.post_adjust_tail || lmt_packaging_state.pre_adjust_tail) {
1683                        tex_adjust_passon(r, current);
1684                    } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_adjust)) {
1685                        tex_adjust_attach(r, current);
1686                    }
1687                    p = next;
1688                    continue;
1689                } else {
1690                    break;
1691                }
1692            default:
1693                break;
1694        }
1695        /*
1696            This is kind of tricky: q is the pre-last pointer so we don't change it when we're
1697            inside a disc node. This way of keeping track of the last node is different from the
1698            previous engine.
1699        */
1700        if (disc_level > 0) {
1701            p = node_next(p);
1702            if (! p) {
1703                --disc_level;
1704                p = pack_interrupt[disc_level];
1705            }
1706        } else {
1707            q = p;
1708            p = node_next(p);
1709        }
1710    }
1711    box_height(r) = h;
1712    box_depth(r) = d;
1713    /*tex
1714        Determine the value of |width(r)| and the appropriate glue setting; then |return| or |goto
1715        common_ending|. When we get to the present part of the program, |x| is the natural width of
1716        the box being packaged.
1717    */
1718    switch (m) { 
1719        case packing_additional: 
1720            w += x;
1721            break;
1722        case packing_adapted: 
1723            w = x;
1724            break;
1725    }
1726    box_width(r) = w;
1727    x = w - x;
1728    /*tex Now |x| is the excess to be made up. */
1729    if (x == 0) {
1730        box_glue_sign(r) = normal_glue_sign;
1731        box_glue_order(r) = normal_glue_order;
1732        box_glue_set(r) = 0.0;
1733        goto EXIT;
1734    } else if (x > 0) {
1735        /*tex
1736            Determine horizontal glue stretch setting, then |return| or |goto common_ending|. If
1737            |hpack| is called with |m=cal_expand_ratio| we calculate |font_expand_ratio| and return
1738            without checking for overfull or underfull box.
1739        */
1740        halfword o = tex_aux_used_order(lmt_packaging_state.total_stretch);
1741        if ((m == packing_expanded) && (o == normal_glue_order) && (font_stretch > 0)) {
1742            lmt_packaging_state.font_expansion_ratio = tex_divide_scaled_n(x, font_stretch, scaling_factor_double);
1743            goto EXIT;
1744        }
1745        box_glue_order(r) = o;
1746        box_glue_sign(r) = stretching_glue_sign;
1747        if (lmt_packaging_state.total_stretch[o]) {
1748            box_glue_set(r) = (glueratio) ((double) x / lmt_packaging_state.total_stretch[o]);
1749        } else {
1750            /*tex There's nothing to stretch. */
1751            box_glue_sign(r) = normal_glue_sign;
1752            box_glue_set(r) = 0.0;
1753        }
1754        if (o == normal_glue_order && box_list(r)) {
1755            /*tex
1756                Report an underfull hbox and |goto common_ending|, if this box is sufficiently bad.
1757            */
1758            lmt_packaging_state.last_badness = tex_badness(x, lmt_packaging_state.total_stretch[normal_glue_order]);
1759            if (lmt_packaging_state.last_badness > hbadness_par) {
1760                int callback_id = lmt_callback_defined(hpack_quality_callback);
1761                if (callback_id > 0) {
1762                    if (q) { 
1763                        halfword rule = null;
1764                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1765                            lmt_packaging_state.last_badness > 100  ? "underfull" : "loose", // beware, this needs to be adapted to the configureable 99 
1766                            lmt_packaging_state.last_badness,
1767                            r,
1768                            abs(lmt_packaging_state.pack_begin_line),
1769                            lmt_input_state.input_line,
1770                            tex_current_input_file_name(),
1771                            &rule
1772                        );
1773                        if (rule && rule != r) {
1774                            tex_aux_append_diagnostic_rule(r, rule);
1775                        }
1776                    }
1777                } else {
1778                    tex_print_nlp();
1779                    if (lmt_packaging_state.last_badness > 100) {
1780                        tex_print_format("%l[package: underfull \\hbox (badness %i)", lmt_packaging_state.last_badness);
1781                    } else {
1782                        tex_print_format("%l[package: loose \\hbox (badness %i)", lmt_packaging_state.last_badness);
1783                    }
1784                    goto COMMON_ENDING;
1785                }
1786            }
1787        }
1788        goto EXIT;
1789    } else {
1790        /*tex
1791            Determine horizontal glue shrink setting, then |return| or |goto common_ending|,
1792        */
1793        halfword o = tex_aux_used_order(lmt_packaging_state.total_shrink);
1794        if ((m == packing_expanded) && (o == normal_glue_order) && (font_shrink > 0)) {
1795            lmt_packaging_state.font_expansion_ratio = tex_divide_scaled_n(x, font_shrink, scaling_factor_double);
1796            goto EXIT;
1797        }
1798        box_glue_order(r) = o;
1799        box_glue_sign(r) = shrinking_glue_sign;
1800        if (lmt_packaging_state.total_shrink[o]) {
1801            box_glue_set(r) = (glueratio) ((double) (-x) / (double) lmt_packaging_state.total_shrink[o]);
1802        } else {
1803            /*tex There's nothing to shrink. */
1804            box_glue_sign(r) = normal_glue_sign;
1805            box_glue_set(r) = 0.0;
1806        }
1807        if (o == normal_glue_order && box_list(r)) {
1808            if (lmt_packaging_state.total_shrink[o] < -x) {
1809                int overshoot = -x - lmt_packaging_state.total_shrink[normal_glue_order];
1810                lmt_packaging_state.last_badness = scaling_factor_squared;
1811                lmt_packaging_state.last_overshoot = overshoot;
1812                /*tex Use the maximum shrinkage */
1813                box_glue_set(r) = 1.0;
1814                /*tex Report an overfull hbox and |goto common_ending|, if this box is sufficiently bad. */
1815                if ((overshoot > hfuzz_par) || (hbadness_par < 100)) {
1816                    int callback_id = lmt_callback_defined(hpack_quality_callback);
1817                    halfword rule = null;
1818                    if (callback_id > 0) {
1819                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1820                            "overfull",
1821                            overshoot,
1822                            r,
1823                            abs(lmt_packaging_state.pack_begin_line),
1824                            lmt_input_state.input_line,
1825                            tex_current_input_file_name(),
1826                            &rule);
1827                    } else if (q && overfull_rule_par > 0) {
1828                        rule = tex_new_rule_node(normal_rule_subtype);
1829                        rule_width(rule) = overfull_rule_par;
1830                    }
1831                    if (rule && rule != r) {
1832                        tex_aux_append_diagnostic_rule(r, rule);
1833                    }
1834                    if (callback_id == 0) {
1835                        tex_print_nlp();
1836                        tex_print_format("%l[package: overfull \\hbox (%p too wide)", overshoot);
1837                        goto COMMON_ENDING;
1838                    }
1839                }
1840            } else {
1841                /*tex Report a tight hbox and |goto common_ending|, if this box is sufficiently bad. */
1842                lmt_packaging_state.last_badness = tex_badness(-x, lmt_packaging_state.total_shrink[normal_glue_order]);
1843                if (lmt_packaging_state.last_badness > hbadness_par) {
1844                    int callback_id = lmt_callback_defined(hpack_quality_callback);
1845                    if (callback_id > 0) {
1846                        halfword rule = null;
1847                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1848                            "tight",
1849                            lmt_packaging_state.last_badness,
1850                            r,
1851                            abs(lmt_packaging_state.pack_begin_line),
1852                            lmt_input_state.input_line,
1853                            tex_current_input_file_name(),
1854                            &rule);
1855                        if (rule && rule != r) {
1856                            tex_aux_append_diagnostic_rule(r, rule);
1857                        }
1858                    } else {
1859                        tex_print_nlp();
1860                        tex_print_format("%l[package: tight \\hbox (badness %i)", lmt_packaging_state.last_badness);
1861                        goto COMMON_ENDING;
1862                    }
1863                }
1864            }
1865        }
1866        goto EXIT;
1867    }
1868  COMMON_ENDING:
1869    /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */
1870    if (lmt_page_builder_state.output_active) {
1871        tex_print_format(" has occurred while \\output is active]");
1872    } else if (lmt_packaging_state.pack_begin_line == 0) {
1873        tex_print_format(" detected at line %i]", lmt_input_state.input_line);
1874    } else if (lmt_packaging_state.pack_begin_line > 0) {
1875        tex_print_format(" in paragraph at lines %i--%i]", lmt_packaging_state.pack_begin_line, lmt_input_state.input_line);
1876    } else {
1877        tex_print_format(" in alignment at lines %i--%i]", -lmt_packaging_state.pack_begin_line, lmt_input_state.input_line);
1878    }
1879    tex_print_ln();
1880    lmt_print_state.font_in_short_display = null_font;
1881    if (tracing_full_boxes_par > 0) {
1882        halfword detail = show_node_details_par;
1883        show_node_details_par = tracing_full_boxes_par;
1884        tex_short_display(box_list(r));
1885        tex_print_ln();
1886        tex_begin_diagnostic();
1887        tex_show_box(r);
1888        tex_end_diagnostic();
1889        show_node_details_par = detail;
1890    }
1891  EXIT:
1892    if ((m == packing_expanded) && (lmt_packaging_state.font_expansion_ratio != 0)) {
1893        lmt_packaging_state.font_expansion_ratio = fix_int(lmt_packaging_state.font_expansion_ratio, -scaling_factor, scaling_factor);
1894        q = box_list(r);
1895        box_list(r) = null;
1896        tex_flush_node(r);
1897        /*tex This nested call uses the more or less global font_expand_ratio. */
1898        r = tex_hpack(q, w, packing_substitute, hpack_dir, holding_none_option, box_limit_none);
1899    } else { 
1900        if (has_uleader) { 
1901           set_box_package_state(r, package_u_leader_found);
1902        }
1903        if (has_limit > 0) {
1904            tex_limit(r);
1905        }
1906    }
1907    /*tex Here we reset the |font_expand_ratio|. */
1908    lmt_packaging_state.font_expansion_ratio = 0;
1909    return r;
1910}
1911
1912halfword tex_filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, halfword d, int just_pack, halfword attr, int state, int retain)
1913{
1914    halfword head;
1915    singleword direction = (singleword) checked_direction_value(d);
1916//    (void) state; /*tex Why do we pass it? Probably a left-over from an experiment. */
1917    if (just_pack) {
1918        head = node_next(p);
1919    } else if (node_type(p) == temp_node && ! node_next(p)) {
1920        head = node_next(p);
1921    } else {
1922        /*tex Maybe here: |node_prev(p) = null|. */
1923        head = node_next(p);
1924        if (head) {
1925            node_prev(head) = null;
1926            if (tex_list_has_glyph(head)) {
1927                tex_handle_hyphenation(head, qt);
1928                head = tex_handle_glyphrun(head, grp, direction);
1929            }
1930            if (head) {
1931                /*tex ignores empty anyway. Maybe also pass tail? */
1932                head = lmt_hpack_filter_callback(head, w, m, grp, direction, attr);
1933            }
1934        }
1935    }
1936    head = tex_hpack(head, w, m, direction, retain, box_limit_none);
1937    if (has_box_package_state(head, package_u_leader_found)) {
1938        if (head && normalize_line_mode_permitted(normalize_line_mode_par, flatten_h_leaders_mode)) { 
1939            if (! is_box_package_state(state, package_u_leader_delayed)) {
1940                tex_flatten_leaders(head, grp, just_pack);
1941            }
1942        }
1943    }
1944    return head;
1945}
1946
1947/*tex Here is a function to calculate the natural whd of a (horizontal) node list. */
1948
1949scaledwhd tex_natural_hsizes(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order)
1950{
1951    scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
1952    scaled gp = 0;
1953    scaled gm = 0;
1954    while (p && p != pp) {
1955        switch (node_type(p)) {
1956            case glyph_node:
1957                {
1958                    scaledwhd whd = tex_glyph_dimensions_ex(p);
1959                    siz.wd += whd.wd;
1960                    if (whd.ht > siz.ht) {
1961                        siz.ht = whd.ht;
1962                    }
1963                    if (whd.dp > siz.dp) {
1964                        siz.dp = whd.dp;
1965                    }
1966                    break;
1967                }
1968            case hlist_node:
1969            case vlist_node:
1970                {
1971                    scaled s = box_shift_amount(p);
1972                    scaledwhd whd = tex_pack_dimensions(p);
1973                    siz.wd += whd.wd;
1974                    if (whd.ht - s > siz.ht) {
1975                        siz.ht = whd.ht - s;
1976                    }
1977                    if (whd.dp + s > siz.dp) {
1978                        siz.dp = whd.dp + s;
1979                    }
1980                    break;
1981                }
1982            case unset_node:
1983                siz.wd += box_width(p);
1984                if (box_height(p) > siz.ht) {
1985                    siz.ht = box_height(p);
1986                }
1987                if (box_depth(p) > siz.dp) {
1988                    siz.dp = box_depth(p);
1989                }
1990                break;
1991            case rule_node:
1992                siz.wd += rule_width(p);
1993                if (rule_height(p) > siz.ht) {
1994                    siz.ht = rule_height(p);
1995                }
1996                if (rule_depth(p) > siz.dp) {
1997                    siz.dp = rule_depth(p);
1998                }
1999                break;
2000            case glue_node:
2001                siz.wd += glue_amount(p);
2002                switch (g_sign) {
2003                    case stretching_glue_sign:
2004                        if (glue_stretch_order(p) == g_order) {
2005                            gp += glue_stretch(p);
2006                        }
2007                        break;
2008                    case shrinking_glue_sign:
2009                        if (glue_shrink_order(p) == g_order) {
2010                            gm += glue_shrink(p);
2011                        }
2012                        break;
2013                }
2014                if (is_leader(p)) {
2015                    halfword gl = glue_leader_ptr(p);
2016                    halfword ht = 0;
2017                    halfword dp = 0;
2018                    switch (node_type(gl)) {
2019                        case hlist_node:
2020                        case vlist_node:
2021                            ht = box_height(gl);
2022                            dp = box_depth(gl);
2023                            break;
2024                        case rule_node:
2025                            ht = rule_height(gl);
2026                            dp = rule_depth(gl);
2027                            break;
2028                    }
2029                    if (ht) {
2030                        siz.ht = ht;
2031                    }
2032                    if (dp > siz.dp) {
2033                        siz.dp = dp;
2034                    }
2035                }
2036                break;
2037            case kern_node:
2038                siz.wd += tex_kern_dimension_ex(p);
2039                break;
2040            case disc_node:
2041                {
2042                    scaledwhd whd = tex_natural_hsizes(disc_no_break_head(p), null, g_mult, g_sign, g_order); /* hm, really glue here?  */
2043                    siz.wd += whd.wd;
2044                    if (whd.ht > siz.ht) {
2045                        siz.ht = whd.ht;
2046                    }
2047                    if (whd.dp > siz.dp) {
2048                        siz.dp = whd.dp;
2049                    }
2050                }
2051                break;
2052            case math_node:
2053                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2054                    siz.wd += math_surround(p);
2055                } else {
2056                    siz.wd += math_amount(p);
2057                    switch (g_sign) {
2058                        case stretching_glue_sign:
2059                            if (math_stretch_order(p) == g_order) {
2060                                gp += math_stretch(p);
2061                            }
2062                            break;
2063                        case shrinking_glue_sign:
2064                            if (math_shrink_order(p) == g_order) {
2065                                gm += math_shrink(p);
2066                            }
2067                            break;
2068                    }
2069                }
2070                break;
2071            case sub_box_node:
2072                /* really? */
2073                break;
2074            case sub_mlist_node:
2075                {
2076                    /* hack */
2077                    scaledwhd whd = tex_natural_hsizes(kernel_math_list(p), null, 0.0, 0, 0);
2078                    siz.wd += whd.wd;
2079                    if (whd.ht > siz.ht) {
2080                        siz.ht = whd.ht;
2081                    }
2082                    if (whd.dp > siz.dp) {
2083                        siz.dp = whd.dp;
2084                    }
2085                }
2086                break;
2087            default:
2088                break;
2089        }
2090        p = node_next(p);
2091    }
2092    siz.ns = siz.wd;
2093    switch (g_sign) {
2094        case stretching_glue_sign:
2095            siz.wd += glueround((glueratio)(g_mult) * (glueratio)(gp));
2096            break;
2097        case shrinking_glue_sign:
2098            siz.wd -= glueround((glueratio)(g_mult) * (glueratio)(gm));
2099            break;
2100    }
2101    return siz;
2102}
2103
2104scaledwhd tex_natural_vsizes(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order)
2105{
2106    scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
2107    scaled gp = 0;
2108    scaled gm = 0;
2109    while (p && p != pp) {
2110        switch (node_type(p)) {
2111            case hlist_node:
2112            case vlist_node:
2113                {
2114                    scaled s = box_shift_amount(p);
2115                    scaledwhd whd = tex_pack_dimensions(p);
2116                    if (whd.wd + s > siz.wd) {
2117                        siz.wd = whd.wd + s;
2118                    }
2119                    siz.ht += siz.dp + whd.ht;
2120                    siz.dp = whd.dp;
2121                }
2122                break;
2123            case unset_node:
2124                siz.ht += siz.dp + box_height(p);
2125                siz.dp = box_depth(p);
2126                if (box_width(p) > siz.wd) {
2127                    siz.wd = box_width(p);
2128                }
2129                break;
2130            case rule_node:
2131                siz.ht += siz.dp + rule_height(p);
2132                siz.dp = rule_depth(p);
2133                if (rule_width(p) > siz.wd) {
2134                    siz.wd = rule_width(p);
2135                }
2136                break;
2137            case glue_node:
2138                {
2139                    siz.ht += siz.dp + glue_amount(p);
2140                    siz.dp = 0;
2141                    if (is_leader(p)) {
2142                        halfword gl = glue_leader_ptr(p);
2143                        halfword wd = 0;
2144                        switch (node_type(gl)) {
2145                            case hlist_node:
2146                            case vlist_node:
2147                                wd = box_width(gl);
2148                                break;
2149                            case rule_node:
2150                                wd = rule_width(gl);
2151                                break;
2152                        }
2153                        if (wd > siz.wd) {
2154                            siz.wd = wd;
2155                        }
2156                    }
2157                    switch (g_sign) {
2158                        case stretching_glue_sign:
2159                            if (glue_stretch_order(p) == g_order) {
2160                                gp += glue_stretch(p);
2161                            }
2162                            break;
2163                        case shrinking_glue_sign:
2164                            if (glue_shrink_order(p) == g_order) {
2165                                gm += glue_shrink(p);
2166                            }
2167                            break;
2168                    }
2169                    break;
2170                }
2171            case kern_node:
2172                siz.ht += siz.dp + kern_amount(p);
2173                siz.dp = 0;
2174                break;
2175            case glyph_node:
2176                tex_confusion("glyph in vpack");
2177                break;
2178            case disc_node:
2179                tex_confusion("discretionary in vpack");
2180                break;
2181            default:
2182                break;
2183        }
2184        p = node_next(p);
2185    }
2186    siz.ns = siz.ht;
2187    switch (g_sign) {
2188        case stretching_glue_sign:
2189            siz.ht += glueround((glueratio)(g_mult) * (glueratio)(gp));
2190            break;
2191        case shrinking_glue_sign:
2192            siz.ht -= glueround((glueratio)(g_mult) * (glueratio)(gm));
2193            break;
2194    }
2195    return siz;
2196}
2197
2198/*tex simplified variant with less memory access */
2199
2200halfword tex_natural_width(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order)
2201{
2202    scaled wd = 0;
2203    scaled gp = 0;
2204    scaled gm = 0;
2205    while (p && p != pp) {
2206        /* no real gain over check in switch */
2207        switch (node_type(p)) {
2208            case glyph_node:
2209                wd += tex_glyph_width(p); /* Plus expansion? */
2210                break;
2211            case hlist_node:
2212            case vlist_node:
2213            case unset_node:
2214                wd += box_width(p);
2215                break;
2216            case rule_node:
2217                wd += rule_width(p);
2218                break;
2219            case glue_node:
2220                wd += glue_amount(p);
2221                switch (g_sign) {
2222                    case stretching_glue_sign:
2223                        if (glue_stretch_order(p) == g_order) {
2224                            gp += glue_stretch(p);
2225                        }
2226                        break;
2227                    case shrinking_glue_sign:
2228                        if (glue_shrink_order(p) == g_order) {
2229                            gm += glue_shrink(p);
2230                        }
2231                        break;
2232                }
2233                break;
2234            case kern_node:
2235                wd += kern_amount(p); // + kern_expansion(p);
2236                break;
2237            case disc_node:
2238                wd += tex_natural_width(disc_no_break(p), null, g_mult, g_sign, g_order);
2239                break;
2240            case math_node:
2241                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2242                    wd += math_surround(p);
2243                } else {
2244                    wd += math_amount(p);
2245                    switch (g_sign) {
2246                        case stretching_glue_sign:
2247                            if (math_stretch_order(p) == g_order) {
2248                                gp += math_stretch(p);
2249                            }
2250                            break;
2251                        case shrinking_glue_sign:
2252                            if (math_shrink_order(p) == g_order) {
2253                                gm += math_shrink(p);
2254                            }
2255                            break;
2256                    }
2257                }
2258                break;
2259            default:
2260                break;
2261        }
2262        p = node_next(p);
2263    }
2264    switch (g_sign) {
2265        case stretching_glue_sign:
2266            wd += glueround((glueratio) (g_mult) * (glueratio) (gp));
2267            break;
2268        case shrinking_glue_sign:
2269            wd -= glueround((glueratio) (g_mult) * (glueratio) (gm));
2270            break;
2271    }
2272    return wd;
2273}
2274
2275halfword tex_natural_hsize(halfword p, halfword *correction)
2276{
2277    scaled wd = 0;
2278    halfword c = null;
2279    while (p) {
2280        switch (node_type(p)) {
2281            case glyph_node:
2282                wd += tex_glyph_width(p); /* Plus expansion? */
2283                break;
2284            case hlist_node:
2285            case vlist_node:
2286            case unset_node:
2287                wd += box_width(p);
2288                break;
2289            case rule_node:
2290                wd += rule_width(p);
2291                break;
2292            case glue_node:
2293                wd += glue_amount(p);
2294                if (node_subtype(p) == correction_skip_glue) {
2295                    c = p;
2296                }
2297                break;
2298            case kern_node:
2299                wd += kern_amount(p); //  + kern_expansion(p);
2300                break;
2301            case disc_node:
2302                wd += tex_natural_hsize(disc_no_break(p), NULL);
2303                break;
2304            case math_node:
2305                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2306                    wd += math_surround(p);
2307                } else {
2308                    wd += math_amount(p);
2309                }
2310                break;
2311            default:
2312                break;
2313        }
2314        p = node_next(p);
2315    }
2316    if (correction) {
2317        *correction = c;
2318    }
2319    return wd;
2320}
2321
2322halfword tex_natural_vsize(halfword p)
2323{
2324    scaled ht = 0;
2325    scaled dp = 0;
2326    while (p) {
2327        switch (node_type(p)) {
2328            case hlist_node:
2329            case vlist_node:
2330                ht += dp + box_height(p);
2331                dp = box_depth(p);
2332                break;
2333            case unset_node:
2334                ht += dp + box_height(p);
2335                dp = box_depth(p);
2336                break;
2337            case rule_node:
2338                ht += dp + rule_height(p);
2339                dp = rule_depth(p);
2340                break;
2341            case glue_node:
2342                ht += dp + glue_amount(p);
2343                dp = 0;
2344                break;
2345            case kern_node:
2346                ht += dp + kern_amount(p);
2347                dp = 0;
2348                break;
2349            default:
2350                break;
2351        }
2352        p = node_next(p);
2353    }
2354    return ht + dp;
2355}
2356
2357/*tex
2358
2359    The |vpack| subroutine is actually a special case of a slightly more general routine called
2360    |vpackage|, which has four parameters. The fourth parameter, which is |max_dimension| in the case
2361    of |vpack|, specifies the maximum depth of the page box that is constructed. The depth is first
2362    computed by the normal rules; if it exceeds this limit, the reference point is simply moved
2363    down until the limiting depth is attained. We actually hav efive parameters because we also
2364    deal with teh direction.
2365
2366*/
2367
2368halfword tex_vpack(halfword p, scaled targetheight, int m, scaled targetdepth, singleword pack_direction, int retain, scaled *excess)
2369{
2370    scaled width = 0;
2371    scaled depth = 0;
2372    scaled height = 0;
2373    int has_uleader = 0;
2374    /*tex the box node that will be returned */
2375    halfword box = tex_new_node(vlist_node, unknown_list);
2376    int has_limit = box_limit_mode_vlist ? 1 : -1;
2377    (void) retain; /* todo */
2378    box_dir(box) = pack_direction;
2379    box_shift_amount(box) = 0;
2380    box_list(box) = p;
2381    lmt_packaging_state.last_badness = 0;
2382    lmt_packaging_state.last_overshoot = 0;
2383    for (int i = normal_glue_order; i <= filll_glue_order; i++) {
2384        lmt_packaging_state.total_stretch[i] = 0;
2385        lmt_packaging_state.total_shrink[i] = 0;
2386    }
2387    while (p) {
2388        /*tex
2389
2390            Examine node |p| in the vlist, taking account of its effect on the dimensions of the
2391            new box; then advance |p| to the next node.
2392
2393        */
2394        halfword n = node_next(p);
2395        switch (node_type(p)) {
2396            case hlist_node:
2397            case vlist_node:
2398                {
2399                    /*tex
2400
2401                        Incorporate box dimensions into the dimensions of the vbox that will
2402                        contain it.
2403
2404                    */
2405                    scaled shift = box_shift_amount(p);
2406                    scaledwhd whd = tex_pack_dimensions(p);
2407                    if (whd.wd + shift > width) {
2408                        width = whd.wd + shift;
2409                    }
2410                    height += depth + whd.ht;
2411                    depth = whd.dp;
2412                    tex_aux_promote_pre_migrated(box, p);
2413                    tex_aux_promote_post_migrated(box, p);
2414                }
2415                break;
2416            case unset_node:
2417                height += depth + box_height(p);
2418                depth = box_depth(p);
2419                if (box_width(p) > width) {
2420                    width = box_width(p);
2421                }
2422             // tex_aux_promote_pre_migrated(box, p);
2423             // tex_aux_promote_post_migrated(box, p);
2424                break;
2425            case rule_node:
2426                height += depth + rule_height(p);
2427                depth = rule_depth(p);
2428                if (rule_width(p) > width) {
2429                    width = rule_width(p);
2430                }
2431                break;
2432            case glue_node:
2433                /*tex Incorporate glue into the vertical totals. */
2434                {
2435                    halfword o;
2436                    height += depth + glue_amount(p);
2437                    depth = 0;
2438                    o = glue_stretch_order(p);
2439                    lmt_packaging_state.total_stretch[o] += glue_stretch(p);
2440                    o = glue_shrink_order(p);
2441                    lmt_packaging_state.total_shrink[o] += glue_shrink(p);
2442                    if (is_leader(p)) {
2443                        halfword gl = glue_leader_ptr(p);
2444                        scaled wd = 0;
2445                        switch (node_type(gl)) {
2446                            case hlist_node:
2447                            case vlist_node:
2448                                wd = box_width(gl);
2449                                break;
2450                            case rule_node:
2451                                wd = rule_width(gl);
2452                                break;
2453                        }
2454                        if (wd > width) {
2455                            width = wd;
2456                        }
2457                        if (node_subtype(p) == u_leaders) {
2458                            has_uleader = 1;
2459                        }
2460                    }
2461                    if (! has_limit && tex_has_glue_option(p, glue_option_limit)) {
2462                        has_limit = 1;
2463                    }
2464                    break;
2465                }
2466            case kern_node:
2467                height += depth + kern_amount(p);
2468                depth = 0;
2469                break;
2470            case insert_node:
2471                if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_insert)) {
2472                    halfword l = insert_list(p);
2473                    tex_aux_post_migrate(box, p);
2474                    while (l) {
2475                        l = node_type(l) == insert_node ? tex_aux_post_migrate(box, l) : node_next(l);
2476                    }
2477                }
2478                break;
2479            case mark_node:
2480                 if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_mark)) {
2481                    tex_aux_post_migrate(box, p);
2482                 }
2483                 break;
2484            case glyph_node:
2485                tex_confusion("glyph in vpack");
2486                break;
2487            case disc_node:
2488                tex_confusion("discretionary in vpack");
2489                break;
2490            default:
2491                break;
2492        }
2493        p = n;
2494    }
2495    box_width(box) = width;
2496    if (depth > targetdepth) {
2497        height += depth - targetdepth;
2498        box_depth(box) = targetdepth;
2499    } else {
2500        box_depth(box) = depth;
2501    }
2502    /*tex
2503
2504        Determine the value of |height(r)| and the appropriate glue setting; then |return| or |goto
2505        common_ending|. When we get to the present part of the program, |x| is the natural height of
2506        the box being packaged.
2507    */
2508    if (m == packing_additional) {
2509        targetheight += height;
2510    }
2511    box_height(box) = targetheight;
2512    {
2513        scaled targetexcess = targetheight - height;
2514        /*tex Now |x| is the excess to be made up. */
2515        if (excess) { 
2516            *excess = targetexcess; 
2517        }
2518        if (targetexcess == 0) {
2519            box_glue_sign(box) = normal_glue_sign;
2520            box_glue_order(box) = normal_glue_order;
2521            box_glue_set(box) = 0.0;
2522         // goto EXIT;
2523        } else if (targetexcess > 0) {
2524            /*tex Determine vertical glue stretch setting, then |return| or |goto common_ending|. */
2525            halfword o = tex_aux_used_order(lmt_packaging_state.total_stretch);
2526            box_glue_order(box) = o;
2527            box_glue_sign(box) = stretching_glue_sign;
2528            if (lmt_packaging_state.total_stretch[o] != 0) {
2529                box_glue_set(box) = (glueratio) ((double) targetexcess / lmt_packaging_state.total_stretch[o]);
2530            } else {
2531                /*tex There's nothing to stretch. */
2532                box_glue_sign(box) = normal_glue_sign;
2533                box_glue_set(box) = 0.0;
2534            }
2535            if (o == normal_glue_order && box_list(box)) {
2536                /*tex Report an underfull vbox and |goto common_ending|, if this box is sufficiently bad. */
2537                lmt_packaging_state.last_badness = tex_badness(targetexcess, lmt_packaging_state.total_stretch[normal_glue_order]);
2538                if (lmt_packaging_state.last_badness > vbadness_par) {
2539                    int callback_id = lmt_callback_defined(vpack_quality_callback);
2540                    if (callback_id > 0) {
2541                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2542                            lmt_packaging_state.last_badness > 100 ? "underfull" : "loose",
2543                            lmt_packaging_state.last_badness,
2544                            box,
2545                            abs(lmt_packaging_state.pack_begin_line),
2546                            lmt_input_state.input_line,
2547                            tex_current_input_file_name()
2548                        );
2549                     // goto EXIT;
2550                    } else {
2551                        tex_print_nlp();
2552                        if (lmt_packaging_state.last_badness > 100) {
2553                            tex_print_format("%l[package: underfull \\vbox (badness %i)", lmt_packaging_state.last_badness);
2554                        } else {
2555                            tex_print_format("%l[package: loose \\vbox (badness %i)", lmt_packaging_state.last_badness);
2556                        }
2557                        goto COMMON_ENDING;
2558                    }
2559                }
2560            }
2561         // goto EXIT;
2562        } else {
2563            /*tex Determine vertical glue shrink setting, then |return| or |goto common_ending|. */
2564            halfword o = tex_aux_used_order(lmt_packaging_state.total_shrink);
2565            box_glue_order(box) = o;
2566            box_glue_sign(box) = shrinking_glue_sign;
2567            if (lmt_packaging_state.total_shrink[o] != 0) {
2568                box_glue_set(box) = (glueratio) ((double) (-targetexcess) / lmt_packaging_state.total_shrink[o]);
2569            } else {
2570                /*tex There's nothing to shrink. */
2571                box_glue_sign(box) = normal_glue_sign;
2572                box_glue_set(box) = 0.0;
2573            }
2574            if (o == normal_glue_order && box_list(box)) {
2575                if (lmt_packaging_state.total_shrink[o] < -targetexcess) {
2576                    int overshoot = -targetexcess - lmt_packaging_state.total_shrink[normal_glue_order];
2577                    lmt_packaging_state.last_badness = scaling_factor_squared;
2578                    lmt_packaging_state.last_overshoot = overshoot;
2579                    /*tex Use the maximum shrinkage */
2580                    box_glue_set(box)  = 1.0;
2581                    /*tex Report an overfull vbox and |goto common_ending|, if this box is sufficiently bad. */
2582                    if ((overshoot > vfuzz_par) || (vbadness_par < 100)) {
2583                        int callback_id = lmt_callback_defined(vpack_quality_callback);
2584                        if (callback_id > 0) {
2585                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2586                                "overfull",
2587                                overshoot,
2588                                box,
2589                                abs(lmt_packaging_state.pack_begin_line),
2590                                lmt_input_state.input_line,
2591                                tex_current_input_file_name()
2592                            );
2593                         // goto EXIT;
2594                        } else {
2595                            tex_print_nlp();
2596                            tex_print_format("%l[package: overfull \\vbox (%p too high)", - targetexcess - lmt_packaging_state.total_shrink[normal_glue_order]);
2597                            goto COMMON_ENDING;
2598                        }
2599                    }
2600                } else {
2601                    /*tex Report a tight vbox and |goto common_ending|, if this box is sufficiently bad. */
2602                    lmt_packaging_state.last_badness = tex_badness(-targetexcess, lmt_packaging_state.total_shrink[normal_glue_order]);
2603                    if (lmt_packaging_state.last_badness > vbadness_par) {
2604                        int callback_id = lmt_callback_defined(vpack_quality_callback);
2605                        if (callback_id > 0) {
2606                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2607                                "tight",
2608                                lmt_packaging_state.last_badness,
2609                                box,
2610                                abs(lmt_packaging_state.pack_begin_line),
2611                                lmt_input_state.input_line,
2612                                tex_current_input_file_name()
2613                            );
2614                         // goto EXIT;
2615                        } else {
2616                            tex_print_nlp();
2617                            tex_print_format("%l[package: tight \\vbox (badness %i)", lmt_packaging_state.last_badness);
2618                            goto COMMON_ENDING;
2619                        }
2620                    }
2621                }
2622            }
2623         // goto EXIT;
2624        }
2625    }
2626    goto EXIT;
2627  COMMON_ENDING:
2628    /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */
2629    if (lmt_page_builder_state.output_active) {
2630        tex_print_format(" has occurred while \\output is active]");
2631    } else if (lmt_packaging_state.pack_begin_line != 0) {
2632        tex_print_format(" in alignment at lines %i--%i]", abs(lmt_packaging_state.pack_begin_line), lmt_input_state.input_line);
2633    } else {
2634        tex_print_format(" detected at line %i]", lmt_input_state.input_line);
2635    }
2636    tex_print_ln();
2637    tex_begin_diagnostic();
2638    tex_show_box(box);
2639    tex_end_diagnostic();
2640  EXIT:
2641    if (has_uleader) { 
2642        set_box_package_state(box, package_u_leader_found);
2643    }
2644    if (has_limit > 0) {
2645        tex_limit(box);
2646    }
2647    /*tex Further (experimental) actions can go here. */
2648    return box;
2649}
2650
2651halfword tex_filtered_vpack(halfword p, scaled h, int m, scaled maxdepth, int grp, halfword direction, int just_pack, halfword attr, int state, int retain, int *excess)
2652{
2653    halfword result = p;
2654    if (! just_pack) {
2655        /* We have to do it here because we cannot nest packaging! */
2656        if (result && normalize_line_mode_permitted(normalize_line_mode_par, flatten_h_leaders_mode)) { 
2657            halfword c = result; 
2658            while (c) {
2659                switch (node_type(c)) {
2660                    case hlist_node:
2661                    case vlist_node:
2662                        if (has_box_package_state(c, package_u_leader_found)) {
2663                            if (! is_box_package_state(state, package_u_leader_delayed)) {
2664                                tex_flatten_leaders(c, grp, just_pack);
2665                            }
2666                        }
2667                        break;
2668                }
2669                c = node_next(c);
2670            }
2671        }
2672        result = lmt_vpack_filter_callback(result, h, m, maxdepth, grp, direction, attr);
2673    }
2674    result = tex_vpack(result, h, m, maxdepth, (singleword) checked_direction_value(direction), retain, excess);
2675    if (result && normalize_par_mode_permitted(normalize_par_mode_par, flatten_v_leaders_mode) && ! is_box_package_state(state, package_u_leader_delayed)) {
2676        tex_flatten_leaders(result, grp, just_pack);
2677    }
2678    if (! just_pack) {
2679        result = lmt_packed_vbox_filter_callback(result, grp);
2680    }
2681    return result;
2682}
2683
2684static scaled tex_aux_first_height(halfword boxnode) 
2685{
2686    halfword list = box_list(boxnode);
2687    if (list) {
2688        switch (node_type(list)) {
2689            case hlist_node:
2690            case vlist_node:
2691                return box_height(list);
2692            case rule_node:
2693                return rule_height(list);
2694        }
2695    }
2696    return 0;
2697}
2698
2699static void tex_aux_set_vnature(halfword boxnode, int nature)
2700{
2701    switch (nature) { 
2702        case vtop_code: 
2703        case tsplit_code: 
2704            {
2705                /*tex
2706
2707                    Read just the height and depth of |boxnode| (|boxnode|), for |\vtop|. The height of
2708                    a |\vtop| box is inherited from the first item on its list, if that item is an
2709                    |hlist_node|, |vlist_node|, or |rule_node|; otherwise the |\vtop| height is zero.
2710
2711                */
2712                scaled height = tex_aux_first_height(boxnode);
2713                box_depth(boxnode) = box_total(boxnode) - height;
2714                box_height(boxnode) = height;
2715                box_package_state(boxnode) = vtop_package_state;
2716            }
2717            break;
2718        case vbox_code: 
2719        case vsplit_code: 
2720            box_package_state(boxnode) = vbox_package_state;
2721            break;
2722        case dbox_code: 
2723        case dsplit_code: 
2724            box_package_state(boxnode) = dbox_package_state;
2725            break;
2726    }
2727}
2728
2729
2730/*tex
2731    Here we always start out in l2r mode and without shift. After all we need to be compatible with
2732    how it was before.
2733*/
2734
2735void tex_run_vcenter(void)
2736{
2737    tex_aux_scan_full_spec(direct_box_flag, vcenter_group, direction_l2r, 0, 0, -1, 0);
2738    tex_normal_paragraph(vcenter_par_context);
2739    tex_push_nest();
2740    cur_list.mode = internal_vmode;
2741    cur_list.prev_depth = ignore_depth_criterion_par;
2742    if (every_vbox_par) {
2743        tex_begin_token_list(every_vbox_par, every_vbox_text);
2744    }
2745}
2746
2747void tex_finish_vcenter_group(void)
2748{
2749    if (! tex_wrapped_up_paragraph(vcenter_par_context, 1)) {
2750        halfword p;
2751        tex_end_paragraph(vcenter_group, vcenter_par_context);
2752        tex_package(vbox_code); /* todo: vcenter_code */
2753        p = tex_pop_tail();
2754        if (p) {
2755            switch (node_type(p)) {
2756                case vlist_node:
2757                    {
2758                        scaled delta = box_total(p);
2759                        box_height(p) = tex_half_scaled(delta);
2760                        box_depth(p) = delta - box_height(p);
2761                        break;
2762                    }
2763                case simple_noad:
2764                    node_subtype(p) = vcenter_noad_subtype;
2765                    break;
2766                /*
2767                case style_node:
2768                    break;
2769                */
2770            }
2771            tex_tail_append(p);
2772        }
2773    }
2774}
2775
2776void tex_package(singleword nature)
2777{
2778    int grp = cur_group;
2779    scaled maxdepth = box_max_depth_par;
2780    tex_unsave();
2781    {
2782        lmt_save_state.save_stack_data.ptr -= saved_box_n_of_records;
2783        halfword boxnode = null; /*tex Aka |cur_box|. */
2784        /*tex 
2785            We need to save some over the callback because it can do something that messes with 
2786            the stack entries that has already been decremented. 
2787        */
2788        halfword slot = saved_box_slot;
2789        halfword context = saved_box_context;
2790        halfword dirptr = saved_box_dir_pointer;
2791        halfword orientation = saved_box_orientation;
2792        halfword anchor = saved_box_anchor;
2793        halfword geometry = saved_box_geometry;
2794        halfword attrlist = saved_box_attr_list;
2795        halfword state = saved_box_state;
2796        scaled shift = saved_box_shift;
2797        halfword source = saved_box_source;
2798        halfword target = saved_box_target;
2799        halfword axis = saved_box_axis;
2800        halfword mainclass = saved_box_class;
2801        halfword callback = saved_box_callback;
2802        halfword option = saved_box_option;
2803        scaled xoffset = saved_box_xoffset;
2804        scaled yoffset = saved_box_yoffset;
2805        scaled xmove = saved_box_xmove;
2806        scaled ymove = saved_box_ymove;
2807        if (cur_list.mode == restricted_hmode) {
2808            boxnode = tex_filtered_hpack(
2809                cur_list.head, cur_list.tail, 
2810                saved_box_amount, saved_box_packing, grp, 
2811                saved_box_direction, saved_box_pack, attrlist, state, saved_box_retain
2812            );
2813            node_subtype(boxnode) = hbox_list;
2814            if (option & saved_box_reverse_option) {
2815                box_list(boxnode) = tex_reversed_node_list(box_list(boxnode));
2816            }
2817            box_package_state(boxnode) = hbox_package_state;
2818        } else {
2819            boxnode = tex_filtered_vpack(
2820                node_next(cur_list.head), 
2821                saved_box_amount, saved_box_packing, maxdepth, grp, 
2822                saved_box_direction, saved_box_pack, attrlist, state, saved_box_retain, 
2823                NULL
2824            );
2825            box_package_state(boxnode) = hbox_package_state;
2826            tex_aux_set_vnature(boxnode, nature);
2827        }
2828        if (option & saved_box_limit_option && box_list(boxnode)) {
2829            tex_limit(boxnode);
2830        }
2831        if (dirptr) {
2832            /*tex Adjust back |text_dir_ptr| for |scan_spec| */
2833            tex_flush_node_list(lmt_dir_state.text_dir_ptr);
2834            lmt_dir_state.text_dir_ptr = dirptr;
2835        }
2836        /*
2837            An attribute is not assigned beforehand, just passed. But, when some is assigned we need to
2838            retain it. So, how do we deal with attributes that are added? Maybe we have to merge
2839            changes? Or maybe an extra option in hpack ... some day.
2840        */
2841        tex_attach_attribute_list_attribute(boxnode, attrlist);
2842        delete_attribute_reference(attrlist);
2843        /* */
2844        if (tex_has_geometry(geometry, offset_geometry) || tex_has_geometry(geometry, orientation_geometry)) {
2845            scaled wd = box_width(boxnode);
2846            scaled ht = box_height(boxnode);
2847            scaled dp = box_depth(boxnode);
2848            if (xmove) {
2849                xoffset = tex_aux_checked_dimension1(xoffset + xmove);
2850                wd = tex_aux_checked_dimension2(wd + xmove);
2851            }
2852            if (ymove) {
2853                yoffset = tex_aux_checked_dimension1(yoffset + ymove);
2854                ht = tex_aux_checked_dimension2(ht + ymove);
2855                dp = tex_aux_checked_dimension2(dp - ymove);
2856            }
2857            box_w_offset(boxnode) = wd;
2858            box_h_offset(boxnode) = ht;
2859            box_d_offset(boxnode) = dp;
2860            switch (orientationonly(orientation)) {
2861                case 0 : /*   0 */
2862                    break;
2863                case 2 : /* 180 */
2864                    box_height(boxnode) = dp;
2865                    box_depth(boxnode) = ht;
2866                    geometry |= orientation_geometry;
2867                    break;
2868                case 1 : /*  90 */
2869                case 3 : /* 270 */
2870                    box_width(boxnode) = ht + dp;
2871                    box_height(boxnode) = wd;
2872                    box_depth(boxnode) = 0;
2873                    geometry |= orientation_geometry;
2874                    break;
2875                case 4 : /*   0 */
2876                    box_height(boxnode) = ht + dp;
2877                    box_depth(boxnode) = 0;
2878                    geometry |= orientation_geometry;
2879                    break;
2880                case 5 : /* 180 */
2881                    box_height(boxnode) = 0;
2882                    box_depth(boxnode) = ht + dp;
2883                    geometry |= orientation_geometry;
2884                    break;
2885                default :
2886                    break;
2887            }
2888            if (xoffset || yoffset) {
2889                box_x_offset(boxnode) = xoffset;
2890                box_y_offset(boxnode) = yoffset;
2891                geometry |= offset_geometry;
2892            }
2893        }
2894        if (source || target) {
2895            box_source_anchor(boxnode) = source;
2896            box_target_anchor(boxnode) = target;
2897            geometry |= anchor_geometry;
2898        }
2899        box_anchor(boxnode) = anchor;
2900        box_orientation(boxnode) = orientation;
2901        box_geometry(boxnode) = (singleword) geometry;
2902        if (option & saved_box_container_option) {
2903            node_subtype(boxnode) = container_list;
2904        }
2905        box_axis(boxnode) = (singleword) axis;
2906        box_package_state(boxnode) |= (singleword) state;
2907        tex_pop_nest();
2908        tex_box_end(context, boxnode, shift, mainclass, slot, callback);
2909    }
2910}
2911
2912static int local_box_mapping[] = { 
2913    [local_left_box_box_code  ] = local_left_box_code,
2914    [local_right_box_box_code ] = local_right_box_code,
2915    [local_middle_box_box_code] = local_middle_box_code
2916};
2917
2918void tex_run_unpackage(void)
2919{
2920    int code = cur_chr; /*tex should we copy? */
2921    switch (code) {
2922        case box_code:
2923        case copy_code:
2924        case unpack_code:
2925            {
2926                halfword index = tex_scan_box_register_number();
2927                halfword box = box_register(index);
2928                if (box) {
2929                    int bad = 0;
2930                    switch (cur_list.mode) {
2931                        case vmode: 
2932                        case internal_vmode: 
2933                            if (node_type(box) != vlist_node) { 
2934                                bad = 1;
2935                            }
2936                            break;
2937                        case hmode: 
2938                        case restricted_hmode: 
2939                            if (node_type(box) != hlist_node) { 
2940                                bad = 1;
2941                            }
2942                            break;
2943                        case mmode: 
2944                        case inline_mmode: 
2945                            bad = 1;
2946                            break;
2947                    }
2948                    if (bad) {
2949                        tex_handle_error(
2950                            normal_error_type,
2951                            "Incompatible list can't be unboxed",
2952                            "Sorry, Pandora. (You sneaky devil.) I refuse to unbox an \\hbox in vertical mode\n"
2953                            "or vice versa. And I can't open any boxes in math mode."
2954                        );
2955                    } else {
2956                        /*tex Todo: check head, not needed, always a temp. */
2957                        /*tex We go via variables because we do varmem assignments. Probably not needed */
2958                        halfword tail = cur_list.tail;
2959                        halfword list = box_list(box);
2960                        halfword pre_migrated  = code == unpack_code ? null : box_pre_migrated(box);
2961                        halfword post_migrated = code == unpack_code ? null : box_post_migrated(box);
2962                        /* probably overkill as we inject vadjust again, maybe it should be an option, 'bind' or so */
2963                    //  halfword pre_adjusted  = box_pre_adjusted(box);
2964                    //  halfword post_adjusted = box_post_adjusted(box);
2965                        halfword pre_adjusted  = code == unpack_code ? null : box_pre_adjusted(box);
2966                        halfword post_adjusted = code == unpack_code ? null : box_post_adjusted(box);
2967                     // halfword pre_adjusted  = code == (unpack_code || is_h_mode(cur_list.mode)) ? null : box_pre_adjusted(box);
2968                     // halfword post_adjusted = code == (unpack_code || is_h_mode(cur_list.mode)) ? null : box_post_adjusted(box);
2969                        int prunekerns = list && node_type(box) == hlist_node && normalize_line_mode_permitted(normalize_line_mode_par, remove_margin_kerns_mode);
2970                        /*tex 
2971                            This topskip move is an ugly catch for wrong usage: when messing with 
2972                            vboxes the migrated material can end up outside the unvboxed (pagebody)
2973                            content which is not what one wants. The way to prevent is to force 
2974                            hmode when unboxing. It makes no sense to complicate this mechanism 
2975                            even more by soem fragile hackery. Best just create what gets asked 
2976                            and remember that thsi was decided. We need to copy the list earlier 
2977                            when we do this! 
2978                        */
2979                        /*
2980                        if (list) { 
2981                            if (code == copy_code) {
2982                                list = tex_copy_node_list(list, null);
2983                            } else {
2984                                box_list(box) = null;
2985                            }     
2986                        }
2987                        {
2988                            int movetopskip = list && (pre_adjusted || pre_migrated) && node_type(list) == glue_node && node_subtype(list) == top_skip_glue;
2989                            if (movetopskip) {
2990                                halfword topskip = list; 
2991                                list = node_next(list);
2992                                node_prev(topskip) = null;
2993                                node_next(topskip) = null;
2994                                tex_try_couple_nodes(tail, topskip);
2995                                tail = topskip;
2996                                if (list) { 
2997                                    node_prev(list) = null;
2998                                }
2999                                if (pre_adjusted && tracing_adjusts_par > 1) {
3000                                    tex_begin_diagnostic();
3001                                    tex_print_format("[adjust: topskip moved]");
3002                                    tex_end_diagnostic();
3003                                }
3004                            }
3005                        }
3006                        */ 
3007                        if (pre_adjusted) {
3008                            if (code == copy_code) {
3009                                pre_adjusted  = tex_copy_node_list(pre_adjusted, null);
3010                            } else {
3011                                box_pre_adjusted(box) = null;
3012                            }
3013                            tail = tex_flush_adjust_prepend(pre_adjusted, tail);
3014                        }
3015                        if (pre_migrated) {
3016                            if (code == copy_code) {
3017                                pre_migrated  = tex_copy_node_list(pre_migrated, null);
3018                            } else {
3019                                box_pre_migrated(box) = null;
3020                            }
3021                            tex_try_couple_nodes(tail, pre_migrated);
3022                            tail = tex_tail_of_node_list(pre_migrated);
3023                        }
3024                        if (list) {
3025                            if (code == copy_code) {
3026                                list = tex_copy_node_list(list, null);
3027                            } else {
3028                                box_list(box) = null;
3029                            }     
3030                            tex_try_couple_nodes(tail, list);
3031                            tail = tex_tail_of_node_list(list);
3032                        }
3033                        if (post_migrated) {
3034                            if (code == copy_code) {
3035                                post_migrated = tex_copy_node_list(post_migrated, null);
3036                            } else {
3037                                box_post_migrated(box) = null;
3038                            }
3039                            tex_try_couple_nodes(tail, post_migrated);
3040                            tail = tex_tail_of_node_list(post_migrated);
3041                        }
3042                        if (post_adjusted) {
3043                            if (code == copy_code) {
3044                                post_adjusted = tex_copy_node_list(post_adjusted, null);
3045                            } else {
3046                                box_post_adjusted(box) = null;
3047                            }
3048                            tail = tex_flush_adjust_append(post_adjusted, tail);
3049                        }
3050                        if (code != copy_code) {
3051                            box_register(index) = null;
3052                            tex_flush_node(box);
3053                        }
3054                        cur_list.tail = prunekerns ? tex_wipe_margin_kerns(cur_list.head) : tex_tail_of_node_list(tail);
3055                    }
3056                }
3057                break;
3058            }
3059        case page_discards_code:
3060            {
3061                tex_try_couple_nodes(cur_list.tail, lmt_packaging_state.page_discards_head);
3062                cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3063                lmt_packaging_state.page_discards_head = null;
3064                break;
3065            }
3066        case split_discards_code:
3067            {
3068                tex_try_couple_nodes(cur_list.tail, lmt_packaging_state.split_discards_head);
3069                cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3070                lmt_packaging_state.split_discards_head = null;
3071                break;
3072            }
3073        case insert_box_code:
3074        case insert_copy_code:
3075            {
3076                /*
3077                    This one is sensitive for messing with callbacks. Somehow attributes and the if
3078                    stack ifs can get corrupted but I have no clue yet how that happens but temp
3079                    nodes have the same size so ...
3080                */
3081                halfword index = tex_scan_integer(0, NULL);
3082                if (tex_valid_insert_id(index)) {
3083                    halfword boxnode = tex_get_insert_content(index); /* also checks for id */
3084                    if (boxnode) {
3085                        if (! is_v_mode(cur_list.mode)) {
3086                            tex_handle_error(
3087                                normal_error_type,
3088                                "Unpacking an inserts can only happen in vertical mode.",
3089                                NULL
3090                            );
3091                        } else if (node_type(boxnode) == vlist_node) {
3092                            if (code == insert_copy_code) {
3093                                boxnode = tex_copy_node(boxnode);
3094                            } else {
3095                                tex_set_insert_content(index, null);
3096                            }
3097                            if (boxnode) {
3098                                halfword list = box_list(boxnode);
3099                                if (list) {
3100                                    tex_try_couple_nodes(cur_list.tail, list);
3101                                    cur_list.tail = tex_tail_of_node_list(list);
3102                                    box_list(boxnode) = null;
3103                                }
3104                                tex_flush_node(boxnode);
3105                            }
3106                        } else {
3107                            /* error, maybe migration list  */
3108                        }
3109                    }
3110                }
3111                break;
3112            }
3113        case local_left_box_box_code:
3114        case local_right_box_box_code:
3115        case local_middle_box_box_code:
3116            {
3117                tex_try_couple_nodes(cur_list.tail, tex_get_local_boxes(local_box_mapping[code]));
3118                cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3119                break;
3120            }
3121        default:
3122            {
3123                tex_confusion("weird unpackage");
3124                break;
3125            }
3126    }
3127}
3128
3129/*tex
3130
3131    When a box is being appended to the current vertical list, the baselineskip calculation is
3132    handled by the |append_to_vlist| routine.
3133
3134    Todo: maybe store some more in lines, so that we can get more consistent spacing (for instance
3135    the |baseline_skip_par| and |prev_depth_par| are now pars and not values frozen with the line.
3136    But as usual we can expect side effects so \unknown
3137
3138*/
3139
3140static halfword tex_aux_depth_correction(halfword b, const line_break_properties *properties)
3141{
3142    /*tex The deficiency of space between baselines: */
3143    halfword p;
3144    halfword height = has_box_package_state(b, dbox_package_state) ? tex_aux_first_height(b) : box_height(b);
3145    halfword depth = cur_list.prev_depth;
3146    if (properties) {
3147        scaled d = glue_amount(properties->baseline_skip) - depth - height;
3148        if (d < properties->line_skip_limit) {
3149            p = tex_new_glue_node(properties->line_skip, line_skip_glue);
3150        } else {
3151            p = tex_new_glue_node(properties->baseline_skip, baseline_skip_glue);
3152            glue_amount(p) = d;
3153        }
3154    } else {
3155        scaled d = glue_amount(baseline_skip_par) - depth - height;
3156        if (d < line_skip_limit_par) {
3157            p = tex_new_param_glue_node(line_skip_code, line_skip_glue);
3158        } else {
3159            p = tex_new_param_glue_node(baseline_skip_code, baseline_skip_glue);
3160            glue_amount(p) = d; /* we keep the stretch and shrink */
3161        }
3162    }
3163    return p;
3164}
3165
3166void tex_append_to_vlist(halfword b, int location, const line_break_properties *properties)
3167{
3168    if (location >= 0) { 
3169        halfword result = null;
3170        halfword next_depth = ignore_depth_criterion_par;
3171        int prev_set = 0;
3172        int check_depth = 0;
3173        if (lmt_append_to_vlist_callback(b, location, cur_list.prev_depth, &result, &next_depth, &prev_set, &check_depth)) {
3174            if (prev_set) {
3175                cur_list.prev_depth = next_depth;
3176            }
3177            if (check_depth && result && (cur_list.prev_depth > ignore_depth_criterion_par)) {
3178                /*tex
3179                    We only deal with a few types and one can always at the \LUA\ end check for some of
3180                    these and decide not to apply the correction.
3181                */
3182                switch (node_type(result)) {
3183                    case hlist_node:
3184                    case vlist_node:
3185                    case rule_node:
3186                        {
3187                            halfword glue = tex_aux_depth_correction(result, properties);
3188                            tex_tail_append(glue);
3189                            break;
3190                        }
3191                }
3192            }
3193            if (result) { 
3194                tex_tail_append_list(result);
3195            }
3196            return;
3197        }
3198    }
3199    if (cur_list.prev_depth > ignore_depth_criterion_par) {
3200        halfword glue = tex_aux_depth_correction(b, properties);
3201        tex_tail_append(glue);
3202    }
3203    tex_tail_append(b);
3204    cur_list.prev_depth = box_depth(b);
3205}
3206
3207/*tex
3208
3209    When |saving_vdiscards| is positive then the glue, kern, and penalty nodes removed by the page
3210    builder or by |\vsplit| from the top of a vertical list are saved in special lists instead of
3211    being discarded.
3212
3213    The |vsplit| procedure, which implements \TEX's |\vsplit| operation, is considerably simpler
3214    than |line_break| because it doesn't have to worry about hyphenation, and because its mission
3215    is to discover a single break instead of an optimum sequence of breakpoints. But before we get
3216    into the details of |vsplit|, we need to consider a few more basic things.
3217
3218    A subroutine called |prune_page_top| takes a pointer to a vlist and returns a pointer to a
3219    modified vlist in which all glue, kern, and penalty nodes have been deleted before the first
3220    box or rule node. However, the first box or rule is actually preceded by a newly created glue
3221    node designed so that the topmost baseline will be at distance |split_top_skip| from the top,
3222    whenever this is possible without backspacing.
3223
3224    When the second argument |s| is |false| the deleted nodes are destroyed, otherwise they are
3225    collected in a list starting at |split_discards|.
3226
3227    Are the prev pointers okay here?
3228
3229*/
3230
3231halfword tex_prune_page_top(halfword p, int s)
3232{
3233    /*tex Lags one step behind |p|. */
3234    halfword prev_p = temp_head;
3235    halfword r = null;
3236    node_next(temp_head) = p;
3237    while (p) {
3238        switch (node_type(p)) {
3239            case hlist_node:
3240            case vlist_node:
3241            case rule_node:
3242                {
3243                    /*tex Insert glue for |split_top_skip| and set |p| to |null|. */
3244                    halfword h = node_type(p) == rule_node ? rule_height(p) : box_height(p);
3245                    halfword q = tex_new_param_glue_node(split_top_skip_code, split_top_skip_glue);
3246                    node_next(prev_p) = q;
3247                    node_next(q) = p;
3248                    glue_amount(q) = glue_amount(q) > h ? glue_amount(q) - h : 0;
3249                    p = null;
3250                }
3251                break;
3252            case boundary_node:
3253                /* shouldn't we discard */
3254            case whatsit_node:
3255            case mark_node:
3256            case insert_node:
3257                prev_p = p;
3258                p = node_next(prev_p);
3259                break;
3260            case glue_node:
3261            case kern_node:
3262            case penalty_node:
3263                {
3264                    halfword q = p;
3265                    p = node_next(q);
3266                    node_next(q) = null;
3267                    node_next(prev_p) = p;
3268                    if (s) {
3269                        if (lmt_packaging_state.split_discards_head) {
3270                            node_next(r) = q;
3271                        } else {
3272                            lmt_packaging_state.split_discards_head = q;
3273                        }
3274                        r = q;
3275                    } else {
3276                        tex_flush_node_list(q);
3277                    }
3278                }
3279                break;
3280            default:
3281                tex_confusion("pruning page top");
3282                break;
3283        }
3284    }
3285    return node_next(temp_head);
3286}
3287
3288/*tex
3289
3290    The next subroutine finds the best place to break a given vertical list so as to obtain a box
3291    of height~|h|, with maximum depth~|d|. A pointer to the beginning of the vertical list is given,
3292    and a pointer to the optimum breakpoint is returned. The list is effectively followed by a
3293    forced break, i.e., a penalty node with the |eject_penalty|; if the best break occurs at this
3294    artificial node, the value |null| is returned.
3295
3296    An array of six |scaled| distances is used to keep track of the height from the beginning of
3297    the list to the current place, just as in |line_break|. In fact, we use one of the same arrays,
3298    only changing its name to reflect its new significance.
3299
3300    The distance from first active node to |cur_p| is stored in |active_height|.
3301
3302    A global variable |best_height_plus_depth| will be set to the natural size of the box (without
3303    stretching or shrinking) that corresponds to the optimum breakpoint found by |vert_break|. This
3304    value is used by the insertion splitting algorithm of the page builder.
3305
3306    \starttyping
3307    scaled best_height_plus_depth;
3308    \stoptyping
3309
3310 */
3311
3312static halfword tex_aux_vert_badness(scaled goal, scaled active_height[])
3313{
3314    if (active_height[total_advance_amount] < goal) {
3315        if (active_height[total_fi_amount] || active_height[total_fil_amount] || active_height[total_fill_amount] || active_height[total_filll_amount]) {
3316            return 0;
3317        } else {
3318            return tex_badness(goal - active_height[total_advance_amount], active_height[total_stretch_amount]);
3319        }
3320    } else if (active_height[total_advance_amount] - goal > active_height[total_shrink_amount]) {
3321        return awful_bad;
3322    } else {
3323        return tex_badness(active_height[total_advance_amount] - goal, active_height[total_shrink_amount]);
3324    }
3325}
3326
3327static halfword tex_aux_vert_costs(halfword badness, halfword penalty)
3328{
3329    if (badness >= awful_bad) {
3330        return badness; 
3331    } else if (penalty <= eject_penalty) {
3332        return penalty;
3333    } else if (badness < infinite_bad) {
3334        return badness + penalty;
3335    } else {
3336        return deplorable;
3337    }
3338}
3339
3340halfword tex_vert_break(halfword current, scaled height, scaled depth)
3341{
3342    /*tex
3343        If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal breakpoint, an
3344        initial glue node is not a legal breakpoint.
3345    */
3346    halfword previous = current;
3347    /*tex The to be checked penalty value: */
3348    halfword penalty = 0;
3349    /*tex The smallest badness plus penalties found so far: */
3350    halfword least_cost = awful_bad;
3351    /*tex The most recent break that leads to |least_cost|: */
3352    halfword best_place = null;
3353    /*tex The depth of previous box in the list: */
3354    scaled previous_depth = 0;
3355    /*tex The various glue components: */
3356    scaled active_height[10] = { 0 };
3357    while (1) {
3358        /*tex
3359            If node |p| is a legal breakpoint, check if this break is the best known, and |goto
3360            done| if |p| is null or if the page-so-far is already too full to accept more stuff.
3361
3362            A subtle point to be noted here is that the maximum depth~|d| might be negative, so
3363            |cur_height| and |prev_dp| might need to be corrected even after a glue or kern node.
3364        */
3365        if (current) {
3366            /*tex
3367                Use node |p| to update the current height and depth measurements; if this node is
3368                not a legal breakpoint, |goto not_found| or |update_heights|, otherwise set |pi|
3369                to the associated penalty at the break.
3370            */
3371            switch (node_type(current)) {
3372                case hlist_node:
3373                case vlist_node:
3374                    /*tex
3375                        If we do this we also need to subtract the dimensions and bubble it up. But
3376                        at least we could inline the inserts.
3377                    */
3378                    /*
3379                    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_post)) {
3380                        // same code as in page builder
3381                    }
3382                    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_pre)) {
3383                        // same code as in page builder
3384                        continue;
3385                    }
3386                    */
3387                    active_height[total_advance_amount] += previous_depth + box_height(current);
3388                    previous_depth = box_depth(current);
3389                    goto NOT_FOUND;
3390                case rule_node:
3391                    active_height[total_advance_amount] += previous_depth + rule_height(current);
3392                    previous_depth = rule_depth(current);
3393                    goto NOT_FOUND;
3394                case boundary_node:
3395                case whatsit_node:
3396                    goto NOT_FOUND;
3397                case glue_node:
3398                    if (precedes_break(previous)) {
3399                        penalty = 0;
3400                        break;
3401                    } else {
3402                        goto UPDATE_HEIGHTS;
3403                    }
3404                case kern_node:
3405                    if (node_next(current) && node_type(node_next(current)) == glue_node) {
3406                        penalty = 0;
3407                        break;
3408                    } else {
3409                        goto UPDATE_HEIGHTS;
3410                    }
3411                case penalty_node:
3412                    penalty = penalty_amount(current);
3413// option: penalty = 0;
3414                    break;
3415                case mark_node:
3416                case insert_node:
3417                    goto NOT_FOUND;
3418                default:
3419                    tex_confusion("vertical break");
3420                    break;
3421            }
3422        } else {
3423            penalty = eject_penalty;
3424        }
3425        /*tex
3426            Check if node |p| is a new champion breakpoint; then |goto done| if |p| is a forced
3427            break or if the page-so-far is already too full.
3428        */
3429        if (penalty < infinite_penalty) {
3430            /*tex Compute the badness, |b|, using |awful_bad| if the box is too full. */
3431            int badness = tex_aux_vert_badness(height, active_height);
3432            int costs = tex_aux_vert_costs(badness, penalty);
3433            if (costs <= least_cost) {
3434                best_place = current;
3435                least_cost = costs;
3436                lmt_packaging_state.best_height_plus_depth = active_height[total_advance_amount] + previous_depth;
3437                /*tex 
3438                    Here's a patch suggested by DEK related to glue in inserts that needs to be taken 
3439                    into account. That patch is not applied to regular \TEX\ for compatibility reasons.
3440                */
3441                if (lmt_packaging_state.best_height_plus_depth > (height + previous_depth) && costs < awful_bad) { 
3442                    lmt_packaging_state.best_height_plus_depth = height + previous_depth;
3443                }
3444            }
3445            if ((costs == awful_bad) || (penalty <= eject_penalty)) {
3446                return best_place;
3447            }
3448        }
3449      UPDATE_HEIGHTS:
3450        /*tex
3451            Update the current height and depth measurements with respect to a glue or kern node~|p|.
3452            Vertical lists that are subject to the |vert_break| procedure should not contain infinite
3453            shrinkability, since that would permit any amount of information to fit on one page. We 
3454            only end up here for glue and kern nodes.
3455        */
3456        switch(node_type(current)) {
3457            case kern_node:
3458                active_height[total_advance_amount] += previous_depth + kern_amount(current);
3459                previous_depth = 0;
3460                goto KEEP_GOING; /* We assume a positive depth. */
3461            case glue_node:
3462                active_height[total_stretch_amount + glue_stretch_order(current)] += glue_stretch(current);
3463                active_height[total_shrink_amount] += glue_shrink(current);
3464                if (glue_shrink_order(current) != normal_glue_order  && glue_shrink(current)) {
3465                    tex_handle_error(
3466                        normal_error_type,
3467                        "Infinite glue shrinkage found in box being split",
3468                        "The box you are \\vsplitting contains some infinitely shrinkable glue, e.g.,\n"
3469                        "'\\vss' or '\\vskip 0pt minus 1fil'. Such glue doesn't belong there; but you can\n"
3470                        "safely proceed, since the offensive shrinkability has been made finite."
3471                    );
3472                    glue_shrink_order(current) = normal_glue_order;
3473                }
3474                active_height[total_advance_amount] += previous_depth+ glue_amount(current);
3475                previous_depth = 0;
3476                goto KEEP_GOING; /* We assume a positive depth. */
3477        }
3478      NOT_FOUND:
3479        if (previous_depth > depth) {
3480            active_height[total_advance_amount] += previous_depth - depth;
3481            previous_depth = depth;
3482        }
3483      KEEP_GOING:
3484        previous = current;
3485        current = node_next(previous);
3486    }
3487    return best_place; /* This location is unreachable but some compilers like it this way. */
3488}
3489
3490/*tex
3491
3492    Now we are ready to consider |vsplit| itself. Most of its work is accomplished by the two
3493    subroutines that we have just considered.
3494
3495    Given the number of a vlist box |n|, and given a desired page height |h|, the |vsplit|
3496    function finds the best initial segment of the vlist and returns a box for a page of height~|h|.
3497    The remainder of the vlist, if any, replaces the original box, after removing glue and penalties
3498    and adjusting for |split_top_skip|. Mark nodes in the split-off box are used to set the values
3499    of |split_first_mark| and |split_bot_mark|; we use the fact that |split_first_mark(x) = null| if
3500    and only if |split_bot_mark(x) = null|.
3501
3502    The original box becomes \quote {void} if and only if it has been entirely extracted. The
3503    extracted box is \quote {void} if and only if the original box was void (or if it was,
3504    erroneously, an hlist box).
3505
3506    Extract a page of height |h| from box |n|:
3507*/
3508
3509halfword tex_vsplit(halfword n, scaled h, int m)
3510{
3511    /*tex the box to be split */
3512    halfword v = box_register(n);
3513    tex_flush_node_list(lmt_packaging_state.split_discards_head);
3514    lmt_packaging_state.split_discards_head = null;
3515    for (halfword i = 0; i <= lmt_mark_state.mark_data.ptr; i++) {
3516        tex_delete_mark(i, split_first_mark_code);
3517        tex_delete_mark(i, split_bot_mark_code);
3518    }
3519    /*tex Dispense with trivial cases of void or bad boxes. */
3520    if (! v) {
3521        return null;
3522    } else if (node_type(v) != vlist_node) {
3523        tex_handle_error(
3524            normal_error_type,
3525            "\\vsplit needs a \\vbox",
3526            "The box you are trying to split is an \\hbox. I can't split such a box, so I''ll\n"
3527            "leave it alone."
3528        );
3529        return null;
3530    } else {
3531        /*tex points to where the break occurs */
3532        halfword q = tex_vert_break(box_list(v), h, split_max_depth_par);
3533        /*tex
3534
3535            Look at all the marks in nodes before the break, and set the final link to |null| at
3536            the break. It's possible that the box begins with a penalty node that is the quote
3537            {best} break, so we must be careful to handle this special case correctly.
3538
3539        */
3540        halfword p = box_list(v);
3541        /*tex The direction of the box to be split, obsolete! */
3542        int vdir = box_dir(v);
3543        if (p == q) {
3544            box_list(v) = null;
3545        } else {
3546            while (1) {
3547                if (node_type(p) == mark_node) {
3548                    tex_update_split_mark(p);
3549                }
3550                if (node_next(p) == q) {
3551                    node_next(p) = null;
3552                    break;
3553                } else {
3554                    p = node_next(p);
3555                }
3556            }
3557        }
3558        q = tex_prune_page_top(q, saving_vdiscards_par > 0);
3559        p = box_list(v);
3560        box_list(v) = null;
3561        tex_flush_node(v);
3562        if (q) {
3563            box_register(n) = tex_filtered_vpack(q, 0, packing_additional, max_depth_par, split_keep_group, vdir, 0, 0, 0, holding_none_option, NULL);
3564        } else {
3565            /*tex The |eq_level| of the box stays the same. */
3566            box_register(n) = null;
3567        }
3568        return tex_filtered_vpack(p, m == packing_additional ? 0 : h, m, max_depth_par, split_off_group, vdir, 0, 0, 0, holding_none_option, NULL);
3569    }
3570}
3571
3572/*tex
3573
3574    Now that we can see what eventually happens to boxes, we can consider the first steps in their
3575    creation. The |begin_box| routine is called when |box_context| is a context specification,
3576    |cur_chr| specifies the type of box desired, and |cur_cmd=make_box|.
3577
3578*/
3579
3580void tex_begin_box(int boxcontext, scaled shift, halfword slot, halfword callback)
3581{
3582    halfword code = cur_chr;
3583    halfword boxnode = null; /*tex Aka |cur_box|. */
3584    switch (code) {
3585        case box_code:
3586            {
3587                halfword n = tex_scan_box_register_number();
3588                boxnode = box_register(n);
3589                /*tex The box becomes void, at the same level. */
3590                box_register(n) = null;
3591                break;
3592            }
3593        case copy_code:
3594            {
3595                halfword n = tex_scan_box_register_number();
3596                boxnode = box_register(n) ? tex_copy_node(box_register(n)) : null;
3597                break;
3598            }
3599     /* case unpack_code: */
3600     /*     break;        */
3601         case last_box_code:
3602            /*tex
3603                If the current list ends with a box node, delete it from the list and make |boxnode|
3604                point to it; otherwise set |boxnode := null|.
3605            */
3606            boxnode = null;
3607            if (is_m_mode(cur_list.mode)) {
3608                tex_you_cant_error(
3609                    "Sorry; this \\lastbox will be void."
3610                );
3611            } else if (cur_list.mode == vmode && cur_list.head == cur_list.tail) {
3612                tex_you_cant_error(
3613                    "Sorry...I usually can't take things from the current page.\n"
3614                    "This \\lastbox will therefore be void."
3615                );
3616            } else if (cur_list.head != cur_list.tail) {
3617                switch (node_type(cur_list.tail)) {
3618                    case hlist_node:
3619                    case vlist_node:
3620                        {
3621                            /*tex Remove the last box */
3622                            halfword q = node_prev(cur_list.tail);
3623                            if (! q || node_next(q) != cur_list.tail) {
3624                                q = cur_list.head;
3625                                while (node_next(q) != cur_list.tail)
3626                                    q = node_next(q);
3627                            }
3628                            tex_uncouple_node(cur_list.tail);
3629                            boxnode = cur_list.tail;
3630                            box_shift_amount(boxnode) = 0;
3631                            cur_list.tail = q;
3632                            node_next(cur_list.tail) = null;
3633                        }
3634                        break;
3635                }
3636            }
3637            break;
3638        case tsplit_code:
3639        case vsplit_code:
3640        case dsplit_code:
3641            {
3642                /*tex
3643                    Split off part of a vertical box, make |boxnode| point to it. Here we deal with
3644                    things like |\vsplit 13 to 100pt|. Maybe todo: just split off one line.
3645
3646                */
3647                halfword mode = packing_exactly ;
3648                halfword index = tex_scan_box_register_number();
3649                halfword size = 0;
3650                halfword attrlist = null;
3651                while (1) {
3652                    switch (tex_scan_character("atuATU", 0, 1, 0)) {
3653                        case 0:
3654                            goto DONE;
3655                        case 'a': case 'A':
3656                            if (tex_scan_mandate_keyword("attr", 1)) {
3657                                attrlist = tex_scan_attribute(attrlist);
3658                            }
3659                            break;
3660                        case 't': case 'T':
3661                            if (tex_scan_mandate_keyword("to", 1)) {
3662                                mode = packing_exactly ;
3663                                size = tex_scan_dimension(0, 0, 0, 0, NULL);
3664                            }
3665                            break;
3666                        case 'u': case 'U':
3667                            if (tex_scan_mandate_keyword("upto", 1)) {
3668                                mode = packing_additional;
3669                                size = tex_scan_dimension(0, 0, 0, 0, NULL);
3670                            }
3671                            break;
3672                        default:
3673                            tex_aux_show_keyword_error("attr|upto|to");
3674                            goto DONE;
3675                    }
3676                }
3677              DONE:
3678                boxnode = tex_vsplit(index, size, mode);
3679                tex_aux_set_vnature(boxnode, code);
3680                if (attrlist) { 
3681                    tex_attach_attribute_list_attribute(boxnode, attrlist);
3682                }
3683            }
3684            break;
3685        case insert_box_code:
3686        case insert_copy_code:
3687            {
3688                halfword index = tex_scan_integer(0, NULL);
3689                if (tex_valid_insert_id(index)) {
3690                    boxnode = tex_get_insert_content(index);
3691                    if (boxnode) {
3692                        if (node_type(boxnode) == vlist_node) {
3693                            if (code == insert_copy_code) {
3694                                boxnode = tex_copy_node(boxnode);
3695                            } else {
3696                                tex_set_insert_content(index, null);
3697                            }
3698                        } else {
3699                            tex_set_insert_content(index, null);
3700                            /* error, maybe migration list  */
3701                        }
3702                    }
3703                }
3704                break;
3705            }
3706        case local_left_box_box_code:
3707            {
3708                boxnode = tex_get_local_boxes(local_left_box_code);
3709                break;
3710            }
3711        case local_right_box_box_code:
3712            {
3713                boxnode = tex_get_local_boxes(local_right_box_code);
3714                break;
3715            }
3716        case local_middle_box_box_code:
3717            {
3718                boxnode = tex_get_local_boxes(local_middle_box_code);
3719                break;
3720            }
3721       /*tex
3722
3723            Initiate the construction of an hbox or vbox, then |return|. Here is where we
3724            enter restricted horizontal mode or internal vertical mode, in order to make a
3725            box. The juggling with codes and addition or subtraction was somewhat messy.
3726
3727        */
3728        default:
3729            {
3730                quarterword direction;
3731                int justpack = 0;
3732                quarterword group = vbox_group;
3733                int mode = vmode;
3734                int adjusted = 0;
3735                switch (cur_list.mode) {
3736                    case vmode:
3737                    case internal_vmode:
3738                        direction = dir_lefttoright;
3739                        if (boxcontext == direct_box_flag) {
3740                            adjusted = 1;
3741                        }
3742                        break;
3743                    case hmode:
3744                    case restricted_hmode:
3745                        direction = (singleword) text_direction_par;
3746                        break;
3747                    case mmode:
3748                    case inline_mmode:
3749                        direction = (singleword) math_direction_par;
3750                        break;
3751                    default: 
3752                        direction = direction_unknown;
3753                        break;
3754                }
3755                switch (code) {
3756                    case tpack_code:
3757                     // mode = vmode; 
3758                        justpack = 1;
3759                        group = vtop_group;
3760                        break;
3761                    case vpack_code:
3762                     // mode = vmode; 
3763                        justpack = 1;
3764                     // group = vbox_group;
3765                        break;
3766                    case hpack_code:
3767                        mode = hmode; 
3768                        justpack = 1;
3769                        group = adjusted ? adjusted_hbox_group : hbox_group;
3770                        break;
3771                    case dpack_code:
3772                     // mode = vmode; 
3773                        justpack = 1;
3774                        group = dbox_group;
3775                        break;
3776                    case vtop_code:
3777                     // mode = vmode;
3778                     // justpack = 0;
3779                        group = vtop_group;
3780                        break;
3781                    case vbox_code:
3782                     // mode = vmode;
3783                     // justpack = 0;
3784                     // group = vbox_group;
3785                        break;
3786                    case hbox_code:   
3787                        mode = hmode;
3788                     // justpack = 0;
3789                        group = adjusted ? adjusted_hbox_group : hbox_group;
3790                        break;
3791                    case dbox_code:
3792                     // mode = vmode;
3793                     // justpack = 0;
3794                        group = dbox_group;
3795                        break;
3796                }
3797                tex_aux_scan_full_spec(boxcontext, group, direction, justpack, shift, slot, callback);
3798                tex_normal_paragraph(vmode_par_context);
3799                tex_push_nest();
3800                update_tex_internal_dir_state(0);
3801                cur_list.mode = - mode;
3802                if (mode == vmode) {
3803                    cur_list.prev_depth = ignore_depth_criterion_par;
3804                    if (every_vbox_par) {
3805                        tex_begin_token_list(every_vbox_par, every_vbox_text);
3806                    }
3807                } else {
3808                    cur_list.space_factor = default_space_factor;
3809                    if (every_hbox_par) {
3810                        tex_begin_token_list(every_hbox_par, every_hbox_text);
3811                    }
3812                }
3813                return;
3814            }
3815    }
3816    /*tex In simple cases, we use the box immediately. */
3817    tex_box_end(boxcontext, boxnode, shift, unset_noad_class, slot, callback);
3818}
3819