~ubuntu-branches/ubuntu/saucy/luatex/saucy

« back to all changes in this revision

Viewing changes to source/texk/web2c/luatexdir/tex/packaging.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Preining
  • Date: 2009-12-25 09:47:05 UTC
  • mfrom: (1.1.9 upstream) (4.2.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091225094705-y33rpflo8t4u9nag
Tags: 0.50.0-1
* new upstream release
* disable fix-hurd-ftbfs patch, included upstream
* disable upstram-fixes, included upstream
* disable ubuntu_libpoppler-0.11, not needed anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* packaging.c
 
2
 
 
3
   Copyright 2009 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
#include <ptexlib.h>
 
21
 
 
22
static const char _svn_version[] =
 
23
    "$Id: packaging.c 3261 2009-12-18 11:38:21Z taco $"
 
24
    "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.50.0/source/texk/web2c/luatexdir/tex/packaging.c $";
 
25
 
 
26
#define scan_normal_dimen() scan_dimen(false,false,false)
 
27
 
 
28
#define prev_depth      cur_list.aux_field.cint
 
29
#define space_factor    cur_list.aux_field.hh.lhfield
 
30
#define box(A) eqtb[box_base+(A)].hh.rh
 
31
 
 
32
#define text_direction int_par(text_direction_code)
 
33
#define body_direction int_par(body_direction_code)
 
34
#define every_hbox equiv(every_hbox_loc)
 
35
#define every_vbox equiv(every_vbox_loc)
 
36
#define box_max_depth dimen_par(box_max_depth_code)
 
37
 
 
38
/*
 
39
We're essentially done with the parts of \TeX\ that are concerned with
 
40
the input (|get_next|) and the output (|ship_out|). So it's time to
 
41
get heavily into the remaining part, which does the real work of typesetting.
 
42
 
 
43
After lists are constructed, \TeX\ wraps them up and puts them into boxes.
 
44
Two major subroutines are given the responsibility for this task: |hpack|
 
45
applies to horizontal lists (hlists) and |vpack| applies to vertical lists
 
46
(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions
 
47
of the resulting boxes, and to adjust the glue if one of those dimensions
 
48
is pre-specified. The computed sizes normally enclose all of the material
 
49
inside the new box; but some items may stick out if negative glue is used,
 
50
if the box is overfull, or if a \.{\\vbox} includes other boxes that have
 
51
been shifted left.
 
52
 
 
53
The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node|
 
54
for a box containing the hlist that starts at |p|. Parameter |w| specifies
 
55
a width; and parameter |m| is either `|exactly|' or `|additional|'.  Thus,
 
56
|hpack(p,w,exactly)| produces a box whose width is exactly |w|, while
 
57
|hpack(p,w,additional)| yields a box whose width is the natural width plus
 
58
|w|.  It is convenient to define a macro called `|natural|' to cover the
 
59
most common case, so that we can say |hpack(p,natural)| to get a box that
 
60
has the natural width of list |p|.
 
61
 
 
62
Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a
 
63
box containing the vlist that starts at |p|. In this case |w| represents
 
64
a height instead of a width; the parameter |m| is interpreted as in |hpack|.
 
65
*/
 
66
 
 
67
/*
 
68
The parameters to |hpack| and |vpack| correspond to \TeX's primitives
 
69
like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note
 
70
that `\.{\\hbox}' with no dimension following it is equivalent to
 
71
`\.{\\hbox} \.{spread} \.{0pt}'.  The |scan_spec| subroutine scans such
 
72
constructions in the user's input, including the mandatory left brace that
 
73
follows them, and it puts the specification onto |save_stack| so that the
 
74
desired box can later be obtained by executing the following code:
 
75
$$\vbox{\halign{#\hfil\cr
 
76
|save_ptr:=save_ptr-1;|\cr
 
77
|hpack(p,saved_value(0),saved_level(0)).|\cr}}$$
 
78
*/
 
79
 
 
80
void scan_spec(group_code c)
 
81
{                               /* scans a box specification and left brace */
 
82
    int spec_code;
 
83
    if (scan_keyword("to")) {
 
84
        spec_code = exactly;
 
85
        scan_normal_dimen();
 
86
    } else if (scan_keyword("spread")) {
 
87
        spec_code = additional;
 
88
        scan_normal_dimen();
 
89
    } else {
 
90
        spec_code = additional;
 
91
        cur_val = 0;
 
92
    }
 
93
    set_saved_record(0, saved_boxspec, spec_code, cur_val);
 
94
    save_ptr++;
 
95
    new_save_level(c);
 
96
    scan_left_brace();
 
97
}
 
98
 
 
99
/*
 
100
 
 
101
When scanning, special care is necessary to ensure that the special
 
102
|save_stack| codes are placed just below the new group code, because
 
103
scanning can change |save_stack| when \.{\\csname} appears.  
 
104
 
 
105
This coincides with the text on |dir| and |attr| keywords, as these 
 
106
are exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the
 
107
input stream (the others are \.{\\vcenter}, \.{\\valign}, and
 
108
\.{\\halign}).
 
109
*/
 
110
 
 
111
 
 
112
void scan_full_spec(group_code c, int spec_direction)
 
113
{                               /* scans a box specification and left brace */
 
114
    int s;                      /* temporarily saved value */
 
115
    int i;
 
116
    int v;
 
117
    int spec_code;
 
118
    halfword attr_list;
 
119
    s = 0;
 
120
    if (attr_list_cache == cache_disabled)
 
121
        update_attribute_cache();
 
122
    attr_list = attr_list_cache;
 
123
    assert(saved_type(0) == saved_boxcontext);
 
124
    s = saved_value(0);         /* the box context */
 
125
  CONTINUE:
 
126
    while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
 
127
        get_x_token();
 
128
        if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd)
 
129
            back_input();
 
130
    }
 
131
    if (scan_keyword("attr")) {
 
132
        scan_register_num();
 
133
        i = cur_val;
 
134
        scan_optional_equals();
 
135
        scan_int();
 
136
        v = cur_val;
 
137
        if ((attr_list != null) && (attr_list == attr_list_cache)) {
 
138
            attr_list = copy_attribute_list(attr_list_cache);
 
139
            add_node_attr_ref(attr_list);       /* will be used once */
 
140
        }
 
141
        attr_list = do_set_attribute(attr_list, i, v);
 
142
        goto CONTINUE;
 
143
    }
 
144
    if (scan_keyword("dir")) {
 
145
        scan_direction();
 
146
        spec_direction = cur_val;
 
147
        goto CONTINUE;
 
148
    }
 
149
    if (attr_list == attr_list_cache) {
 
150
        add_node_attr_ref(attr_list);
 
151
    }
 
152
    if (scan_keyword("to")) {
 
153
        spec_code = exactly;
 
154
    } else if (scan_keyword("spread")) {
 
155
        spec_code = additional;
 
156
    } else {
 
157
        spec_code = additional;
 
158
        cur_val = 0;
 
159
        goto FOUND;
 
160
    }
 
161
    scan_normal_dimen();
 
162
  FOUND:
 
163
    set_saved_record(0, saved_boxcontext, 0, s);
 
164
    set_saved_record(1, saved_boxspec, spec_code, cur_val);
 
165
    /* DIR: Adjust |text_dir_ptr| for |scan_spec| */
 
166
    if (spec_direction != -1) {
 
167
        set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
 
168
        text_dir_ptr = new_dir(spec_direction);
 
169
    } else {
 
170
        set_saved_record(2, saved_boxdir, spec_direction, null);
 
171
    }
 
172
    set_saved_record(3, saved_boxattr, 0, attr_list);
 
173
    save_ptr += 4;
 
174
    new_save_level(c);
 
175
    scan_left_brace();
 
176
    eq_word_define(int_base + body_direction_code, spec_direction);
 
177
    eq_word_define(int_base + par_direction_code, spec_direction);
 
178
    eq_word_define(int_base + text_direction_code, spec_direction);
 
179
}
 
180
 
 
181
 
 
182
/*
 
183
To figure out the glue setting, |hpack| and |vpack| determine how much
 
184
stretchability and shrinkability are present, considering all four orders
 
185
of infinity. The highest order of infinity that has a nonzero coefficient
 
186
is then used as if no other orders were present.
 
187
 
 
188
For example, suppose that the given list contains six glue nodes with
 
189
the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill.
 
190
Then the total is essentially 2fil; and if a total additional space of 6pt
 
191
is to be achieved by stretching, the actual amounts of stretch will be
 
192
0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be
 
193
considered. (The `fill' glue is therefore not really stretching infinitely
 
194
with respect to `fil'; nobody would actually want that to happen.)
 
195
 
 
196
The arrays |total_stretch| and |total_shrink| are used to determine how much
 
197
glue of each kind is present. A global variable |last_badness| is used
 
198
to implement \.{\\badness}.
 
199
*/
 
200
 
 
201
scaled total_stretch[5];
 
202
scaled total_shrink[5];         /* glue found by |hpack| or |vpack| */
 
203
int last_badness;               /* badness of the most recently packaged box */
 
204
 
 
205
/*
 
206
If the global variable |adjust_tail| is non-null, the |hpack| routine
 
207
also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node|
 
208
items and appends the resulting material onto the list that ends at
 
209
location |adjust_tail|.
 
210
*/
 
211
 
 
212
halfword adjust_tail;           /* tail of adjustment list */
 
213
 
 
214
/*
 
215
Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
 
216
|pre_adjust_tail| instead of |adjust_tail|.
 
217
*/
 
