texbuildpage.c /size: 68 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    When \TEX\ appends new material to its main vlist in vertical mode, it uses a method something
10    like |vsplit| to decide where a page ends, except that the calculations are done \quote {on
11    line} as new items come in. The main complication in this process is that insertions must be
12    put into their boxes and removed from the vlist, in a more-or-less optimum manner.
13
14    We shall use the term \quote {current page} for that part of the main vlist that is being
15    considered as a candidate for being broken off and sent to the user's output routine. The
16    current page starts at |node_next(page_head)|, and it ends at |page_tail|. We have |page_head =
17    page_tail| if this list is empty.
18
19    Utter chaos would reign if the user kept changing page specifications while a page is being
20    constructed, so the page builder keeps the pertinent specifications frozen as soon as the page
21    receives its first box or insertion. The global variable |page_contents| is |empty| when the
22    current page contains only mark nodes and content-less whatsit nodes; it is |inserts_only|
23    if the page contains only insertion nodes in addition to marks and whatsits. Glue nodes, kern
24    nodes, and penalty nodes are discarded until a box or rule node appears, at which time
25    |page_contents| changes to |box_there|. As soon as |page_contents| becomes non-|empty|, the
26    current |vsize| and |max_depth| are squirreled away into |page_goal| and |page_max_depth|; the
27    latter values will be used until the page has been forwarded to the user's output routine. The
28    |\topskip| adjustment is made when |page_contents| changes to |box_there|.
29
30    Although |page_goal| starts out equal to |vsize|, it is decreased by the scaled natural
31    height-plus-depth of the insertions considered so far, and by the |\skip| corrections for
32    those insertions. Therefore it represents the size into which the non-inserted material
33    should fit, assuming that all insertions in the current page have been made.
34
35    The global variables |best_page_break| and |least_page_cost| correspond respectively to the
36    local variables |best_place| and |least_cost| in the |vert_break| routine that we have already
37    studied; i.e., they record the location and value of the best place currently known for
38    breaking the current page. The value of |page_goal| at the time of the best break is stored in
39    |best_size|.
40
41*/
42
43page_builder_state_info lmt_page_builder_state = {
44    .page_tail        = null,
45    .contents         = 0,
46    .max_depth        = 0,
47    .best_break       = null,
48    .least_cost       = 0,
49    .best_size        = 0,
50    .goal             = 0,
51    .vsize            = 0,
52    .total            = 0,
53    .depth            = 0,
54    .excess           = 0,
55    .page_so_far      = { 0 },
56    .insert_penalties = 0,
57    .insert_heights   = 0,
58    .last_glue        = max_halfword,
59    .last_penalty     = 0,
60    .last_kern        = 0,
61    .last_node_type   = unknown_node_type,
62    .last_node_subtype= unknown_node_subtype,
63    .last_extra_used  = 0,
64    .last_boundary    = 0,
65    .output_active    = 0,
66    .dead_cycles      = 0,
67    .current_state    = 0 
68};
69
70/*tex
71    Because we have an extra order (fi) we need to intercept it in the case of the page builder 
72    although we could decide to support it here too. 
73*/
74
75# define page_stretched(order) lmt_page_builder_state.page_so_far[page_stretch_state+order] 
76
77static void tex_aux_fire_up (halfword c);
78
79/*tex
80
81    The page builder has another data structure to keep track of insertions. This is a list of
82    four-word nodes, starting and ending at |page_insert_head|. That is, the first element of the
83    list is node |t$_1$ = node_next(page_insert_head)|; node $r_j$ is followed by |t$_{j+1}$ =
84    node_next(t$_j$)|; and if there are |n| items we have |$_{n+1}$ >= page_insert_head|. The
85    |subtype| field of each node in this list refers to an insertion number; for example, |\insert
86    250| would correspond to a node whose |subtype| is |qi(250)| (the same as the |subtype| field
87    of the relevant |insert_node|). These |subtype| fields are in increasing order, and |subtype
88    (page_insert_head) = 65535|, so |page_insert_head| serves as a convenient sentinel at the end
89    of the list. A record is present for each insertion number that appears in the current page.
90
91    The |type| field in these nodes distinguishes two possibilities that might occur as we look
92    ahead before deciding on the optimum page break. If |type(r) = inserting_node|, then |height(r)|
93    contains the total of the height-plus-depth dimensions of the box and all its inserts seen so
94    far. If |type(r) = split_up_node|, then no more insertions will be made into this box, because at
95    least one previous insertion was too big to fit on the current page; |broken_ptr(r)| points to
96    the node where that insertion will be split, if \TEX\ decides to split it, |broken_insert(r)|
97    points to the insertion node that was tentatively split, and |height(r)| includes also the
98    natural height plus depth of the part that would be split off.
99
100    In both cases, |last_insert(r)| points to the last |insert_node| encountered for box
101    |qo(subtype(r))| that would be at least partially inserted on the next page; and
102    |best_insert(r)| points to the last such |insert_node| that should actually be inserted, to get
103    the page with minimum badness among all page breaks considered so far. We have |best_insert
104    (r) = null| if and only if no insertion for this box should be made to produce this optimum page.
105
106    Pages are built by appending nodes to the current list in \TEX's vertical mode, which is at the
107    outermost level of the semantic nest. This vlist is split into two parts; the \quote {current
108    page} that we have been talking so much about already, and the |quote {contribution list} that
109    receives new nodes as they are created. The current page contains everything that the page
110    builder has accounted for in its data structures, as described above, while the contribution
111    list contains other things that have been generated by other parts of \TEX\ but have not yet
112    been seen by the page builder. The contribution list starts at |vlink (contribute_head)|, and it
113    ends at the current node in \TEX's vertical mode.
114
115    When \TEX\ has appended new material in vertical mode, it calls the procedure |build_page|,
116    which tries to catch up by moving nodes from the contribution list to the current page. This
117    procedure will succeed in its goal of emptying the contribution list, unless a page break is
118    discovered, i.e., unless the current page has grown to the point where the optimum next page
119    break has been determined. In the latter case, the nodes after the optimum break will go back
120    onto the contribution list, and control will effectively pass to the user's output routine.
121
122    We make |type (page_head) = glue_node|, so that an initial glue node on the current page will
123    not be considered a valid breakpoint. We keep this old tex trickery of cheating with node types
124    but have to make sure that the size is valid to do so (and we have different sizes!).
125
126*/
127
128void tex_initialize_pagestate(void)
129{
130    lmt_page_builder_state.page_tail = page_head;
131    lmt_page_builder_state.contents = contribute_nothing;
132    lmt_page_builder_state.max_depth = 0;
133    lmt_page_builder_state.best_break = null;
134    lmt_page_builder_state.least_cost = 0;
135    lmt_page_builder_state.best_size = 0;
136    lmt_page_builder_state.goal = 0;
137    lmt_page_builder_state.vsize = 0;
138    lmt_page_builder_state.total = 0;
139    lmt_page_builder_state.depth = 0;
140    for (int i = page_initial_state; i <= page_shrink_state; i++) { 
141        lmt_page_builder_state.page_so_far[i] = 0;
142    } 
143    lmt_page_builder_state.insert_penalties = 0;
144    lmt_page_builder_state.insert_heights = 0;
145    lmt_page_builder_state.last_glue = max_halfword;
146    lmt_page_builder_state.last_penalty = 0;
147    lmt_page_builder_state.last_kern = 0;
148    lmt_page_builder_state.last_extra_used = 0;
149    lmt_page_builder_state.last_boundary = 0;
150    lmt_page_builder_state.last_node_type = unknown_node_type;
151    lmt_page_builder_state.last_node_subtype = unknown_node_subtype;
152    lmt_page_builder_state.output_active = 0;
153    lmt_page_builder_state.dead_cycles = 0;
154    lmt_page_builder_state.current_state = 0;
155}
156
157void tex_initialize_buildpage(void)
158{
159    node_type(page_insert_head) = split_node;
160    node_subtype(page_insert_head) = insert_split_subtype;
161    insert_index(page_insert_head) = 65535;          /*tex some signal */
162    node_next(page_insert_head) = page_insert_head;
163    node_type(page_head) = glue_node;                /*tex brr, a temp node has a different size than a glue node */
164    node_subtype(page_head) = page_glue;             /*tex basically: unset */
165}
166
167/*tex
168
169    An array |page_so_far| records the heights and depths of everything on the current page. This
170    array contains six |scaled| numbers, like the similar arrays already considered in |line_break|
171    and |vert_break|; and it also contains |page_goal| and |page_depth|, since these values are all
172    accessible to the user via |set_page_dimension| commands. The value of |page_so_far[1]| is also
173    called |page_total|. The stretch and shrink components of the |\skip| corrections for each
174    insertion are included in |page_so_far|, but the natural space components of these corrections
175    are not, since they have been subtracted from |page_goal|.
176
177    The variable |page_depth| records the depth of the current page; it has been adjusted so that it
178    is at most |page_max_depth|. The variable |last_glue| points to the glue specification of the
179    most recent node contributed from the contribution list, if this was a glue node; otherwise
180    |last_glue = max_halfword|. (If the contribution list is nonempty, however, the value of
181    |last_glue| is not necessarily accurate.) The variables |last_penalty|, |last_kern|, and
182    |last_node_type| are similar. And finally, |insert_penalties| holds the sum of the penalties
183    associated with all split and floating insertions.
184
185    Here is a procedure that is called when the |page_contents| is changing from |empty| to
186    |inserts_only| or |box_there|.
187
188*/
189
190void tex_additional_page_skip(void)
191{
192    if (page_total > 0 && additional_page_skip_par) {
193        page_stretch += glue_stretch(additional_page_skip_par);
194        page_shrink += glue_shrink(additional_page_skip_par);
195        page_total += glue_amount(additional_page_skip_par);
196        update_tex_additional_page_skip(null);
197    }
198}
199
200static void tex_aux_save_best_page_specs(void)
201{
202    page_last_depth = page_depth;
203    page_last_height = page_total;
204    page_last_stretch = page_stretch;
205    page_last_fistretch = page_fistretch;
206    page_last_filstretch = page_filstretch;  
207    page_last_fillstretch = page_fillstretch; 
208    page_last_filllstretch = page_filllstretch;
209    page_last_shrink = page_shrink;
210}
211
212static void tex_aux_freeze_page_specs(int s)
213{
214    lmt_page_builder_state.contents = s;
215    lmt_page_builder_state.max_depth = max_depth_par;
216    lmt_page_builder_state.least_cost = awful_bad;
217 /* page_builder_state.insert_heights = 0; */ /* up to the user */
218    for (int i = page_initial_state; i <= page_shrink_state; i++) { 
219        lmt_page_builder_state.page_so_far[i] = 0;
220        lmt_page_builder_state.page_last_so_far[i] = 0;
221    } 
222    page_goal = vsize_par;
223    page_vsize = vsize_par;
224    page_depth = 0;
225    page_total = 0;
226    page_excess = 0;
227    page_last_depth = 0;
228    page_last_height = 0;
229    if (initial_page_skip_par) {
230        page_stretch = glue_stretch(initial_page_skip_par);
231        page_shrink = glue_shrink(initial_page_skip_par);
232        page_total = glue_amount(initial_page_skip_par);
233    }
234    if (additional_page_skip_par) {
235        page_stretch += glue_stretch(additional_page_skip_par);
236        page_shrink += glue_shrink(additional_page_skip_par);
237        page_total += glue_amount(additional_page_skip_par);
238        update_tex_additional_page_skip(null);
239    }
240    if (tracing_pages_par > 0) {
241        tex_begin_diagnostic();
242        tex_print_format(
243            "[page: frozen state, goal=%p, maxdepth=%p, contribution=%s, insertheights=%p]",
244            page_goal,
245            lmt_page_builder_state.max_depth,
246            lmt_interface.page_contribute_values[s].name,
247            lmt_page_builder_state.insert_heights
248        );
249        tex_end_diagnostic();
250    }
251}
252
253static void update_page_goal(halfword index, scaled total, scaled delta)
254{
255    page_goal -= delta;
256    lmt_page_builder_state.insert_heights += total;
257    if (lmt_page_builder_state.insert_heights > max_dimension) {
258        lmt_page_builder_state.insert_heights = max_dimension;
259    }
260    if (tracing_inserts_par > 0) {
261        tex_begin_diagnostic();
262        tex_print_format(
263            "[page: update page goal for insert, index=%i, total=%p, insertheights=%p, vsize=%p, delta=%p, goal=%p]",
264            index, total, lmt_page_builder_state.insert_heights, page_vsize, delta, page_goal
265        );
266        tex_end_diagnostic();
267    }
268}
269
270/*tex
271
272    The global variable |output_active| is true during the time the user's output routine is
273    driving \TEX. The page builder is ready to start a fresh page if we initialize the following
274    state variables. (However, the page insertion list is initialized elsewhere.)
275
276*/
277
278static void tex_aux_start_new_page(void)
279{
280    lmt_page_builder_state.contents = contribute_nothing;
281    lmt_page_builder_state.page_tail = page_head;
282    node_next(page_head) = null;
283    lmt_page_builder_state.last_glue = max_halfword;
284    lmt_page_builder_state.last_penalty = 0;
285    lmt_page_builder_state.last_kern = 0;
286    lmt_page_builder_state.last_boundary = 0;
287    lmt_page_builder_state.last_node_type = unknown_node_type;
288    lmt_page_builder_state.last_node_subtype = unknown_node_subtype;
289    page_depth = 0;
290    lmt_page_builder_state.max_depth = 0;
291}
292
293/*tex
294
295    At certain times box |\outputbox| is supposed to be void (i.e., |null|), or an insertion box is
296    supposed to be ready to accept a vertical list. If not, an error message is printed, and the
297    following subroutine flushes the unwanted contents, reporting them to the user.
298
299*/
300
301static halfword tex_aux_delete_box_content(int n)
302{
303    tex_begin_diagnostic();
304    tex_print_format("[page: deleting box]");
305    tex_show_box(n);
306    tex_end_diagnostic();
307    tex_flush_node_list(n);
308    return null;
309}
310
311/*tex
312
313    The following procedure guarantees that an insert box is not an |\hbox|. A user can actually
314    mess with this box, unless we decide to come up with a dedicated data structure for it.
315
316*/
317
318static int tex_aux_valid_insert_content(halfword content)
319{
320    if (content && node_type(content) == hlist_node) {
321        /*tex It's not always a box so we need to adapt this message some day. */
322        tex_handle_error(
323            normal_error_type,
324            "Insertions can only be added to a vbox",
325            "Tut tut: You're trying to \\insert into a \\box register that now contains an\n"
326            "\\hbox. Proceed, and I'll discard its present contents."
327        );
328        return 0;
329    } else {
330        return 1;
331    }
332}
333
334/*tex
335
336    \TEX\ is not always in vertical mode at the time |build_page| is called; the current mode
337    reflects what \TEX\ should return to, after the contribution list has been emptied. A call on
338    |build_page| should be immediately followed by |goto big_switch|, which is \TEX's central
339    control point.
340
341    Append contributions to the current page.
342
343*/
344
345static void tex_aux_display_page_break_cost(halfword badness, halfword penalty, halfword cost, int moveon, int fireup)
346{
347    tex_begin_diagnostic();
348    tex_print_format("[page: break, total %P, goal %p, badness %B, penalty %i, cost %B%s, moveon %s, fireup %s]",
349        page_total, page_stretch, page_fistretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
350        page_goal, badness, penalty, cost, cost < lmt_page_builder_state.least_cost ? "#" : "",
351        moveon ? "yes" : "no", fireup ? "yes" : "no"
352    );
353    tex_end_diagnostic();
354}
355
356static void tex_aux_display_insertion_split_cost(halfword index, scaled height, halfword penalty)
357{
358    /*tex Display the insertion split cost. */
359    tex_begin_diagnostic();
360    tex_print_format("[page: split insert %i: height %p, depth %p, penalty %i]",
361        index, height, lmt_packaging_state.best_height_plus_depth, penalty
362    );
363    tex_end_diagnostic();
364}
365
366static halfword tex_aux_page_badness(scaled goal)
367{
368    if (page_total < goal) {
369        if (page_fistretch || page_filstretch || page_fillstretch || page_filllstretch) {
370            return 0;
371        } else {
372            return tex_badness(goal - page_total, page_stretch);
373        }
374    } else if (page_total - goal > page_shrink) {
375        return awful_bad;
376    } else {
377        return tex_badness(page_total - goal, page_shrink);
378    }
379}
380
381inline static halfword tex_aux_page_costs(halfword badness, halfword penalty)
382{
383    if (lmt_page_builder_state.insert_penalties >= infinite_penalty) {
384        return awful_bad;
385    } else if (badness >= awful_bad) {
386        return badness; /* trigger fireup */
387    } else if (penalty <= eject_penalty) {
388        return penalty; /* trigger fireup */
389    } else if (badness < infinite_bad) {
390        return badness + penalty + lmt_page_builder_state.insert_penalties;
391    } else {
392        return deplorable;
393    }
394}
395
396static halfword tex_aux_insert_topskip(halfword height, int contribution)
397{
398    if (lmt_page_builder_state.contents != contribute_nothing) {
399        lmt_page_builder_state.contents = contribution;
400    } else {
401        tex_aux_freeze_page_specs(contribution);
402    }
403    {
404        halfword glue = tex_new_param_glue_node(tex_glue_is_zero(initial_top_skip_par) ? top_skip_code : initial_top_skip_code, top_skip_glue);
405        if (glue_amount(glue) > height) {
406            glue_amount(glue) -= height;
407        } else {
408            glue_amount(glue) = 0;
409        }
410        return glue;
411    }
412}
413
414/*tex
415    Append an insertion to the current page and |goto contribute|. The insertion number (index) is 
416    registered in the subtype (not any more for a while).
417*/
418
419static void tex_aux_append_insert(halfword current)
420{
421    halfword index = insert_index(current); /* initially 65K */
422    halfword location = page_insert_head;
423    halfword multiplier = tex_get_insert_multiplier(index);
424    halfword content = tex_get_insert_content(index);
425    scaled limit = tex_get_insert_limit(index);
426    int slot = 1;
427    if (lmt_page_builder_state.contents == contribute_nothing) {
428        tex_aux_freeze_page_specs(contribute_insert);
429    }
430    while (index >= insert_index(node_next(location))) {
431        location = node_next(location);
432        slot += 1 ;
433    }
434    if (insert_index(location) != index) {
435        /*tex
436
437            Create a page insertion node with |subtype(r) = qi(n)|, and include the glue correction 
438            for box |n| in the current page state. We take note of the value of |\skip| |n| and the
439            height plus depth of |\box| |n| only when the first |\insert n| node is encountered for 
440            a new page. A user who changes the contents of |\box| |n| after that first |\insert n| 
441            had better be either extremely careful or extremely lucky, or both.
442
443            We need to handle this too:
444
445            [content]
446            [max(space shared,space n)]
447            [class n]
448            .........
449            [space m]
450            [class m]
451
452            For now a callback can deal with this but maybe we need to have a more advanced 
453            mechanism for this (and more control over inserts in general).
454
455        */
456        halfword splitnode = tex_new_node(split_node, normal_split_subtype);
457        scaled advance = 0;
458        halfword distance = lmt_get_insert_distance(index, slot); /*tex Callback: we get a copy! */
459        split_insert_index(splitnode) = index;
460        tex_try_couple_nodes(splitnode, node_next(location));
461        tex_couple_nodes(location, splitnode);
462        location = splitnode;
463        if (! tex_aux_valid_insert_content(content)) {
464            content = tex_aux_delete_box_content(content);
465            tex_set_insert_content(index, content);
466        };
467        if (content) {
468            box_height(location) = box_total(content);
469        } else {
470            box_height(location) = 0;
471        }
472        split_best_insert(location) = null;
473        if (multiplier == scaling_factor) {
474            advance = box_height(location);
475        } else {
476            advance = tex_x_over_n(box_height(location), scaling_factor) * multiplier;
477        }
478        advance += glue_amount(distance);
479        update_page_goal(index, 0, advance); /*tex Here gets no height added! */
480        page_stretched(glue_stretch_order(distance)) += glue_stretch(distance);
481        page_shrink += glue_shrink(distance);
482        if (glue_shrink_order(distance) != normal_glue_order && glue_shrink(distance)) {
483            tex_handle_error(
484                normal_error_type,
485                "Infinite glue shrinkage inserted from \\skip%i",
486                index,
487                "The correction glue for page breaking with insertions must have finite\n"
488                "shrinkability. But you may proceed, since the offensive shrinkability has been\n"
489                "made finite."
490            );
491        }
492        tex_flush_node(distance);
493    }
494    /*tex I really need to check this logic with the original \LUATEX\ code. */
495    if (node_type(location) == split_node && node_subtype(location) == insert_split_subtype) {
496        lmt_page_builder_state.insert_penalties += insert_float_cost(current);
497    } else {
498        scaled delta = page_goal - page_total - page_depth + page_shrink;
499        scaled needed = insert_total_height(current);
500        split_last_insert(location) = current;
501        /*tex This much room is left if we shrink the maximum. */
502        if (multiplier != scaling_factor) {
503            /*tex This much room is needed. */
504            needed = tex_x_over_n(needed, scaling_factor) * multiplier;
505        }
506        if ((needed <= 0 || needed <= delta) && (insert_total_height(current) + box_height(location) <= limit)) {
507            update_page_goal(index, insert_total_height(current), needed);
508            box_height(location) += insert_total_height(current);
509        } else {
510            /*tex
511
512                Find the best way to split the insertion, and change |subtype(r)| to 
513                |split_up_inserting_code|.
514
515                Here is the code that will split a long footnote between pages, in an emergency. 
516                The current situation deserves to be recapitulated: Node |p| is an insertion 
517                into box |n|; the insertion will not fit, in its entirety, either because it
518                would make the total contents of box |n| greater than |\dimen| |n|, or because 
519                it would make the incremental amount of growth |h| greater than the available 
520                space |delta|, or both. (This amount |h| has been weighted by the insertion 
521                scaling factor, i.e., by |\count| |n| over 1000.) Now we will choose the best
522                way to break the vlist of the insertion, using the same criteria as in the 
523                |\vsplit| operation.
524
525            */
526            scaled height;
527            halfword breaknode, penalty;
528            if (multiplier <= 0) {
529                height = max_dimension;
530            } else {
531                height = page_goal - page_total - page_depth;
532                if (multiplier != scaling_factor) {
533                    height = tex_x_over_n(height, multiplier) * scaling_factor;
534                }
535            }
536            if (height > limit - box_height(location)) {
537                height = limit - box_height(location);
538            }
539            breaknode = tex_vert_break(insert_list(current), height, insert_max_depth(current));
540            box_height(location) += lmt_packaging_state.best_height_plus_depth;
541            penalty = breaknode ? (node_type(breaknode) == penalty_node ? penalty_amount(breaknode) : 0) : eject_penalty;
542            if (tracing_pages_par > 0) {
543                tex_aux_display_insertion_split_cost(index, height, penalty);
544            }
545            if (multiplier != scaling_factor) {
546                lmt_packaging_state.best_height_plus_depth = tex_x_over_n(lmt_packaging_state.best_height_plus_depth, scaling_factor) * multiplier;
547            }
548            update_page_goal(index, lmt_packaging_state.best_height_plus_depth, lmt_packaging_state.best_height_plus_depth);
549            node_subtype(location) = insert_split_subtype;
550            split_broken(location) = breaknode;
551            split_broken_insert(location) = current;
552            lmt_page_builder_state.insert_penalties += penalty;
553        }
554    }
555}
556
557inline static int tex_aux_get_penalty_option(halfword current) 
558{
559    while (1) { 
560        current = node_prev(current);
561        if (current && current != contribute_head) {
562            switch (node_type(current)) {
563                case glue_node: 
564                    break;
565                case penalty_node: 
566                    if (penalty_amount(current) >= infinite_penalty) {
567                        if (tex_has_penalty_option(current, penalty_option_widowed)) {
568                            if (tracing_pages_par > 1) {
569                                tex_begin_diagnostic();
570                                tex_print_format("[page: widowed]");
571                                tex_end_diagnostic();
572                            }
573                            return penalty_option_widowed;
574                        } else if (tex_has_penalty_option(current, penalty_option_clubbed)) {
575                            if (tracing_pages_par > 1) {
576                                tex_begin_diagnostic();
577                                tex_print_format("[page: clubbed]");
578                                tex_end_diagnostic();
579                            }
580                            return penalty_option_clubbed;
581                        }            
582                    }
583                    return 0;
584                default:
585                    return 0;
586            }
587        } else { 
588            return 0;
589        }
590    }
591 // return 0;
592}
593
594
595static void tex_aux_initialize_show_build_node(int callback_id)
596{
597    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", initialize_show_build_context);
598}
599
600static void tex_aux_step_show_build_node(int callback_id, halfword current)
601{
602    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNdd->", step_show_build_context, 
603        current,
604        page_goal,
605        page_total
606    );
607}
608
609static void tex_aux_check_show_build_node(int callback_id, halfword current, int badness, int costs, int penalty, int *moveon, int *fireup)
610{
611    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNbbddd->bb", check_show_build_context, 
612        current,
613        *moveon, 
614        *fireup,
615        badness, 
616        costs, 
617        penalty, 
618        moveon, 
619        fireup
620    );
621}
622
623static void tex_aux_skip_show_build_node(int callback_id, halfword current)
624{
625    (void) current;
626    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dN->", skip_show_build_context);
627}
628
629static void tex_aux_move_show_build_node(int callback_id, halfword current)
630{
631    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNddddb->", move_show_build_context, 
632        current,
633        page_last_height,
634        page_last_depth,
635        page_last_stretch,
636        page_last_shrink,
637        (page_last_fistretch || page_last_filstretch || page_last_fillstretch || page_last_filllstretch) ? 1 : 0
638    );
639}
640
641static void tex_aux_fireup_show_build_node(int callback_id, halfword current)
642{
643    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dN->", fireup_show_build_context,
644        current 
645    );
646}
647
648static void tex_aux_wrapup_show_build_node(int callback_id)
649{
650    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", wrapup_show_build_context);
651}
652
653static int tex_aux_topskip_restart(halfword current, int where, scaled height, scaled depth, int tracing)
654{
655    if (lmt_page_builder_state.contents < contribute_box) {
656        /*tex
657            Initialize the current page, insert the |\topskip| glue ahead of |p|, and |goto 
658            continue|.
659        */
660        halfword gluenode = tex_aux_insert_topskip(height, where);
661        tex_couple_nodes(gluenode, current);
662        tex_couple_nodes(contribute_head, gluenode);
663        if (tracing > 1) {
664            tex_begin_diagnostic();
665            tex_print_format("[page: initialize, topskip at %s]", where == contribute_box ? "box" : "rule");
666            tex_end_diagnostic();
667        }
668        return 1;
669    } else {
670        /*tex Move a box to the current page, then |goto contribute|. */
671        page_total += page_depth + height;
672        page_depth = depth;
673        return 0;
674    }
675}
676
677static int tex_aux_migrating_restart(halfword current, int tracing)
678{
679    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_post)) {
680        halfword head = box_post_migrated(current);
681        if (head) {
682            halfword tail = tex_tail_of_node_list(head);
683            if (tracing > 1 || tracing_adjusts_par > 1) {
684                tex_begin_diagnostic();
685                tex_print_format("[adjust: post, mvl]");
686                tex_print_node_list(head, "post", show_box_depth_par, show_box_breadth_par);
687                tex_end_diagnostic();
688            }   
689            if (node_next(current)) {
690                tex_couple_nodes(tail, node_next(current));
691            } else {
692                contribute_tail = tail;
693            }
694            tex_couple_nodes(current, head);
695            box_post_migrated(current) = null;
696        }
697    }
698    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_pre)) {
699        halfword head = box_pre_migrated(current);
700        if (head) {
701            halfword tail = tex_tail_of_node_list(head);
702            if (tracing > 1 || tracing_adjusts_par > 1) {
703                tex_begin_diagnostic();
704                tex_print_format("[adjust: pre, mvl]");
705                tex_print_node_list(head, "pre", show_box_depth_par, show_box_breadth_par);
706                tex_end_diagnostic();
707            }
708            tex_couple_nodes(tail, current);
709            tex_couple_nodes(contribute_head, current);
710            // if (contribute_head == contribute_tail) {
711            //     contribute_tail = tail; 
712            // }
713            box_pre_migrated(current) = null;
714            return 1;
715        }
716    }
717    return 0;
718}
719
720/*tex
721    We just triggered the pagebuilder for which we needed a contribution. We fake a zero penalty 
722    so that all gets processed. The main rationale is that we get a better indication of what we 
723    do. Of course a callback can remove this node  so that it is never seen. Triggering from the 
724    callback is not doable.
725*/
726
727static halfword tex_aux_process_boundary(halfword current)
728{
729    halfword penaltynode = tex_new_node(penalty_node, user_penalty_subtype);
730    /* todo: copy attributes */
731    tex_page_boundary_message("processed as penalty", 0);
732    tex_try_couple_nodes(node_prev(current), penaltynode);
733    tex_try_couple_nodes(penaltynode, node_next(current));
734    tex_flush_node(current);
735    penalty_amount(penaltynode) = boundary_data(current);
736    current = penaltynode;
737    node_next(contribute_head) = current;
738    return current;
739}
740
741static void tex_aux_reconsider_goal(halfword current, halfword *badness, halfword *costs, halfword *penalty, int tracing)
742{
743    (void) current;
744    if (*badness >= awful_bad && page_extra_goal_par) {
745        switch (tex_aux_get_penalty_option(lmt_page_builder_state.page_tail)) {
746            case penalty_option_widowed: 
747                if (page_total <= (page_goal + page_extra_goal_par)) {
748                    halfword extrabadness = tex_aux_page_badness(page_goal + page_extra_goal_par);
749                    halfword extracosts = tex_aux_page_costs(extrabadness, *penalty);
750                    if (tracing > 0) {
751                        tex_begin_diagnostic();
752                        tex_print_format(
753                            "[page: extra check, total=%P, goal=%p, extragoal=%p, badness=%B, costs=%i, extrabadness=%B, extracosts=%i]",
754                            page_total, page_stretch, page_filstretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
755                            page_goal, page_extra_goal_par,
756                            *badness, *costs, extrabadness, extracosts
757                        );
758                        tex_end_diagnostic();
759                    }
760                    /* we need to append etc. so an extra loop cycle */
761                    *badness = extrabadness; 
762                    *costs = extracosts;
763                    /* */
764                    lmt_page_builder_state.last_extra_used = 1;
765                    if (tracing > 1) {
766                        tex_begin_diagnostic();
767                        tex_print_format("[page: widowed]");
768                        tex_end_diagnostic();
769                    }
770                }
771                break;
772            case penalty_option_clubbed: 
773                if (page_total >= (page_goal - page_extra_goal_par)) {
774                    /* we force a flush and no extra loop cycle */
775                    *penalty = eject_penalty;
776                    /* */
777                    if (tracing > 1) {
778                        tex_begin_diagnostic();
779                        tex_print_format("[page: clubbed]");
780                        tex_end_diagnostic();
781                    }
782                }
783                break;
784        }
785    }
786}
787
788static void tex_aux_contribute_glue(halfword current)
789{
790    page_stretched(glue_stretch_order(current)) += glue_stretch(current);
791    page_shrink += glue_shrink(current);
792    if (glue_shrink_order(current) != normal_glue_order && glue_shrink(current)) {
793        tex_handle_error(
794            normal_error_type,
795            "Infinite glue shrinkage found on current page",
796            "The page about to be output contains some infinitely shrinkable glue, e.g.,\n"
797            "'\\vss' or '\\vskip 0pt minus 1fil'. Such glue doesn't belong there; but you can\n"
798            "safely proceed, since the offensive shrinkability has been made finite."
799        );
800        tex_reset_glue_to_zero(current);
801        glue_shrink_order(current) = normal_glue_order;
802    }
803}
804
805/*tex
806    Maybe: migrate everything beforehand is somewhat nicer when we use the builder for multicolumns
807    balancing etc. 
808*/
809
810void tex_build_page(halfword context, halfword boundary)
811{
812    if (! lmt_page_builder_state.output_active) {
813        lmt_page_filter_callback(context, boundary);
814    }
815    if (node_next(contribute_head) && ! lmt_page_builder_state.output_active) {
816        /*tex The (upcoming) penalty to be added to the badness: */
817        halfword penalty = 0;
818        int callback_id = lmt_callback_defined(show_build_callback);
819        int tracing = tracing_pages_par;
820        if (callback_id) { 
821           tex_aux_initialize_show_build_node(callback_id);
822        }
823        do {
824            /*tex 
825                We update the values of |\lastskip|, |\lastpenalty|, |\lastkern| and 
826                |\lastboundary| as we go.
827            */
828            halfword current = node_next(contribute_head);
829            halfword type = node_type(current);
830            quarterword subtype = node_subtype(current);
831            if (callback_id) { 
832               tex_aux_step_show_build_node(callback_id, current);
833            }
834            if (lmt_page_builder_state.last_glue != max_halfword) {
835                tex_flush_node(lmt_page_builder_state.last_glue);
836                lmt_page_builder_state.last_glue = max_halfword;
837            }
838            lmt_page_builder_state.last_penalty = 0;
839            lmt_page_builder_state.last_kern = 0;
840            lmt_page_builder_state.last_boundary = 0;
841            lmt_page_builder_state.last_node_type = type;
842            lmt_page_builder_state.last_node_subtype = subtype;
843            lmt_page_builder_state.last_extra_used = 0;
844            /*tex
845
846                Move node |p| to the current page; if it is time for a page break, put the nodes
847                following the break back onto the contribution list, and |return| to the users
848                output routine if there is one.
849
850                The code here is an example of a many-way switch into routines that merge together
851                in different places. Some people call this unstructured programming, but the author
852                doesn't see much wrong with it, as long as the various labels have a well-understood
853                meaning.
854
855                If the current page is empty and node |p| is to be deleted, |goto done1|; otherwise
856                use node |p| to update the state of the current page; if this node is an insertion,
857                |goto contribute|; otherwise if this node is not a legal breakpoint,
858                |goto contribute| or |update_heights|; otherwise set |pi| to the penalty associated
859                with this breakpoint.
860
861                The title of this section is already so long, it seems best to avoid making it more
862                accurate but still longer, by mentioning the fact that a kern node at the end of
863                the contribution list will not be contributed until we know its successor.
864
865            */
866            switch (type) {
867                case hlist_node:
868                case vlist_node:
869                    if (tex_aux_migrating_restart(current, tracing)) {
870                        continue;
871                    } else if (tex_aux_topskip_restart(current, contribute_box, box_height(current), box_depth(current), tracing)) {
872                        continue;
873                    } else {
874                        goto CONTRIBUTE;
875                    }
876                case rule_node:
877                    if (tex_aux_topskip_restart(current, contribute_rule, rule_height(current), rule_depth(current), tracing)) {
878                        continue;
879                    } else {
880                        goto CONTRIBUTE;
881                    }
882                case boundary_node:
883                    if (subtype == page_boundary) {
884                        lmt_page_builder_state.last_boundary = boundary_data(current);
885                    }
886                    if (lmt_page_builder_state.contents < contribute_box) {
887                        goto DISCARD;
888                    } else if (subtype == page_boundary) {
889                        current = tex_aux_process_boundary(current);
890                        penalty = 0;
891                        break;
892                    } else {
893                        goto DISCARD;
894                    }
895                case whatsit_node:
896                    goto CONTRIBUTE;
897                case glue_node:
898                    lmt_page_builder_state.last_glue = tex_new_glue_node(current, subtype);
899                    if (lmt_page_builder_state.contents < contribute_box) {
900                        goto DISCARD;
901                    } else if (precedes_break(lmt_page_builder_state.page_tail)) {
902                        penalty = 0;
903                        break;
904                    } else {
905                        goto UPDATEHEIGHTS;
906                    }
907                case kern_node:
908                    lmt_page_builder_state.last_kern = kern_amount(current);
909                    if (lmt_page_builder_state.contents < contribute_box) {
910                        goto DISCARD;
911                    } else if (! node_next(current)) {
912                        return;
913                    } else if (node_type(node_next(current)) == glue_node) {
914                        penalty = 0;
915                        break;
916                    } else {
917                        goto UPDATEHEIGHTS;
918                    }
919                case penalty_node:
920                    lmt_page_builder_state.last_penalty = penalty_amount(current);
921                    if (lmt_page_builder_state.contents < contribute_box) {
922                        goto DISCARD;
923                    } else {
924                        penalty = penalty_amount(current);
925                        break;
926                    }
927                case mark_node:
928                    goto CONTRIBUTE;
929                case insert_node:
930                    tex_aux_append_insert(current);
931                    goto CONTRIBUTE;
932                default:
933                    tex_formatted_error("pagebuilder", "invalid %N node in vertical mode", current);
934                    break;
935            }
936            /*tex
937                Check if node |p| is a new champion breakpoint; then if it is time for a page break,
938                prepare for output, and either fire up the users output routine and |return| or
939                ship out the page and |goto done|.
940            */
941            if (tracing > 1) {
942                tex_begin_diagnostic();
943                tex_print_format("[page: compute: %N, %d, penalty=%i]", current, current, penalty);
944                tex_end_diagnostic();
945            }
946            if (penalty < infinite_penalty) {
947                /*tex
948                    Compute the badness, |b|, of the current page, using |awful_bad| if the box is
949                    too full. The |c| variable holds the costs.
950                */
951                halfword badness = tex_aux_page_badness(page_goal);
952                halfword costs = tex_aux_page_costs(badness, penalty);
953                lmt_page_builder_state.last_extra_used = 0;
954                if (tracing > 1) {
955                    tex_begin_diagnostic();
956                    tex_print_format("[page: calculate, %N, %d, total=%P, goal=%p, badness=%B, costs=%i]", 
957                        current, current, 
958                        page_total, page_stretch, page_fistretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
959                        page_goal,
960                        badness, costs
961                    );
962                    tex_end_diagnostic();
963                }
964                tex_aux_reconsider_goal(current, &badness, &costs, &penalty, tracing);
965                {
966                    int moveon = costs <= lmt_page_builder_state.least_cost;
967                    int fireup = costs == awful_bad || penalty <= eject_penalty;
968                    if (callback_id) { 
969                        tex_aux_check_show_build_node(callback_id, current, badness, lmt_page_builder_state.last_penalty, costs, &moveon, &fireup);
970                    }
971                    if (tracing > 0) {
972                        tex_aux_display_page_break_cost(badness, penalty, costs, moveon, fireup);
973                    }
974                    if (moveon) {
975                        halfword insert = node_next(page_insert_head);
976                        lmt_page_builder_state.best_break = current;
977                        lmt_page_builder_state.best_size = page_goal;
978                        lmt_page_builder_state.insert_penalties = 0;
979                        lmt_page_builder_state.least_cost = costs;
980                        while (insert != page_insert_head) {
981                            split_best_insert(insert) = split_last_insert(insert);
982                            insert = node_next(insert);
983                        }
984                        tex_aux_save_best_page_specs();
985                        if (callback_id) { 
986                           tex_aux_move_show_build_node(callback_id, current);
987                        }
988                    }
989                    if (fireup) {
990                        if (tracing > 1) {
991                            tex_begin_diagnostic();
992                            tex_print_format("[page: fireup: %N, %d]", current, current);
993                            tex_end_diagnostic();
994                        }
995                        if (callback_id) { 
996                           tex_aux_fireup_show_build_node(callback_id, current);
997                        }
998                        /*tex Output the current page at the best place. */
999                        tex_aux_fire_up(current);
1000                        if (lmt_page_builder_state.output_active) {
1001                            /*tex User's output routine will act. */
1002                            if (callback_id) { 
1003                               tex_aux_wrapup_show_build_node(callback_id);
1004                            }
1005                            return;
1006                        } else {
1007                            /*tex The page has been shipped out by default output routine. */
1008                            continue;
1009                        }
1010                    }
1011                }
1012            } else {
1013                if (callback_id) { 
1014                    tex_aux_skip_show_build_node(callback_id, current);
1015                }
1016            }
1017          UPDATEHEIGHTS:
1018            /*tex
1019                Go here to record glue in the |active_height| table. Update the current page
1020                measurements with respect to the glue or kern specified by node~|p|.
1021            */
1022            if (tracing > 1) {
1023                tex_begin_diagnostic();
1024                tex_print_format("[page: update, %N, %d]", current, current);
1025                tex_end_diagnostic();
1026            }
1027            switch(node_type(current)) {
1028                case kern_node:
1029                    page_total += page_depth + kern_amount(current);
1030                    page_depth = 0;
1031                    goto APPEND;
1032                case glue_node:
1033                    tex_aux_contribute_glue(current);
1034                    page_total += page_depth + glue_amount(current);
1035                    page_depth = 0;
1036                    goto APPEND;
1037            }
1038          CONTRIBUTE:
1039            /*tex
1040                Go here to link a node into the current page. Make sure that |page_max_depth| is
1041                not exceeded.
1042            */
1043            if (tracing > 1) {
1044                tex_begin_diagnostic();
1045                tex_print_format("[page: contribute, %N, %d]", current, current);
1046                tex_end_diagnostic();
1047            }
1048            if (page_depth > lmt_page_builder_state.max_depth) {
1049                page_total += page_depth - lmt_page_builder_state.max_depth;
1050                page_depth = lmt_page_builder_state.max_depth;
1051            }
1052          APPEND:
1053            if (tracing > 1) {
1054                tex_begin_diagnostic();
1055                tex_print_format("[page: append, %N, %d]", current, current);
1056                tex_end_diagnostic();
1057            }
1058            /*tex Link node |p| into the current page and |goto done|. We assume a positive depth. */
1059            tex_couple_nodes(lmt_page_builder_state.page_tail, current);
1060            lmt_page_builder_state.page_tail = current;
1061            tex_try_couple_nodes(contribute_head, node_next(current));
1062            node_next(current) = null;
1063            continue; // or: break; 
1064          DISCARD:
1065            if (tracing > 1) {
1066                tex_begin_diagnostic();
1067                tex_print_format("[page: discard, %N, %d]", current, current);
1068                tex_end_diagnostic();
1069            }
1070            /*tex Recycle node |p|. */
1071            tex_try_couple_nodes(contribute_head, node_next(current));
1072            node_next(current) = null;
1073            if (saving_vdiscards_par > 0) {
1074                if (lmt_packaging_state.page_discards_head) {
1075                    tex_couple_nodes(lmt_packaging_state.page_discards_tail, current);
1076                } else {
1077                    lmt_packaging_state.page_discards_head = current;
1078                }
1079                lmt_packaging_state.page_discards_tail = current;
1080            } else {
1081                tex_flush_node_list(current);
1082            }
1083        } while (node_next(contribute_head));
1084        /*tex Make the contribution list empty by setting its tail to |contribute_head|. */
1085        contribute_tail = contribute_head;
1086    }
1087}
1088
1089/*tex
1090
1091    When the page builder has looked at as much material as could appear before the next page break,
1092    it makes its decision. The break that gave minimum badness will be used to put a completed page
1093    into box |\outputbox|, with insertions appended to their other boxes.
1094
1095    We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The program uses the fact
1096    that |bot_mark(x) <> null| implies |first_mark(x) <> null|; it also knows that |bot_mark(x) =
1097    null| implies |top_mark(x) = first_mark(x) = null|.
1098
1099    The |fire_up| subroutine prepares to output the current page at the best place; then it fires
1100    up the user's output routine, if there is one, or it simply ships out the page. There is one
1101    parameter, |c|, which represents the node that was being contributed to the page when the
1102    decision to force an output was made.
1103
1104*/
1105
1106static void tex_aux_fire_up(halfword c)
1107{
1108    /*tex nodes being examined and/or changed */
1109    halfword current, previous, lastinsert;
1110    /*tex Set the value of |output_penalty|. */
1111    if (node_type(lmt_page_builder_state.best_break) == penalty_node) {
1112        update_tex_output_penalty(penalty_amount(lmt_page_builder_state.best_break));
1113        penalty_amount(lmt_page_builder_state.best_break) = infinite_penalty;
1114    } else {
1115        update_tex_output_penalty(infinite_penalty);
1116    }
1117    tex_update_top_marks();
1118    /*tex
1119        Put the optimal current page into box |output_box|, update |first_mark| and |bot_mark|,
1120        append insertions to their boxes, and put the remaining nodes back on the contribution
1121        list.
1122
1123        As the page is finally being prepared for output, pointer |p| runs through the vlist, with
1124        |prev_p| trailing behind; pointer |q| is the tail of a list of insertions that are being
1125        held over for a subsequent page.
1126    */
1127    if (c == lmt_page_builder_state.best_break) {
1128        /*tex |c| not yet linked in */
1129        lmt_page_builder_state.best_break = null;
1130    }
1131    /*tex Ensure that box |output_box| is empty before output. */
1132    if (box_register(output_box_par)) {
1133        tex_handle_error(
1134            normal_error_type,
1135            "\\box%i is not void",
1136            output_box_par,
1137            "You shouldn't use \\box\\outputbox except in \\output routines. Proceed, and I'll\n"
1138            "discard its present contents."
1139        );
1140        box_register(output_box_par) = tex_aux_delete_box_content(box_register(output_box_par));
1141    }
1142    /*
1143    {
1144        int callback_id = lmt_callback_defined(fire_up_output_callback);
1145        if (callback_id != 0) {
1146            halfword insert = node_next(page_insert_head);
1147            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "->");
1148        }
1149    }
1150    */
1151    /*tex This will count the number of insertions held over. */
1152    {
1153        halfword save_split_top_skip = split_top_skip_par;
1154        lmt_page_builder_state.insert_penalties = 0;
1155        if (holding_inserts_par <= 0) {
1156            /*tex
1157                Prepare all the boxes involved in insertions to act as queues. If many insertions are
1158                supposed to go into the same box, we want to know the position of the last node in that
1159                box, so that we don't need to waste time when linking further information into it. The
1160                |last_insert| fields of the page insertion nodes are therefore used for this purpose
1161                during the packaging phase.
1162
1163                This is tricky: |last_insert| directly points to a \quote {address} in the node list,
1164                that is: the row where |list_ptr| sits. The |raw_list_ptr| macro is just an offset to
1165                the base index of the node. Then |node_next| will start out there and follow the list.
1166                So, |last_insert| kind of points to a subnode (as in disc nodes) of size 1.
1167
1168                    last_insert => [shift][list]
1169
1170                which fakes:
1171
1172                    last_insert => [type|subtype][next] => [real node with next]
1173
1174                and with shift being zero this (when it would be queried) will be seen as a hlist node
1175                of type zero with subtype zero, but ... it is not really such a node which means that
1176                other properties are not valid! Normally this is ok, because \TEX\ only follows this
1177                list and never looks at the parent. But, when accessing from \LUA\ this is asking for
1178                troubles. However, as all happens in the page builder, we don't really expose this and
1179                if we would (somehow, e.g. via a callback) then for sure we would need to make sure
1180                that the node |last_insert(r)| points to is made into a new kind of node: one with
1181                size 1 and type |fake_node| or so, just to be sure (so that at the \LUA\ end no
1182                properties can be asked).
1183
1184                Of course I can be wrong here and changing the approach would involve patching some
1185                code that I don't want to touch. I need a test case for \quote {following the chain}.
1186            */
1187            halfword insert = node_next(page_insert_head);
1188            while (insert != page_insert_head) {
1189                if (split_best_insert(insert)) {
1190                    halfword index = insert_index(insert);
1191                    halfword content = tex_get_insert_content(index);
1192                    if (! tex_aux_valid_insert_content(content)) {
1193                        content = tex_aux_delete_box_content(content);
1194                    }
1195                    if (! content) {
1196                        /*tex
1197                            So we package the content in a box. Originally this is a hlist which
1198                            is somewhat strange because we're operating in vmode. The box is still
1199                            empty!
1200                        */
1201                        content = tex_new_null_box_node(vlist_node, insert_result_list);
1202                        tex_set_insert_content(index, content);
1203                    }
1204                    /*tex
1205                        We locate the place where we can add. We have an (unpackaged) list here so we
1206                        need to go to the end. Here we have this sort of hackery |box(n) + 5 == row of
1207                        list ptr, a fake node of size 1| trick.
1208                    */
1209                    split_last_insert(insert) = tex_tail_of_node_list(insert_first_box(content));
1210                }
1211                insert = node_next(insert);
1212            }
1213        }
1214        previous = page_head;
1215        current = node_next(previous);
1216        lastinsert = hold_head;
1217        node_next(lastinsert) = null;
1218        while (current != lmt_page_builder_state.best_break) {
1219            switch (node_type(current)) {
1220                case insert_node:
1221                    if (holding_inserts_par <= 0) {
1222                        /*tex
1223                            Either insert the material specified by node |p| into the appropriate box, or
1224                            hold it for the next page; also delete node |p| from the current page.
1225
1226                            We will set |best_insert := null| and package the box corresponding to
1227                            insertion node |r|, just after making the final insertion into that box. If
1228                            this final insertion is |split_up_node|, the remainder after splitting and
1229                            pruning (if any) will be carried over to the next page.
1230                        */
1231                        /*tex should the present insertion be held over? */
1232                        int wait = 0;
1233                        halfword insert = node_next(page_insert_head);
1234                        while (insert_index(insert) != insert_index(current)) {
1235                            insert = node_next(insert);
1236                        }
1237                        if (split_best_insert(insert)) {
1238                            halfword split = split_last_insert(insert);
1239                            tex_try_couple_nodes(split, insert_list(current));
1240                            if (split_best_insert(insert) == current) {
1241                                /*tex
1242                                    Wrap up the box specified by node |r|, splitting node |p| if called
1243                                    for and set |wait| if node |p| holds a remainder after splitting.
1244                                */
1245                                if (node_type(insert) == split_node && node_subtype(insert) == insert_split_subtype && (split_broken_insert(insert) == current) && split_broken(insert)) {
1246                                    while (node_next(split) != split_broken(insert)) {
1247                                        split = node_next(split);
1248                                    }
1249                                    node_next(split) = null;
1250                                    split_top_skip_par = insert_split_top(current);
1251                                    insert_list(current) = tex_prune_page_top(split_broken(insert), 0);
1252                                    if (insert_list(current)) {
1253                                        /*tex
1254                                            We only determine the total height of the list stored in
1255                                            the insert node.
1256                                         */
1257                                        halfword list = insert_list(current);
1258                                        halfword result = tex_vpack(list, 0, packing_additional, max_dimension, direction_unknown, holding_none_option, NULL);
1259                                        insert_total_height(current) = box_total(result);
1260                                        box_list(result) = null;
1261                                        tex_flush_node(result);
1262                                        wait = 1;
1263                                    }
1264                                }
1265                                split_best_insert(insert) = null;
1266                                {
1267                                    /*tex
1268                                        We need this juggling in order to also set the old school box
1269                                        when we're in traditional mode.
1270                                    */
1271                                    halfword index = insert_index(insert);
1272                                    halfword content = tex_get_insert_content(index);
1273                                    halfword list = box_list(content);
1274                                    halfword result = tex_vpack(list, 0, packing_additional, max_dimension, dir_lefttoright, holding_none_option, NULL);
1275                                    tex_set_insert_content(index, result);
1276                                    box_list(content) = null;
1277                                    tex_flush_node(content);
1278                                }
1279                            } else {
1280                                split_last_insert(insert) = tex_tail_of_node_list(split);
1281                            }
1282                        } else {
1283                            wait = 1;
1284                        }
1285                        /*tex
1286                            Either append the insertion node |p| after node |q|, and remove it from the
1287                            current page, or delete |node(p)|.
1288                        */
1289                        tex_try_couple_nodes(previous, node_next(current));
1290                        node_next(current) = null;
1291                        if (wait) {
1292                            tex_couple_nodes(lastinsert, current);
1293                            lastinsert = current;
1294                            ++lmt_page_builder_state.insert_penalties;
1295                        } else {
1296                            insert_list(current) = null;
1297                            tex_flush_node(current);
1298                        }
1299                        current = previous;
1300                    }
1301                    break;
1302                case mark_node:
1303                    tex_update_first_and_bot_mark(current);
1304                    break;
1305            }
1306            previous = current;
1307            current = node_next(current);
1308        }
1309        split_top_skip_par = save_split_top_skip;
1310    }
1311    /*tex
1312        Break the current page at node |p|, put it in box~|output_box|, and put the remaining nodes
1313        on the contribution list.
1314
1315        When the following code is executed, the current page runs from node |vlink (page_head)| to
1316        node |prev_p|, and the nodes from |p| to |page_tail| are to be placed back at the front of
1317        the contribution list. Furthermore the heldover insertions appear in a list from |vlink
1318        (hold_head)| to |q|; we will put them into the current page list for safekeeping while the
1319        user's output routine is active. We might have |q = hold_head|; and |p = null| if and only
1320        if |prev_p = page_tail|. Error messages are suppressed within |vpackage|, since the box
1321        might appear to be overfull or underfull simply because the stretch and shrink from the
1322        |\skip| registers for inserts are not actually present in the box.
1323    */
1324    if (current) {
1325        if (! node_next(contribute_head)) {
1326            contribute_tail = lmt_page_builder_state.page_tail;
1327        }
1328        tex_couple_nodes(lmt_page_builder_state.page_tail, node_next(contribute_head));
1329        tex_couple_nodes(contribute_head, current);
1330        node_next(previous) = null;
1331    }
1332    /*tex When we pack the box we inhibit error messages. */
1333    {
1334        halfword save_vbadness = vbadness_par;
1335        halfword save_vfuzz = vfuzz_par;
1336        vbadness_par = infinite_bad;
1337        vfuzz_par = max_dimension;
1338        tex_show_marks();
1339     // if (1) { 
1340            box_register(output_box_par) = tex_filtered_vpack(node_next(page_head), lmt_page_builder_state.best_size, packing_exactly, lmt_page_builder_state.max_depth, output_group, dir_lefttoright, 0, 0, 0, holding_none_option, &lmt_page_builder_state.excess);
1341     // } else { 
1342     //     /* maybe an option one day */
1343     //     box_register(output_box_par) = tex_filtered_vpack(node_next(page_head), 0, packing_additional, lmt_page_builder_state.max_depth, output_group, dir_lefttoright, 0, 0, 0, holding_none_option));
1344     // }
1345        vbadness_par = save_vbadness;
1346        vfuzz_par = save_vfuzz;
1347    }
1348    if (lmt_page_builder_state.last_glue != max_halfword) {
1349        tex_flush_node(lmt_page_builder_state.last_glue);
1350    }
1351    /*tex Start a new current page. This sets |last_glue := max_halfword|. */
1352    tex_aux_start_new_page(); 
1353    /*tex So depth is now forgotten .. hm. */
1354    if (lastinsert != hold_head) {
1355        node_next(page_head) = node_next(hold_head);
1356        lmt_page_builder_state.page_tail = lastinsert;
1357    }
1358    /*tex Delete the page-insertion nodes. */
1359    {
1360        halfword r = node_next(page_insert_head);
1361        while (r != page_insert_head) {
1362            lastinsert = node_next(r);
1363            tex_flush_node(r);
1364            r = lastinsert;
1365        }
1366    }
1367    node_next(page_insert_head) = page_insert_head;
1368    tex_update_first_marks();
1369    if (output_routine_par) {
1370        if (lmt_page_builder_state.dead_cycles >= max_dead_cycles_par) {
1371            /*tex Explain that too many dead cycles have occurred in a row. */
1372            tex_handle_error(
1373                normal_error_type,
1374                "Output loop --- %i consecutive dead cycles",
1375                lmt_page_builder_state.dead_cycles,
1376                "I've concluded that your \\output is awry; it never does a \\shipout, so I'm\n"
1377                "shipping \\box\\outputbox out myself. Next time increase \\maxdeadcycles if you\n"
1378                "want me to be more patient!"
1379            );
1380        } else {
1381            /*tex Fire up the users output routine and |return|. */
1382            lmt_page_builder_state.output_active = 1;
1383            ++lmt_page_builder_state.dead_cycles;
1384            tex_push_nest();
1385            cur_list.mode = internal_vmode;
1386            cur_list.prev_depth = ignore_depth_criterion_par;
1387            cur_list.mode_line = -lmt_input_state.input_line;
1388            tex_begin_token_list(output_routine_par, output_text);
1389            tex_new_save_level(output_group);
1390            tex_normal_paragraph(output_par_context);
1391            tex_scan_left_brace();
1392            return;
1393        }
1394    }
1395    /*tex
1396        Perform the default output routine. The list of heldover insertions, running from |vlink
1397        (page_head)| to |page_tail|, must be moved to the contribution list when the user has
1398        specified no output routine.
1399    */
1400
1401    /* todo: double link */
1402
1403    if (node_next(page_head)) {
1404        if (node_next(contribute_head)) {
1405            node_next(lmt_page_builder_state.page_tail) = node_next(contribute_head);
1406        }
1407        else {
1408            contribute_tail = lmt_page_builder_state.page_tail;
1409        }
1410        node_next(contribute_head) = node_next(page_head);
1411        node_next(page_head) = null;
1412        lmt_page_builder_state.page_tail = page_head;
1413    }
1414    if (lmt_packaging_state.page_discards_head) {
1415        tex_flush_node_list(lmt_packaging_state.page_discards_head);
1416        lmt_packaging_state.page_discards_head = null;
1417    }
1418    if (box_register(output_box_par)) {
1419        tex_flush_node_list(box_register(output_box_par));
1420        box_register(output_box_par) = null;
1421    }
1422}
1423
1424/*tex
1425
1426    When the user's output routine finishes, it has constructed a vlist in internal vertical mode,
1427    and \TEX\ will do the following:
1428
1429*/
1430
1431void tex_resume_after_output(void)
1432{
1433    if (lmt_input_state.cur_input.loc || ((lmt_input_state.cur_input.token_type != output_text) && (lmt_input_state.cur_input.token_type != backed_up_text))) {
1434        /*tex Recover from an unbalanced output routine */
1435        tex_handle_error(
1436            normal_error_type,
1437            "Unbalanced output routine",
1438            "Your sneaky output routine has problematic {'s and/or }'s. I can't handle that\n"
1439            "very well; good luck."
1440        );
1441        /*tex Loops forever if reading from a file, since |null = min_halfword <= 0|. */
1442        do {
1443            tex_get_token();
1444        } while (lmt_input_state.cur_input.loc);
1445    }
1446    /*tex Conserve stack space in case more outputs are triggered. */
1447    tex_end_token_list();
1448    tex_end_paragraph(bottom_level_group, output_par_context); /*tex No |wrapped_up_paragraph| here. */
1449    tex_unsave();
1450    lmt_page_builder_state.output_active = 0;
1451    lmt_page_builder_state.insert_penalties = 0;
1452    /*tex Ensure that box |output_box| is empty after output. */
1453    if (box_register(output_box_par)) {
1454        tex_handle_error(
1455            normal_error_type,
1456            "Output routine didn't use all of \\box%i", output_box_par,
1457            "Your \\output commands should empty \\box\\outputbox, e.g., by saying\n"
1458            "'\\shipout\\box\\outputbox'. Proceed; I'll discard its present contents."
1459        );
1460        box_register(output_box_par) = tex_aux_delete_box_content(box_register(output_box_par));;
1461    }
1462    if (lmt_insert_state.storing == insert_storage_delay && tex_insert_stored()) {
1463        if (tracing_inserts_par > 0) {
1464            tex_print_levels();
1465            tex_print_str(lmt_insert_state.head ? "<delaying inserts>" : "<no inserts to delay>");
1466            if (lmt_insert_state.head && tracing_inserts_par > 1) {
1467                tex_show_node_list(lmt_insert_state.head, max_integer, max_integer);
1468            }
1469        }
1470        tex_try_couple_nodes(lmt_page_builder_state.page_tail, lmt_insert_state.head);
1471        lmt_page_builder_state.page_tail = lmt_insert_state.tail;
1472        lmt_insert_state.head = null;
1473        lmt_insert_state.tail = null;
1474    }
1475    if (cur_list.tail != cur_list.head) {
1476        /*tex Current list goes after heldover insertions. */
1477        tex_try_couple_nodes(lmt_page_builder_state.page_tail, node_next(cur_list.head));
1478        lmt_page_builder_state.page_tail = cur_list.tail;
1479    }
1480    if (node_next(page_head)) {
1481        /* Both go before heldover contributions. */
1482        if (! node_next(contribute_head)) {
1483            contribute_tail = lmt_page_builder_state.page_tail;
1484        }
1485        tex_try_couple_nodes(lmt_page_builder_state.page_tail, node_next(contribute_head));
1486        tex_try_couple_nodes(contribute_head, node_next(page_head));
1487        node_next(page_head) = null;
1488        lmt_page_builder_state.page_tail = page_head;
1489    }
1490    if (lmt_insert_state.storing == insert_storage_inject) {
1491        halfword h = node_next(contribute_head);
1492        while (h) {
1493            halfword n = node_next(h);
1494            if (node_type(h) == insert_node) {
1495                tex_try_couple_nodes(node_prev(h), n);
1496                tex_insert_restore(h);
1497            }
1498            h = n;
1499        }
1500        if (tracing_inserts_par > 0) {
1501            tex_print_levels();
1502            tex_print_str(lmt_insert_state.head ? "<storing inserts>" : "<no inserts to store>");
1503            if (lmt_insert_state.head && tracing_inserts_par > 1) {
1504                tex_show_node_list(lmt_insert_state.head, max_integer, max_integer);
1505            }
1506        }
1507    }
1508    lmt_insert_state.storing = insert_storage_ignore;
1509    tex_flush_node_list(lmt_packaging_state.page_discards_head);
1510    lmt_packaging_state.page_discards_head = null;
1511    tex_pop_nest();
1512    tex_build_page(after_output_page_context, 0);
1513}
1514