3
Copyright 2009 Taco Hoekwater <taco@luatex.org>
5
This file is part of LuaTeX.
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.
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.
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/>. */
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 $";
26
#define scan_normal_dimen() scan_dimen(false,false,false)
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
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)
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.
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
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|.
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|.
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}}$$
80
void scan_spec(group_code c)
81
{ /* scans a box specification and left brace */
83
if (scan_keyword("to")) {
86
} else if (scan_keyword("spread")) {
87
spec_code = additional;
90
spec_code = additional;
93
set_saved_record(0, saved_boxspec, spec_code, cur_val);
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.
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
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 */
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 */
126
while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
128
if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd)
131
if (scan_keyword("attr")) {
134
scan_optional_equals();
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 */
141
attr_list = do_set_attribute(attr_list, i, v);
144
if (scan_keyword("dir")) {
146
spec_direction = cur_val;
149
if (attr_list == attr_list_cache) {
150
add_node_attr_ref(attr_list);
152
if (scan_keyword("to")) {
154
} else if (scan_keyword("spread")) {
155
spec_code = additional;
157
spec_code = additional;
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);
170
set_saved_record(2, saved_boxdir, spec_direction, null);
172
set_saved_record(3, saved_boxattr, 0, attr_list);
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);
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.
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.)
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}.
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 */
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|.
212
halfword adjust_tail; /* tail of adjustment list */
215
Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
216
|pre_adjust_tail| instead of |adjust_tail|.
219
halfword pre_adjust_tail;
221
int font_expand_ratio; /* current expansion ratio */
222
halfword last_leftmost_char;
223
halfword last_rightmost_char;
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 */
228
/* This procedure is called repeatedly from inside the line break algorithm. */
230
void set_prev_char_p(halfword p)
235
scaled char_stretch(halfword p)
237
internal_font_number k;
240
internal_font_number f;
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);
249
return round_xn_over_d(dw, ef, 1000);
254
scaled char_shrink(halfword p)
256
internal_font_number k;
259
internal_font_number f;
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);
268
return round_xn_over_d(dw, ef, 1000);
273
scaled kern_stretch(halfword p)
277
if ((prev_char_p == null) || (vlink(prev_char_p) != p)
278
|| (vlink(p) == null))
282
if (!((is_char_node(l) && is_char_node(r) &&
283
(font(l) == font(r)) && (pdf_font_stretch(font(l)) != null_font))))
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);
290
scaled kern_shrink(halfword p)
294
if ((prev_char_p == null) || (vlink(prev_char_p) != p)
295
|| (vlink(p) == null))
299
if (!((is_char_node(l) && is_char_node(r) &&
300
(font(l) == font(r)) && (pdf_font_shrink(font(l)) != null_font))))
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);
307
void do_subst_font(halfword p, int ex_ratio)
309
internal_font_number f, k;
312
if (type(p) == disc_node) {
313
r = vlink(pre_break(p));
316
do_subst_font(r, ex_ratio);
319
r = vlink(post_break(p));
322
do_subst_font(r, ex_ratio);
325
r = vlink(no_break(p));
328
do_subst_font(r, ex_ratio);
333
if (is_char_node(p)) {
336
pdf_error("font expansion", "invalid node type");
340
ef = get_ef_code(f, character(r));
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
348
} else if ((pdf_font_expand_ratio(f) == 0)
349
&& (pdf_font_shrink(f) != null_font) && (ex_ratio < 0)) {
351
ext_xn_over_d(ex_ratio * ef,
352
-pdf_font_expand_ratio(pdf_font_shrink
359
if (!is_char_node(p)) { /* todo: this should be: if(is_ligature()) */
369
scaled char_pw(halfword p, int side)
371
internal_font_number f;
373
if (side == left_side)
374
last_leftmost_char = null;
376
last_rightmost_char = null;
379
if (!is_char_node(p))
382
if (side == left_side) {
383
c = get_lp_code(f, character(p));
384
last_leftmost_char = p;
386
c = get_rp_code(f, character(p));
387
last_rightmost_char = p;
391
return round_xn_over_d(quad(f), c, 1000);
394
halfword new_margin_kern(scaled w, halfword p, int side)
397
k = new_node(margin_kern_node, side);
400
pdf_error("margin kerning", "invalid pointer to marginal char node");
401
q = new_char(font(p), character(p));
407
Here is |hpack|, which is place where we do font substituting when
408
font expansion is being used.
411
halfword hpack(halfword p, scaled w, int m, int pack_direction)
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 */
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 */
425
halfword pack_interrupt[8];
430
r = new_node(hlist_node, min_quarterword);
431
if (pack_direction == -1) {
432
box_dir(r) = text_direction;
434
box_dir(r) = pack_direction;
437
hpack_dir = box_dir(r);
442
if (m == cal_expand_ratio) {
446
font_expand_ratio = 0;
449
/* Clear dimensions to zero */
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;
465
while ((p != null) || (disc_level > 0)) {
468
p = pack_interrupt[disc_level];
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.
481
if (m >= cal_expand_ratio) {
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);
491
whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
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. */
508
whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
518
if (type(p) >= rule_node)
522
if (height(p) - s > h)
524
if (depth(p) + s > d)
530
if (adjust_tail != null) {
531
/* Transfer node |p| to the adjustment list */
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
539
while (vlink(q) != p)
541
if (type(p) == adjust_node) {
542
if (adjust_pre(p) != 0)
543
update_adjust_list(pre_adjust_tail);
545
update_adjust_list(adjust_tail);
547
adjust_ptr(vlink(q)) = null;
548
flush_node(vlink(q));
550
vlink(adjust_tail) = p;
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);
568
hpack_dir = dir_dir(dir_ptr);
572
if ((subtype(p) == pdf_refxform_node)
573
|| (subtype(p) == pdf_refximage_node)) {
576
if (height(p) - s > h)
578
if (depth(p) + s > d)
584
/* Incorporate glue into the horizontal totals */
587
o = stretch_order(g);
588
total_stretch[o] = total_stretch[o] + stretch(g);
590
total_shrink[o] = total_shrink[o] + shrink(g);
591
if (subtype(p) >= a_leaders) {
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)))
605
font_stretch - width(p) - char_pw(margin_char(p),
607
font(margin_char(p)) = f;
608
do_subst_font(margin_char(p), -1000);
609
if (f != font(margin_char(p)))
611
font_shrink - width(p) - char_pw(margin_char(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));
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)
628
else if (font_expand_ratio < 0)
633
width(p) = get_kern(font(prev_char_p),
634
character(prev_char_p),
635
character(vlink(p)));
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);
660
if (adjust_tail != null)
661
vlink(adjust_tail) = null;
662
if (pre_adjust_tail != null)
663
vlink(pre_adjust_tail) = null;
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. */
673
x = w - x; /* now |x| is the excess to be made up */
675
glue_sign(r) = normal;
676
glue_order(r) = normal;
677
set_glue_ratio_zero(glue_set(r));
680
/* Determine horizontal glue stretch setting, then |return|
681
or \hbox{|goto common_ending|} */
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. */
686
/* Determine the stretch order */
687
if (total_stretch[filll] != 0)
689
else if (total_stretch[fill] != 0)
691
else if (total_stretch[fil] != 0)
693
else if (total_stretch[sfi] != 0)
698
if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
699
font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
703
glue_sign(r) = stretching;
704
if (total_stretch[o] != 0) {
705
glue_set(r) = unfloat((double) x / total_stretch[o]);
707
glue_sign(r) = normal;
708
set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */
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)) {
717
if (last_badness > 100)
718
tprint_nl("Underfull \\hbox (badness ");
720
tprint_nl("Loose \\hbox (badness ");
721
print_int(last_badness);
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)
733
else if (total_shrink[fill] != 0)
735
else if (total_shrink[fil] != 0)
737
else if (total_shrink[sfi] != 0)
742
if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
743
font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
747
glue_sign(r) = shrinking;
748
if (total_shrink[o] != 0) {
749
glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
751
glue_sign(r) = normal;
752
set_glue_ratio_zero(glue_set(r)); /* there's nothing to shrink */
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)
765
vlink(q) = new_rule();
766
rule_dir(vlink(q)) = box_dir(r);
767
width(vlink(q)) = dimen_par(overfull_rule_code);
770
tprint_nl("Overfull \\hbox (");
771
print_scaled(-x - total_shrink[normal]);
772
tprint("pt too wide");
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)) {
781
tprint_nl("Tight \\hbox (badness ");
782
print_int(last_badness);
791
/* Finish issuing a diagnostic message for an overfull or underfull hbox */
793
tprint(") has occurred while \\output is active");
795
if (pack_begin_line != 0) {
796
if (pack_begin_line > 0)
797
tprint(") in paragraph at lines ");
799
tprint(") in alignment at lines ");
800
print_int(abs(pack_begin_line));
803
tprint(") detected at line ");
809
font_in_short_display = null_font;
810
short_display(list_ptr(r));
814
end_diagnostic(true);
817
if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
818
font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
822
r = hpack(q, w, subst_ex_font, hpack_dir);
824
while (dir_ptr != null)
829
halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp,
833
new_hyphenation(p, qt);
834
(void) new_ligkern(p, qt); /* don't care about the tail in this case */
836
q = lua_hpack_filter(q, w, m, grp, pac);
837
return hpack(q, w, m, pac);
840
/* here is a function to calculate the natural whd of a (horizontal) node list */
842
scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
843
int g_sign, int g_order, int pack_direction)
845
scaled s; /* shift amount */
846
halfword g; /* points to a glue specification */
847
internal_font_number f; /* the font in a |char_node| */
849
scaled_whd xx; /* for recursion */
850
scaled_whd whd, siz = { 0, 0, 0 };
851
if (pack_direction == -1) {
852
hpack_dir = text_direction;
854
hpack_dir = pack_direction;
856
while (p != pp && p != null) {
857
while (is_char_node(p) && p != pp) {
859
whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
867
if (p != pp && p != null) {
872
whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
874
if (whd.ht - s > siz.ht)
876
if (whd.dp + s > siz.dp)
882
if (type(p) >= rule_node)
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;
892
if ((subtype(p) == pdf_refxform_node)
893
|| (subtype(p) == pdf_refximage_node)) {
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;
905
if (g_sign != normal) {
906
if (g_sign == stretching) {
907
if (stretch_order(g) == g_order) {
909
float_round(float_cast(g_mult) * stretch(g));
911
} else if (shrink_order(g) == g_order) {
912
siz.wd -= float_round(float_cast(g_mult) * shrink(g));
915
if (subtype(p) >= a_leaders) {
917
if (height(g) > siz.ht)
919
if (depth(g) > siz.dp)
923
case margin_kern_node:
928
siz.wd += surround(p);
931
xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order,
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.
956
int pack_begin_line; /* source file line where the current paragraph
957
or alignment began; a negative value denotes alignment */
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.
968
halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
970
halfword r; /* the box node that will be returned */
971
scaled w, d, x; /* width, depth, and natural height */
973
scaled s; /* shift amount */
974
halfword g; /* points to a glue specification */
975
int o; /* order of infinity */
977
r = new_node(vlist_node, 0);
978
if (pack_direction == -1) {
979
box_dir(r) = body_direction;
981
box_dir(r) = pack_direction;
983
subtype(r) = min_quarterword;
987
/* Clear dimensions to zero */
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;
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)) {
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);
1023
if (type(p) >= rule_node)
1026
s = shift_amount(p);
1027
if (width(p) + s > w)
1031
/* Incorporate a whatsit node into a vbox */
1032
if ((subtype(p) == pdf_refxform_node)
1033
|| (subtype(p) == pdf_refximage_node)) {
1037
if (width(p) + s > w)
1042
/* Incorporate glue into the vertical totals */
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) {
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)
1081
x = h - x; /* now |x| is the excess to be made up */
1083
glue_sign(r) = normal;
1084
glue_order(r) = normal;
1085
set_glue_ratio_zero(glue_set(r));
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)
1093
else if (total_stretch[fill] != 0)
1095
else if (total_stretch[fil] != 0)
1097
else if (total_stretch[sfi] != 0)
1103
glue_sign(r) = stretching;
1104
if (total_stretch[o] != 0) {
1105
glue_set(r) = unfloat((double) x / total_stretch[o]);
1107
glue_sign(r) = normal;
1108
set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */
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)) {
1117
if (last_badness > 100)
1118
tprint_nl("Underfull \\vbox (badness ");
1120
tprint_nl("Loose \\vbox (badness ");
1121
print_int(last_badness);
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)
1134
else if (total_shrink[fill] != 0)
1136
else if (total_shrink[fil] != 0)
1138
else if (total_shrink[sfi] != 0)
1144
glue_sign(r) = shrinking;
1145
if (total_shrink[o] != 0) {
1146
glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
1148
glue_sign(r) = normal;
1149
set_glue_ratio_zero(glue_set(r)); /* there's nothing to shrink */
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)) {
1158
tprint_nl("Overfull \\vbox (");
1159
print_scaled(-x - total_shrink[normal]);
1160
tprint("pt too high");
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)) {
1169
tprint_nl("Tight \\vbox (badness ");
1170
print_int(last_badness);
1179
/* Finish issuing a diagnostic message or an overfull or underfull vbox */
1180
if (output_active) {
1181
tprint(") has occurred while \\output is active");
1183
if (pack_begin_line != 0) { /* it's actually negative */
1184
tprint(") in alignment at lines ");
1185
print_int(abs(pack_begin_line));
1188
tprint(") detected at line ");
1195
end_diagnostic(true);
1199
halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp,
1204
q = lua_vpack_filter(q, h, m, l, grp, pack_direction);
1205
return vpackage(q, h, m, l, pack_direction);
1208
void finish_vcenter(void)
1213
p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
1215
p = math_vcenter_group(p);
1221
scaled h; /* height of box */
1222
halfword p; /* first node in a box */
1223
scaled d; /* max depth */
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;
1235
cur_box = filtered_vpackage(vlink(cur_list.head_field),
1236
saved_value(1), saved_level(1), d, grp,
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.
1246
p = list_ptr(cur_box);
1248
if (type(p) <= rule_node)
1250
depth(cur_box) = depth(cur_box) - h + height(cur_box);
1251
height(cur_box) = h;
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);
1261
replace_attribute_list(cur_box, saved_value(3));
1263
box_end(saved_value(0));
1268
When a box is being appended to the current vertical list, the
1269
baselineskip calculation is handled by the |append_to_vlist| routine.
1272
void append_to_vlist(halfword b)
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);
1280
d = width(glue_par(baseline_skip_code)) - prev_depth - height(b);
1282
if (d < dimen_par(line_skip_limit_code)) {
1283
p = new_param_glue(line_skip_code);
1285
p = new_skip_param(baseline_skip_code);
1286
width(temp_ptr) = d; /* |temp_ptr=glue_ptr(p)| */
1288
couple_nodes(cur_list.tail_field, p);
1289
cur_list.tail_field = p;
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);
1296
prev_depth = depth(b);
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.
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} */
1309
halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */
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.
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.
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|.
1329
halfword prune_page_top(halfword p, boolean s)
1331
halfword prev_p; /* lags one step behind |p| */
1332
halfword q, r; /* temporary variables for list manipulation */
1334
vlink(temp_head) = p;
1341
/* Insert glue for |split_top_skip| and set~|p:=null| */
1342
q = new_skip_param(split_top_skip_code);
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);
1348
width(temp_ptr) = 0;
1365
if (split_disc == null)
1375
confusion("pruning");
1379
return vlink(temp_head);
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.
1391
scaled active_height[10]; /* distance from first active node to~|cur_p| */
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.
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 */
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.)
1409
scaled best_height_plus_depth; /* height of the best box, without stretching or shrinking */
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);
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. */
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 */
1447
cur_height = cur_height + prev_dp + height(p);
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);
1461
if (precedes_break(prev_p))
1464
goto UPDATE_HEIGHTS;
1467
if (vlink(p) == null)
1474
goto UPDATE_HEIGHTS;
1484
confusion("vertbreak");
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))
1497
b = badness(h - cur_height, active_height[2]);
1498
} else if (cur_height - h > active_height[7]) {
1501
b = badness(cur_height - h, active_height[7]);
1504
if (b < awful_bad) {
1505
if (pi <= eject_penalty)
1507
else if (b < inf_bad)
1512
if (b <= least_cost) {
1515
best_height_plus_depth = cur_height + prev_dp;
1517
if ((b == awful_bad) || (pi <= eject_penalty))
1521
if ((type(p) < glue_node) || (type(p) > kern_node))
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. */
1530
if (type(p) == kern_node) {
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.");
1544
shrink_order(r) = normal;
1550
cur_height = cur_height + prev_dp + width(q);
1555
cur_height = cur_height + prev_dp - d;
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.
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|.
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).
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 */
1592
flush_node_list(split_disc);
1594
for (i = 0; i <= biggest_used_mark; i++) {
1595
delete_split_first_mark(i);
1596
delete_split_bot_mark(i);
1598
/* Dispense with trivial cases of void or bad boxes */
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.");
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. */
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);
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)));
1635
if (vlink(p) == q) {
1643
q = prune_page_top(q, int_par(saving_vdiscards_code) > 0);
1648
box(n) = null; /* the |eq_level| of the box stays the same */
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,
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|.
1665
void begin_box(int box_context)
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;
1673
scan_register_num();
1674
cur_box = box(cur_val);
1675
box(cur_val) = null; /* the box becomes void, at the same level */
1678
scan_register_num();
1679
cur_box = copy_node_list(box(cur_val));
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| */
1685
if (abs(cur_list.mode_field) == mmode) {
1687
help1("Sorry; this \\lastbox will be void.");
1689
} else if ((cur_list.mode_field == vmode)
1690
&& (cur_list.head_field == cur_list.tail_field)) {
1692
help2("Sorry...I usually can't take things from the current page.",
1693
"This \\lastbox will therefore be void.");
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)
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;
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();
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.");
1726
scan_normal_dimen();
1727
cur_box = vsplit(n, cur_val);
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)) {
1737
spec_direction = body_direction;
1740
spec_direction = text_direction;
1743
spec_direction = math_direction;
1747
if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
1748
scan_full_spec(adjusted_hbox_group, spec_direction);
1750
scan_full_spec(hbox_group, spec_direction);
1753
scan_full_spec(vbox_group, spec_direction);
1755
scan_full_spec(vtop_group, spec_direction);
1761
cur_list.mode_field = -k;
1763
prev_depth = dimen_par(pdf_ignored_dimen_code);
1764
if (every_vbox != null)
1765
begin_token_list(every_vbox, every_vbox_text);
1767
space_factor = 1000;
1768
if (every_hbox != null)
1769
begin_token_list(every_hbox, every_hbox_text);
1774
box_end(box_context); /* in simple cases, we use the box immediately */