218
 
 
219
halfword pre_adjust_tail;
 
220
 
 
221
int font_expand_ratio;          /* current expansion ratio */
 
222
halfword last_leftmost_char;
 
223
halfword last_rightmost_char;
 
224
 
 
225
halfword next_char_p;           /* pointer to the next char of an implicit kern */
 
226
halfword prev_char_p;           /* pointer to the previous char of an implicit kern */
 
227
 
 
228
/* This procedure is called repeatedly from inside the line break algorithm. */
 
229
 
 
230
void set_prev_char_p(halfword p)
 
231
{
 
232
    prev_char_p = p;
 
233
}
 
234
 
 
235
scaled char_stretch(halfword p)
 
236
{
 
237
    internal_font_number k;
 
238
    scaled dw;
 
239
    int ef;
 
240
    internal_font_number f;
 
241
    int c;
 
242
    f = font(p);
 
243
    c = character(p);
 
244
    k = pdf_font_stretch(f);
 
245
    ef = get_ef_code(f, c);
 
246
    if ((k != null_font) && (ef > 0)) {
 
247
        dw = char_width(k, c) - char_width(f, c);
 
248
        if (dw > 0)
 
249
            return round_xn_over_d(dw, ef, 1000);
 
250
    }
 
251
    return 0;
 
252
}
 
253
 
 
254
scaled char_shrink(halfword p)
 
255
{
 
256
    internal_font_number k;
 
257
    scaled dw;
 
258
    int ef;
 
259
    internal_font_number f;
 
260
    int c;
 
261
    f = font(p);
 
262
    c = character(p);
 
263
    k = pdf_font_shrink(f);
 
264
    ef = get_ef_code(f, c);
 
265
    if ((k != null_font) && (ef > 0)) {
 
266
        dw = char_width(f, c) - char_width(k, c);
 
267
        if (dw > 0)
 
268
            return round_xn_over_d(dw, ef, 1000);
 
269
    }
 
270
    return 0;
 
271
}
 
272
 
 
273
scaled kern_stretch(halfword p)
 
274
{
 
275
    halfword l, r;
 
276
    scaled d;
 
277
    if ((prev_char_p == null) || (vlink(prev_char_p) != p)
 
278
        || (vlink(p) == null))
 
279
        return 0;
 
280
    l = prev_char_p;
 
281
    r = vlink(p);
 
282
    if (!((is_char_node(l) && is_char_node(r) &&
 
283
           (font(l) == font(r)) && (pdf_font_stretch(font(l)) != null_font))))
 
284
        return 0;
 
285
    d = get_kern(pdf_font_stretch(font(l)), character(l), character(r));
 
286
    return round_xn_over_d(d - width(p),
 
287
                           get_ef_code(font(l), character(l)), 1000);
 
288
}
 
289
 
 
290
scaled kern_shrink(halfword p)
 
291
{
 
292
    halfword l, r;
 
293
    scaled d;
 
294
    if ((prev_char_p == null) || (vlink(prev_char_p) != p)
 
295
        || (vlink(p) == null))
 
296
        return 0;
 
297
    l = prev_char_p;
 
298
    r = vlink(p);
 
299
    if (!((is_char_node(l) && is_char_node(r) &&
 
300
           (font(l) == font(r)) && (pdf_font_shrink(font(l)) != null_font))))
 
301
        return 0;
 
302
    d = get_kern(pdf_font_shrink(font(l)), character(l), character(r));
 
303
    return round_xn_over_d(width(p) - d,
 
304
                           get_ef_code(font(l), character(l)), 1000);
 
305
}
 
306
 
 
307
void do_subst_font(halfword p, int ex_ratio)
 
308
{
 
309
    internal_font_number f, k;
 
310
    halfword r;
 
311
    int ef;
 
312
    if (type(p) == disc_node) {
 
313
        r = vlink(pre_break(p));
 
314
        while (r != null) {
 
315
            if (is_char_node(r))
 
316
                do_subst_font(r, ex_ratio);
 
317
            r = vlink(r);
 
318
        }
 
319
        r = vlink(post_break(p));
 
320
        while (r != null) {
 
321
            if (is_char_node(r))
 
322
                do_subst_font(r, ex_ratio);
 
323
            r = vlink(r);
 
324
        }
 
325
        r = vlink(no_break(p));
 
326
        while (r != null) {
 
327
            if (is_char_node(r))
 
328
                do_subst_font(r, ex_ratio);
 
329
            r = vlink(r);
 
330
        }
 
331
        return;
 
332
    }
 
333
    if (is_char_node(p)) {
 
334
        r = p;
 
335
    } else {
 
336
        pdf_error("font expansion", "invalid node type");
 
337
        return;
 
338
    }
 
339
    f = font(r);
 
340
    ef = get_ef_code(f, character(r));
 
341
    if (ef == 0)
 
342
        return;
 
343
    if ((pdf_font_expand_ratio(f) == 0) &&
 
344
        (pdf_font_stretch(f) != null_font) && (ex_ratio > 0)) {
 
345
        k = expand_font(f, ext_xn_over_d(ex_ratio * ef,
 
346
                                         pdf_font_expand_ratio(pdf_font_stretch
 
347
                                                               (f)), 1000000));
 
348
    } else if ((pdf_font_expand_ratio(f) == 0)
 
349
               && (pdf_font_shrink(f) != null_font) && (ex_ratio < 0)) {
 
350
        k = expand_font(f,
 
351
                        ext_xn_over_d(ex_ratio * ef,
 
352
                                      -pdf_font_expand_ratio(pdf_font_shrink
 
353
                                                             (f)), 1000000));
 
354
    } else {
 
355
        k = f;
 
356
    }
 
357
    if (k != f) {
 
358
        font(r) = k;
 
359
        if (!is_char_node(p)) { /* todo: this should be: if(is_ligature()) */
 
360
            r = lig_ptr(p);
 
361
            while (r != null) {
 
362
                font(r) = k;
 
363
                r = vlink(r);
 
364
            }
 
365
        }
 
366
    }
 
367
}
 
368
 
 
369
scaled char_pw(halfword p, int side)
 
370
{
 
371
    internal_font_number f;
 
372
    int c;
 
373
    if (side == left_side)
 
374
        last_leftmost_char = null;
 
375
    else
 
376
        last_rightmost_char = null;
 
377
    if (p == null)
 
378
        return 0;
 
379
    if (!is_char_node(p))
 
380
        return 0;
 
381
    f = font(p);
 
382
    if (side == left_side) {
 
383
        c = get_lp_code(f, character(p));
 
384
        last_leftmost_char = p;
 
385
    } else {
 
386
        c = get_rp_code(f, character(p));
 
387
        last_rightmost_char = p;
 
388
    }
 
389
    if (c == 0)
 
390
        return 0;
 
391
    return round_xn_over_d(quad(f), c, 1000);
 
392
}
 
393
 
 
394
halfword new_margin_kern(scaled w, halfword p, int side)
 
395
{
 
396
    halfword k, q;
 
397
    k = new_node(margin_kern_node, side);
 
398
    width(k) = w;
 
399
    if (p == null)
 
400
        pdf_error("margin kerning", "invalid pointer to marginal char node");
 
401
    q = new_char(font(p), character(p));
 
402
    margin_char(k) = q;
 
403
    return k;
 
404
}
 
405
 
 
406
/*
 
407
Here is |hpack|, which is place where we do font substituting when
 
408
font expansion is being used.
 
409
*/
 
410
 
 
411
halfword hpack(halfword p, scaled w, int m, int pack_direction)
 
