1% buildpage.w
2%
3% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25@ @c
26#define box(A) eqtb[box_base+(A)].hh.rh
27#define count(A) eqtb[count_base+(A)].hh.rh
28#undef skip
29#define skip(A) eqtb[skip_base+(A)].hh.rh
30#define dimen(A) eqtb[scaled_base+(A)].hh.rh
31
32#define vbadness int_par(vbadness_code)
33#define max_dead_cycles int_par(max_dead_cycles_code)
34#define output_box int_par(output_box_code)
35#define body_direction int_par(body_direction_code)
36#define holding_inserts int_par(holding_inserts_code)
37
38#define vsize dimen_par(vsize_code)
39#define vfuzz dimen_par(vfuzz_code)
40#define max_depth dimen_par(max_depth_code)
41#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code)
42
43#define output_routine equiv(output_routine_loc)
44#define split_top_skip glue_par(split_top_skip_code)
45
46#define prev_depth cur_list.prev_depth_field
47#define mode_line cur_list.ml_field
48#define mode cur_list.mode_field
49#define tail cur_list.tail_field
50#define head cur_list.head_field
51
52@ When \TeX\ appends new material to its main vlist in vertical mode, it uses
53a method something like |vsplit| to decide where a page ends, except that
54the calculations are done ``on line'' as new items come in.
55The main complication in this process is that insertions must be put
56into their boxes and removed from the vlist, in a more-or-less optimum manner.
57
58We shall use the term ``current page'' for that part of the main vlist that
59is being considered as a candidate for being broken off and sent to the
60user's output routine. The current page starts at |vlink(page_head)|, and
61it ends at |page_tail|.  We have |page_head=page_tail| if this list is empty.
62@^current page@>
63
64Utter chaos would reign if the user kept changing page specifications
65while a page is being constructed, so the page builder keeps the pertinent
66specifications frozen as soon as the page receives its first box or
67insertion.  The global variable |page_contents| is |empty| when the
68current page contains only mark nodes and content-less whatsit nodes; it
69is |inserts_only| if the page contains only insertion nodes in addition to
70marks and whatsits.  Glue nodes, kern nodes, and penalty nodes are
71discarded until a box or rule node appears, at which time |page_contents|
72changes to |box_there|.  As soon as |page_contents| becomes non-|empty|,
73the current |vsize| and |max_depth| are squirreled away into |page_goal|
74and |page_max_depth|; the latter values will be used until the page has
75been forwarded to the user's output routine. The \.{\\topskip} adjustment
76is made when |page_contents| changes to |box_there|.
77
78Although |page_goal| starts out equal to |vsize|, it is decreased by the
79scaled natural height-plus-depth of the insertions considered so far, and by
80the \.{\\skip} corrections for those insertions. Therefore it represents
81the size into which the non-inserted material should fit, assuming that
82all insertions in the current page have been made.
83
84The global variables |best_page_break| and |least_page_cost| correspond
85respectively to the local variables |best_place| and |least_cost| in the
86|vert_break| routine that we have already studied; i.e., they record the
87location and value of the best place currently known for breaking the
88current page. The value of |page_goal| at the time of the best break is
89stored in |best_size|.
90
91@c
92halfword page_tail;             /* the final node on the current page */
93int page_contents;              /* what is on the current page so far? */
94scaled page_max_depth;          /* maximum box depth on page being built */
95halfword best_page_break;       /* break here to get the best page known so far */
96int least_page_cost;            /* the score for this currently best page */
97scaled best_size;               /* its |page_goal| */
98
99
100@ The page builder has another data structure to keep track of insertions.
101This is a list of four-word nodes, starting and ending at |page_ins_head|.
102That is, the first element of the list is node |r@t$_1$@>=vlink(page_ins_head)|;
103node $r_j$ is followed by |r@t$_{j+1}$@>=vlink(r@t$_j$@>)|; and if there are
104|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of
105each node in this list refers to an insertion number; for example, `\.{\\insert
106250}' would correspond to a node whose |subtype| is |qi(250)|
107(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
108fields are in increasing order, and |subtype(page_ins_head)=65535|, so
109|page_ins_head| serves as a convenient sentinel
110at the end of the list. A record is present for each insertion number that
111appears in the current page.
112
113The |type| field in these nodes distinguishes two possibilities that
114might occur as we look ahead before deciding on the optimum page break.
115If |type(r)=inserting_node|, then |height(r)| contains the total of the
116height-plus-depth dimensions of the box and all its inserts seen so far.
117 |type(r)=split_up_node|, then no more insertions will be made into this box,
118because at least one previous insertion was too big to fit on the current
119page; |broken_ptr(r)| points to the node where that insertion will be
120split, if \TeX\ decides to split it, |broken_ins(r)| points to the
121insertion node that was tentatively split, and |height(r)| includes also the
122natural height plus depth of the part that would be split off.
123
124In both cases, |last_ins_ptr(r)| points to the last |ins_node|
125encountered for box |qo(subtype(r))| that would be at least partially
126inserted on the next page; and |best_ins_ptr(r)| points to the last
127such |ins_node| that should actually be inserted, to get the page with
128minimum badness among all page breaks considered so far. We have
129|best_ins_ptr(r)=null| if and only if no insertion for this box should
130be made to produce this optimum page.
131
132
133
134@ Pages are built by appending nodes to the current list in \TeX's
135vertical mode, which is at the outermost level of the semantic nest. This
136vlist is split into two parts; the ``current page'' that we have been
137talking so much about already, and the ``contribution list'' that receives
138new nodes as they are created.  The current page contains everything that
139the page builder has accounted for in its data structures, as described
140above, while the contribution list contains other things that have been
141generated by other parts of \TeX\ but have not yet been
142seen by the page builder.
143The contribution list starts at |vlink(contrib_head)|, and it ends at the
144current node in \TeX's vertical mode.
145
146When \TeX\ has appended new material in vertical mode, it calls the procedure
147|build_page|, which tries to catch up by moving nodes from the contribution
148list to the current page. This procedure will succeed in its goal of
149emptying the contribution list, unless a page break is discovered, i.e.,
150unless the current page has grown to the point where the optimum next
151page break has been determined. In the latter case, the nodes after the
152optimum break will go back onto the contribution list, and control will
153effectively pass to the user's output routine.
154
155We make |type(page_head)=glue_node|, so that an initial glue node on
156the current page will not be considered a valid breakpoint.
157
158@c
159void initialize_buildpage(void)
160{
161    subtype(page_ins_head) = 65535;
162    type(page_ins_head) = split_up_node;
163    vlink(page_ins_head) = page_ins_head;
164
165    type(page_head) = glue_node;
166    subtype(page_head) = normal;
167}
168
169
170@ An array |page_so_far| records the heights and depths of everything
171on the current page. This array contains six |scaled| numbers, like the
172similar arrays already considered in |line_break| and |vert_break|; and it
173also contains |page_goal| and |page_depth|, since these values are
174all accessible to the user via |set_page_dimen| commands. The
175value of |page_so_far[1]| is also called |page_total|.  The stretch
176and shrink components of the \.{\\skip} corrections for each insertion are
177included in |page_so_far|, but the natural space components of these
178corrections are not, since they have been subtracted from |page_goal|.
179
180The variable |page_depth| records the depth of the current page; it has been
181adjusted so that it is at most |page_max_depth|. The variable
182|last_glue| points to the glue specification of the most recent node
183contributed from the contribution list, if this was a glue node; otherwise
184|last_glue=max_halfword|. (If the contribution list is nonempty,
185however, the value of |last_glue| is not necessarily accurate.)
186The variables |last_penalty|, |last_kern|, and |last_node_type|
187are similar.  And
188finally, |insert_penalties| holds the sum of the penalties associated with
189all split and floating insertions.
190
191@c
192scaled page_so_far[8];          /* height and glue of the current page */
193halfword last_glue;             /* used to implement \.{\\lastskip} */
194int last_penalty;               /* used to implement \.{\\lastpenalty} */
195scaled last_kern;               /* used to implement \.{\\lastkern} */
196int last_node_type;             /* used to implement \.{\\lastnodetype} */
197int insert_penalties;           /* sum of the penalties for held-over insertions */
198
199#define print_plus(A,B) do {			\
200	if (page_so_far[(A)]!=0) {		\
201	    tprint(" plus ");			\
202	    print_scaled(page_so_far[(A)]);	\
203	    tprint((B));			\
204	}					\
205    } while (0)
206
207void print_totals(void)
208{
209    print_scaled(page_total);
210    print_plus(2, "");
211    print_plus(3, "fil");
212    print_plus(4, "fill");
213    print_plus(5, "filll");
214    if (page_shrink != 0) {
215        tprint(" minus ");
216        print_scaled(page_shrink);
217    }
218}
219
220
221@ Here is a procedure that is called when the |page_contents| is changing
222from |empty| to |inserts_only| or |box_there|.
223
224@c
225#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
226#define set_page_so_far_zero(A) page_so_far[(A)]=0
227
228void freeze_page_specs(int s)
229{
230    page_contents = s;
231    page_goal = vsize;
232    page_max_depth = max_depth;
233    page_depth = 0;
234    do_all_six(set_page_so_far_zero);
235    least_page_cost = awful_bad;
236    if (int_par(tracing_pages_code) > 0) {
237        begin_diagnostic();
238        tprint_nl("%% goal height=");
239        print_scaled(page_goal);
240        tprint(", max depth=");
241        print_scaled(page_max_depth);
242        end_diagnostic(false);
243    }
244}
245
246
247@ The global variable |output_active| is true during the time the
248user's output routine is driving \TeX.
249
250@c
251boolean output_active;          /* are we in the midst of an output routine? */
252
253
254@ The page builder is ready to start a fresh page if we initialize
255the following state variables. (However, the page insertion list is initialized
256elsewhere.)
257
258@c
259void start_new_page(void)
260{
261    page_contents = empty;
262    page_tail = page_head;
263    vlink(page_head) = null;
264    last_glue = max_halfword;
265    last_penalty = 0;
266    last_kern = 0;
267    last_node_type = -1;
268    page_depth = 0;
269    page_max_depth = 0;
270}
271
272
273@ At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|),
274or an insertion box is supposed to be ready to accept a vertical list.
275If not, an error message is printed, and the following subroutine
276flushes the unwanted contents, reporting them to the user.
277
278@c
279static void box_error(int n)
280{
281    error();
282    begin_diagnostic();
283    tprint_nl("The following box has been deleted:");
284    show_box(box(n));
285    end_diagnostic(true);
286    flush_node_list(box(n));
287    box(n) = null;
288}
289
290
291@ The following procedure guarantees that a given box register
292does not contain an \.{\\hbox}.
293
294@c
295static void ensure_vbox(int n)
296{
297    halfword p;                 /* the box register contents */
298    p = box(n);
299    if (p != null && type(p) == hlist_node) {
300        print_err("Insertions can only be added to a vbox");
301        help3("Tut tut: You're trying to \\insert into a",
302              "\\box register that now contains an \\hbox.",
303              "Proceed, and I'll discard its present contents.");
304        box_error(n);
305    }
306}
307
308
309@ \TeX\ is not always in vertical mode at the time |build_page|
310is called; the current mode reflects what \TeX\ should return to, after
311the contribution list has been emptied. A call on |build_page| should
312be immediately followed by `|goto big_switch|', which is \TeX's central
313control point.
314
315@c
316void build_page(void)
317{                               /* append contributions to the current page */
318    halfword p;                 /* the node being appended */
319    halfword q, r;              /* nodes being examined */
320    int b, c;                   /* badness and cost of current page */
321    int pi;                     /* penalty to be added to the badness */
322    int n;                      /* insertion box number */
323    scaled delta, h, w;         /* sizes used for insertion calculations */
324    pi = 0;
325    if ((vlink(contrib_head) == null) || output_active)
326        return;
327    do {
328      CONTINUE:
329        p = vlink(contrib_head);
330        /* Update the values of |last_glue|, |last_penalty|, and |last_kern| */
331        if (last_glue != max_halfword) {
332            delete_glue_ref(last_glue);
333            last_glue = max_halfword;
334        }
335        last_penalty = 0;
336        last_kern = 0;
337        last_node_type = type(p) + 1;
338        if (type(p) == glue_node) {
339            last_glue = glue_ptr(p);
340            add_glue_ref(last_glue);
341        } else if (type(p) == penalty_node) {
342            last_penalty = penalty(p);
343        } else if (type(p) == kern_node) {
344            last_kern = width(p);
345        }
346
347        /* Move node |p| to the current page; if it is time for a page break,
348           put the nodes following the break back onto the contribution list,
349           and |return| to the users output routine if there is one */
350
351        /* The code here is an example of a many-way switch into routines that
352           merge together in different places. Some people call this unstructured
353           programming, but the author doesn't see much wrong with it, as long as
354           the various labels have a well-understood meaning.
355         */
356        /* If the current page is empty and node |p| is to be deleted, |goto done1|;
357           otherwise use node |p| to update the state of the current page;
358           if this node is an insertion, |goto contribute|; otherwise if this node
359           is not a legal breakpoint, |goto contribute| or |update_heights|;
360           otherwise set |pi| to the penalty associated with this breakpoint */
361        /* The title of this section is already so long, it seems best to avoid
362           making it more accurate but still longer, by mentioning the fact that a
363           kern node at the end of the contribution list will not be contributed until
364           we know its successor. */
365        switch (type(p)) {
366        case hlist_node:
367        case vlist_node:
368        case rule_node:
369            if (page_contents < box_there) {
370                /* Initialize the current page, insert the \.{\\topskip} glue
371                   ahead of |p|, and |goto continue| */
372                if (page_contents == empty)
373                    freeze_page_specs(box_there);
374                else
375                    page_contents = box_there;
376                q = new_skip_param(top_skip_code);      /* now |temp_ptr=glue_ptr(q)| */
377                if ((type(p) == hlist_node) && is_mirrored(body_direction)) {
378                    if (width(temp_ptr) > depth(p))
379                        width(temp_ptr) = width(temp_ptr) - depth(p);
380                    else
381                        width(temp_ptr) = 0;
382                } else {
383                    if (width(temp_ptr) > height(p))
384                        width(temp_ptr) = width(temp_ptr) - height(p);
385                    else
386                        width(temp_ptr) = 0;
387                }
388                couple_nodes(q, p);
389                couple_nodes(contrib_head, q);
390                goto CONTINUE;
391
392            } else {
393                /* Prepare to move a box or rule node to the current page,
394                   then |goto contribute| */
395                if ((type(p) == hlist_node) && is_mirrored(body_direction)) {
396                    page_total = page_total + page_depth + depth(p);
397                    page_depth = height(p);
398                } else {
399                    page_total = page_total + page_depth + height(p);
400                    page_depth = depth(p);
401                }
402                goto CONTRIBUTE;
403
404            }
405            break;
406        case whatsit_node:
407            /* Prepare to move whatsit |p| to the current page,
408               then |goto contribute| */
409            if ((subtype(p) == pdf_refxform_node)
410                || (subtype(p) == pdf_refximage_node)) {
411                page_total = page_total + page_depth + height(p);
412                page_depth = depth(p);
413            }
414            goto CONTRIBUTE;
415
416            break;
417        case glue_node:
418            if (page_contents < box_there)
419                goto DONE1;
420            else if (precedes_break(page_tail))
421                pi = 0;
422            else
423                goto UPDATE_HEIGHTS;
424            break;
425        case kern_node:
426            if (page_contents < box_there)
427                goto DONE1;
428            else if (vlink(p) == null)
429                goto EXIT;
430            else if (type(vlink(p)) == glue_node)
431                pi = 0;
432            else
433                goto UPDATE_HEIGHTS;
434            break;
435        case penalty_node:
436            if (page_contents < box_there)
437                goto DONE1;
438            else
439                pi = penalty(p);
440            break;
441        case mark_node:
442            goto CONTRIBUTE;
443            break;
444        case ins_node:
445            /* Append an insertion to the current page and |goto contribute| */
446            if (page_contents == empty)
447                freeze_page_specs(inserts_only);
448            n = subtype(p);
449            r = page_ins_head;
450            while (n >= subtype(vlink(r)))
451                r = vlink(r);
452            if (subtype(r) != n) {
453                /* Create a page insertion node with |subtype(r)=qi(n)|, and
454                   include the glue correction for box |n| in the
455                   current page state */
456                /* We take note of the value of \.{\\skip} |n| and the height plus depth
457                   of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
458                   encountered for a new page. A user who changes the contents of \.{\\box}~|n|
459                   after that first \.{\\insert}~|n| had better be either extremely careful
460                   or extremely lucky, or both. */
461
462                q = new_node(inserting_node, n);
463                try_couple_nodes(q, vlink(r));
464                couple_nodes(r, q);
465                r = q;
466                ensure_vbox(n);
467                if (box(n) == null)
468                    height(r) = 0;
469                else
470                    height(r) = height(box(n)) + depth(box(n));
471                best_ins_ptr(r) = null;
472                q = skip(n);
473                if (count(n) == 1000)
474                    h = height(r);
475                else
476                    h = x_over_n(height(r), 1000) * count(n);
477                page_goal = page_goal - h - width(q);
478                if (stretch_order(q) > 1)
479                    page_so_far[1 + stretch_order(q)] =
480                        page_so_far[1 + stretch_order(q)] + stretch(q);
481                else
482                    page_so_far[2 + stretch_order(q)] =
483                        page_so_far[2 + stretch_order(q)] + stretch(q);
484                page_shrink = page_shrink + shrink(q);
485                if ((shrink_order(q) != normal) && (shrink(q) != 0)) {
486                    print_err("Infinite glue shrinkage inserted from \\skip");
487                    print_int(n);
488                    help3
489                        ("The correction glue for page breaking with insertions",
490                         "must have finite shrinkability. But you may proceed,",
491                         "since the offensive shrinkability has been made finite.");
492                    error();
493                }
494
495            }
496            if (type(r) == split_up_node) {
497                insert_penalties = insert_penalties + float_cost(p);
498            } else {
499                last_ins_ptr(r) = p;
500                delta = page_goal - page_total - page_depth + page_shrink;
501                /* this much room is left if we shrink the maximum */
502                if (count(n) == 1000)
503                    h = height(p);
504                else
505                    h = x_over_n(height(p), 1000) * count(n);   /* this much room is needed */
506                if (((h <= 0) || (h <= delta))
507                    && (height(p) + height(r) <= dimen(n))) {
508                    page_goal = page_goal - h;
509                    height(r) = height(r) + height(p);
510                } else {
511                    /* Find the best way to split the insertion, and change
512                       |type(r)| to |split_up_node| */
513                    /* Here is the code that will split a long footnote between pages, in an
514                       emergency. The current situation deserves to be recapitulated: Node |p|
515                       is an insertion into box |n|; the insertion will not fit, in its entirety,
516                       either because it would make the total contents of box |n| greater than
517                       \.{\\dimen} |n|, or because it would make the incremental amount of growth
518                       |h| greater than the available space |delta|, or both. (This amount |h| has
519                       been weighted by the insertion scaling factor, i.e., by \.{\\count} |n|
520                       over 1000.) Now we will choose the best way to break the vlist of the
521                       insertion, using the same criteria as in the \.{\\vsplit} operation.
522                     */
523                    if (count(n) <= 0) {
524                        w = max_dimen;
525                    } else {
526                        w = page_goal - page_total - page_depth;
527                        if (count(n) != 1000)
528                            w = x_over_n(w, count(n)) * 1000;
529                    }
530                    if (w > dimen(n) - height(r))
531                        w = dimen(n) - height(r);
532                    q = vert_break(ins_ptr(p), w, depth(p));
533                    height(r) = height(r) + best_height_plus_depth;
534                    if (int_par(tracing_pages_code) > 0) {
535                        /* Display the insertion split cost */
536                        begin_diagnostic();
537                        tprint_nl("% split");
538                        print_int(n);
539                        tprint(" to ");
540                        print_scaled(w);
541                        print_char(',');
542                        print_scaled(best_height_plus_depth);
543                        tprint(" p=");
544                        if (q == null)
545                            print_int(eject_penalty);
546                        else if (type(q) == penalty_node)
547                            print_int(penalty(q));
548                        else
549                            print_char('0');
550                        end_diagnostic(false);
551
552                    }
553                    if (count(n) != 1000)
554                        best_height_plus_depth =
555                            x_over_n(best_height_plus_depth, 1000) * count(n);
556                    page_goal = page_goal - best_height_plus_depth;
557                    type(r) = split_up_node;
558                    broken_ptr(r) = q;
559                    broken_ins(r) = p;
560                    if (q == null)
561                        insert_penalties = insert_penalties + eject_penalty;
562                    else if (type(q) == penalty_node)
563                        insert_penalties = insert_penalties + penalty(q);
564                }
565            }
566            goto CONTRIBUTE;
567
568            break;
569        default:
570            fprintf(stderr, "type(p)=%d\n", type(p));
571            confusion("page");
572            break;
573        }
574
575        /* Check if node |p| is a new champion breakpoint; then if it is time for
576           a page break, prepare for output, and either fire up the users
577           output routine and |return| or ship out the page and |goto done| */
578
579        if (pi < inf_penalty) {
580            /* Compute the badness, |b|, of the current page,
581               using |awful_bad| if the box is too full */
582            if (page_total < page_goal) {
583                if ((page_so_far[3] != 0) || (page_so_far[4] != 0) ||
584                    (page_so_far[5] != 0))
585                    b = 0;
586                else
587                    b = badness(page_goal - page_total, page_so_far[2]);
588            } else if (page_total - page_goal > page_shrink) {
589                b = awful_bad;
590            } else {
591                b = badness(page_total - page_goal, page_shrink);
592            }
593
594            if (b < awful_bad) {
595                if (pi <= eject_penalty)
596                    c = pi;
597                else if (b < inf_bad)
598                    c = b + pi + insert_penalties;
599                else
600                    c = deplorable;
601            } else {
602                c = b;
603            }
604            if (insert_penalties >= 10000)
605                c = awful_bad;
606            if (int_par(tracing_pages_code) > 0) {
607                /* Display the page break cost */
608                begin_diagnostic();
609                tprint_nl("%");
610                tprint(" t=");
611                print_totals();
612                tprint(" g=");
613                print_scaled(page_goal);
614                tprint(" b=");
615                if (b == awful_bad)
616                    print_char('*');
617                else
618                    print_int(b);
619                tprint(" p=");
620                print_int(pi);
621                tprint(" c=");
622                if (c == awful_bad)
623                    print_char('*');
624                else
625                    print_int(c);
626                if (c <= least_page_cost)
627                    print_char('#');
628                end_diagnostic(false);
629
630            }
631            if (c <= least_page_cost) {
632                best_page_break = p;
633                best_size = page_goal;
634                least_page_cost = c;
635                r = vlink(page_ins_head);
636                while (r != page_ins_head) {
637                    best_ins_ptr(r) = last_ins_ptr(r);
638                    r = vlink(r);
639                }
640            }
641            if ((c == awful_bad) || (pi <= eject_penalty)) {
642                fire_up(p);     /* output the current page at the best place */
643                if (output_active)
644                    goto EXIT;  /* user's output routine will act */
645                goto DONE;      /* the page has been shipped out by default output routine */
646            }
647        }
648
649        if ((type(p) < glue_node) || (type(p) > kern_node))
650            goto CONTRIBUTE;
651
652      UPDATE_HEIGHTS:          /* go here to record glue in the |active_height| table */
653
654        /* Update the current page measurements with respect to the
655           glue or kern specified by node~|p| */
656        if (type(p) == kern_node) {
657            q = p;
658        } else {
659            q = glue_ptr(p);
660            if (stretch_order(q) > 1)
661                page_so_far[1 + stretch_order(q)] =
662                    page_so_far[1 + stretch_order(q)] + stretch(q);
663            else
664                page_so_far[2 + stretch_order(q)] =
665                    page_so_far[2 + stretch_order(q)] + stretch(q);
666            page_shrink = page_shrink + shrink(q);
667            if ((shrink_order(q) != normal) && (shrink(q) != 0)) {
668                print_err("Infinite glue shrinkage found on current page");
669                help4("The page about to be output contains some infinitely",
670                      "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
671                      "Such glue doesn't belong there; but you can safely proceed,",
672                      "since the offensive shrinkability has been made finite.");
673                error();
674                r = new_spec(q);
675                shrink_order(r) = normal;
676                delete_glue_ref(q);
677                glue_ptr(p) = r;
678                q = r;
679            }
680        }
681        page_total = page_total + page_depth + width(q);
682        page_depth = 0;
683
684      CONTRIBUTE:              /* go here to link a node into the current page */
685
686        /* Make sure that |page_max_depth| is not exceeded */
687        if (page_depth > page_max_depth) {
688            page_total = page_total + page_depth - page_max_depth;
689            page_depth = page_max_depth;
690        }
691
692        /* Link node |p| into the current page and |goto done| */
693        couple_nodes(page_tail, p);
694        page_tail = p;
695        try_couple_nodes(contrib_head,vlink(p));
696        vlink(p) = null;
697        goto DONE;
698      DONE1:
699        /* Recycle node |p| */
700        try_couple_nodes(contrib_head,vlink(p));
701        vlink(p) = null;
702        if (int_par(saving_vdiscards_code) > 0) {
703            if (page_disc == null) {
704                page_disc = p;
705            } else {
706                couple_nodes(tail_page_disc, p);
707            }
708            tail_page_disc = p;
709        } else {
710            flush_node_list(p);
711        }
712      DONE:
713        ;
714    } while (vlink(contrib_head) != null);
715    /* Make the contribution list empty by setting its tail to |contrib_head| */
716    contrib_tail = contrib_head;
717  EXIT:
718    ;
719}
720
721@ When the page builder has looked at as much material as could appear before
722the next page break, it makes its decision. The break that gave minimum
723badness will be used to put a completed ``page'' into box \.{\\outputbox}, with insertions
724appended to their other boxes.
725
726We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
727program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|;
728it also knows that |bot_mark(x)=null| implies |top_mark(x)=first_mark(x)=null|.
729
730The |fire_up| subroutine prepares to output the current page at the best
731place; then it fires up the user's output routine, if there is one,
732or it simply ships out the page. There is one parameter, |c|, which represents
733the node that was being contributed to the page when the decision to
734force an output was made.
735
736@c
737void fire_up(halfword c)
738{
739    halfword p, q, r, s;        /* nodes being examined and/or changed */
740    halfword prev_p;            /* predecessor of |p| */
741    int n;                      /* insertion box number */
742    boolean wait;               /* should the present insertion be held over? */
743    int save_vbadness;          /* saved value of |vbadness| */
744    scaled save_vfuzz;          /* saved value of |vfuzz| */
745    halfword save_split_top_skip;       /* saved value of |split_top_skip| */
746    halfword i;                 /* for looping through the marks */
747
748    /* Set the value of |output_penalty| */
749    if (type(best_page_break) == penalty_node) {
750        geq_word_define(int_base + output_penalty_code,
751                        penalty(best_page_break));
752        penalty(best_page_break) = inf_penalty;
753    } else {
754        geq_word_define(int_base + output_penalty_code, inf_penalty);
755    }
756
757    for (i = 0; i <= biggest_used_mark; i++) {
758        if (bot_mark(i) != null) {
759            if (top_mark(i) != null)
760                delete_token_ref(top_mark(i));
761            set_top_mark(i, bot_mark(i));
762            add_token_ref(top_mark(i));
763            delete_first_mark(i);
764        }
765    }
766    /* Put the optimal current page into box |output_box|, update |first_mark| and
767       |bot_mark|, append insertions to their boxes, and put the
768       remaining nodes back on the contribution list; */
769
770    /* As the page is finally being prepared for output,
771       pointer |p| runs through the vlist, with |prev_p| trailing behind;
772       pointer |q| is the tail of a list of insertions that
773       are being held over for a subsequent page. */
774
775    if (c == best_page_break)
776        best_page_break = null; /* |c| not yet linked in */
777    /* Ensure that box |output_box| is empty before output */
778    if (box(output_box) != null) {
779        print_err("\\box");
780        print_int(output_box);
781        tprint(" is not void");
782        help2("You shouldn't use \\box\\outputbox except in \\output routines.",
783              "Proceed, and I'll discard its present contents.");
784        box_error(output_box);
785    }
786
787    insert_penalties = 0;       /* this will count the number of insertions held over */
788    save_split_top_skip = split_top_skip;
789    if (holding_inserts <= 0) {
790        /* Prepare all the boxes involved in insertions to act as queues */
791        /* If many insertions are supposed to go into the same box, we want to know
792           the position of the last node in that box, so that we don't need to waste time
793           when linking further information into it. The |last_ins_ptr| fields of the
794           page insertion nodes are therefore used for this purpose during the
795           packaging phase. */
796
797        r = vlink(page_ins_head);
798        while (r != page_ins_head) {
799            if (best_ins_ptr(r) != null) {
800                n = subtype(r);
801                ensure_vbox(n);
802                if (box(n) == null)
803                    box(n) = new_null_box();
804                p = box(n) + list_offset;
805                while (vlink(p) != null)
806                    p = vlink(p);
807                last_ins_ptr(r) = p;
808            }
809            r = vlink(r);
810        }
811
812    }
813    q = hold_head;
814    vlink(q) = null;
815    prev_p = page_head;
816    p = vlink(prev_p);
817    while (p != best_page_break) {
818        if (type(p) == ins_node) {
819            if (holding_inserts <= 0) {
820                /* Either insert the material specified by node |p| into the
821                   appropriate box, or hold it for the next page;
822                   also delete node |p| from the current page */
823                /* We will set |best_ins_ptr:=null| and package the box corresponding to
824                   insertion node~|r|, just after making the final insertion into that box.
825                   If this final insertion is `|split_up_node|', the remainder after splitting
826                   and pruning (if any) will be carried over to the next page. */
827                r = vlink(page_ins_head);
828                while (subtype(r) != subtype(p))
829                    r = vlink(r);
830                if (best_ins_ptr(r) == null) {
831                    wait = true;
832                } else {
833                    wait = false;
834                    s = last_ins_ptr(r);
835                    vlink(s) = ins_ptr(p);
836                    if (best_ins_ptr(r) == p) {
837                        /* Wrap up the box specified by node |r|, splitting node |p| if
838                           called for; set |wait:=true| if node |p| holds a remainder after
839                           splitting */
840                        if (type(r) == split_up_node) {
841                            if ((broken_ins(r) == p) && (broken_ptr(r) != null)) {
842                                while (vlink(s) != broken_ptr(r))
843                                    s = vlink(s);
844                                vlink(s) = null;
845                                split_top_skip = split_top_ptr(p);
846                                ins_ptr(p) =
847                                    prune_page_top(broken_ptr(r), false);
848                                if (ins_ptr(p) != null) {
849                                    temp_ptr =
850                                        vpack(ins_ptr(p), 0, additional, -1);
851                                    height(p) =
852                                        height(temp_ptr) + depth(temp_ptr);
853                                    list_ptr(temp_ptr) = null;
854                                    flush_node(temp_ptr);
855                                    wait = true;
856                                }
857                            }
858                        }
859                        best_ins_ptr(r) = null;
860                        n = subtype(r);
861                        temp_ptr = list_ptr(box(n));
862                        list_ptr(box(n)) = null;
863                        flush_node(box(n));
864                        box(n) = vpack(temp_ptr, 0, additional, body_direction);
865
866                    } else {
867                        while (vlink(s) != null)
868                            s = vlink(s);
869                        last_ins_ptr(r) = s;
870                    }
871                }
872                /* Either append the insertion node |p| after node |q|, and remove it
873                   from the current page, or delete |node(p)| */
874                try_couple_nodes(prev_p, vlink(p));
875                vlink(p) = null;
876                if (wait) {
877                    couple_nodes(q, p);
878                    q = p;
879                    incr(insert_penalties);
880                } else {
881                    ins_ptr(p) = null;
882                    flush_node(p);
883                }
884                p = prev_p;
885
886            }
887        } else if (type(p) == mark_node) {
888            /* Update the values of |first_mark| and |bot_mark| */
889            if (first_mark(mark_class(p)) == null) {
890                set_first_mark(mark_class(p), mark_ptr(p));
891                add_token_ref(first_mark(mark_class(p)));
892            }
893            if (bot_mark(mark_class(p)) != null)
894                delete_token_ref(bot_mark(mark_class(p)));
895            set_bot_mark(mark_class(p), mark_ptr(p));
896            add_token_ref(bot_mark(mark_class(p)));
897
898        }
899        prev_p = p;
900        p = vlink(prev_p);
901    }
902    split_top_skip = save_split_top_skip;
903    /* Break the current page at node |p|, put it in box~|output_box|,
904       and put the remaining nodes on the contribution list */
905    /* When the following code is executed, the current page runs from node
906       |vlink(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail|
907       are to be placed back at the front of the contribution list. Furthermore
908       the heldover insertions appear in a list from |vlink(hold_head)| to |q|; we
909       will put them into the current page list for safekeeping while the user's
910       output routine is active.  We might have |q=hold_head|; and |p=null| if
911       and only if |prev_p=page_tail|. Error messages are suppressed within
912       |vpackage|, since the box might appear to be overfull or underfull simply
913       because the stretch and shrink from the \.{\\skip} registers for inserts
914       are not actually present in the box. */
915
916    if (p != null) {
917        if (vlink(contrib_head) == null) {
918            contrib_tail = page_tail;
919        }
920        couple_nodes(page_tail,vlink(contrib_head));
921        couple_nodes(contrib_head, p);
922        vlink(prev_p) = null;
923    }
924    save_vbadness = vbadness;
925    vbadness = inf_bad;
926    save_vfuzz = vfuzz;
927    vfuzz = max_dimen;          /* inhibit error messages */
928    box(output_box) =
929        filtered_vpackage(vlink(page_head), best_size, exactly, page_max_depth,
930                          output_group, body_direction);
931    vbadness = save_vbadness;
932    vfuzz = save_vfuzz;
933    if (last_glue != max_halfword)
934        delete_glue_ref(last_glue);
935    /* Start a new current page */
936    start_new_page();           /* this sets |last_glue:=max_halfword| */
937    if (q != hold_head) {
938        vlink(page_head) = vlink(hold_head);
939        page_tail = q;
940    }
941
942    /* Delete the page-insertion nodes */
943    r = vlink(page_ins_head);
944    while (r != page_ins_head) {
945        q = vlink(r);
946        flush_node(r);
947        r = q;
948    }
949    vlink(page_ins_head) = page_ins_head;
950
951    for (i = 0; i <= biggest_used_mark; i++) {
952        if ((top_mark(i) != null) && (first_mark(i) == null)) {
953            set_first_mark(i, top_mark(i));
954            add_token_ref(top_mark(i));
955        }
956    }
957    if (output_routine != null) {
958        if (dead_cycles >= max_dead_cycles) {
959            /* Explain that too many dead cycles have occurred in a row */
960            print_err("Output loop---");
961            print_int(dead_cycles);
962            tprint(" consecutive dead cycles");
963            help3("I've concluded that your \\output is awry; it never does a",
964                  "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time",
965                  "increase \\maxdeadcycles if you want me to be more patient!");
966            error();
967
968        } else {
969            /* Fire up the users output routine and |return| */
970            output_active = true;
971            incr(dead_cycles);
972            push_nest();
973            mode = -vmode;
974            prev_depth = pdf_ignored_dimen;
975            mode_line = -line;
976            begin_token_list(output_routine, output_text);
977            new_save_level(output_group);
978            normal_paragraph();
979            scan_left_brace();
980            return;
981
982        }
983    }
984    /* Perform the default output routine */
985    /* The list of heldover insertions, running from |vlink(page_head)| to
986       |page_tail|, must be moved to the contribution list when the user has
987       specified no output routine. */
988    if (vlink(page_head) != null) {
989        if (vlink(contrib_head) == null) {
990            contrib_tail = page_tail;
991        } else {
992            vlink(page_tail) = vlink(contrib_head);
993        }
994        vlink(contrib_head) = vlink(page_head);
995        vlink(page_head) = null;
996        page_tail = page_head;
997    }
998    flush_node_list(page_disc);
999    page_disc = null;
1000    ship_out(static_pdf, box(output_box), SHIPPING_PAGE);
1001    box(output_box) = null;
1002}
1003
1004
1005@ When the user's output routine finishes, it has constructed a vlist
1006in internal vertical mode, and \TeX\ will do the following:
1007
1008@c
1009void resume_after_output(void)
1010{
1011    if ((iloc != null)
1012        || ((token_type != output_text) && (token_type != backed_up))) {
1013        /* Recover from an unbalanced output routine */
1014        print_err("Unbalanced output routine");
1015        help2("Your sneaky output routine has problematic {'s and/or }'s.",
1016              "I can't handle that very well; good luck.");
1017        error();
1018        do {
1019            get_token();
1020        } while (iloc != null);
1021        /* loops forever if reading from a file, since |null=min_halfword<=0| */
1022
1023    }
1024    end_token_list();           /* conserve stack space in case more outputs are triggered */
1025    end_graf(bottom_level);
1026    unsave();
1027    output_active = false;
1028    insert_penalties = 0;
1029    /* Ensure that box |output_box| is empty after output */
1030    if (box(output_box) != null) {
1031        print_err("Output routine didn't use all of \\box");
1032        print_int(output_box);
1033        help3("Your \\output commands should empty \\box\\outputbox,",
1034              "e.g., by saying `\\shipout\\box\\outputbox'.",
1035              "Proceed; I'll discard its present contents.");
1036        box_error(output_box);
1037    }
1038
1039    if (tail != head) {         /* current list goes after heldover insertions */
1040        try_couple_nodes(page_tail, vlink(head));
1041        page_tail = tail;
1042    }
1043    if (vlink(page_head) != null) {     /* and both go before heldover contributions */
1044        if (vlink(contrib_head) == null)
1045            contrib_tail = page_tail;
1046        try_couple_nodes(page_tail, vlink(contrib_head));
1047        try_couple_nodes(contrib_head, vlink(page_head));
1048        vlink(page_head) = null;
1049        page_tail = page_head;
1050    }
1051    flush_node_list(page_disc);
1052    page_disc = null;
1053    pop_nest();
1054    //lua_node_filter_s(buildpage_filter_callback, "after_output");
1055    lua_node_filter_s(buildpage_filter_callback,lua_key_index(after_output));
1056   build_page();
1057
1058}
1059