boundary nodes made consistent (cleanup and document): WARNING: bump the format numbe...
[luatex.git] / source / texk / web2c / luatexdir / tex / packaging.w
blob5b9aa8b89957263428b3a1618008536ce48dc839
1 % packaging.w
3 % Copyright 2009-2010 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/>.
20 @ @c
22 #include "ptexlib.h"
24 @ @c
25 #define scan_normal_dimen() scan_dimen(false,false,false)
27 #define prev_depth cur_list.prev_depth_field
28 #define space_factor cur_list.space_factor_field
29 #define box(A) eqtb[box_base+(A)].hh.rh
31 #define every_hbox equiv(every_hbox_loc)
32 #define every_vbox equiv(every_vbox_loc)
33 #define box_max_depth dimen_par(box_max_depth_code)
35 @ We're essentially done with the parts of \TeX\ that are concerned with the
36 input (|get_next|) and the output (|ship_out|). So it's time to get heavily into
37 the remaining part, which does the real work of typesetting.
39 After lists are constructed, \TeX\ wraps them up and puts them into boxes. Two
40 major subroutines are given the responsibility for this task: |hpack| applies to
41 horizontal lists (hlists) and |vpack| applies to vertical lists (vlists). The
42 main duty of |hpack| and |vpack| is to compute the dimensions of the resulting
43 boxes, and to adjust the glue if one of those dimensions is pre-specified. The
44 computed sizes normally enclose all of the material inside the new box; but some
45 items may stick out if negative glue is used, if the box is overfull, or if a
46 \.{\\vbox} includes other boxes that have been shifted left.
48 The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a box
49 containing the hlist that starts at |p|. Parameter |w| specifies a width; and
50 parameter |m| is either `|exactly|' or `|additional|'. Thus, |hpack(p,w,exactly)|
51 produces a box whose width is exactly |w|, while |hpack(p,w,additional)| yields a
52 box whose width is the natural width plus |w|. It is convenient to define a macro
53 called `|natural|' to cover the most common case, so that we can say
54 |hpack(p,natural)| to get a box that has the natural width of list |p|.
56 Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box
57 containing the vlist that starts at |p|. In this case |w| represents a height
58 instead of a width; the parameter |m| is interpreted as in |hpack|.
60 @ The parameters to |hpack| and |vpack| correspond to \TeX's primitives like
61 `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that
62 `\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox}
63 \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in the
64 user's input, including the mandatory left brace that follows them, and it puts
65 the specification onto |save_stack| so that the desired box can later be obtained
66 by executing the following code: $$\vbox{\halign{#\hfil\cr
67 |save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$
71 void scan_spec(group_code c)
73 int spec_code;
74 if (scan_keyword("to")) {
75 spec_code = exactly;
76 scan_normal_dimen();
77 } else if (scan_keyword("spread")) {
78 spec_code = additional;
79 scan_normal_dimen();
80 } else {
81 spec_code = additional;
82 cur_val = 0;
84 set_saved_record(0, saved_boxspec, spec_code, cur_val);
85 save_ptr++;
86 new_save_level(c);
87 scan_left_brace();
91 void scan_spec(group_code c)
92 { /* scans a box specification and left brace */
93 int spec_code;
94 boolean done = false ;
95 do {
96 get_x_token();
97 } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
98 if (cur_cmd == left_brace_cmd) {
99 spec_code = additional;
100 cur_val = 0;
101 done = true;
102 } else {
103 /* todo: attr */
104 back_input();
105 if (scan_keyword("to")) {
106 spec_code = exactly;
107 scan_normal_dimen();
108 } else if (scan_keyword("spread")) {
109 spec_code = additional;
110 scan_normal_dimen();
111 } else {
112 spec_code = additional;
113 cur_val = 0;
116 set_saved_record(0, saved_boxspec, spec_code, cur_val);
117 save_ptr++;
118 new_save_level(c);
119 if (!done) {
120 scan_left_brace();
124 @ When scanning, special care is necessary to ensure that the special
125 |save_stack| codes are placed just below the new group code, because scanning can
126 change |save_stack| when \.{\\csname} appears.
128 This coincides with the text on |dir| and |attr| keywords, as these are exaclty
129 the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input stream (the
130 others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}).
134 void scan_full_spec(group_code c, int spec_direction)
136 int s;
137 int i;
138 int v;
139 int spec_code;
140 halfword attr_list;
141 if (attr_list_cache == cache_disabled)
142 update_attribute_cache();
143 attr_list = attr_list_cache;
144 s = saved_value(0);
145 CONTINUE:
146 while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
147 get_x_token();
148 if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd)
149 back_input();
151 if (scan_keyword("attr")) {
152 scan_register_num();
153 i = cur_val;
154 scan_optional_equals();
155 scan_int();
156 v = cur_val;
157 if ((attr_list != null) && (attr_list == attr_list_cache)) {
158 attr_list = copy_attribute_list(attr_list_cache);
159 add_node_attr_ref(attr_list);
161 attr_list = do_set_attribute(attr_list, i, v);
162 goto CONTINUE;
164 if (scan_keyword("dir")) {
165 scan_direction();
166 spec_direction = cur_val;
167 goto CONTINUE;
169 if (attr_list == attr_list_cache) {
170 add_node_attr_ref(attr_list);
172 if (scan_keyword("to")) {
173 spec_code = exactly;
174 } else if (scan_keyword("spread")) {
175 spec_code = additional;
176 } else {
177 spec_code = additional;
178 cur_val = 0;
179 goto FOUND;
181 scan_normal_dimen();
182 FOUND:
183 set_saved_record(0, saved_boxcontext, 0, s);
184 set_saved_record(1, saved_boxspec, spec_code, cur_val);
185 if (spec_direction != -1) {
186 set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
187 text_dir_ptr = new_dir(spec_direction);
188 } else {
189 set_saved_record(2, saved_boxdir, spec_direction, null);
191 set_saved_record(3, saved_boxattr, 0, attr_list);
192 save_ptr += 4;
193 new_save_level(c);
194 scan_left_brace();
195 eq_word_define(int_base + body_direction_code, spec_direction);
196 eq_word_define(int_base + par_direction_code, spec_direction);
197 eq_word_define(int_base + text_direction_code, spec_direction);
201 /* scans a box specification and left brace */
203 void scan_full_spec(group_code c, int spec_direction, int just_pack)
205 int s;
206 int i;
207 int v;
208 int spec_code;
209 boolean done = false ;
210 halfword attr_list;
211 if (attr_list_cache == cache_disabled)
212 update_attribute_cache();
213 attr_list = attr_list_cache;
214 s = saved_value(0); /* the box context */
215 do {
216 get_x_token();
217 } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
218 if (cur_cmd == left_brace_cmd) {
219 goto QUICK;
220 } else {
221 back_input();
222 goto KEYWORDS;
224 CONTINUE:
225 while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
226 get_x_token();
227 if (cur_cmd == left_brace_cmd) {
228 goto QUICK;
229 } else if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) {
230 back_input();
231 break;
234 KEYWORDS:
235 if (scan_keyword("attr")) {
236 scan_register_num();
237 i = cur_val;
238 scan_optional_equals();
239 scan_int();
240 v = cur_val;
241 if ((attr_list != null) && (attr_list == attr_list_cache)) {
242 attr_list = copy_attribute_list(attr_list_cache);
243 add_node_attr_ref(attr_list); /* will be used once */
245 attr_list = do_set_attribute(attr_list, i, v);
246 goto CONTINUE;
248 if (scan_keyword("dir")) {
249 scan_direction();
250 spec_direction = cur_val;
251 goto CONTINUE;
253 if (attr_list == attr_list_cache) {
254 add_node_attr_ref(attr_list);
256 if (scan_keyword("to")) {
257 spec_code = exactly;
258 } else if (scan_keyword("spread")) {
259 spec_code = additional;
260 } else {
261 spec_code = additional;
262 cur_val = 0;
263 goto FOUND;
265 scan_normal_dimen();
266 goto FOUND;
267 QUICK:
268 spec_code = additional;
269 cur_val = 0;
270 add_node_attr_ref(attr_list);
271 done = true;
272 FOUND:
273 set_saved_record(0, saved_boxcontext, 0, s);
274 set_saved_record(1, saved_boxspec, spec_code, cur_val);
275 /* DIR: Adjust |text_dir_ptr| for |scan_spec| */
276 if (spec_direction != -1) {
277 set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
278 text_dir_ptr = new_dir(spec_direction);
279 } else {
280 set_saved_record(2, saved_boxdir, spec_direction, null);
282 set_saved_record(3, saved_boxattr, 0, attr_list);
283 set_saved_record(4, saved_boxpack, 0, just_pack);
284 save_ptr += 5;
285 new_save_level(c);
286 if (! done) {
287 scan_left_brace();
289 /* no gain: if (body_direction != spec_direction) etc */
290 eq_word_define(int_base + body_direction_code, spec_direction);
291 eq_word_define(int_base + par_direction_code, spec_direction);
292 eq_word_define(int_base + text_direction_code, spec_direction);
295 @ To figure out the glue setting, |hpack| and |vpack| determine how much
296 stretchability and shrinkability are present, considering all four orders of
297 infinity. The highest order of infinity that has a nonzero coefficient is then
298 used as if no other orders were present.
300 For example, suppose that the given list contains six glue nodes with the
301 respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then the
302 total is essentially 2fil; and if a total additional space of 6pt is to be
303 achieved by stretching, the actual amounts of stretch will be 0pt, 0pt, 15pt,
304 0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The `fill' glue
305 is therefore not really stretching infinitely with respect to `fil'; nobody would
306 actually want that to happen.)
308 The arrays |total_stretch| and |total_shrink| are used to determine how much glue
309 of each kind is present. A global variable |last_badness| is used to implement
310 \.{\\badness}.
313 scaled total_stretch[5];
314 scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */
315 int last_badness; /* badness of the most recently packaged box */
317 @ If the global variable |adjust_tail| is non-null, the |hpack| routine also
318 removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items and
319 appends the resulting material onto the list that ends at location |adjust_tail|.
322 halfword adjust_tail; /* tail of adjustment list */
325 @ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
326 |pre_adjust_tail| instead of |adjust_tail|.
329 halfword pre_adjust_tail;
331 halfword last_leftmost_char;
332 halfword last_rightmost_char;
334 halfword next_char_p; /* pointer to the next char of an implicit kern */
335 halfword prev_char_p; /* pointer to the previous char of an implicit kern */
337 @ This procedure is called repeatedly from inside the line break algorithm.
339 void set_prev_char_p(halfword p)
341 prev_char_p = p;
345 the kern stretch / shrink code was (or had become) rather weird ... the width field
346 is set, and then used in a second calculation, repeatedly, so why is that ... maybe some
347 some weird left-over ... anyway, the values are so small that in practice they are not
348 significant at all when the backend sees them because a few hundred sp positive or
349 negative are just noise there (so adjustlevel 3 has hardly any consequence for the
350 result but is more efficient)
354 @ @c
355 scaled char_stretch(halfword p)
357 internal_font_number f = font(p);
358 int m = font_max_stretch(f);
359 if (m > 0) {
360 int c = character(p);
361 int ef = get_ef_code(f, c);
362 if (ef > 0) {
363 scaled dw = calc_char_width(f, c, m) - char_width(f, c) - x_advance(p);
364 if (dw > 0) {
365 return round_xn_over_d(dw, ef, 1000);
369 return 0;
372 @ @c
373 scaled char_shrink(halfword p)
375 internal_font_number f = font(p);
376 int m = font_max_shrink(f);
377 if (m > 0) {
378 int c = character(p);
379 int ef = get_ef_code(f, c);
380 if (ef > 0) {
381 scaled dw = char_width(f, c) + x_advance(p) - calc_char_width(f, c, -m);
382 if (dw > 0) {
383 return round_xn_over_d(dw, ef, 1000);
387 return 0;
390 @ @c
392 scaled kern_stretch(halfword p)
394 halfword l, r;
395 scaled d;
396 int m;
397 if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
398 return 0;
399 l = prev_char_p;
400 // we need a left char
401 if (!is_char_node(l))
402 return 0;
403 r = vlink(p);
404 // and a right char
405 if (!is_char_node(r))
406 return 0;
407 // and a reason to kern
408 if ((font(l) != font(r)) || (font_max_stretch(font(l)) == 0))
409 return 0;
410 m = font_max_stretch(font(l));
411 d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
412 d = round_xn_over_d(d, 1000 + m, 1000);
413 return round_xn_over_d(d - width(p), get_ef_code(font(l), character(l)), 1000);
417 scaled kern_stretch(halfword p)
419 int m;
420 scaled d, e, x;
421 scaled w = width(p) ;
422 halfword l;
423 halfword r;
424 if (w == 0) {
425 /* why bother about zero kerns */
426 return 0;
428 l = prev_char_p ;
429 if ((l == null) || (vlink(l) != p)) {
430 /* we only care about kerns following a char*/
431 return 0;
433 r = vlink(p);
434 if (r == null) {
435 /* we only care about kerns between a char and something else */
437 if (!(is_char_node(l) && is_char_node(r))) {
438 /* we want two chars (but but don't care about the fonts) */
439 return 0;
441 /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
442 m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2;
443 if (m == 0) {
444 /* nothing to kern */
445 return 0;
447 d = round_xn_over_d(w, 1000 + m, 1000);
448 /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
449 e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
450 if (e == 1000) {
451 x = d - w;
452 } else {
453 x = round_xn_over_d(d - w, e, 1000);
456 printf("STRETCH w=%i s=%i x=%i\n",w,e+m,x);
458 return x;
461 @ @c
463 scaled kern_shrink(halfword p)
465 halfword l, r;
466 scaled d;
467 int m;
468 if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
469 return 0;
470 l = prev_char_p;
471 // we need a left char
472 if (!is_char_node(l))
473 return 0;
474 r = vlink(p);
475 // and a right char
476 if (!is_char_node(r))
477 return 0;
478 // and a reason to kern
479 if ((font(l) != font(r)) || (font_max_shrink(font(l)) == 0))
480 return 0;
481 m = font_max_stretch(font(l));
482 d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
483 d = round_xn_over_d(d, 1000 - m, 1000);
484 return round_xn_over_d(width(p) - d, get_ef_code(font(l), character(l)), 1000);
488 scaled kern_shrink(halfword p)
490 int m;
491 scaled d, e, x;
492 scaled w = width(p) ;
493 halfword l;
494 halfword r;
495 if (w == 0) {
496 /* why bother about zero kerns */
497 return 0;
499 l = prev_char_p ;
500 if ((l == null) || (vlink(l) != p)) {
501 /* we only care about kerns following a char*/
502 return 0;
504 r = vlink(p);
505 if (r == null) {
506 /* we only care about kerns between a char and something else */
508 if (!(is_char_node(l) && is_char_node(r))) {
509 /* we want two chars (but but don't care about the fonts) */
510 return 0;
512 /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
513 m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2;
514 if (m == 0) {
515 /* nothing to kern */
516 return 0;
518 d = round_xn_over_d(w, 1000 - m, 1000);
519 e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
520 if (e == 1000) {
521 x = w - d ;
522 } else {
523 x = round_xn_over_d(w - d, e, 1000);
526 printf("SHRINK w=%i s=%i x=%i\n",w,e+m,x);
528 return x;
531 @ @c
532 void do_subst_font(halfword p, int ex_ratio)
534 if (type(p) == disc_node) {
535 halfword r = vlink(pre_break(p));
536 while (r != null) {
537 if (is_char_node(r))
538 do_subst_font(r, ex_ratio);
539 r = vlink(r);
541 r = vlink(post_break(p));
542 while (r != null) {
543 if (is_char_node(r))
544 do_subst_font(r, ex_ratio);
545 r = vlink(r);
547 r = vlink(no_break(p));
548 while (r != null) {
549 if (is_char_node(r))
550 do_subst_font(r, ex_ratio);
551 r = vlink(r);
553 return;
555 if (! is_char_node(p)) {
556 normal_error("font expansion", "invalid node type");
557 return;
558 } else {
559 internal_font_number f = font(p);
560 int ef = get_ef_code(f, character(p));
561 if (ef == 0)
562 return;
563 if ((font_max_stretch(f) > 0) && (ex_ratio > 0)) {
564 int ex_stretch = ext_xn_over_d(ex_ratio * ef, font_max_stretch(f), 1000000);
565 ex_glyph(p) = fix_expand_value(f, ex_stretch)*1000;
566 } else if ((font_max_shrink(f) > 0) && (ex_ratio < 0)) {
567 int ex_shrink = ext_xn_over_d(ex_ratio * ef, font_max_shrink(f), 1000000);
568 ex_glyph(p) = fix_expand_value(f, ex_shrink)*1000;
573 @ @c
574 scaled char_pw(halfword p, int side)
576 internal_font_number f;
577 int c, w;
578 if (side == left_side)
579 last_leftmost_char = null;
580 else
581 last_rightmost_char = null;
582 if (p == null)
583 return 0;
584 if (!is_char_node(p))
585 return 0;
586 f = font(p);
587 if (side == left_side) {
588 c = get_lp_code(f, character(p));
589 last_leftmost_char = p;
590 } else {
591 c = get_rp_code(f, character(p));
592 last_rightmost_char = p;
594 if (c == 0)
595 return 0;
596 w = quad(f);
597 return round_xn_over_d(w, c, 1000);
600 @ @c
601 halfword new_margin_kern(scaled w, halfword p, int side)
603 halfword k, q;
604 k = new_node(margin_kern_node, side);
605 width(k) = w;
606 if (p == null)
607 normal_error("margin kerning", "invalid pointer to marginal char node");
608 q = new_char(font(p), character(p));
609 margin_char(k) = q;
610 return k;
613 @ Here is |hpack|, which is place where we do font substituting when font
614 expansion is being used.
617 int font_expand_ratio = 0; /* current expansion ratio, needed for recursive call */
619 halfword hpack(halfword p, scaled w, int m, int pack_direction)
621 halfword r; /* the box node that will be returned */
622 halfword q; /* trails behind |p| */
623 scaled h = 0; /* height */
624 scaled d = 0; /* depth */
625 scaled x = 0; /* natural width */
626 scaled_whd whd;
627 scaled s; /* shift amount */
628 int o; /* order of infinity */
629 halfword dir_ptr1 = null; /* for managing the direction stack */
630 int hpack_dir; /* the current direction */
631 int disc_level = 0;
632 halfword pack_interrupt[8];
633 scaled font_stretch = 0;
634 scaled font_shrink = 0;
635 int adjust_spacing = int_par(adjust_spacing_code);
638 int font_expand_ratio = 0;
640 last_badness = 0;
641 r = new_node(hlist_node, min_quarterword); /* the box node that will be returned */
642 if (pack_direction == -1) {
643 hpack_dir = text_direction;
644 } else {
645 hpack_dir = pack_direction;
647 box_dir(r) = hpack_dir;
649 potential optimimization, save a little but neglectable in practice (not so
650 many empty boxes are used)
652 if (p == null) {
653 width(r) = w;
654 return r;
657 push_dir(dir_ptr1,hpack_dir); /* push null */
658 q = r + list_offset; /* hm, adding something to a node address? */
659 vlink(q) = p;
660 if (m == cal_expand_ratio) {
661 prev_char_p = null; /* why not always */
663 if (adjust_spacing > 2) {
664 adjust_spacing = 0;
666 total_stretch[normal] = 0;
667 total_shrink[normal] = 0;
668 total_stretch[sfi] = 0;
669 total_shrink[sfi] = 0;
670 total_stretch[fil] = 0;
671 total_shrink[fil] = 0;
672 total_stretch[fill] = 0;
673 total_shrink[fill] = 0;
674 total_stretch[filll] = 0;
675 total_shrink[filll] = 0;
677 RESWITCH:
678 while ((p != null) || (disc_level > 0)) {
679 if (p == null) {
680 decr(disc_level);
681 p = pack_interrupt[disc_level];
682 goto RESWITCH;
685 Examine node |p| in the hlist, taking account of its effect
686 on the dimensions of the new box, or moving it to the adjustment list;
687 then advance |p| to the next node
689 while (is_char_node(p)) {
691 Incorporate character dimensions into the dimensions of the hbox
692 that will contain~it, then move to the next node.
694 The following code is part of \TeX's inner loop; i.e., adding
695 another character of text to the user's input will cause each of
696 these instructions to be exercised one more time.
698 if (m >= cal_expand_ratio) {
699 prev_char_p = p;
700 if (m == cal_expand_ratio) {
701 font_stretch += char_stretch(p);
702 font_shrink += char_shrink(p);
703 } else if (m == subst_ex_font) {
704 do_subst_font(p, font_expand_ratio);
707 whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
708 x += whd.wd;
709 if (whd.ht > h)
710 h = whd.ht;
711 if (whd.dp > d)
712 d = whd.dp;
713 p = vlink(p);
715 if (p != null) {
716 switch (type(p)) {
717 case hlist_node:
718 case vlist_node:
720 Incorporate box dimensions into the dimensions of the hbox
721 that will contain~it
723 The code here implicitly uses the fact that running dimensions are
724 indicated by |null_flag|, which will be ignored in the calculations
725 because it is a highly negative number.
727 s = shift_amount(p);
728 whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
729 x += whd.wd;
730 if (whd.ht - s > h)
731 h = whd.ht - s;
732 if (whd.dp + s > d)
733 d = whd.dp + s;
734 break;
736 case rule_node:
737 case unset_node:
738 x += width(p);
739 if (type(p) >= rule_node) // always
740 s = 0;
741 else
742 s = shift_amount(p);
743 if (height(p) - s > h)
744 h = height(p) - s;
745 if (depth(p) + s > d)
746 d = depth(p) + s;
747 break;
749 case rule_node:
750 case unset_node:
751 x += width(p);
752 if (height(p) > h)
753 h = height(p);
754 if (depth(p) > d)
755 d = depth(p);
756 break;
757 /* */
758 case glue_node:
759 /* Incorporate glue into the horizontal totals */
760 x += width(p);
761 o = stretch_order(p);
762 total_stretch[o] = total_stretch[o] + stretch(p);
763 o = shrink_order(p);
764 total_shrink[o] = total_shrink[o] + shrink(p);
765 if (subtype(p) >= a_leaders) {
766 halfword g = leader_ptr(p);
767 if (height(g) > h)
768 h = height(g);
769 if (depth(g) > d)
770 d = depth(g);
772 break;
773 case kern_node:
774 x += width(p);
775 if (subtype(p) == font_kern && adjust_spacing) {
776 /* so only when 1 or 2 */
777 if (m == cal_expand_ratio) {
778 font_stretch = font_stretch + kern_stretch(p);
779 font_shrink = font_shrink + kern_shrink(p);
780 } else if (m == subst_ex_font) {
781 /* this is the finalizer */
782 int k = 0;
783 if (font_expand_ratio > 0) {
784 k = kern_stretch(p);
785 } else if (font_expand_ratio < 0) {
786 k = kern_shrink(p);
788 ex_kern(p) = k;
789 x += k;
791 if (x!=0) printf("SET %i %i %i\n",font_expand_ratio,k,x);
795 break;
796 case disc_node:
797 if (m == subst_ex_font)
798 do_subst_font(p, font_expand_ratio);
799 if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) {
800 pack_interrupt[disc_level] = vlink(p);
801 incr(disc_level);
802 p = no_break(p);
804 break;
805 case dir_node:
806 /* Adjust the dir stack for the |hpack| routine */
807 if (dir_dir(p) >= 0) {
808 hpack_dir = dir_dir(p);
809 push_dir_node(dir_ptr1,p);
810 } else {
811 pop_dir_node(dir_ptr1);
812 if (dir_ptr1 != null)
813 hpack_dir = dir_dir(dir_ptr1);
815 break;
816 case math_node:
817 /* begin mathskip code */
818 if (glue_is_zero(p)) {
819 x += surround(p);
820 break;
821 } else {
822 /* fall through: mathskip */
824 /* end mathskip code */
825 case margin_kern_node:
826 if (m == cal_expand_ratio) {
827 int f = font(margin_char(p));
828 do_subst_font(margin_char(p), 1000);
829 if (f != font(margin_char(p)))
830 font_stretch = font_stretch - width(p) - char_pw(margin_char(p), subtype(p));
831 font(margin_char(p)) = f;
832 do_subst_font(margin_char(p), -1000);
833 if (f != font(margin_char(p)))
834 font_shrink = font_shrink - width(p) - char_pw(margin_char(p), subtype(p));
835 font(margin_char(p)) = f;
836 } else if (m == subst_ex_font) {
837 do_subst_font(margin_char(p), font_expand_ratio);
838 width(p) = -char_pw(margin_char(p), subtype(p));
840 x += width(p);
841 break;
842 case ins_node:
843 case mark_node:
844 case adjust_node:
846 Transfer node |p| to the adjustment list.
848 Although node |q| is not necessarily the immediate predecessor of node |p|,
849 it always points to some node in the list preceding |p|. Thus, we can delete
850 nodes by moving |q| when necessary. The algorithm takes linear time, and the
851 extra computation does not intrude on the inner loop unless it is necessary
852 to make a deletion.
854 if (adjust_tail != null || pre_adjust_tail != null) {
855 while (vlink(q) != p)
856 q = vlink(q);
857 if (type(p) == adjust_node) {
858 if (adjust_pre(p) != 0)
859 update_adjust_list(pre_adjust_tail);
860 else
861 update_adjust_list(adjust_tail);
862 p = vlink(p);
863 adjust_ptr(vlink(q)) = null;
864 flush_node(vlink(q));
865 } else {
866 vlink(adjust_tail) = p;
867 adjust_tail = p;
868 p = vlink(p);
870 vlink(q) = p;
871 p = q;
873 break;
874 /* */
875 default:
876 break;
878 p = vlink(p);
883 if (adjust_tail != null)
884 vlink(adjust_tail) = null;
885 if (pre_adjust_tail != null)
886 vlink(pre_adjust_tail) = null;
887 height(r) = h;
888 depth(r) = d;
890 Determine the value of |width(r)| and the appropriate glue setting; then
891 |return| or |goto common_ending|.
893 When we get to the present part of the program, |x| is the natural width
894 of the box being packaged.
896 if (m == additional)
897 w = x + w;
898 width(r) = w;
899 x = w - x;
900 /* now |x| is the excess to be made up */
901 if (x == 0) {
902 glue_sign(r) = normal;
903 glue_order(r) = normal;
904 set_glue_ratio_zero(glue_set(r));
905 goto EXIT;
906 } else if (x > 0) {
908 Determine horizontal glue stretch setting, then |return|
909 or \hbox{|goto common_ending|}.
911 If |hpack| is called with |m=cal_expand_ratio| we calculate
912 |font_expand_ratio| and return without checking for overfull or
913 underfull box.
915 if (total_stretch[filll] != 0)
916 o = filll;
917 else if (total_stretch[fill] != 0)
918 o = fill;
919 else if (total_stretch[fil] != 0)
920 o = fil;
921 else if (total_stretch[sfi] != 0)
922 o = sfi;
923 else
924 o = normal;
926 if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
927 font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
928 goto EXIT;
930 glue_order(r) = (quarterword) o;
931 glue_sign(r) = stretching;
932 if (total_stretch[o] != 0) {
933 glue_set(r) = unfloat((double) x / total_stretch[o]);
934 } else {
935 /* there's nothing to stretch */
936 glue_sign(r) = normal;
937 set_glue_ratio_zero(glue_set(r));
939 if (o == normal) {
940 if (list_ptr(r) != null) {
942 Report an underfull hbox and |goto common_ending|, if this box
943 is sufficiently bad.
945 last_badness = badness(x, total_stretch[normal]);
946 if (last_badness > int_par(hbadness_code)) {
947 int callback_id = callback_defined(hpack_quality_callback);
948 if (callback_id > 0) {
949 halfword rule = null;
950 if (last_badness > 100) {
951 run_callback(callback_id, "SdNdd->N","underfull",last_badness,r,abs(pack_begin_line),line,&rule);
952 } else {
953 run_callback(callback_id, "SdNdd->N","loose",last_badness,r,abs(pack_begin_line),line,&rule);
955 if (rule != null) {
956 while (vlink(q) != null) {
957 q = vlink(q);
959 couple_nodes(q,rule);
961 } else {
962 print_ln();
963 if (last_badness > 100) {
964 tprint_nl("Underfull \\hbox (badness ");
965 } else {
966 tprint_nl("Loose \\hbox (badness ");
968 print_int(last_badness);
969 goto COMMON_ENDING;
974 goto EXIT;
975 } else {
977 Determine horizontal glue shrink setting, then |return|
978 or \hbox{|goto common_ending|},
980 if (total_shrink[filll] != 0)
981 o = filll;
982 else if (total_shrink[fill] != 0)
983 o = fill;
984 else if (total_shrink[fil] != 0)
985 o = fil;
986 else if (total_shrink[sfi] != 0)
987 o = sfi;
988 else
989 o = normal;
991 if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
992 font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
993 goto EXIT;
995 glue_order(r) = (quarterword) o;
996 glue_sign(r) = shrinking;
997 if (total_shrink[o] != 0) {
998 glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
999 } else {
1000 /* there's nothing to shrink */
1001 glue_sign(r) = normal;
1002 set_glue_ratio_zero(glue_set(r));
1004 if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
1005 int overshoot = -x - total_shrink[normal] ;
1006 last_badness = 1000000;
1007 /* use the maximum shrinkage */
1008 set_glue_ratio_one(glue_set(r));
1010 Report an overfull hbox and |goto common_ending|, if this box
1011 is sufficiently bad.
1013 if ((overshoot > dimen_par(hfuzz_code)) || (int_par(hbadness_code) < 100)) {
1014 int callback_id = callback_defined(hpack_quality_callback);
1015 halfword rule = null;
1016 if (callback_id > 0) {
1017 run_callback(callback_id, "SdNdd->N","overfull",overshoot,r,abs(pack_begin_line),line,&rule);
1018 } else if (dimen_par(overfull_rule_code) > 0) {
1019 rule = new_rule(normal_rule);
1020 rule_dir(rule) = box_dir(r);
1021 width(rule) = dimen_par(overfull_rule_code);
1023 if (rule != null) {
1024 while (vlink(q) != null) {
1025 q = vlink(q);
1027 couple_nodes(q,rule);
1029 if (callback_id == 0) {
1030 print_ln();
1031 tprint_nl("Overfull \\hbox (");
1032 print_scaled(overshoot);
1033 tprint("pt too wide");
1034 goto COMMON_ENDING;
1037 } else if (o == normal) {
1038 if (list_ptr(r) != null) {
1040 Report a tight hbox and |goto common_ending|, if this box is
1041 sufficiently bad.
1043 last_badness = badness(-x, total_shrink[normal]);
1044 if (last_badness > int_par(hbadness_code)) {
1045 int callback_id = callback_defined(hpack_quality_callback);
1046 if (callback_id > 0) {
1047 halfword rule = null;
1048 run_callback(callback_id, "SdNdd->N","tight",last_badness,r,abs(pack_begin_line),line,&rule);
1049 if (rule != null) {
1050 while (vlink(q) != null) {
1051 q = vlink(q);
1053 couple_nodes(q,rule);
1055 } else {
1056 print_ln();
1057 tprint_nl("Tight \\hbox (badness ");
1058 print_int(last_badness);
1059 goto COMMON_ENDING;
1064 goto EXIT;
1067 COMMON_ENDING:
1069 Finish issuing a diagnostic message for an overfull or underfull
1070 hbox.
1072 if (output_active) {
1073 tprint(") has occurred while \\output is active");
1074 } else {
1075 if (pack_begin_line != 0) {
1076 if (pack_begin_line > 0) {
1077 tprint(") in paragraph at lines ");
1078 } else {
1079 tprint(") in alignment at lines ");
1081 print_int(abs(pack_begin_line));
1082 tprint("--");
1083 } else {
1084 tprint(") detected at line ");
1086 print_int(line);
1089 print_ln();
1090 font_in_short_display = null_font;
1091 short_display(list_ptr(r));
1092 print_ln();
1093 begin_diagnostic();
1094 show_box(r);
1095 end_diagnostic(true);
1096 EXIT:
1097 if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
1098 font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
1099 q = list_ptr(r);
1100 list_ptr(r) = null;
1101 flush_node(r);
1102 /* this nested call uses the more or less global font_expand_ratio */
1103 r = hpack(q, w, subst_ex_font, hpack_dir);
1105 while (dir_ptr1 != null)
1106 pop_dir_node(dir_ptr1);
1107 /* here we reset the font_expan_ratio */
1108 font_expand_ratio = 0;
1109 return r;
1112 @ @c
1113 halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr)
1115 halfword q;
1116 if (just_pack) {
1117 q = vlink(p);
1118 } else if (type(p) == temp_node && vlink(p) == null) {
1119 q = vlink(p);
1121 q = new_node(hlist_node, min_quarterword);
1122 box_dir(q) = (pac == -1) ? text_direction : pac;
1123 width(q) = w;
1124 return q;
1126 } else {
1127 new_hyphenation(p, qt);
1128 (void) new_ligkern(p, qt); /* we don't care about the tail in this case */
1129 q = vlink(p);
1130 /* maybe here: alink(p) = null */
1131 q = lua_hpack_filter(q, w, m, grp, pac, attr); /* ignores empty anyway */ /* maybe also pass tail */
1133 return hpack(q, w, m, pac);
1136 @ here is a function to calculate the natural whd of a (horizontal) node list
1139 scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
1140 int g_sign, int g_order, int pack_direction)
1142 scaled s; /* shift amount */
1143 halfword g; /* points to a glue specification */
1144 int hpack_dir;
1145 scaled_whd xx; /* for recursion */
1146 scaled_whd whd, siz = { 0, 0, 0 };
1147 if (pack_direction == -1) {
1148 hpack_dir = text_direction;
1149 } else {
1150 hpack_dir = pack_direction;
1152 while (p != pp && p != null) {
1153 while (is_char_node(p) && p != pp) {
1154 whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
1155 siz.wd += whd.wd;
1156 if (whd.ht > siz.ht)
1157 siz.ht = whd.ht;
1158 if (whd.dp > siz.dp)
1159 siz.dp = whd.dp;
1160 p = vlink(p);
1162 if (p != pp && p != null) {
1163 switch (type(p)) {
1164 case hlist_node:
1165 case vlist_node:
1166 s = shift_amount(p);
1167 whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
1168 siz.wd += whd.wd;
1169 if (whd.ht - s > siz.ht)
1170 siz.ht = whd.ht - s;
1171 if (whd.dp + s > siz.dp)
1172 siz.dp = whd.dp + s;
1173 break;
1175 case rule_node:
1176 case unset_node:
1177 siz.wd += width(p);
1178 if (type(p) >= rule_node) // always true
1179 s = 0;
1180 else
1181 s = shift_amount(p);
1182 if (height(p) - s > siz.ht)
1183 siz.ht = height(p) - s;
1184 if (depth(p) + s > siz.dp)
1185 siz.dp = depth(p) + s;
1186 break;
1188 case rule_node:
1189 case unset_node:
1190 siz.wd += width(p);
1191 if (height(p) > siz.ht)
1192 siz.ht = height(p);
1193 if (depth(p) > siz.dp)
1194 siz.dp = depth(p);
1195 break;
1196 /* */
1197 case math_node:
1198 /* begin mathskip code */
1199 if (glue_is_zero(p)) {
1200 siz.wd += surround(p);
1201 break;
1202 } else {
1203 /* fall through: mathskip */
1205 /* end mathskip code */
1206 case glue_node:
1207 siz.wd += width(p);
1208 if (g_sign != normal) {
1209 if (g_sign == stretching) {
1210 if (stretch_order(p) == g_order) {
1211 siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)));
1213 } else if (shrink_order(p) == g_order) {
1214 siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));
1217 if (subtype(p) >= a_leaders) {
1218 g = leader_ptr(p);
1219 if (height(g) > siz.ht)
1220 siz.ht = height(g);
1221 if (depth(g) > siz.dp)
1222 siz.dp = depth(g);
1224 break;
1225 case margin_kern_node:
1226 siz.wd += width(p);
1227 break;
1228 case kern_node:
1229 siz.wd += width(p) + ex_kern(p);
1230 break;
1231 case disc_node:
1232 xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, hpack_dir);
1233 siz.wd += xx.wd;
1234 if (xx.ht > siz.ht)
1235 siz.ht = xx.ht;
1236 if (xx.dp > siz.dp)
1237 siz.dp = xx.dp;
1238 break;
1239 default:
1240 break;
1242 p = vlink(p);
1246 return siz;
1249 @ In order to provide a decent indication of where an overfull or underfull box
1250 originated, we use a global variable |pack_begin_line| that is set nonzero only
1251 when |hpack| is being called by the paragraph builder or the alignment finishing
1252 routine.
1254 @ The source file line where the current paragraph or alignment began; a negative
1255 value denotes alignment:
1258 int pack_begin_line;
1260 @ The |vpack| subroutine is actually a special case of a slightly more general
1261 routine called |vpackage|, which has four parameters. The fourth parameter, which
1262 is |max_dimen| in the case of |vpack|, specifies the maximum depth of the page
1263 box that is constructed. The depth is first computed by the normal rules; if it
1264 exceeds this limit, the reference point is simply moved down until the limiting
1265 depth is attained.
1268 halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
1270 halfword r; /* the box node that will be returned */
1271 scaled w = 0; /* width */
1272 scaled d = 0; /* depth */
1273 scaled x = 0; /* natural height */
1274 scaled_whd whd;
1275 scaled s; /* shift amount */
1276 int o; /* order of infinity */
1277 last_badness = 0;
1278 r = new_node(vlist_node, 0);
1279 if (pack_direction == -1) {
1280 box_dir(r) = body_direction;
1281 } else {
1282 box_dir(r) = pack_direction;
1284 subtype(r) = min_quarterword;
1285 shift_amount(r) = 0;
1286 list_ptr(r) = p;
1287 total_stretch[normal] = 0;
1288 total_shrink[normal] = 0;
1289 total_stretch[sfi] = 0;
1290 total_shrink[sfi] = 0;
1291 total_stretch[fil] = 0;
1292 total_shrink[fil] = 0;
1293 total_stretch[fill] = 0;
1294 total_shrink[fill] = 0;
1295 total_stretch[filll] = 0;
1296 total_shrink[filll] = 0;
1298 while (p != null) {
1300 Examine node |p| in the vlist, taking account of its effect
1301 on the dimensions of the new box; then advance |p| to the next
1302 node.
1304 if (is_char_node(p)) {
1305 confusion("vpack");
1306 } else {
1307 switch (type(p)) {
1308 case hlist_node:
1309 case vlist_node:
1311 Incorporate box dimensions into the dimensions of
1312 the vbox that will contain it.
1314 s = shift_amount(p);
1315 whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false);
1316 if (whd.wd + s > w)
1317 w = whd.wd + s;
1318 x += d + whd.ht;
1319 d = whd.dp;
1320 break;
1322 case rule_node:
1323 case unset_node:
1324 x += d + height(p);
1325 d = depth(p);
1326 if (type(p) >= rule_node) // always
1327 s = 0;
1328 else
1329 s = shift_amount(p);
1330 if (width(p) + s > w)
1331 w = width(p) + s;
1332 break;
1334 case rule_node:
1335 case unset_node:
1336 x += d + height(p);
1337 d = depth(p);
1338 if (width(p) > w)
1339 w = width(p);
1340 break;
1341 /* */
1342 case glue_node:
1343 /* Incorporate glue into the vertical totals */
1344 x += d;
1345 d = 0;
1346 x += width(p);
1347 o = stretch_order(p);
1348 total_stretch[o] = total_stretch[o] + stretch(p);
1349 o = shrink_order(p);
1350 total_shrink[o] = total_shrink[o] + shrink(p);
1351 if (subtype(p) >= a_leaders) {
1352 halfword g = leader_ptr(p);
1353 if (width(g) > w)
1354 w = width(g);
1356 break;
1357 case kern_node:
1358 x += d + width(p);
1359 d = 0;
1360 break;
1361 default:
1362 break;
1365 p = vlink(p);
1367 width(r) = w;
1368 if (d > l) {
1369 x += d - l;
1370 depth(r) = l;
1371 } else {
1372 depth(r) = d;
1375 Determine the value of |height(r)| and the appropriate glue setting;
1376 then |return| or |goto common_ending|.
1378 When we get to the present part of the program, |x| is the natural
1379 height of the box being packaged.
1381 if (m == additional)
1382 h = x + h;
1383 height(r) = h;
1384 x = h - x;
1385 /* now |x| is the excess to be made up */
1386 if (x == 0) {
1387 glue_sign(r) = normal;
1388 glue_order(r) = normal;
1389 set_glue_ratio_zero(glue_set(r));
1390 return r;
1391 } else if (x > 0) {
1393 Determine vertical glue stretch setting, then |return|
1394 or \hbox{|goto common_ending|}.
1396 if (total_stretch[filll] != 0)
1397 o = filll;
1398 else if (total_stretch[fill] != 0)
1399 o = fill;
1400 else if (total_stretch[fil] != 0)
1401 o = fil;
1402 else if (total_stretch[sfi] != 0)
1403 o = sfi;
1404 else
1405 o = normal;
1407 glue_order(r) = (quarterword) o;
1408 glue_sign(r) = stretching;
1409 if (total_stretch[o] != 0) {
1410 glue_set(r) = unfloat((double) x / total_stretch[o]);
1411 } else {
1412 glue_sign(r) = normal;
1413 set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */
1415 if (o == normal) {
1416 if (list_ptr(r) != null) {
1418 Report an underfull vbox and |goto common_ending|, if this box
1419 is sufficiently bad.
1421 last_badness = badness(x, total_stretch[normal]);
1422 if (last_badness > int_par(vbadness_code)) {
1423 int callback_id = callback_defined(vpack_quality_callback);
1424 if (callback_id > 0) {
1425 if (last_badness > 100) {
1426 run_callback(callback_id, "SdNdd->","underfull",last_badness,r,abs(pack_begin_line),line);
1427 } else {
1428 run_callback(callback_id, "SdNdd->","loose",last_badness,r,abs(pack_begin_line),line);
1430 goto EXIT;
1431 } else {
1432 print_ln();
1433 if (last_badness > 100) {
1434 tprint_nl("Underfull \\vbox (badness ");
1435 } else {
1436 tprint_nl("Loose \\vbox (badness ");
1438 print_int(last_badness);
1439 goto COMMON_ENDING;
1444 return r;
1446 } else {
1448 Determine vertical glue shrink setting, then |return|
1449 or \hbox{|goto common_ending|}.
1451 if (total_shrink[filll] != 0)
1452 o = filll;
1453 else if (total_shrink[fill] != 0)
1454 o = fill;
1455 else if (total_shrink[fil] != 0)
1456 o = fil;
1457 else if (total_shrink[sfi] != 0)
1458 o = sfi;
1459 else
1460 o = normal;
1462 glue_order(r) = (quarterword) o;
1463 glue_sign(r) = shrinking;
1464 if (total_shrink[o] != 0) {
1465 glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
1466 } else {
1467 /* there's nothing to shrink */
1468 glue_sign(r) = normal;
1469 set_glue_ratio_zero(glue_set(r));
1471 if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
1472 int overshoot = -x - total_shrink[normal];
1473 last_badness = 1000000;
1474 /* use the maximum shrinkage */
1475 set_glue_ratio_one(glue_set(r));
1477 Report an overfull vbox and |goto common_ending|, if this box
1478 is sufficiently bad.
1480 if ((overshoot > dimen_par(vfuzz_code)) || (int_par(vbadness_code) < 100)) {
1481 int callback_id = callback_defined(vpack_quality_callback);
1482 if (callback_id > 0) {
1483 run_callback(callback_id, "SdNdd->","overfull",overshoot,r,abs(pack_begin_line),line);
1484 goto EXIT;
1485 } else {
1486 print_ln();
1487 tprint_nl("Overfull \\vbox (");
1488 print_scaled(-x - total_shrink[normal]);
1489 tprint("pt too high");
1490 goto COMMON_ENDING;
1493 } else if (o == normal) {
1494 if (list_ptr(r) != null) {
1496 Report a tight vbox and |goto common_ending|, if this box is
1497 sufficiently bad.
1499 last_badness = badness(-x, total_shrink[normal]);
1500 if (last_badness > int_par(vbadness_code)) {
1501 int callback_id = callback_defined(vpack_quality_callback);
1502 if (callback_id > 0) {
1503 run_callback(callback_id, "SdNdd->","tight",last_badness,r,abs(pack_begin_line),line);
1504 goto EXIT;
1505 } else {
1506 print_ln();
1507 tprint_nl("Tight \\vbox (badness ");
1508 print_int(last_badness);
1509 goto COMMON_ENDING;
1514 return r;
1517 COMMON_ENDING:
1518 /* Finish issuing a diagnostic message or an overfull or underfull vbox */
1519 if (output_active) {
1520 tprint(") has occurred while \\output is active");
1521 } else {
1522 if (pack_begin_line != 0) {
1523 /* it's actually negative */
1524 tprint(") in alignment at lines ");
1525 print_int(abs(pack_begin_line));
1526 tprint("--");
1527 } else {
1528 tprint(") detected at line ");
1530 print_int(line);
1531 print_ln();
1533 begin_diagnostic();
1534 show_box(r);
1535 end_diagnostic(true);
1536 EXIT:
1537 return r;
1540 @ @c
1541 halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr)
1543 halfword q = p;
1544 if (!just_pack)
1545 /* if (q != null) */
1546 q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr);
1547 return vpackage(q, h, m, l, pack_direction);
1550 @ @c
1551 void finish_vcenter(void)
1553 halfword p;
1554 unsave();
1555 save_ptr--;
1556 p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
1557 pop_nest();
1558 p = math_vcenter_group(p);
1559 tail_append(p);
1562 @ @c
1563 void package(int c)
1565 halfword saved0, saved2, saved3, saved4;
1566 int grp = cur_group;
1567 scaled d = box_max_depth; /* max depth */
1568 unsave();
1569 save_ptr -= 5;
1570 saved0 = saved_value(0);
1571 saved2 = saved_value(2);
1572 saved3 = saved_value(3);
1573 saved4 = saved_value(4);
1574 if (cur_list.mode_field == -hmode) {
1575 cur_box = filtered_hpack(cur_list.head_field, cur_list.tail_field,
1576 saved_value(1), saved_level(1), grp, saved_level(2), saved4, saved3);
1577 subtype(cur_box) = hbox_list;
1578 } else {
1579 cur_box = filtered_vpackage(vlink(cur_list.head_field),
1580 saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3);
1581 if (c == vtop_code) {
1583 Read just the height and depth of |cur_box|, for \.{\\vtop}. The
1584 height of a `\.{\\vtop}' box is inherited from the first item on
1585 its list, if that item is an |hlist_node|, |vlist_node|, or
1586 |rule_node|; otherwise the \.{\\vtop} height is zero.
1588 scaled h = 0;
1589 halfword p = list_ptr(cur_box);
1590 if ((p != null) && (type(p) <= rule_node)) {
1591 /* hlist, vlist, rule */
1592 h = height(p);
1594 depth(cur_box) = depth(cur_box) - h + height(cur_box);
1595 height(cur_box) = h;
1598 if (saved2 != null) {
1599 /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */
1600 flush_node_list(text_dir_ptr);
1601 text_dir_ptr = saved2;
1604 replace_attribute_list(cur_box, saved3);
1605 pop_nest();
1606 box_end(saved0);
1609 @ When a box is being appended to the current vertical list, the baselineskip
1610 calculation is handled by the |append_to_vlist| routine.
1613 void append_to_vlist(halfword b, int location)
1615 scaled d; /* deficiency of space between baselines */
1616 halfword p; /* a new glue node */
1617 boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ;
1618 halfword result = null;
1619 halfword next_depth = ignore_depth;
1620 boolean prev_set = false ;
1621 if (lua_appendtovlist_callback(b,location,prev_depth,mirrored,&result,&next_depth,&prev_set)) {
1622 while (result != null) {
1623 couple_nodes(cur_list.tail_field, result);
1624 cur_list.tail_field = result;
1625 result = vlink(result);
1627 if (prev_set) {
1628 prev_depth = next_depth;
1630 } else {
1631 if (prev_depth > ignore_depth) {
1632 if (mirrored) {
1633 d = width(glue_par(baseline_skip_code)) - prev_depth - depth(b);
1634 } else {
1635 d = width(glue_par(baseline_skip_code)) - prev_depth - height(b);
1637 if (d < dimen_par(line_skip_limit_code)) {
1638 p = new_param_glue(line_skip_code);
1639 } else {
1640 p = new_skip_param(baseline_skip_code);
1641 width(p) = d;
1643 couple_nodes(cur_list.tail_field, p);
1644 cur_list.tail_field = p;
1646 couple_nodes(cur_list.tail_field, b);
1647 cur_list.tail_field = b;
1648 if (mirrored) {
1649 prev_depth = height(b);
1650 } else {
1651 prev_depth = depth(b);
1656 @ When |saving_vdiscards| is positive then the glue, kern, and penalty nodes
1657 removed by the page builder or by \.{\\vsplit} from the top of a vertical list
1658 are saved in special lists instead of being discarded.
1661 #define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */
1662 #define page_disc disc_ptr[last_box_code] /* first item removed by page builder */
1663 #define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */
1665 halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */
1667 @ The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is
1668 considerably simpler than |line_break| because it doesn't have to worry about
1669 hyphenation, and because its mission is to discover a single break instead of an
1670 optimum sequence of breakpoints. But before we get into the details of |vsplit|,
1671 we need to consider a few more basic things.
1673 A subroutine called |prune_page_top| takes a pointer to a vlist and returns a
1674 pointer to a modified vlist in which all glue, kern, and penalty nodes have been
1675 deleted before the first box or rule node. However, the first box or rule is
1676 actually preceded by a newly created glue node designed so that the topmost
1677 baseline will be at distance |split_top_skip| from the top, whenever this is
1678 possible without backspacing.
1680 When the second argument |s| is |false| the deleted nodes are destroyed,
1681 otherwise they are collected in a list starting at |split_disc|.
1684 halfword prune_page_top(halfword p, boolean s)
1686 halfword q;
1687 halfword prev_p = temp_head; /* lags one step behind |p| */
1688 halfword r = null;
1689 vlink(temp_head) = p;
1690 while (p != null) {
1691 switch (type(p)) {
1692 case hlist_node:
1693 case vlist_node:
1694 case rule_node:
1695 /* Insert glue for |split_top_skip| and set~|p:=null| */
1696 q = new_skip_param(split_top_skip_code);
1697 vlink(prev_p) = q;
1698 vlink(q) = p;
1699 if (width(q) > height(p))
1700 width(q) = width(q) - height(p);
1701 else
1702 width(q) = 0;
1703 p = null;
1704 break;
1705 case boundary_node:
1706 case whatsit_node:
1707 case mark_node:
1708 case ins_node:
1709 prev_p = p;
1710 p = vlink(prev_p);
1711 break;
1712 case glue_node:
1713 case kern_node:
1714 case penalty_node:
1715 q = p;
1716 p = vlink(q);
1717 vlink(q) = null;
1718 vlink(prev_p) = p;
1719 if (s) {
1720 if (split_disc == null)
1721 split_disc = q;
1722 else
1723 vlink(r) = q;
1724 r = q;
1725 } else {
1726 flush_node_list(q);
1728 break;
1729 default:
1730 confusion("pruning");
1731 break;
1734 return vlink(temp_head);
1737 @ The next subroutine finds the best place to break a given vertical list so as
1738 to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the beginning
1739 of the vertical list is given, and a pointer to the optimum breakpoint is
1740 returned. The list is effectively followed by a forced break, i.e., a penalty
1741 node with the |eject_penalty|; if the best break occurs at this artificial node,
1742 the value |null| is returned.
1745 scaled active_height[10]; /* distance from first active node to~|cur_p| */
1747 @ An array of six |scaled| distances is used to keep track of the height from the
1748 beginning of the list to the current place, just as in |line_break|. In fact, we
1749 use one of the same arrays, only changing its name to reflect its new
1750 significance.
1753 #define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
1754 #define set_height_zero(A) active_height[A]=0 /* initialize the height to zero */
1756 @ A global variable |best_height_plus_depth| will be set to the natural size of
1757 the box that corresponds to the optimum breakpoint found by |vert_break|. (This
1758 value is used by the insertion-splitting algorithm of the page builder.)
1760 @ height of the best box, without stretching or shrinking
1763 scaled best_height_plus_depth;
1765 /* finds optimum page break */
1767 halfword vert_break(halfword p, scaled h, scaled d)
1769 halfword prev_p = p; /* if |p| is a glue node, |type(prev_p)| determines whether |p| is a
1770 legal breakpoint, an initial glue node is not a legal breakpoint */
1771 int pi = 0; /* penalty value */
1772 int b; /* badness at a trial breakpoint */
1773 int t; /* |type| of the node following a kern */
1774 int least_cost; /* the smallest badness plus penalties found so far */
1775 halfword best_place = null; /* the most recent break that leads to |least_cost| */
1776 scaled prev_dp = 0; /* depth of previous box in the list */
1777 least_cost = awful_bad;
1778 do_all_six(set_height_zero);
1779 while (1) {
1780 /* If node |p| is a legal breakpoint, check if this break is
1781 the best known, and |goto done| if |p| is null or
1782 if the page-so-far is already too full to accept more stuff */
1783 /* A subtle point to be noted here is that the maximum depth~|d| might be
1784 negative, so |cur_height| and |prev_dp| might need to be corrected even
1785 after a glue or kern node. */
1787 if (p == null) {
1788 pi = eject_penalty;
1789 } else {
1790 /* Use node |p| to update the current height and depth measurements;
1791 if this node is not a legal breakpoint, |goto not_found|
1792 or |update_heights|,
1793 otherwise set |pi| to the associated penalty at the break */
1794 switch (type(p)) {
1795 case hlist_node:
1796 case vlist_node:
1797 case rule_node:
1798 cur_height = cur_height + prev_dp + height(p);
1799 prev_dp = depth(p);
1800 goto NOT_FOUND;
1801 break;
1802 case boundary_node:
1803 case whatsit_node:
1804 goto NOT_FOUND;
1805 break;
1806 case glue_node:
1807 if (precedes_break(prev_p))
1808 pi = 0;
1809 else
1810 goto UPDATE_HEIGHTS;
1811 break;
1812 case kern_node:
1813 if (vlink(p) == null)
1814 t = penalty_node;
1815 else
1816 t = type(vlink(p));
1817 if (t == glue_node)
1818 pi = 0;
1819 else
1820 goto UPDATE_HEIGHTS;
1821 break;
1822 case penalty_node:
1823 pi = penalty(p);
1824 break;
1825 case mark_node:
1826 case ins_node:
1827 goto NOT_FOUND;
1828 break;
1829 default:
1830 confusion("vertbreak");
1831 break;
1834 /* Check if node |p| is a new champion breakpoint; then |goto done|
1835 if |p| is a forced break or if the page-so-far is already too full */
1836 if (pi < inf_penalty) {
1837 /* Compute the badness, |b|, using |awful_bad| if the box is too full */
1838 if (cur_height < h) {
1839 if ((active_height[3] != 0) || (active_height[4] != 0) ||
1840 (active_height[5] != 0) || (active_height[6] != 0))
1841 b = 0;
1842 else
1843 b = badness(h - cur_height, active_height[2]);
1844 } else if (cur_height - h > active_height[7]) {
1845 b = awful_bad;
1846 } else {
1847 b = badness(cur_height - h, active_height[7]);
1850 if (b < awful_bad) {
1851 if (pi <= eject_penalty)
1852 b = pi;
1853 else if (b < inf_bad)
1854 b = b + pi;
1855 else
1856 b = deplorable;
1858 if (b <= least_cost) {
1859 best_place = p;
1860 least_cost = b;
1861 best_height_plus_depth = cur_height + prev_dp;
1863 if ((b == awful_bad) || (pi <= eject_penalty))
1864 goto DONE;
1867 if ((type(p) < glue_node) || (type(p) > kern_node))
1868 goto NOT_FOUND;
1869 UPDATE_HEIGHTS:
1870 /* Update the current height and depth measurements with
1871 respect to a glue or kern node~|p| */
1872 /* Vertical lists that are subject to the |vert_break| procedure should not
1873 contain infinite shrinkability, since that would permit any amount of
1874 information to ``fit'' on one page. */
1876 if (type(p) != kern_node) {
1877 active_height[2 + stretch_order(p)] += stretch(p);
1878 active_height[7] += shrink(p);
1879 if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
1880 print_err("Infinite glue shrinkage found in box being split");
1881 help4("The box you are \\vsplitting contains some infinitely",
1882 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
1883 "Such glue doesn't belong there; but you can safely proceed,",
1884 "since the offensive shrinkability has been made finite.");
1885 error();
1886 shrink_order(p) = normal;
1889 cur_height = cur_height + prev_dp + width(p);
1890 prev_dp = 0;
1891 NOT_FOUND:
1892 if (prev_dp > d) {
1893 cur_height = cur_height + prev_dp - d;
1894 prev_dp = d;
1896 prev_p = p;
1897 p = vlink(prev_p);
1899 DONE:
1900 return best_place;
1903 @ Now we are ready to consider |vsplit| itself. Most of its work is accomplished
1904 by the two subroutines that we have just considered.
1906 Given the number of a vlist box |n|, and given a desired page height |h|, the
1907 |vsplit| function finds the best initial segment of the vlist and returns a box
1908 for a page of height~|h|. The remainder of the vlist, if any, replaces the
1909 original box, after removing glue and penalties and adjusting for
1910 |split_top_skip|. Mark nodes in the split-off box are used to set the values of
1911 |split_first_mark| and |split_bot_mark|; we use the fact that
1912 |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|.
1914 The original box becomes ``void'' if and only if it has been entirely extracted.
1915 The extracted box is ``void'' if and only if the original box was void (or if it
1916 was, erroneously, an hlist box).
1919 /* extracts a page of height |h| from box |n| */
1921 halfword vsplit(halfword n, scaled h, int m)
1923 halfword v; /* the box to be split */
1924 int vdir; /* the direction of the box to be split */
1925 halfword p; /* runs through the vlist */
1926 halfword q; /* points to where the break occurs */
1927 halfword i; /* for traversing marks lists */
1928 v = box(n);
1929 vdir = box_dir(v);
1930 flush_node_list(split_disc);
1931 split_disc = null;
1932 for (i = 0; i <= biggest_used_mark; i++) {
1933 delete_split_first_mark(i);
1934 delete_split_bot_mark(i);
1936 /* Dispense with trivial cases of void or bad boxes */
1937 if (v == null) {
1938 return null;
1940 if (type(v) != vlist_node) {
1941 print_err("\\vsplit needs a \\vbox");
1942 help2("The box you are trying to split is an \\hbox.",
1943 "i can't split such a box, so I''ll leave it alone.");
1944 error();
1945 return null;
1947 q = vert_break(list_ptr(v), h, dimen_par(split_max_depth_code));
1949 Look at all the marks in nodes before the break, and set the final
1950 link to |null| at the break. It's possible that the box begins with
1951 a penalty node that is the ``best'' break, so we must be careful to
1952 handle this special case correctly.
1954 p = list_ptr(v);
1955 if (p == q) {
1956 list_ptr(v) = null;
1957 } else {
1958 while (1) {
1959 if (type(p) == mark_node) {
1960 if (split_first_mark(mark_class(p)) == null) {
1961 set_split_first_mark(mark_class(p), mark_ptr(p));
1962 set_split_bot_mark(mark_class(p), split_first_mark(mark_class(p)));
1963 set_token_ref_count(split_first_mark(mark_class(p)),
1964 token_ref_count(split_first_mark(mark_class(p))) + 2);
1965 } else {
1966 delete_token_ref(split_bot_mark(mark_class(p)));
1967 set_split_bot_mark(mark_class(p), mark_ptr(p));
1968 add_token_ref(split_bot_mark(mark_class(p)));
1971 if (vlink(p) == q) {
1972 vlink(p) = null;
1973 break;
1975 p = vlink(p);
1978 q = prune_page_top(q, int_par(saving_vdiscards_code) > 0);
1979 p = list_ptr(v);
1980 list_ptr(v) = null;
1981 flush_node(v);
1982 if (q == null) {
1983 /* the |eq_level| of the box stays the same */
1984 box(n) = null;
1985 } else {
1986 box(n) = filtered_vpackage(q, 0, additional, dimen_par(max_depth_code), split_keep_group, vdir, 0, 0);
1988 if (m == exactly) {
1989 return filtered_vpackage(p, h, exactly, dimen_par(split_max_depth_code), split_off_group, vdir, 0, 0);
1990 } else {
1991 return filtered_vpackage(p, 0, additional, dimen_par(max_depth_code), split_off_group, vdir, 0, 0);
1995 @ Now that we can see what eventually happens to boxes, we can consider the first
1996 steps in their creation. The |begin_box| routine is called when |box_context| is
1997 a context specification, |cur_chr| specifies the type of box desired, and
1998 |cur_cmd=make_box|.
2001 void begin_box(int box_context)
2003 halfword q; /* run through the current list */
2004 halfword k; /* 0 or |vmode| or |hmode| */
2005 int n; /* a box number */
2006 int spec_direction = -1;
2007 int just_pack = 0;
2008 switch (cur_chr) {
2009 case box_code:
2010 scan_register_num();
2011 cur_box = box(cur_val);
2012 /* the box becomes void, at the same level */
2013 box(cur_val) = null;
2014 break;
2015 case copy_code:
2016 scan_register_num();
2017 cur_box = copy_node_list(box(cur_val));
2018 break;
2019 case last_box_code:
2021 If the current list ends with a box node, delete it from
2022 the list and make |cur_box| point to it; otherwise set
2023 |cur_box:=null|.
2025 cur_box = null;
2026 if (abs(cur_list.mode_field) == mmode) {
2027 you_cant();
2028 help1("Sorry; this \\lastbox will be void.");
2029 error();
2030 } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) {
2031 you_cant();
2032 help2("Sorry...I usually can't take things from the current page.",
2033 "This \\lastbox will therefore be void.");
2034 error();
2035 } else {
2036 if (cur_list.head_field != cur_list.tail_field) {
2037 /* todo: new code, needs testing */
2039 /* maybe: ((type(cur_list.tail_field) == hlist_node) < rule_node) */
2041 if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) {
2042 /* Remove the last box ... */
2043 q = alink(cur_list.tail_field);
2044 if (q == null || vlink(q) != cur_list.tail_field) {
2045 q = cur_list.head_field;
2046 while (vlink(q) != cur_list.tail_field)
2047 q = vlink(q);
2049 uncouple_node(cur_list.tail_field);
2050 cur_box = cur_list.tail_field;
2051 shift_amount(cur_box) = 0;
2052 cur_list.tail_field = q;
2053 vlink(cur_list.tail_field) = null;
2057 break;
2058 case vsplit_code:
2060 Split off part of a vertical box, make |cur_box| point to it. Here we
2061 deal with things like `\.{\\vsplit 13 to 100pt}'.
2063 scan_register_num();
2064 n = cur_val;
2065 if (!scan_keyword("to")) {
2066 print_err("Missing `to' inserted");
2067 help2("I'm working on `\\vsplit<box number> to <dimen>';",
2068 "will look for the <dimen> next.");
2069 error();
2071 scan_normal_dimen();
2072 cur_box = vsplit(n, cur_val, additional);
2073 break;
2074 default:
2076 Initiate the construction of an hbox or vbox, then |return|. Here is
2077 where we enter restricted horizontal mode or internal vertical mode,
2078 in order to make a box.
2080 switch (cur_chr) {
2081 case tpack_code:
2082 cur_chr = vtop_code;
2083 just_pack = 1;
2084 break;
2085 case vpack_code:
2086 cur_chr = vtop_code + vmode;
2087 just_pack = 1;
2088 break;
2089 case hpack_code:
2090 cur_chr = vtop_code + hmode;
2091 just_pack = 1;
2092 break;
2094 /* */
2095 k = cur_chr - vtop_code;
2096 set_saved_record(0, saved_boxcontext, 0, box_context);
2097 switch (abs(cur_list.mode_field)) {
2098 case vmode:
2099 spec_direction = body_direction;
2100 break;
2101 case hmode:
2102 spec_direction = text_direction;
2103 break;
2104 case mmode:
2105 spec_direction = math_direction;
2106 break;
2108 if (k == hmode) {
2109 if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
2110 scan_full_spec(adjusted_hbox_group, spec_direction,just_pack);
2111 else
2112 scan_full_spec(hbox_group, spec_direction,just_pack);
2113 } else {
2114 if (k == vmode) {
2115 scan_full_spec(vbox_group, spec_direction,just_pack);
2116 } else {
2117 scan_full_spec(vtop_group, spec_direction,just_pack);
2118 k = vmode;
2120 normal_paragraph();
2122 push_nest();
2123 cur_list.mode_field = -k;
2124 if (k == vmode) {
2125 prev_depth = ignore_depth;
2126 if (every_vbox != null)
2127 begin_token_list(every_vbox, every_vbox_text);
2128 } else {
2129 space_factor = 1000;
2130 if (every_hbox != null)
2131 begin_token_list(every_hbox, every_hbox_text);
2133 return;
2134 break;
2136 /* in simple cases, we use the box immediately */
2137 box_end(box_context);