412
{
 
413
    halfword r;                 /* the box node that will be returned */
 
414
    halfword q;                 /* trails behind |p| */
 
415
    scaled h, d, x;             /* height, depth, and natural width */
 
416
    scaled_whd whd;
 
417
    scaled s;                   /* shift amount */
 
418
    halfword g;                 /* points to a glue specification */
 
419
    int o;                      /* order of infinity */
 
420
    internal_font_number f;     /* the font in a |char_node| */
 
421
    halfword dir_ptr;           /* for managing the direction stack */
 
422
    /* BEWARE: this shadows a global |dir_ptr| */
 
423
    int hpack_dir;              /* the current direction */
 
424
    int disc_level;
 
425
    halfword pack_interrupt[8];
 
426
    scaled font_stretch;
 
427
    scaled font_shrink;
 
428
    scaled k;
 
429
    last_badness = 0;
 
430
    r = new_node(hlist_node, min_quarterword);
 
431
    if (pack_direction == -1) {
 
432
        box_dir(r) = text_direction;
 
433
    } else {
 
434
        box_dir(r) = pack_direction;
 
435
        pack_direction = -1;
 
436
    }
 
437
    hpack_dir = box_dir(r);
 
438
    dir_ptr = null;
 
439
    push_dir(hpack_dir);
 
440
    q = r + list_offset;
 
441
    vlink(q) = p;
 
442
    if (m == cal_expand_ratio) {
 
443
        prev_char_p = null;
 
444
        font_stretch = 0;
 
445
        font_shrink = 0;
 
446
        font_expand_ratio = 0;
 
447
    }
 
448
    h = 0;
 
449
    /* Clear dimensions to zero */
 
450
    d = 0;
 
451
    x = 0;
 
452
    total_stretch[normal] = 0;
 
453
    total_shrink[normal] = 0;
 
454
    total_stretch[sfi] = 0;
 
455
    total_shrink[sfi] = 0;
 
456
    total_stretch[fil] = 0;
 
457
    total_shrink[fil] = 0;
 
458
    total_stretch[fill] = 0;
 
459
    total_shrink[fill] = 0;
 
460
    total_stretch[filll] = 0;
 
461
    total_shrink[filll] = 0;
 
462
 
 
463
    disc_level = 0;
 
464
  RESWITCH:
 
465
    while ((p != null) || (disc_level > 0)) {
 
466
        if (p == null) {
 
467
            decr(disc_level);
 
468
            p = pack_interrupt[disc_level];
 
469
            goto RESWITCH;
 
470
        }
 
471
        /* Examine node |p| in the hlist, taking account of its effect
 
472
           on the dimensions of the new box, or moving it to the adjustment list;
 
473
           then advance |p| to the next node */
 
474
        while (is_char_node(p)) {
 
475
            /* Incorporate character dimensions into the dimensions of
 
476
               the hbox that will contain~it, then move to the next node */
 
477
            /* The following code is part of \TeX's inner loop; i.e., adding another
 
478
               character of text to the user's input will cause each of these instructions
 
479
               to be exercised one more time.
 
480
             */
 
481
            if (m >= cal_expand_ratio) {
 
482
                prev_char_p = p;
 
483
                if (m == cal_expand_ratio) {
 
484
                    font_stretch += char_stretch(p);
 
485
                    font_shrink += char_shrink(p);
 
486
                } else if (m == subst_ex_font) {
 
487
                    do_subst_font(p, font_expand_ratio);
 
488
                }
 
489
            }
 
490
            f = font(p);
 
491
            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
 
492
            x += whd.wd;
 
493
            if (whd.ht > h)
 
494
                h = whd.ht;
 
495
            if (whd.dp > d)
 
496
                d = whd.dp;
 
497
            p = vlink(p);
 
498
        }
 
499
        if (p != null) {
 
500
            switch (type(p)) {
 
501
            case hlist_node:
 
502
            case vlist_node:
 
503
                /* Incorporate box dimensions into the dimensions of the hbox that will contain~it */
 
504
                /* The code here implicitly uses the fact that running dimensions are
 
505
                   indicated by |null_flag|, which will be ignored in the calculations
 
506
                   because it is a highly negative number. */
 
507
                s = shift_amount(p);
 
508
                whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
 
509
                x += whd.wd;
 
510
                if (whd.ht - s > h)
 
511
                    h = whd.ht - s;
 
512
                if (whd.dp + s > d)
 
513
                    d = whd.dp + s;
 
514
                break;
 
515
            case rule_node:
 
516
            case unset_node:
 
517
                x += width(p);
 
518
                if (type(p) >= rule_node)
 
519
                    s = 0;
 
520
                else
 
521
                    s = shift_amount(p);
 
522
                if (height(p) - s > h)
 
523
                    h = height(p) - s;
 
524
                if (depth(p) + s > d)
 
525
                    d = depth(p) + s;
 
526
                break;
 
527
            case ins_node:
 
528
            case mark_node:
 
529
            case adjust_node:
 
530
                if (adjust_tail != null) {
 
531
                    /* Transfer node |p| to the adjustment list */
 
532
                    /*
 
533
                       Although node |q| is not necessarily the immediate predecessor of node |p|,
 
534
                       it always points to some node in the list preceding |p|. Thus, we can delete
 
535
                       nodes by moving |q| when necessary. The algorithm takes linear time, and the
 
536
                       extra computation does not intrude on the inner loop unless it is necessary
 
537
                       to make a deletion.
 
538
                     */
 
539
                    while (vlink(q) != p)
 
540
                        q = vlink(q);
 
541
                    if (type(p) == adjust_node) {
 
542
                        if (adjust_pre(p) != 0)
 
543
                            update_adjust_list(pre_adjust_tail);
 
544
                        else
 
545
                            update_adjust_list(adjust_tail);
 
546
                        p = vlink(p);
 
547
                        adjust_ptr(vlink(q)) = null;
 
548
                        flush_node(vlink(q));
 
549
                    } else {
 
550
                        vlink(adjust_tail) = p;
 
551
                        adjust_tail = p;
 
552
                        p = vlink(p);
 
553
                    }
 
554
                    vlink(q) = p;
 
555
                    p = q;
 
556
                }
 
557
                break;
 
558
            case whatsit_node:
 
559
                /* Incorporate a whatsit node into an hbox */
 
560
                if (subtype(p) == dir_node) {
 
561
                    /* DIR: Adjust the dir stack for the |hpack| routine */
 
562
                    if (dir_dir(p) >= 0) {
 
563
                        hpack_dir = dir_dir(p);
 
564
                        push_dir_node(p);
 
565
                    } else {
 
566
                        pop_dir_node();
 
567
                        if (dir_ptr != null)
 
568
                            hpack_dir = dir_dir(dir_ptr);
 
569
                    }
 
570
 
 
571
                } else {
 
572
                    if ((subtype(p) == pdf_refxform_node)
 
573
                        || (subtype(p) == pdf_refximage_node)) {
 
574
                        x += width(p);
 
575
                        s = 0;
 
576
                        if (height(p) - s > h)
 
577
                            h = height(p) - s;
 
578
                        if (depth(p) + s > d)
 
579
                            d = depth(p) + s;
 
580
                    }
 
581
                }
 
582
                break;
 
583
            case glue_node:
 
584
                /* Incorporate glue into the horizontal totals */
 
585
                g = glue_ptr(p);
 
586
                x += width(g);
 
587
                o = stretch_order(g);
 
588
                total_stretch[o] = total_stretch[o] + stretch(g);
 
589
                o = shrink_order(g);
 
590
                total_shrink[o] = total_shrink[o] + shrink(g);
 
591
                if (subtype(p) >= a_leaders) {
 
592
                    g = leader_ptr(p);
 
593
                    if (height(g) > h)
 
594
                        h = height(g);
 
595
                    if (depth(g) > d)
 
596
                        d = depth(g);
 
597
                }
 
598
                break;
 
599
            case margin_kern_node:
 
600
                if (m == cal_expand_ratio) {
 
601
                    f = font(margin_char(p));
 
602
                    do_subst_font(margin_char(p), 1000);
 
603
                    if (f != font(margin_char(p)))
 
604
                        font_stretch =
 
605
                            font_stretch - width(p) - char_pw(margin_char(p),
 
606
                                                              subtype(p));
 
607
                    font(margin_char(p)) = f;
 
608
                    do_subst_font(margin_char(p), -1000);
 
609
                    if (f != font(margin_char(p)))
 
610
                        font_shrink =
 
611
                            font_shrink - width(p) - char_pw(margin_char(p),
 
612
                                                             subtype(p));
 
613
                    font(margin_char(p)) = f;
 
614
                } else if (m == subst_ex_font) {
 
615
                    do_subst_font(margin_char(p), font_expand_ratio);
 
616
                    width(p) = -char_pw(margin_char(p), subtype(p));
 
617
                }
 
618
                x += width(p);
 
619
                break;
 
620
            case kern_node:
 
621
                if (subtype(p) == normal) {
 
622
                    if (m == cal_expand_ratio) {
 
623
                        font_stretch = font_stretch + kern_stretch(p);
 
624
                        font_shrink = font_shrink + kern_shrink(p);
 
625
                    } else if (m == subst_ex_font) {
 
626
                        if (font_expand_ratio > 0)
 
627
                            k = kern_stretch(p);
 
628
                        else if (font_expand_ratio < 0)
 
629
                            k = kern_shrink(p);
 
630
                        else
 
631
                            pdfassert(0);
 
632
                        if (k != 0)
 
633
                            width(p) = get_kern(font(prev_char_p),
 
634
                                                character(prev_char_p),
 
635
                                                character(vlink(p)));
 
636
                    }
 
637
                }
 
638
                x += width(p);
 
639
                break;
 
640
            case math_node:
 
641
                x += surround(p);
 
642
                break;
 
643
            case disc_node:
 
644
                if (m == subst_ex_font)
 
645
                    do_subst_font(p, font_expand_ratio);
 
646
                if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) {
 
647
                    pack_interrupt[disc_level] = vlink(p);
 
648
                    incr(disc_level);
 
649
                    p = no_break(p);
 
650
                }
 
651
                break;
 
652
            default:
 
653
                break;
 
654
            }
 
655
            p = vlink(p);
 
656
        }
 
657
 
 
658
    }
 
659
 
 
660
    if (adjust_tail != null)
 
661
        vlink(adjust_tail) = null;
 
662
    if (pre_adjust_tail != null)
 
663
        vlink(pre_adjust_tail) = null;
 
664
    height(r) = h;
 
665
    depth(r) = d;
 
666
    /* Determine the value of |width(r)| and the appropriate glue setting;
 
667
       then |return| or |goto common_ending| */
 
668
    /* When we get to the present part of the program, |x| is the natural width
 
669
       of the box being packaged. */
 
670
    if (m == additional)
 
671
        w = x + w;
 
672
    width(r) = w;
 
673
    x = w - x;                  /* now |x| is the excess to be made up */
 
674
    if (x == 0) {
 
675
        glue_sign(r) = normal;
 
676
        glue_order(r) = normal;
 
677
        set_glue_ratio_zero(glue_set(r));
 
678
        goto EXIT;
 
679
    } else if (x > 0) {
 
680
        /* Determine horizontal glue stretch setting, then |return|
 
681
           or \hbox{|goto common_ending|} */
 
682
 
 
683
        /* If |hpack| is called with |m=cal_expand_ratio| we calculate
 
684
           |font_expand_ratio| and return without checking for overfull or underfull box. */
 
685
 
 
686
        /* Determine the stretch order */
 
687
        if (total_stretch[filll] != 0)
 
688
            o = filll;
 
689
        else if (total_stretch[fill] != 0)
 
690
            o = fill;
 
691
        else if (total_stretch[fil] != 0)
 
692
            o = fil;
 
693
        else if (total_stretch[sfi] != 0)
 
694
            o = sfi;
 
695
        else
 
696
            o = normal;
 
697
 
 
698
        if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
 
699
            font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
 
700
            goto EXIT;
 
701
        }
 
702
        glue_order(r) = o;
 
703
        glue_sign(r) = stretching;
 
704
        if (total_stretch[o] != 0) {
 
705
            glue_set(r) = unfloat((double) x / total_stretch[o]);
 
706
        } else {
 
707
            glue_sign(r) = normal;
 
708
            set_glue_ratio_zero(glue_set(r));   /* there's nothing to stretch */
 
709
        }
 
710
        if (o == normal) {
 
711
            if (list_ptr(r) != null) {
 
712
                /* Report an underfull hbox and |goto common_ending|, if this box
 
713
                   is sufficiently bad */
 
714
                last_badness = badness(x, total_stretch[normal]);
 
715
                if (last_badness > int_par(hbadness_code)) {
 
716
                    print_ln();
 
717
                    if (last_badness > 100)
 
718
                        tprint_nl("Underfull \\hbox (badness ");
 
719
                    else
 
720
                        tprint_nl("Loose \\hbox (badness ");
 
721
                    print_int(last_badness);
 
722
                    goto COMMON_ENDING;
 
723
                }
 
724
            }
 
725
        }
 
726
        goto EXIT;
 
727
    } else {
 
728
        /* Determine horizontal glue shrink setting, then |return|
 
729
           or \hbox{|goto common_ending|} */
 
730
        /* Determine the shrink order */
 
731
        if (total_shrink[filll] != 0)
 
732
            o = filll;
 
733
        else if (total_shrink[fill] != 0)
 
734
            o = fill;
 
735
        else if (total_shrink[fil] != 0)
 
736
            o = fil;
 
737
        else if (total_shrink[sfi] != 0)
 
738
            o = sfi;
 
739
        else
 
740
            o = normal;
 
741
 
 
742
        if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
 
743
            font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
 
744
            goto EXIT;
 
745
        }
 
746
        glue_order(r) = o;
 
747
        glue_sign(r) = shrinking;
 
748
        if (total_shrink[o] != 0) {
 
749
            glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
 
750
        } else {
 
751
            glue_sign(r) = normal;
 
752
            set_glue_ratio_zero(glue_set(r));   /* there's nothing to shrink */
 
753
        }
 
754
        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
 
755
            last_badness = 1000000;
 
756
            set_glue_ratio_one(glue_set(r));    /* use the maximum shrinkage */
 
757
            /* Report an overfull hbox and |goto common_ending|, if this box
 
758
               is sufficiently bad */
 
759
            if ((-x - total_shrink[normal] > dimen_par(hfuzz_code))
 
760
                || (int_par(hbadness_code) < 100)) {
 
761
                if ((dimen_par(overfull_rule_code) > 0)
 
762
                    && (-x - total_shrink[normal] > dimen_par(hfuzz_code))) {
 
763
                    while (vlink(q) != null)
 
764
                        q = vlink(q);
 
765
                    vlink(q) = new_rule();
 
766
                    rule_dir(vlink(q)) = box_dir(r);
 
767
                    width(vlink(q)) = dimen_par(overfull_rule_code);
 
768
                }
 
769
                print_ln();
 
770
                tprint_nl("Overfull \\hbox (");
 
771
                print_scaled(-x - total_shrink[normal]);
 
772
                tprint("pt too wide");
 
773
                goto COMMON_ENDING;
 
774
            }
 
775
        } else if (o == normal) {
 
776
            if (list_ptr(r) != null) {
 
777
                /* Report a tight hbox and |goto common_ending|, if this box is sufficiently bad */
 
778
                last_badness = badness(-x, total_shrink[normal]);
 
779
                if (last_badness > int_par(hbadness_code)) {
 
780
                    print_ln();
 
781
                    tprint_nl("Tight \\hbox (badness ");
 
782
                    print_int(last_badness);
 
783
                    goto COMMON_ENDING;
 
784
                }
 
785
            }
 
786
        }
 
787
        goto EXIT;
 
788
    }
 
789
 
 
790
  COMMON_ENDING:
 
791
    /* Finish issuing a diagnostic message for an overfull or underfull hbox */
 
792
    if (output_active) {
 
793
        tprint(") has occurred while \\output is active");
 
794
    } else {
 
795
        if (pack_begin_line != 0) {
 
796
            if (pack_begin_line > 0)
 
797
                tprint(") in paragraph at lines ");
 
798
            else
 
799
                tprint(") in alignment at lines ");
 
800
            print_int(abs(pack_begin_line));
 
801
            tprint("--");
 
802
        } else {
 
803
            tprint(") detected at line ");
 
804
        }
 
805
        print_int(line);
 
806
    }
 
807
 
 
808
    print_ln();
 
809
    font_in_short_display = null_font;
 
810
    short_display(list_ptr(r));
 
811
    print_ln();
 
812
    begin_diagnostic();
 
813
    show_box(r);
 
814
    end_diagnostic(true);
 
815
 
 
816
  EXIT:
 
817
    if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
 
818
        font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
 
819
        q = list_ptr(r);
 
820
        list_ptr(r) = null;
 
821
        flush_node(r);
 
822
        r = hpack(q, w, subst_ex_font, hpack_dir);
 
823
    }
 
824
    while (dir_ptr != null)
 
825
        pop_dir_node();
 
826
    return r;
 
827
}
 
828
 
 
829
halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp,
 
830
                        int pac)
 
831
{
 
832
    halfword q;
 
833
    new_hyphenation(p, qt);
 
834
    (void) new_ligkern(p, qt);  /* don't care about the tail in this case */
 
835
    q = vlink(p);
 
836
    q = lua_hpack_filter(q, w, m, grp, pac);
 
837
    return hpack(q, w, m, pac);
 
838
}
 
839
 
 
840
/* here is a function to calculate the natural whd of a (horizontal) node list */
 
841
 
 
842
scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
 
843
                         int g_sign, int g_order, int pack_direction)
 
844
{
 
845
    scaled s;                   /* shift amount */
 
846
    halfword g;                 /* points to a glue specification */
 
847
    internal_font_number f;     /* the font in a |char_node| */
 
848
    int hpack_dir;
 
849
    scaled_whd xx;              /* for recursion */
 
850
    scaled_whd whd, siz = { 0, 0, 0 };
 
851
    if (pack_direction == -1) {
 
852
        hpack_dir = text_direction;
 
853
    } else {
 
854
        hpack_dir = pack_direction;
 
855
    }
 
856
    while (p != pp && p != null) {
 
857
        while (is_char_node(p) && p != pp) {
 
858
            f = font(p);
 
859
            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
 
860
            siz.wd += whd.wd;
 
861
            if (whd.ht > siz.ht)
 
862
                siz.ht = whd.ht;
 
863
            if (whd.dp > siz.dp)
 
864
                siz.dp = whd.dp;
 
865
            p = vlink(p);
 
866
        }
 
867
        if (p != pp && p != null) {
 
868
            switch (type(p)) {
 
869
            case hlist_node:
 
870
            case vlist_node:
 
871
                s = shift_amount(p);
 
872
                whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
 
873
                siz.wd += whd.wd;
 
874
                if (whd.ht - s > siz.ht)
 
875
                    siz.ht = whd.ht - s;
 
876
                if (whd.dp + s > siz.dp)
 
877
                    siz.dp = whd.dp + s;
 
878
                break;
 
879
            case rule_node:
 
880
            case unset_node:
 
881
                siz.wd += width(p);
 
882
                if (type(p) >= rule_node)
 
883
                    s = 0;
 
884
                else
 
885
                    s = shift_amount(p);
 
886
                if (height(p) - s > siz.ht)
 
887
                    siz.ht = height(p) - s;
 
888
                if (depth(p) + s > siz.dp)
 
889
                    siz.dp = depth(p) + s;
 
890
                break;
 
891
            case whatsit_node:
 
892
                if ((subtype(p) == pdf_refxform_node)
 
893
                    || (subtype(p) == pdf_refximage_node)) {
 
894
                    siz.wd += width(p);
 
895
                    s = 0;
 
896
                    if (height(p) - s > siz.ht)
 
897
                        siz.ht = height(p) - s;
 
898
                    if (depth(p) + s > siz.dp)
 
899
                        siz.dp = depth(p) + s;
 
900
                }
 
901
                break;
 
902
            case glue_node:
 
903
                g = glue_ptr(p);
 
904
                siz.wd += width(g);
 
905
                if (g_sign != normal) {
 
906
                    if (g_sign == stretching) {
 
907
                        if (stretch_order(g) == g_order) {
 
908
                            siz.wd +=
 
909
                                float_round(float_cast(g_mult) * stretch(g));
 
910
                        }
 
911
                    } else if (shrink_order(g) == g_order) {
 
912
                        siz.wd -= float_round(float_cast(g_mult) * shrink(g));
 
913
                    }
 
914
                }
 
915
                if (subtype(p) >= a_leaders) {
 
916
                    g = leader_ptr(p);
 
917
                    if (height(g) > siz.ht)
 
918
                        siz.ht = height(g);
 
919
                    if (depth(g) > siz.dp)
 
920
                        siz.dp = depth(g);
 
921
                }
 
922
                break;
 
923
            case margin_kern_node:
 
924
            case kern_node:
 
925
                siz.wd += width(p);
 
926
                break;
 
927
            case math_node:
 
928
                siz.wd += surround(p);
 
929
                break;
 
930
            case disc_node:
 
931
                xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order,
 
932
                                   hpack_dir);
 
933
                siz.wd += xx.wd;
 
934
                if (xx.ht > siz.ht)
 
935
                    siz.ht = xx.ht;
 
936
                if (xx.dp > siz.dp)
 
937
                    siz.dp = xx.dp;
 
938
                break;
 
939
            default:
 
940
                break;
 
941
            }
 
942
            p = vlink(p);
 
943
        }
 
944
 
 
945
    }
 
946
    return siz;
 
947
}
 
948
 
 
949
/*
 
950
In order to provide a decent indication of where an overfull or underfull
 
951
box originated, we use a global variable |pack_begin_line| that is
 
952
set nonzero only when |hpack| is being called by the paragraph builder
 
953
or the alignment finishing routine.
 
954
*/
 
955
 
 
956
int pack_begin_line;            /* source file line where the current paragraph
 
957
                                   or alignment began; a negative value denotes alignment */
 
958
 
 
959
/*
 
960
The |vpack| subroutine is actually a special case of a slightly more
 
961
general routine called |vpackage|, which has four parameters. The fourth
 
962
parameter, which is |max_dimen| in the case of |vpack|, specifies the
 
963
maximum depth of the page box that is constructed. The depth is first
 
964
computed by the normal rules; if it exceeds this limit, the reference
 
965
point is simply moved down until the limiting depth is attained.
 
966
*/
 
967
 
 
968
halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
 
969
{
 
970
    halfword r;                 /* the box node that will be returned */
 
971
    scaled w, d, x;             /* width, depth, and natural height */
 
972
    scaled_whd whd;
 
973
    scaled s;                   /* shift amount */
 
974
    halfword g;                 /* points to a glue specification */
 
975
    int o;                      /* order of infinity */
 
976
    last_badness = 0;
 
977
    r = new_node(vlist_node, 0);
 
978
    if (pack_direction == -1) {
 
979
        box_dir(r) = body_direction;
 
980
    } else {
 
981
        box_dir(r) = pack_direction;
 
982
    }
 
983
    subtype(r) = min_quarterword;
 
984
    shift_amount(r) = 0;
 
985
    list_ptr(r) = p;
 
986
    w = 0;
 
987
    /* Clear dimensions to zero */
 
988
    d = 0;
 
989
    x = 0;
 
990
    total_stretch[normal] = 0;
 
991
    total_shrink[normal] = 0;
 
992
    total_stretch[sfi] = 0;
 
993
    total_shrink[sfi] = 0;
 
994
    total_stretch[fil] = 0;
 
995
    total_shrink[fil] = 0;
 
996
    total_stretch[fill] = 0;
 
997
    total_shrink[fill] = 0;
 
998
    total_stretch[filll] = 0;
 
999
    total_shrink[filll] = 0;
 
1000
 
 
1001
    while (p != null) {
 
1002
        /* Examine node |p| in the vlist, taking account of its effect
 
1003
           on the dimensions of the new box; then advance |p| to the next node */
 
1004
        if (is_char_node(p)) {
 
1005
            confusion("vpack");
 
1006
        } else {
 
1007
            switch (type(p)) {
 
1008
            case hlist_node:
 
1009
            case vlist_node:
 
1010
                /* Incorporate box dimensions into the dimensions of
 
1011
                   the vbox that will contain~it */
 
1012
                s = shift_amount(p);
 
1013
                whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false);
 
1014
                if (whd.wd + s > w)
 
1015
                    w = whd.wd + s;
 
1016
                x += d + whd.ht;
 
1017
                d = whd.dp;
 
1018
                break;
 
1019
            case rule_node:
 
1020
            case unset_node:
 
1021
                x += d + height(p);
 
1022
                d = depth(p);
 
1023
                if (type(p) >= rule_node)
 
1024
                    s = 0;
 
1025
                else
 
1026
                    s = shift_amount(p);
 
1027
                if (width(p) + s > w)
 
1028
                    w = width(p) + s;
 
1029
                break;
 
1030
            case whatsit_node:
 
1031
                /* Incorporate a whatsit node into a vbox */
 
1032
                if ((subtype(p) == pdf_refxform_node)
 
1033
                    || (subtype(p) == pdf_refximage_node)) {
 
1034
                    x += d + height(p);
 
1035
                    d = depth(p);
 
1036
                    s = 0;
 
1037
                    if (width(p) + s > w)
 
1038
                        w = width(p) + s;
 
1039
                }
 
1040
                break;
 
1041
            case glue_node:
 
1042
                /* Incorporate glue into the vertical totals */
 
1043
                x += d;
 
1044
                d = 0;
 
1045
                g = glue_ptr(p);
 
1046
                x += width(g);
 
1047
                o = stretch_order(g);
 
1048
                total_stretch[o] = total_stretch[o] + stretch(g);
 
1049
                o = shrink_order(g);
 
1050
                total_shrink[o] = total_shrink[o] + shrink(g);
 
1051
                if (subtype(p) >= a_leaders) {
 
1052
                    g = leader_ptr(p);
 
1053
                    if (width(g) > w)
 
1054
                        w = width(g);
 
1055
                }
 
1056
                break;
 
1057
            case kern_node:
 
1058
                x += d + width(p);
 
1059
                d = 0;
 
1060
                break;
 
1061
            default:
 
1062
                break;
 
1063
            }
 
1064
        }
 
1065
        p = vlink(p);
 
1066
    }
 
1067
    width(r) = w;
 
1068
    if (d > l) {
 
1069
        x += d - l;
 
1070
        depth(r) = l;
 
1071
    } else {
 
1072
        depth(r) = d;
 
1073
    }
 
1074
    /* Determine the value of |height(r)| and the appropriate glue setting;
 
1075
       then |return| or |goto common_ending| */
 
1076
    /* When we get to the present part of the program, |x| is the natural height
 
1077
       of the box being packaged. */
 
1078
    if (m == additional)
 
1079
        h = x + h;
 
1080
    height(r) = h;
 
1081
    x = h - x;                  /* now |x| is the excess to be made up */
 
1082
    if (x == 0) {
 
1083
        glue_sign(r) = normal;
 
1084
        glue_order(r) = normal;
 
1085
        set_glue_ratio_zero(glue_set(r));
 
1086
        return r;
 
1087
    } else if (x > 0) {
 
1088
        /* Determine vertical glue stretch setting, then |return|
 
1089
           or \hbox{|goto common_ending|} */
 
1090
        /* Determine the stretch order */
 
1091
        if (total_stretch[filll] != 0)
 
1092
            o = filll;
 
1093
        else if (total_stretch[fill] != 0)
 
1094
            o = fill;
 
1095
        else if (total_stretch[fil] != 0)
 
1096
            o = fil;
 
1097
        else if (total_stretch[sfi] != 0)
 
1098
            o = sfi;
 
1099
        else
 
1100
            o = normal;
 
1101
 
 
1102
        glue_order(r) = o;
 
1103
        glue_sign(r) = stretching;
 
1104
        if (total_stretch[o] != 0) {
 
1105
            glue_set(r) = unfloat((double) x / total_stretch[o]);
 
1106
        } else {
 
1107
            glue_sign(r) = normal;
 
1108
            set_glue_ratio_zero(glue_set(r));   /* there's nothing to stretch */
 
1109
        }
 
1110
        if (o == normal) {
 
1111
            if (list_ptr(r) != null) {
 
1112
                /* Report an underfull vbox and |goto common_ending|, if this box
 
1113
                   is sufficiently bad */
 
1114
                last_badness = badness(x, total_stretch[normal]);
 
1115
                if (last_badness > int_par(vbadness_code)) {
 
1116
                    print_ln();
 
1117
                    if (last_badness > 100)
 
1118
                        tprint_nl("Underfull \\vbox (badness ");
 
1119
                    else
 
1120
                        tprint_nl("Loose \\vbox (badness ");
 
1121
                    print_int(last_badness);
 
1122
                    goto COMMON_ENDING;
 
1123
                }
 
1124
            }
 
1125
        }
 
1126
        return r;
 
1127
 
 
1128
    } else {
 
1129
        /* Determine vertical glue shrink setting, then |return|
 
1130
           or \hbox{|goto common_ending|} */
 
1131
        /* Determine the shrink order */
 
1132
        if (total_shrink[filll] != 0)
 
1133
            o = filll;
 
1134
        else if (total_shrink[fill] != 0)
 
1135
            o = fill;
 
1136
        else if (total_shrink[fil] != 0)
 
1137
            o = fil;
 
1138
        else if (total_shrink[sfi] != 0)
 
1139
            o = sfi;
 
1140
        else
 
1141
            o = normal;
 
1142
 
 
1143
        glue_order(r) = o;
 
1144
        glue_sign(r) = shrinking;
 
1145
        if (total_shrink[o] != 0) {
 
1146
            glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
 
1147
        } else {
 
1148
            glue_sign(r) = normal;
 
1149
            set_glue_ratio_zero(glue_set(r));   /* there's nothing to shrink */
 
1150
        }
 
1151
        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
 
1152
            last_badness = 1000000;
 
1153
            set_glue_ratio_one(glue_set(r));    /* use the maximum shrinkage */
 
1154
            /* Report an overfull vbox and |goto common_ending|, if this box is sufficiently bad */
 
1155
            if ((-x - total_shrink[normal] > dimen_par(vfuzz_code))
 
1156
                || (int_par(vbadness_code) < 100)) {
 
1157
                print_ln();
 
1158
                tprint_nl("Overfull \\vbox (");
 
1159
                print_scaled(-x - total_shrink[normal]);
 
1160
                tprint("pt too high");
 
1161
                goto COMMON_ENDING;
 
1162
            }
 
1163
        } else if (o == normal) {
 
1164
            if (list_ptr(r) != null) {
 
1165
                /* Report a tight vbox and |goto common_ending|, if this box is sufficiently bad */
 
1166
                last_badness = badness(-x, total_shrink[normal]);
 
1167
                if (last_badness > int_par(vbadness_code)) {
 
1168
                    print_ln();
 
1169
                    tprint_nl("Tight \\vbox (badness ");
 
1170
                    print_int(last_badness);
 
1171
                    goto COMMON_ENDING;
 
1172
                }
 
1173
            }
 
1174
        }
 
1175
        return r;
 
1176
    }
 
1177
 
 
1178
  COMMON_ENDING:
 
1179
    /* Finish issuing a diagnostic message or an overfull or underfull vbox */
 
1180
    if (output_active) {
 
1181
        tprint(") has occurred while \\output is active");
 
1182
    } else {
 
1183
        if (pack_begin_line != 0) {     /* it's actually negative */
 
1184
            tprint(") in alignment at lines ");
 
1185
            print_int(abs(pack_begin_line));
 
1186
            tprint("--");
 
1187
        } else {
 
1188
            tprint(") detected at line ");
 
1189
        }
 
1190
        print_int(line);
 
1191
        print_ln();
 
1192
    }
 
1193
    begin_diagnostic();
 
1194
    show_box(r);
 
1195
    end_diagnostic(true);
 
1196
    return r;
 
1197
}
 
1198
 
 
1199
halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp,
 
1200
                           int pack_direction)
 
1201
{
 
1202
    halfword q;
 
1203
    q = p;
 
1204
    q = lua_vpack_filter(q, h, m, l, grp, pack_direction);
 
1205
    return vpackage(q, h, m, l, pack_direction);
 
1206
}
 
1207
 
 
1208
void finish_vcenter(void)
 
1209
{
 
1210
    halfword p;
 
1211
    unsave();
 
1212
    save_ptr--;
 
1213
    p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
 
1214
    pop_nest();
 
1215
    p = math_vcenter_group(p);
 
1216
    tail_append(p);
 
1217
}
 
1218
 
 
1219
void package(int c)
 
1220
{
 
1221
    scaled h;                   /* height of box */
 
1222
    halfword p;                 /* first node in a box */
 
1223
    scaled d;                   /* max depth */
 
1224
    int grp;
 
1225
    grp = cur_group;
 
1226
    d = box_max_depth;
 
1227
    unsave();
 
1228
    save_ptr -= 4;
 
1229
    if (cur_list.mode_field == -hmode) {
 
1230
        cur_box = filtered_hpack(cur_list.head_field,
 
1231
                                 cur_list.tail_field, saved_value(1),
 
1232
                                 saved_level(1), grp, saved_level(2));
 
1233
        subtype(cur_box) = HLIST_SUBTYPE_HBOX;
 
1234
    } else {
 
1235
        cur_box = filtered_vpackage(vlink(cur_list.head_field),
 
1236
                                    saved_value(1), saved_level(1), d, grp,
 
1237
                                    saved_level(2));
 
1238
        if (c == vtop_code) {
 
1239
            /* Readjust the height and depth of |cur_box|,  for \.{\\vtop} */
 
1240
            /* The height of a `\.{\\vtop}' box is inherited from the first item on its list,
 
1241
               if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise
 
1242
               the \.{\\vtop} height is zero.
 
1243
             */
 
1244
 
 
1245
            h = 0;
 
1246
            p = list_ptr(cur_box);
 
1247
            if (p != null)
 
1248
                if (type(p) <= rule_node)
 
1249
                    h = height(p);
 
1250
            depth(cur_box) = depth(cur_box) - h + height(cur_box);
 
1251
            height(cur_box) = h;
 
1252
 
 
1253
        }
 
1254
    }
 
1255
    if (saved_value(2) != null) {
 
1256
        /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */
 
1257
        flush_node_list(text_dir_ptr);
 
1258
        text_dir_ptr = saved_value(2);
 
1259
 
 
1260
    }
 
1261
    replace_attribute_list(cur_box, saved_value(3));
 
1262
    pop_nest();
 
1263
    box_end(saved_value(0));
 
1264
}
 
1265
 
 
1266
 
 
1267
/*
 
1268
When a box is being appended to the current vertical list, the
 
1269
baselineskip calculation is handled by the |append_to_vlist| routine.
 
1270
*/
 
1271
 
 
1272
void append_to_vlist(halfword b)
 
1273
{
 
1274
    scaled d;                   /* deficiency of space between baselines */
 
1275
    halfword p;                 /* a new glue node */
 
1276
    if (prev_depth > dimen_par(pdf_ignored_dimen_code)) {
 
1277
        if ((type(b) == hlist_node) && is_mirrored(box_dir(b))) {
 
1278
            d = width(glue_par(baseline_skip_code)) - prev_depth - depth(b);
 
1279
        } else {
 
1280
            d = width(glue_par(baseline_skip_code)) - prev_depth - height(b);
 
1281
        }
 
1282
        if (d < dimen_par(line_skip_limit_code)) {
 
1283
            p = new_param_glue(line_skip_code);
 
1284
        } else {
 
1285
            p = new_skip_param(baseline_skip_code);
 
1286
            width(temp_ptr) = d;        /* |temp_ptr=glue_ptr(p)| */
 
1287
        }
 
1288
        couple_nodes(cur_list.tail_field, p);
 
1289
        cur_list.tail_field = p;
 
1290
    }
 
1291
    couple_nodes(cur_list.tail_field, b);
 
1292
    cur_list.tail_field = b;
 
1293
    if ((type(b) == hlist_node) && is_mirrored(box_dir(b)))
 
1294
        prev_depth = height(b);
 
1295
    else
 
1296
        prev_depth = depth(b);
 
1297
}
 
1298
 
 
1299
/*
 
1300
When |saving_vdiscards| is positive then the glue, kern, and penalty
 
1301
nodes removed by the page builder or by \.{\\vsplit} from the top of a
 
1302
vertical list are saved in special lists instead of being discarded.
 
1303
*/
 
1304
 
 
1305
#define tail_page_disc disc_ptr[copy_code]      /* last item removed by page builder */
 
1306
#define page_disc disc_ptr[last_box_code]       /* first item removed by page builder */
 
1307
#define split_disc disc_ptr[vsplit_code]        /* first item removed by \.{\\vsplit} */
 
1308
 
 
1309
halfword disc_ptr[(vsplit_code + 1)];   /* list pointers */
 
1310
 
 
1311
/*
 
1312
The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation,
 
1313
is considerably simpler than |line_break| because it doesn't have to
 
1314
worry about hyphenation, and because its mission is to discover a single
 
1315
break instead of an optimum sequence of breakpoints.  But before we get
 
1316
into the details of |vsplit|, we need to consider a few more basic things.
 
1317
 
 
1318
A subroutine called |prune_page_top| takes a pointer to a vlist and
 
1319
returns a pointer to a modified vlist in which all glue, kern, and penalty nodes
 
1320
have been deleted before the first box or rule node. However, the first
 
1321
box or rule is actually preceded by a newly created glue node designed so that
 
1322
the topmost baseline will be at distance |split_top_skip| from the top,
 
1323
whenever this is possible without backspacing.
 
1324
 
 
1325
When the second argument |s| is |false| the deleted nodes are destroyed,
 
1326
otherwise they are collected in a list starting at |split_disc|.
 
1327
*/
 
1328
 
 
1329
halfword prune_page_top(halfword p, boolean s)
 
1330
{
 
1331
    halfword prev_p;            /* lags one step behind |p| */
 
1332
    halfword q, r;              /* temporary variables for list manipulation */
 
1333
    prev_p = temp_head;
 
1334
    vlink(temp_head) = p;
 
1335
    r = null;
 
1336
    while (p != null) {
 
1337
        switch (type(p)) {
 
1338
        case hlist_node:
 
1339
        case vlist_node:
 
1340
        case rule_node:
 
1341
            /* Insert glue for |split_top_skip| and set~|p:=null| */
 
1342
            q = new_skip_param(split_top_skip_code);
 
1343
            vlink(prev_p) = q;
 
1344
            vlink(q) = p;       /* now |temp_ptr=glue_ptr(q)| */
 
1345
            if (width(temp_ptr) > height(p))
 
1346
                width(temp_ptr) = width(temp_ptr) - height(p);
 
1347
            else
 
1348
                width(temp_ptr) = 0;
 
1349
            p = null;
 
1350
            break;
 
1351
        case whatsit_node:
 
1352
        case mark_node:
 
1353
        case ins_node:
 
1354
            prev_p = p;
 
1355
            p = vlink(prev_p);
 
1356
            break;
 
1357
        case glue_node:
 
1358
        case kern_node:
 
1359
        case penalty_node:
 
1360
            q = p;
 
1361
            p = vlink(q);
 
1362
            vlink(q) = null;
 
1363
            vlink(prev_p) = p;
 
1364
            if (s) {
 
1365
                if (split_disc == null)
 
1366
                    split_disc = q;
 
1367
                else
 
1368
                    vlink(r) = q;
 
1369
                r = q;
 
1370
            } else {
 
1371
                flush_node_list(q);
 
1372
            }
 
1373
            break;
 
1374
        default:
 
1375
            confusion("pruning");
 
1376
            break;
 
1377
        }
 
1378
    }
 
1379
    return vlink(temp_head);
 
1380
}
 
1381
 
 
1382
/*
 
1383
The next subroutine finds the best place to break a given vertical list
 
1384
so as to obtain a box of height~|h|, with maximum depth~|d|.
 
1385
A pointer to the beginning of the vertical list is given,
 
1386
and a pointer to the optimum breakpoint is returned. The list is effectively
 
1387
followed by a forced break, i.e., a penalty node with the |eject_penalty|;
 
1388
if the best break occurs at this artificial node, the value |null| is returned.
 
1389
*/
 
1390
 
 
1391
scaled active_height[10];       /* distance from first active node to~|cur_p| */
 
1392
 
 
1393
/*
 
1394
An array of six |scaled| distances is used to keep track of the height
 
1395
from the beginning of the list to the current place, just as in |line_break|.
 
1396
In fact, we use one of the same arrays, only changing its name to reflect
 
1397
its new significance.
 
1398
*/
 
1399
 
 
1400
#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
 
1401
#define set_height_zero(A) active_height[A]=0   /* initialize the height to zero */
 
1402
 
 
1403
/*
 
1404
A global variable |best_height_plus_depth| will be set to the natural size
 
1405
of the box that corresponds to the optimum breakpoint found by |vert_break|.
 
1406
(This value is used by the insertion-splitting algorithm of the page builder.)
 
1407
*/
 
1408
 
 
1409
scaled best_height_plus_depth;  /* height of the best box, without stretching or shrinking */
 
1410
 
 
1411
halfword vert_break(halfword p, scaled h, scaled d)
 
1412
{                               /* finds optimum page break */
 
1413
    halfword prev_p;            /* if |p| is a glue node, |type(prev_p)| determines
 
1414
                                   whether |p| is a legal breakpoint */
 
1415
    halfword q, r;              /* glue specifications */
 
1416
    int pi;                     /* penalty value */
 
1417
    int b;                      /* badness at a trial breakpoint */
 
1418
    int least_cost;             /* the smallest badness plus penalties found so far */
 
1419
    halfword best_place;        /* the most recent break that leads to |least_cost| */
 
1420
    scaled prev_dp;             /* depth of previous box in the list */
 
1421
    int t;                      /* |type| of the node following a kern */
 
1422
    prev_p = p;                 /* an initial glue node is not a legal breakpoint */
 
1423
    least_cost = awful_bad;
 
1424
    do_all_six(set_height_zero);
 
1425
    prev_dp = 0;
 
1426
    best_place = null;
 
1427
    pi = 0;
 
1428
    while (1) {
 
1429
        /* If node |p| is a legal breakpoint, check if this break is
 
1430
           the best known, and |goto done| if |p| is null or
 
1431
           if the page-so-far is already too full to accept more stuff */
 
1432
        /* A subtle point to be noted here is that the maximum depth~|d| might be
 
1433
           negative, so |cur_height| and |prev_dp| might need to be corrected even
 
1434
           after a glue or kern node. */
 
1435
 
 
1436
        if (p == null) {
 
1437
            pi = eject_penalty;
 
1438
        } else {
 
1439
            /* Use node |p| to update the current height and depth measurements;
 
1440
               if this node is not a legal breakpoint, |goto not_found|
 
1441
               or |update_heights|,
 
1442
               otherwise set |pi| to the associated penalty at the break */
 
1443
            switch (type(p)) {
 
1444
            case hlist_node:
 
1445
            case vlist_node:
 
1446
            case rule_node:
 
1447
                cur_height = cur_height + prev_dp + height(p);
 
1448
                prev_dp = depth(p);
 
1449
                goto NOT_FOUND;
 
1450
                break;
 
1451
            case whatsit_node:
 
1452
                /* Process whatsit |p| in |vert_break| loop, |goto not_found| */
 
1453
                if ((subtype(p) == pdf_refxform_node)
 
1454
                    || (subtype(p) == pdf_refximage_node)) {
 
1455
                    cur_height = cur_height + prev_dp + height(p);
 
1456
                    prev_dp = depth(p);
 
1457
                }
 
1458
                goto NOT_FOUND;
 
1459
                break;
 
1460
            case glue_node:
 
1461
                if (precedes_break(prev_p))
 
1462
                    pi = 0;
 
1463
                else
 
1464
                    goto UPDATE_HEIGHTS;
 
1465
                break;
 
1466
            case kern_node:
 
1467
                if (vlink(p) == null)
 
1468
                    t = penalty_node;
 
1469
                else
 
1470
                    t = type(vlink(p));
 
1471
                if (t == glue_node)
 
1472
                    pi = 0;
 
1473
                else
 
1474
                    goto UPDATE_HEIGHTS;
 
1475
                break;
 
1476
            case penalty_node:
 
1477
                pi = penalty(p);
 
1478
                break;
 
1479
            case mark_node:
 
1480
            case ins_node:
 
1481
                goto NOT_FOUND;
 
1482
                break;
 
1483
            default:
 
1484
                confusion("vertbreak");
 
1485
                break;
 
1486
            }
 
1487
        }
 
1488
        /* Check if node |p| is a new champion breakpoint; then \(go)|goto done|
 
1489
           if |p| is a forced break or if the page-so-far is already too full */
 
1490
        if (pi < inf_penalty) {
 
1491
            /* Compute the badness, |b|, using |awful_bad| if the box is too full */
 
1492
            if (cur_height < h) {
 
1493
                if ((active_height[3] != 0) || (active_height[4] != 0) ||
 
1494
                    (active_height[5] != 0) || (active_height[6] != 0))
 
1495
                    b = 0;
 
1496
                else
 
1497
                    b = badness(h - cur_height, active_height[2]);
 
1498
            } else if (cur_height - h > active_height[7]) {
 
1499
                b = awful_bad;
 
1500
            } else {
 
1501
                b = badness(cur_height - h, active_height[7]);
 
1502
            }
 
1503
 
 
1504
            if (b < awful_bad) {
 
1505
                if (pi <= eject_penalty)
 
1506
                    b = pi;
 
1507
                else if (b < inf_bad)
 
1508
                    b = b + pi;
 
1509
                else
 
1510
                    b = deplorable;
 
1511
            }
 
1512
            if (b <= least_cost) {
 
1513
                best_place = p;
 
1514
                least_cost = b;
 
1515
                best_height_plus_depth = cur_height + prev_dp;
 
1516
            }
 
1517
            if ((b == awful_bad) || (pi <= eject_penalty))
 
1518
                goto DONE;
 
1519
        }
 
1520
 
 
1521
        if ((type(p) < glue_node) || (type(p) > kern_node))
 
1522
            goto NOT_FOUND;
 
1523
      UPDATE_HEIGHTS:
 
1524
        /* Update the current height and depth measurements with
 
1525
           respect to a glue or kern node~|p| */
 
1526
        /* Vertical lists that are subject to the |vert_break| procedure should not
 
1527
           contain infinite shrinkability, since that would permit any amount of
 
1528
           information to ``fit'' on one page. */
 
1529
 
 
1530
        if (type(p) == kern_node) {
 
1531
            q = p;
 
1532
        } else {
 
1533
            q = glue_ptr(p);
 
1534
            active_height[2 + stretch_order(q)] += stretch(q);
 
1535
            active_height[7] += shrink(q);
 
1536
            if ((shrink_order(q) != normal) && (shrink(q) != 0)) {
 
1537
                print_err("Infinite glue shrinkage found in box being split");
 
1538
                help4("The box you are \\vsplitting contains some infinitely",
 
1539
                      "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
 
1540
                      "Such glue doesn't belong there; but you can safely proceed,",
 
1541
                      "since the offensive shrinkability has been made finite.");
 
1542
                error();
 
1543
                r = new_spec(q);
 
1544
                shrink_order(r) = normal;
 
1545
                delete_glue_ref(q);
 
1546
                glue_ptr(p) = r;
 
1547
                q = r;
 
1548
            }
 
1549
        }
 
1550
        cur_height = cur_height + prev_dp + width(q);
 
1551
        prev_dp = 0;
 
1552
 
 
1553
      NOT_FOUND:
 
1554
        if (prev_dp > d) {
 
1555
            cur_height = cur_height + prev_dp - d;
 
1556
            prev_dp = d;
 
1557
        }
 
1558
 
 
1559
        prev_p = p;
 
1560
        p = vlink(prev_p);
 
1561
    }
 
1562
  DONE:
 
1563
    return best_place;
 
1564
}
 
1565
 
 
1566
/*
 
1567
Now we are ready to consider |vsplit| itself. Most of
 
1568
its work is accomplished by the two subroutines that we have just considered.
 
1569
 
 
1570
Given the number of a vlist box |n|, and given a desired page height |h|,
 
1571
the |vsplit| function finds the best initial segment of the vlist and
 
1572
returns a box for a page of height~|h|. The remainder of the vlist, if
 
1573
any, replaces the original box, after removing glue and penalties and
 
1574
adjusting for |split_top_skip|. Mark nodes in the split-off box are used to
 
1575
set the values of |split_first_mark| and |split_bot_mark|; we use the
 
1576
fact that |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|.
 
1577
 
 
1578
The original box becomes ``void'' if and only if it has been entirely
 
1579
extracted.  The extracted box is ``void'' if and only if the original
 
1580
box was void (or if it was, erroneously, an hlist box).
 
1581
*/
 
1582
 
 
1583
halfword vsplit(halfword n, scaled h)
 
1584
{                               /* extracts a page of height |h| from box |n| */
 
1585
    halfword v;                 /* the box to be split */
 
1586
    int vdir;                   /* the direction of the box to be split */
 
1587
    halfword p;                 /* runs through the vlist */
 
1588
    halfword q;                 /* points to where the break occurs */
 
1589
    halfword i;                 /* for traversing marks lists */
 
1590
    v = box(n);
 
1591
    vdir = box_dir(v);
 
1592
    flush_node_list(split_disc);
 
1593
    split_disc = null;
 
1594
    for (i = 0; i <= biggest_used_mark; i++) {
 
1595
        delete_split_first_mark(i);
 
1596
        delete_split_bot_mark(i);
 
1597
    }
 
1598
    /* Dispense with trivial cases of void or bad boxes */
 
1599
    if (v == null) {
 
1600
        return null;
 
1601
    }
 
1602
    if (type(v) != vlist_node) {
 
1603
        print_err("\\vsplit needs a \\vbox");
 
1604
        help2("The box you are trying to split is an \\hbox.",
 
1605
              "i can't split such a box, so I''ll leave it alone.");
 
1606
        error();
 
1607
        return null;
 
1608
    }
 
1609
 
 
1610
    q = vert_break(list_ptr(v), h, dimen_par(split_max_depth_code));
 
1611
    /* Look at all the marks in nodes before the break, and set the final
 
1612
       link to |null| at the break */
 
1613
    /* It's possible that the box begins with a penalty node that is the
 
1614
       ``best'' break, so we must be careful to handle this special case correctly. */
 
1615
 
 
1616
    p = list_ptr(v);
 
1617
    if (p == q) {
 
1618
        list_ptr(v) = null;
 
1619
    } else {
 
1620
        while (1) {
 
1621
            if (type(p) == mark_node) {
 
1622
                if (split_first_mark(mark_class(p)) == null) {
 
1623
                    set_split_first_mark(mark_class(p), mark_ptr(p));
 
1624
                    set_split_bot_mark(mark_class(p),
 
1625
                                       split_first_mark(mark_class(p)));
 
1626
                    set_token_ref_count(split_first_mark(mark_class(p)),
 
1627
                                        token_ref_count(split_first_mark
 
1628
                                                        (mark_class(p))) + 2);
 
1629
                } else {
 
1630
                    delete_token_ref(split_bot_mark(mark_class(p)));
 
1631
                    set_split_bot_mark(mark_class(p), mark_ptr(p));
 
1632
                    add_token_ref(split_bot_mark(mark_class(p)));
 
1633
                }
 
1634
            }
 
1635
            if (vlink(p) == q) {
 
1636
                vlink(p) = null;
 
1637
                break;
 
1638
            }
 
1639
            p = vlink(p);
 
1640
        }
 
1641
    }
 
1642
 
 
1643
    q = prune_page_top(q, int_par(saving_vdiscards_code) > 0);
 
1644
    p = list_ptr(v);
 
1645
    list_ptr(v) = null;
 
1646
    flush_node(v);
 
1647
    if (q == null)
 
1648
        box(n) = null;          /* the |eq_level| of the box stays the same */
 
1649
    else
 
1650
        box(n) =
 
1651
            filtered_vpackage(q, 0, additional, dimen_par(max_depth_code),
 
1652
                              split_keep_group, vdir);
 
1653
    return filtered_vpackage(p, h, exactly,
 
1654
                             dimen_par(split_max_depth_code), split_off_group,
 
1655
                             vdir);
 
1656
}
 
1657
 
 
1658
/*
 
1659
Now that we can see what eventually happens to boxes, we can consider
 
1660
the first steps in their creation. The |begin_box| routine is called when
 
1661
|box_context| is a context specification, |cur_chr| specifies the type of
 
1662
box desired, and |cur_cmd=make_box|.
 
1663
*/
 
1664
 
 
1665
void begin_box(int box_context)
 
1666
{
 
1667
    halfword q;                 /* run through the current list */
 
1668
    halfword k;                 /* 0 or |vmode| or |hmode| */
 
1669
    int n;                      /* a box number */
 
1670
    int spec_direction = -1;
 
1671
    switch (cur_chr) {
 
1672
    case box_code:
 
1673
        scan_register_num();
 
1674
        cur_box = box(cur_val);
 
1675
        box(cur_val) = null;    /* the box becomes void, at the same level */
 
1676
        break;
 
1677
    case copy_code:
 
1678
        scan_register_num();
 
1679
        cur_box = copy_node_list(box(cur_val));
 
1680
        break;
 
1681
    case last_box_code:
 
1682
        /* If the current list ends with a box node, delete it from
 
1683
           the list and make |cur_box| point to it; otherwise set |cur_box:=null| */
 
1684
        cur_box = null;
 
1685
        if (abs(cur_list.mode_field) == mmode) {
 
1686
            you_cant();
 
1687
            help1("Sorry; this \\lastbox will be void.");
 
1688
            error();
 
1689
        } else if ((cur_list.mode_field == vmode)
 
1690
                   && (cur_list.head_field == cur_list.tail_field)) {
 
1691
            you_cant();
 
1692
            help2("Sorry...I usually can't take things from the current page.",
 
1693
                  "This \\lastbox will therefore be void.");
 
1694
            error();
 
1695
        } else {
 
1696
            if (cur_list.head_field != cur_list.tail_field) {   /* todo: new code,  needs testing */
 
1697
                if ((type(cur_list.tail_field) == hlist_node)
 
1698
                    || (type(cur_list.tail_field) == vlist_node)) {
 
1699
                    /* Remove the last box ... */
 
1700
                    q = alink(cur_list.tail_field);
 
1701
                    if (q == null || vlink(q) != cur_list.tail_field) {
 
1702
                        q = cur_list.head_field;
 
1703
                        while (vlink(q) != cur_list.tail_field)
 
1704
                            q = vlink(q);
 
1705
                    }
 
1706
                    uncouple_node(cur_list.tail_field);
 
1707
                    cur_box = cur_list.tail_field;
 
1708
                    shift_amount(cur_box) = 0;
 
1709
                    cur_list.tail_field = q;
 
1710
                    vlink(cur_list.tail_field) = null;
 
1711
                }
 
1712
            }
 
1713
        }
 
1714
        break;
 
1715
    case vsplit_code:
 
1716
        /* Split off part of a vertical box, make |cur_box| point to it */
 
1717
        /* Here we deal with things like `\.{\\vsplit 13 to 100pt}'. */
 
1718
        scan_register_num();
 
1719
        n = cur_val;
 
1720
        if (!scan_keyword("to")) {
 
1721
            print_err("Missing `to' inserted");
 
1722
            help2("I'm working on `\\vsplit<box number> to <dimen>';",
 
1723
                  "will look for the <dimen> next.");
 
1724
            error();
 
1725
        }
 
1726
        scan_normal_dimen();
 
1727
        cur_box = vsplit(n, cur_val);
 
1728
        break;
 
1729
    default:
 
1730
        /* Initiate the construction of an hbox or vbox, then |return| */
 
1731
        /* Here is where we enter restricted horizontal mode or internal vertical
 
1732
           mode, in order to make a box. */
 
1733
        k = cur_chr - vtop_code;
 
1734
        set_saved_record(0, saved_boxcontext, 0, box_context);
 
1735
        switch (abs(cur_list.mode_field)) {
 
1736
        case vmode:
 
1737
            spec_direction = body_direction;
 
1738
            break;
 
1739
        case hmode:
 
1740
            spec_direction = text_direction;
 
1741
            break;
 
1742
        case mmode:
 
1743
            spec_direction = math_direction;
 
1744
            break;
 
1745
        }
 
1746
        if (k == hmode) {
 
1747
            if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
 
1748
                scan_full_spec(adjusted_hbox_group, spec_direction);
 
1749
            else
 
1750
                scan_full_spec(hbox_group, spec_direction);
 
1751
        } else {
 
1752
            if (k == vmode) {
 
1753
                scan_full_spec(vbox_group, spec_direction);
 
1754
            } else {
 
1755
                scan_full_spec(vtop_group, spec_direction);
 
1756
                k = vmode;
 
1757
            }
 
1758
            normal_paragraph();
 
1759
        }
 
1760
        push_nest();
 
1761
        cur_list.mode_field = -k;
 
1762
        if (k == vmode) {
 
1763
            prev_depth = dimen_par(pdf_ignored_dimen_code);
 
1764
            if (every_vbox != null)
 
1765
                begin_token_list(every_vbox, every_vbox_text);
 
1766
        } else {
 
1767
            space_factor = 1000;
 
1768
            if (every_hbox != null)
 
1769
                begin_token_list(every_hbox, every_hbox_text);
 
1770
        }
 
1771
        return;
 
1772
        break;
 
1773
    }
 
1774
    box_end(box_context);       /* in simple cases, we use the box immediately */
 
1775
}