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
/>.
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
)
74 if
(scan_keyword
("to")) {
77 } else if
(scan_keyword
("spread")) {
78 spec_code
= additional
;
81 spec_code
= additional
;
84 set_saved_record
(0, saved_boxspec
, spec_code
, cur_val
);
91 void scan_spec
(group_code c
)
92 { /* scans a box specification and left brace
*/
94 boolean done
= false
;
97 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
98 if
(cur_cmd
== left_brace_cmd
) {
99 spec_code
= additional
;
105 if
(scan_keyword
("to")) {
108 } else if
(scan_keyword
("spread")) {
109 spec_code
= additional
;
112 spec_code
= additional
;
116 set_saved_record
(0, saved_boxspec
, spec_code
, cur_val
);
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
)
141 if
(attr_list_cache
== cache_disabled
)
142 update_attribute_cache
();
143 attr_list
= attr_list_cache
;
146 while
(cur_cmd
== relax_cmd || cur_cmd
== spacer_cmd
) {
148 if
(cur_cmd
!= relax_cmd
&& cur_cmd != spacer_cmd)
151 if
(scan_keyword
("attr")) {
154 scan_optional_equals
();
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
);
164 if
(scan_keyword
("dir")) {
166 spec_direction
= cur_val
;
169 if
(attr_list
== attr_list_cache
) {
170 add_node_attr_ref
(attr_list
);
172 if
(scan_keyword
("to")) {
174 } else if
(scan_keyword
("spread")) {
175 spec_code
= additional
;
177 spec_code
= additional
;
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
);
189 set_saved_record
(2, saved_boxdir
, spec_direction
, null
);
191 set_saved_record
(3, saved_boxattr
, 0, attr_list
);
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
)
209 boolean done
= false
;
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
*/
217 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
218 if
(cur_cmd
== left_brace_cmd
) {
225 while
(cur_cmd
== relax_cmd || cur_cmd
== spacer_cmd
) {
227 if
(cur_cmd
== left_brace_cmd
) {
229 } else if
(cur_cmd
!= relax_cmd
&& cur_cmd != spacer_cmd) {
235 if
(scan_keyword
("attr")) {
238 scan_optional_equals
();
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
);
248 if
(scan_keyword
("dir")) {
250 spec_direction
= cur_val
;
253 if
(attr_list
== attr_list_cache
) {
254 add_node_attr_ref
(attr_list
);
256 if
(scan_keyword
("to")) {
258 } else if
(scan_keyword
("spread")) {
259 spec_code
= additional
;
261 spec_code
= additional
;
268 spec_code
= additional
;
270 add_node_attr_ref
(attr_list
);
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
);
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
);
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
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
)
345 scaled char_stretch
(halfword p
)
347 internal_font_number f
= font
(p
);
348 int m
= font_max_stretch
(f
);
350 int c
= character
(p
);
351 int ef
= get_ef_code
(f
, c
);
353 scaled dw
= calc_char_width
(f
, c
, m
) - char_width
(f
, c
);
355 return round_xn_over_d
(dw
, ef
, 1000);
363 scaled char_shrink
(halfword p
)
365 internal_font_number f
= font
(p
);
366 int m
= font_max_shrink
(f
);
368 int c
= character
(p
);
369 int ef
= get_ef_code
(f
, c
);
371 scaled dw
= char_width
(f
, c
) - calc_char_width
(f
, c
, -m
);
373 return round_xn_over_d
(dw
, ef
, 1000);
381 scaled kern_stretch
(halfword p
)
386 if
((prev_char_p
== null
) ||
(vlink
(prev_char_p
) != p
) ||
(vlink
(p
) == null
))
389 /* we need a left char
*/
390 if
(!is_char_node
(l
))
393 /* and a right char
*/
394 if
(!is_char_node
(r
))
396 /* and a reason to kern
*/
397 if
((font
(l
) != font
(r
)) ||
(font_max_stretch
(font
(l
)) == 0))
399 m
= font_max_stretch
(font
(l
));
400 d
= get_kern
(font
(l
), character
(l
), character
(r
));
401 d
= round_xn_over_d
(d
, 1000 + m
, 1000);
402 return round_xn_over_d
(d
- width
(p
), get_ef_code
(font
(l
), character
(l
)), 1000);
406 scaled kern_shrink
(halfword p
)
411 if
((prev_char_p
== null
) ||
(vlink
(prev_char_p
) != p
) ||
(vlink
(p
) == null
))
414 /* we need a left char
*/
415 if
(!is_char_node
(l
))
418 /* and a right char
*/
419 if
(!is_char_node
(r
))
421 /* and a reason to kern
*/
422 if
((font
(l
) != font
(r
)) ||
(font_max_shrink
(font
(l
)) == 0))
424 m
= font_max_stretch
(font
(l
));
425 d
= get_kern
(font
(l
), character
(l
), character
(r
));
426 d
= round_xn_over_d
(d
, 1000 - m
, 1000);
427 return round_xn_over_d
(width
(p
) - d
, get_ef_code
(font
(l
), character
(l
)), 1000);
431 void do_subst_font
(halfword p
, int ex_ratio
)
433 if
(type
(p
) == disc_node
) {
434 halfword r
= vlink
(pre_break
(p
));
437 do_subst_font
(r
, ex_ratio
);
440 r
= vlink
(post_break
(p
));
443 do_subst_font
(r
, ex_ratio
);
446 r
= vlink
(no_break
(p
));
449 do_subst_font
(r
, ex_ratio
);
454 if
(! is_char_node
(p
)) {
455 normal_error
("font expansion", "invalid node type");
458 internal_font_number f
= font
(p
);
459 int ef
= get_ef_code
(f
, character
(p
));
462 if
((font_max_stretch
(f
) > 0) && (ex_ratio > 0)) {
463 int ex_stretch
= ext_xn_over_d
(ex_ratio
* ef
, font_max_stretch
(f
), 1000000);
464 ex_glyph
(p
) = fix_expand_value
(f
, ex_stretch
)*1000;
465 } else if
((font_max_shrink
(f
) > 0) && (ex_ratio < 0)) {
466 int ex_shrink
= ext_xn_over_d
(ex_ratio
* ef
, font_max_shrink
(f
), 1000000);
467 ex_glyph
(p
) = fix_expand_value
(f
, ex_shrink
)*1000;
473 scaled char_pw
(halfword p
, int side
)
475 internal_font_number f
;
477 if
(side
== left_side
)
478 last_leftmost_char
= null
;
480 last_rightmost_char
= null
;
483 if
(!is_char_node
(p
))
486 if
(side
== left_side
) {
487 c
= get_lp_code
(f
, character
(p
));
488 last_leftmost_char
= p
;
490 c
= get_rp_code
(f
, character
(p
));
491 last_rightmost_char
= p
;
496 return round_xn_over_d
(w
, c
, 1000);
500 halfword new_margin_kern
(scaled w
, halfword p
, int side
)
503 k
= new_node
(margin_kern_node
, side
);
506 normal_error
("margin kerning", "invalid pointer to marginal char node");
507 q
= new_char
(font
(p
), character
(p
));
512 @ Here is |hpack|
, which is place where we do font substituting when font
513 expansion is being used.
516 halfword hpack
(halfword p
, scaled w
, int m
, int pack_direction
)
518 halfword r
; /* the box node that will be returned
*/
519 halfword q
; /* trails behind |p|
*/
520 scaled h
= 0; /* height
*/
521 scaled d
= 0; /* depth
*/
522 scaled x
= 0; /* natural width
*/
524 scaled s
; /* shift amount
*/
525 halfword g
; /* points to a glue specification
*/
526 int o
; /* order of infinity
*/
527 halfword dir_ptr1
= null
; /* for managing the direction stack
*/
528 int hpack_dir
; /* the current direction
*/
530 halfword pack_interrupt
[8];
531 scaled font_stretch
= 0;
532 scaled font_shrink
= 0;
533 int font_expand_ratio
= 0; /* current expansion ratio
*/
536 r
= new_node
(hlist_node
, min_quarterword
); /* the box node that will be returned
*/
537 if
(pack_direction
== -1) {
538 hpack_dir
= text_direction
;
540 hpack_dir
= pack_direction
;
542 box_dir
(r
) = hpack_dir
;
544 potential optimimization
, save a little but neglectable in practice
(not so
545 many empty boxes are used
)
552 push_dir
(dir_ptr1
,hpack_dir
); /* push null
*/
553 q
= r
+ list_offset
; /* hm
, adding something to a node address?
*/
555 if
(m
== cal_expand_ratio
) {
556 prev_char_p
= null
; /* why not always
*/
558 total_stretch
[normal
] = 0;
559 total_shrink
[normal
] = 0;
560 total_stretch
[sfi
] = 0;
561 total_shrink
[sfi
] = 0;
562 total_stretch
[fil
] = 0;
563 total_shrink
[fil
] = 0;
564 total_stretch
[fill
] = 0;
565 total_shrink
[fill
] = 0;
566 total_stretch
[filll
] = 0;
567 total_shrink
[filll
] = 0;
570 while
((p
!= null
) ||
(disc_level
> 0)) {
573 p
= pack_interrupt
[disc_level
];
577 Examine node |p| in the hlist
, taking account of its effect
578 on the dimensions of the new box
, or moving it to the adjustment list
;
579 then advance |p| to the next node
581 while
(is_char_node
(p
)) {
583 Incorporate character dimensions into the dimensions of the hbox
584 that will contain~it
, then move to the next node.
586 The following code is part of \TeX's inner loop
; i.e.
, adding
587 another character of text to the user's input will cause each of
588 these instructions to be exercised one more time.
590 if
(m
>= cal_expand_ratio
) {
592 if
(m
== cal_expand_ratio
) {
593 font_stretch
+= char_stretch
(p
);
594 font_shrink
+= char_shrink
(p
);
595 } else if
(m
== subst_ex_font
) {
596 do_subst_font
(p
, font_expand_ratio
);
599 whd
= pack_width_height_depth
(hpack_dir
, dir_TRT
, p
, true
);
612 Incorporate box dimensions into the dimensions of the hbox
615 The code here implicitly uses the fact that running dimensions are
616 indicated by |null_flag|
, which will be ignored in the calculations
617 because it is a highly negative number.
620 whd
= pack_width_height_depth
(hpack_dir
, box_dir
(p
), p
, false
);
631 if
(type
(p
) >= rule_node
) // always
635 if
(height
(p
) - s
> h
)
637 if
(depth
(p
) + s
> d
)
651 /* Incorporate glue into the horizontal totals
*/
654 o
= stretch_order
(g
);
655 total_stretch
[o
] = total_stretch
[o
] + stretch
(g
);
657 total_shrink
[o
] = total_shrink
[o
] + shrink
(g
);
658 if
(subtype
(p
) >= a_leaders
) {
667 if
(subtype
(p
) == normal
) {
668 if
(m
== cal_expand_ratio
) {
669 font_stretch
= font_stretch
+ kern_stretch
(p
);
670 font_shrink
= font_shrink
+ kern_shrink
(p
);
671 } else if
(m
== subst_ex_font
) {
672 if
(font_expand_ratio
> 0)
674 else if
(font_expand_ratio
< 0)
677 width
(p
) = get_kern
(font
(prev_char_p
), character
(prev_char_p
), character
(vlink
(p
)));
683 if
(m
== subst_ex_font
)
684 do_subst_font
(p
, font_expand_ratio
);
685 if
((subtype
(p
) != select_disc
) && (vlink(no_break(p)) != null)) {
686 pack_interrupt
[disc_level
] = vlink
(p
);
692 /* Adjust the dir stack for the |hpack| routine
*/
693 if
(dir_dir
(p
) >= 0) {
694 hpack_dir
= dir_dir
(p
);
695 push_dir_node
(dir_ptr1
,p
);
697 pop_dir_node
(dir_ptr1
);
698 if
(dir_ptr1
!= null
)
699 hpack_dir
= dir_dir
(dir_ptr1
);
703 /* begin mathskip code
*/
704 if
(glue_ptr
(p
) == zero_glue
) {
708 /* fall through
: mathskip
*/
710 /* end mathskip code
*/
711 case margin_kern_node
:
712 if
(m
== cal_expand_ratio
) {
713 int f
= font
(margin_char
(p
));
714 do_subst_font
(margin_char
(p
), 1000);
715 if
(f
!= font
(margin_char
(p
)))
716 font_stretch
= font_stretch
- width
(p
) - char_pw
(margin_char
(p
), subtype
(p
));
717 font
(margin_char
(p
)) = f
;
718 do_subst_font
(margin_char
(p
), -1000);
719 if
(f
!= font
(margin_char
(p
)))
720 font_shrink
= font_shrink
- width
(p
) - char_pw
(margin_char
(p
), subtype
(p
));
721 font
(margin_char
(p
)) = f
;
722 } else if
(m
== subst_ex_font
) {
723 do_subst_font
(margin_char
(p
), font_expand_ratio
);
724 width
(p
) = -char_pw
(margin_char
(p
), subtype
(p
));
732 Transfer node |p| to the adjustment list.
734 Although node |q| is not necessarily the immediate predecessor of node |p|
,
735 it always points to some node in the list preceding |p|. Thus
, we can delete
736 nodes by moving |q| when necessary. The algorithm takes linear time
, and the
737 extra computation does not intrude on the inner loop unless it is necessary
740 if
(adjust_tail
!= null || pre_adjust_tail
!= null
) {
741 while
(vlink
(q
) != p
)
743 if
(type
(p
) == adjust_node
) {
744 if
(adjust_pre
(p
) != 0)
745 update_adjust_list
(pre_adjust_tail
);
747 update_adjust_list
(adjust_tail
);
749 adjust_ptr
(vlink
(q
)) = null
;
750 flush_node
(vlink
(q
));
752 vlink
(adjust_tail
) = p
;
769 if
(adjust_tail
!= null
)
770 vlink
(adjust_tail
) = null
;
771 if
(pre_adjust_tail
!= null
)
772 vlink
(pre_adjust_tail
) = null
;
776 Determine the value of |width
(r
)| and the appropriate glue setting
; then
777 |return| or |goto common_ending|.
779 When we get to the present part of the program
, |x| is the natural width
780 of the box being packaged.
786 /* now |x| is the excess to be made up
*/
788 glue_sign
(r
) = normal
;
789 glue_order
(r
) = normal
;
790 set_glue_ratio_zero
(glue_set
(r
));
794 Determine horizontal glue stretch setting
, then |return|
795 or \hbox
{|goto common_ending|
}.
797 If |hpack| is called with |m
=cal_expand_ratio| we calculate
798 |font_expand_ratio| and return without checking for overfull or
801 if
(total_stretch
[filll
] != 0)
803 else if
(total_stretch
[fill
] != 0)
805 else if
(total_stretch
[fil
] != 0)
807 else if
(total_stretch
[sfi
] != 0)
812 if
((m
== cal_expand_ratio
) && (o == normal) && (font_stretch > 0)) {
813 font_expand_ratio
= divide_scaled_n
(x
, font_stretch
, 1000.0);
816 glue_order
(r
) = (quarterword
) o
;
817 glue_sign
(r
) = stretching
;
818 if
(total_stretch
[o
] != 0) {
819 glue_set
(r
) = unfloat
((double
) x
/ total_stretch
[o
]);
821 /* there's nothing to stretch
*/
822 glue_sign
(r
) = normal
;
823 set_glue_ratio_zero
(glue_set
(r
));
826 if
(list_ptr
(r
) != null
) {
828 Report an underfull hbox and |goto common_ending|
, if this box
831 last_badness
= badness
(x
, total_stretch
[normal
]);
832 if
(last_badness
> int_par
(hbadness_code
)) {
833 int callback_id
= callback_defined
(hpack_quality_callback
);
834 if
(callback_id
> 0) {
835 halfword rule
= null
;
836 if
(last_badness
> 100) {
837 run_callback
(callback_id
, "SdNdd->N","underfull",last_badness
,r
,abs
(pack_begin_line
),line
,&rule);
839 run_callback
(callback_id
, "SdNdd->N","loose",last_badness
,r
,abs
(pack_begin_line
),line
,&rule);
842 while
(vlink
(q
) != null
) {
845 couple_nodes
(q
,rule
);
849 if
(last_badness
> 100) {
850 tprint_nl
("Underfull \\hbox (badness ");
852 tprint_nl
("Loose \\hbox (badness ");
854 print_int
(last_badness
);
863 Determine horizontal glue shrink setting
, then |return|
864 or \hbox
{|goto common_ending|
},
866 if
(total_shrink
[filll
] != 0)
868 else if
(total_shrink
[fill
] != 0)
870 else if
(total_shrink
[fil
] != 0)
872 else if
(total_shrink
[sfi
] != 0)
877 if
((m
== cal_expand_ratio
) && (o == normal) && (font_shrink > 0)) {
878 font_expand_ratio
= divide_scaled_n
(x
, font_shrink
, 1000.0);
881 glue_order
(r
) = (quarterword
) o
;
882 glue_sign
(r
) = shrinking
;
883 if
(total_shrink
[o
] != 0) {
884 glue_set
(r
) = unfloat
((double
) (-x
) / (double
) total_shrink
[o
]);
886 /* there's nothing to shrink
*/
887 glue_sign
(r
) = normal
;
888 set_glue_ratio_zero
(glue_set
(r
));
890 if
((total_shrink
[o
] < -x
) && (o == normal) && (list_ptr(r) != null)) {
891 int overshoot
= -x
- total_shrink
[normal
] ;
892 last_badness
= 1000000;
893 /* use the maximum shrinkage
*/
894 set_glue_ratio_one
(glue_set
(r
));
896 Report an overfull hbox and |goto common_ending|
, if this box
899 if
((overshoot
> dimen_par
(hfuzz_code
)) ||
(int_par
(hbadness_code
) < 100)) {
900 int callback_id
= callback_defined
(hpack_quality_callback
);
901 halfword rule
= null
;
902 if
(callback_id
> 0) {
903 run_callback
(callback_id
, "SdNdd->N","overfull",overshoot
,r
,abs
(pack_begin_line
),line
,&rule);
904 } else if
(dimen_par
(overfull_rule_code
) > 0) {
905 rule
= new_rule
(normal_rule
);
906 rule_dir
(rule
) = box_dir
(r
);
907 width
(rule
) = dimen_par
(overfull_rule_code
);
910 while
(vlink
(q
) != null
) {
913 couple_nodes
(q
,rule
);
915 if
(callback_id
== 0) {
917 tprint_nl
("Overfull \\hbox (");
918 print_scaled
(overshoot
);
919 tprint
("pt too wide");
923 } else if
(o
== normal
) {
924 if
(list_ptr
(r
) != null
) {
926 Report a tight hbox and |goto common_ending|
, if this box is
929 last_badness
= badness
(-x
, total_shrink
[normal
]);
930 if
(last_badness
> int_par
(hbadness_code
)) {
931 int callback_id
= callback_defined
(hpack_quality_callback
);
932 if
(callback_id
> 0) {
933 halfword rule
= null
;
934 run_callback
(callback_id
, "SdNdd->N","tight",last_badness
,r
,abs
(pack_begin_line
),line
,&rule);
936 while
(vlink
(q
) != null
) {
939 couple_nodes
(q
,rule
);
943 tprint_nl
("Tight \\hbox (badness ");
944 print_int
(last_badness
);
955 Finish issuing a diagnostic message for an overfull or underfull
959 tprint
(") has occurred while \\output is active");
961 if
(pack_begin_line
!= 0) {
962 if
(pack_begin_line
> 0) {
963 tprint
(") in paragraph at lines ");
965 tprint
(") in alignment at lines ");
967 print_int
(abs
(pack_begin_line
));
970 tprint
(") detected at line ");
976 font_in_short_display
= null_font
;
977 short_display
(list_ptr
(r
));
981 end_diagnostic
(true
);
983 if
((m
== cal_expand_ratio
) && (font_expand_ratio != 0)) {
984 font_expand_ratio
= fix_int
(font_expand_ratio
, -1000, 1000);
988 r
= hpack
(q
, w
, subst_ex_font
, hpack_dir
);
990 while
(dir_ptr1
!= null
)
991 pop_dir_node
(dir_ptr1
);
997 halfword filtered_hpack
(halfword p
, halfword qt
, scaled w
, int m
, int grp
, int pac
)
1000 new_hyphenation
(p
, qt
);
1001 (void
) new_ligkern
(p
, qt
); // we don't care about the tail in this case
1003 q
= lua_hpack_filter
(q
, w
, m
, grp
, pac
);
1004 return hpack
(q
, w
, m
, pac
);
1008 halfword filtered_hpack
(halfword p
, halfword qt
, scaled w
, int m
, int grp
, int pac
, int just_pack
)
1013 } else if
(type
(p
) == temp_node
&& vlink(p) == null) {
1016 q
= new_node
(hlist_node
, min_quarterword
);
1017 box_dir
(q
) = (pac
== -1) ? text_direction
: pac
;
1022 new_hyphenation
(p
, qt
);
1023 (void
) new_ligkern
(p
, qt
); /* we don't care about the tail in this case
*/
1025 /* maybe here
: alink
(p
) = null
*/
1026 q
= lua_hpack_filter
(q
, w
, m
, grp
, pac
); /* ignores empty anyway
*/ /* maybe also pass tail
*/
1028 return hpack
(q
, w
, m
, pac
);
1031 @ here is a function to calculate the natural whd of a
(horizontal
) node list
1034 scaled_whd natural_sizes
(halfword p
, halfword pp
, glue_ratio g_mult
,
1035 int g_sign
, int g_order
, int pack_direction
)
1037 scaled s
; /* shift amount
*/
1038 halfword g
; /* points to a glue specification
*/
1040 scaled_whd xx
; /* for recursion
*/
1041 scaled_whd whd
, siz
= { 0, 0, 0 };
1042 if
(pack_direction
== -1) {
1043 hpack_dir
= text_direction
;
1045 hpack_dir
= pack_direction
;
1047 while
(p
!= pp
&& p != null) {
1048 while
(is_char_node
(p
) && p != pp) {
1049 whd
= pack_width_height_depth
(hpack_dir
, dir_TRT
, p
, true
);
1051 if
(whd.ht
> siz.ht
)
1053 if
(whd.dp
> siz.dp
)
1057 if
(p
!= pp
&& p != null) {
1061 s
= shift_amount
(p
);
1062 whd
= pack_width_height_depth
(hpack_dir
, box_dir
(p
), p
, false
);
1064 if
(whd.ht
- s
> siz.ht
)
1065 siz.ht
= whd.ht
- s
;
1066 if
(whd.dp
+ s
> siz.dp
)
1067 siz.dp
= whd.dp
+ s
;
1073 if
(type
(p
) >= rule_node
) // always true
1076 s
= shift_amount
(p
);
1077 if
(height
(p
) - s
> siz.ht
)
1078 siz.ht
= height
(p
) - s
;
1079 if
(depth
(p
) + s
> siz.dp
)
1080 siz.dp
= depth
(p
) + s
;
1086 if
(height
(p
) > siz.ht
)
1088 if
(depth
(p
) > siz.dp
)
1093 /* begin mathskip code
*/
1094 if
(glue_ptr
(p
) == zero_glue
) {
1095 siz.wd
+= surround
(p
);
1098 /* fall through
: mathskip
*/
1100 /* end mathskip code
*/
1104 if
(g_sign
!= normal
) {
1105 if
(g_sign
== stretching
) {
1106 if
(stretch_order
(g
) == g_order
) {
1107 siz.wd
+= float_round
(float_cast
(g_mult
) * float_cast
(stretch
(g
)));
1109 } else if
(shrink_order
(g
) == g_order
) {
1110 siz.wd
-= float_round
(float_cast
(g_mult
) * float_cast
(shrink
(g
)));
1113 if
(subtype
(p
) >= a_leaders
) {
1115 if
(height
(g
) > siz.ht
)
1117 if
(depth
(g
) > siz.dp
)
1121 case margin_kern_node
:
1126 xx
= natural_sizes
(no_break
(p
), null
, g_mult
, g_sign
, g_order
, hpack_dir
);
1143 @ In order to provide a decent indication of where an overfull or underfull box
1144 originated
, we use a global variable |pack_begin_line| that is set nonzero only
1145 when |hpack| is being called by the paragraph builder or the alignment finishing
1148 @ The source file line where the current paragraph or alignment began
; a negative
1149 value denotes alignment
:
1152 int pack_begin_line
;
1154 @ The |vpack| subroutine is actually a special case of a slightly more general
1155 routine called |vpackage|
, which has four parameters. The fourth parameter
, which
1156 is |max_dimen| in the case of |vpack|
, specifies the maximum depth of the page
1157 box that is constructed. The depth is first computed by the normal rules
; if it
1158 exceeds this limit
, the reference point is simply moved down until the limiting
1162 halfword vpackage
(halfword p
, scaled h
, int m
, scaled l
, int pack_direction
)
1164 halfword r
; /* the box node that will be returned
*/
1165 scaled w
= 0; /* width
*/
1166 scaled d
= 0; /* depth
*/
1167 scaled x
= 0; /* natural height
*/
1169 scaled s
; /* shift amount
*/
1170 halfword g
; /* points to a glue specification
*/
1171 int o
; /* order of infinity
*/
1173 r
= new_node
(vlist_node
, 0);
1174 if
(pack_direction
== -1) {
1175 box_dir
(r
) = body_direction
;
1177 box_dir
(r
) = pack_direction
;
1179 subtype
(r
) = min_quarterword
;
1180 shift_amount
(r
) = 0;
1182 total_stretch
[normal
] = 0;
1183 total_shrink
[normal
] = 0;
1184 total_stretch
[sfi
] = 0;
1185 total_shrink
[sfi
] = 0;
1186 total_stretch
[fil
] = 0;
1187 total_shrink
[fil
] = 0;
1188 total_stretch
[fill
] = 0;
1189 total_shrink
[fill
] = 0;
1190 total_stretch
[filll
] = 0;
1191 total_shrink
[filll
] = 0;
1195 Examine node |p| in the vlist
, taking account of its effect
1196 on the dimensions of the new box
; then advance |p| to the next
1199 if
(is_char_node
(p
)) {
1206 Incorporate box dimensions into the dimensions of
1207 the vbox that will contain it.
1209 s
= shift_amount
(p
);
1210 whd
= pack_width_height_depth
(box_dir
(r
), box_dir
(p
), p
, false
);
1221 if
(type
(p
) >= rule_node
) // always
1224 s
= shift_amount
(p
);
1225 if
(width
(p
) + s
> w
)
1238 /* Incorporate glue into the vertical totals
*/
1243 o
= stretch_order
(g
);
1244 total_stretch
[o
] = total_stretch
[o
] + stretch
(g
);
1245 o
= shrink_order
(g
);
1246 total_shrink
[o
] = total_shrink
[o
] + shrink
(g
);
1247 if
(subtype
(p
) >= a_leaders
) {
1271 Determine the value of |height
(r
)| and the appropriate glue setting
;
1272 then |return| or |goto common_ending|.
1274 When we get to the present part of the program
, |x| is the natural
1275 height of the box being packaged.
1277 if
(m
== additional
)
1281 /* now |x| is the excess to be made up
*/
1283 glue_sign
(r
) = normal
;
1284 glue_order
(r
) = normal
;
1285 set_glue_ratio_zero
(glue_set
(r
));
1289 Determine vertical glue stretch setting
, then |return|
1290 or \hbox
{|goto common_ending|
}.
1292 if
(total_stretch
[filll
] != 0)
1294 else if
(total_stretch
[fill
] != 0)
1296 else if
(total_stretch
[fil
] != 0)
1298 else if
(total_stretch
[sfi
] != 0)
1303 glue_order
(r
) = (quarterword
) o
;
1304 glue_sign
(r
) = stretching
;
1305 if
(total_stretch
[o
] != 0) {
1306 glue_set
(r
) = unfloat
((double
) x
/ total_stretch
[o
]);
1308 glue_sign
(r
) = normal
;
1309 set_glue_ratio_zero
(glue_set
(r
)); /* there's nothing to stretch
*/
1312 if
(list_ptr
(r
) != null
) {
1314 Report an underfull vbox and |goto common_ending|
, if this box
1315 is sufficiently bad.
1317 last_badness
= badness
(x
, total_stretch
[normal
]);
1318 if
(last_badness
> int_par
(vbadness_code
)) {
1319 int callback_id
= callback_defined
(vpack_quality_callback
);
1320 if
(callback_id
> 0) {
1321 if
(last_badness
> 100) {
1322 run_callback
(callback_id
, "SdNdd->","underfull",last_badness
,r
,abs
(pack_begin_line
),line
);
1324 run_callback
(callback_id
, "SdNdd->","loose",last_badness
,r
,abs
(pack_begin_line
),line
);
1329 if
(last_badness
> 100) {
1330 tprint_nl
("Underfull \\vbox (badness ");
1332 tprint_nl
("Loose \\vbox (badness ");
1334 print_int
(last_badness
);
1344 Determine vertical glue shrink setting
, then |return|
1345 or \hbox
{|goto common_ending|
}.
1347 if
(total_shrink
[filll
] != 0)
1349 else if
(total_shrink
[fill
] != 0)
1351 else if
(total_shrink
[fil
] != 0)
1353 else if
(total_shrink
[sfi
] != 0)
1358 glue_order
(r
) = (quarterword
) o
;
1359 glue_sign
(r
) = shrinking
;
1360 if
(total_shrink
[o
] != 0) {
1361 glue_set
(r
) = unfloat
((double
) (-x
) / total_shrink
[o
]);
1363 /* there's nothing to shrink
*/
1364 glue_sign
(r
) = normal
;
1365 set_glue_ratio_zero
(glue_set
(r
));
1367 if
((total_shrink
[o
] < -x
) && (o == normal) && (list_ptr(r) != null)) {
1368 int overshoot
= -x
- total_shrink
[normal
];
1369 last_badness
= 1000000;
1370 /* use the maximum shrinkage
*/
1371 set_glue_ratio_one
(glue_set
(r
));
1373 Report an overfull vbox and |goto common_ending|
, if this box
1374 is sufficiently bad.
1376 if
((overshoot
> dimen_par
(vfuzz_code
)) ||
(int_par
(vbadness_code
) < 100)) {
1377 int callback_id
= callback_defined
(vpack_quality_callback
);
1378 if
(callback_id
> 0) {
1379 run_callback
(callback_id
, "SdNdd->","overfull",overshoot
,r
,abs
(pack_begin_line
),line
);
1383 tprint_nl
("Overfull \\vbox (");
1384 print_scaled
(-x
- total_shrink
[normal
]);
1385 tprint
("pt too high");
1389 } else if
(o
== normal
) {
1390 if
(list_ptr
(r
) != null
) {
1392 Report a tight vbox and |goto common_ending|
, if this box is
1395 last_badness
= badness
(-x
, total_shrink
[normal
]);
1396 if
(last_badness
> int_par
(vbadness_code
)) {
1397 int callback_id
= callback_defined
(vpack_quality_callback
);
1398 if
(callback_id
> 0) {
1399 run_callback
(callback_id
, "SdNdd->","tight",last_badness
,r
,abs
(pack_begin_line
),line
);
1403 tprint_nl
("Tight \\vbox (badness ");
1404 print_int
(last_badness
);
1414 /* Finish issuing a diagnostic message or an overfull or underfull vbox
*/
1415 if
(output_active
) {
1416 tprint
(") has occurred while \\output is active");
1418 if
(pack_begin_line
!= 0) {
1419 /* it's actually negative
*/
1420 tprint
(") in alignment at lines ");
1421 print_int
(abs
(pack_begin_line
));
1424 tprint
(") detected at line ");
1431 end_diagnostic
(true
);
1437 halfword filtered_vpackage
(halfword p
, scaled h
, int m
, scaled l
, int grp
, int pack_direction
, int just_pack
)
1441 /* if
(q
!= null
) */
1442 q
= lua_vpack_filter
(q
, h
, m
, l
, grp
, pack_direction
);
1443 return vpackage
(q
, h
, m
, l
, pack_direction
);
1447 void finish_vcenter
(void
)
1452 p
= vpack
(vlink
(cur_list.head_field
), saved_value
(0), saved_level
(0), -1);
1454 p
= math_vcenter_group
(p
);
1461 halfword saved0
, saved2
, saved3
, saved4
;
1462 int grp
= cur_group
;
1463 scaled d
= box_max_depth
; /* max depth
*/
1466 saved0
= saved_value
(0);
1467 saved2
= saved_value
(2);
1468 saved3
= saved_value
(3);
1469 saved4
= saved_value
(4);
1470 if
(cur_list.mode_field
== -hmode
) {
1471 cur_box
= filtered_hpack
(cur_list.head_field
, cur_list.tail_field
,
1472 saved_value
(1), saved_level
(1), grp
, saved_level
(2), saved4
);
1473 subtype
(cur_box
) = hbox_list
;
1475 cur_box
= filtered_vpackage
(vlink
(cur_list.head_field
),
1476 saved_value
(1), saved_level
(1), d
, grp
, saved_level
(2), saved4
);
1477 if
(c
== vtop_code
) {
1479 Read just the height and depth of |cur_box|
, for \.
{\\vtop
}. The
1480 height of a `\.
{\\vtop
}' box is inherited from the first item on
1481 its list
, if that item is an |hlist_node|
, |vlist_node|
, or
1482 |rule_node|
; otherwise the \.
{\\vtop
} height is zero.
1485 halfword p
= list_ptr
(cur_box
);
1486 if
((p
!= null
) && (type(p) <= rule_node)) {
1487 /* hlist
, vlist
, rule
*/
1490 depth
(cur_box
) = depth
(cur_box
) - h
+ height
(cur_box
);
1491 height
(cur_box
) = h
;
1494 if
(saved2
!= null
) {
1495 /* DIR: Adjust back |text_dir_ptr| for |scan_spec|
*/
1496 flush_node_list
(text_dir_ptr
);
1497 text_dir_ptr
= saved2
;
1500 replace_attribute_list
(cur_box
, saved3
);
1505 @ When a box is being appended to the current vertical list
, the baselineskip
1506 calculation is handled by the |append_to_vlist| routine.
1509 void append_to_vlist
(halfword b
, int location
)
1511 scaled d
; /* deficiency of space between baselines
*/
1512 halfword p
; /* a new glue node
*/
1513 boolean mirrored
= (type
(b
) == hlist_node
) && is_mirrored(box_dir(b)) ;
1514 halfword result
= null
;
1515 halfword next_depth
= ignore_depth
;
1516 boolean prev_set
= false
;
1517 if
(lua_appendtovlist_callback
(b
,location
,prev_depth
,mirrored
,&result,&next_depth,&prev_set)) {
1518 while
(result
!= null
) {
1519 couple_nodes
(cur_list.tail_field
, result
);
1520 cur_list.tail_field
= result
;
1521 result
= vlink
(result
);
1524 prev_depth
= next_depth
;
1527 if
(prev_depth
> ignore_depth
) {
1529 d
= width
(glue_par
(baseline_skip_code
)) - prev_depth
- depth
(b
);
1531 d
= width
(glue_par
(baseline_skip_code
)) - prev_depth
- height
(b
);
1533 if
(d
< dimen_par
(line_skip_limit_code
)) {
1534 p
= new_param_glue
(line_skip_code
);
1536 p
= new_skip_param
(baseline_skip_code
);
1537 /* |temp_ptr
=glue_ptr
(p
)|
*/
1538 width
(temp_ptr
) = d
;
1540 couple_nodes
(cur_list.tail_field
, p
);
1541 cur_list.tail_field
= p
;
1543 couple_nodes
(cur_list.tail_field
, b
);
1544 cur_list.tail_field
= b
;
1546 prev_depth
= height
(b
);
1548 prev_depth
= depth
(b
);
1553 @ When |saving_vdiscards| is positive then the glue
, kern
, and penalty nodes
1554 removed by the page builder or by \.
{\\vsplit
} from the top of a vertical list
1555 are saved in special lists instead of being discarded.
1558 #define tail_page_disc disc_ptr
[copy_code
] /* last item removed by page builder
*/
1559 #define page_disc disc_ptr
[last_box_code
] /* first item removed by page builder
*/
1560 #define split_disc disc_ptr
[vsplit_code
] /* first item removed by \.
{\\vsplit
} */
1562 halfword disc_ptr
[(vsplit_code
+ 1)]; /* list pointers
*/
1564 @ The |vsplit| procedure
, which implements \TeX's \.
{\\vsplit
} operation
, is
1565 considerably simpler than |line_break| because it doesn't have to worry about
1566 hyphenation
, and because its mission is to discover a single break instead of an
1567 optimum sequence of breakpoints. But before we get into the details of |vsplit|
,
1568 we need to consider a few more basic things.
1570 A subroutine called |prune_page_top| takes a pointer to a vlist and returns a
1571 pointer to a modified vlist in which all glue
, kern
, and penalty nodes have been
1572 deleted before the first box or rule node. However
, the first box or rule is
1573 actually preceded by a newly created glue node designed so that the topmost
1574 baseline will be at distance |split_top_skip| from the top
, whenever this is
1575 possible without backspacing.
1577 When the second argument |s| is |false| the deleted nodes are destroyed
,
1578 otherwise they are collected in a list starting at |split_disc|.
1581 halfword prune_page_top
(halfword p
, boolean s
)
1584 halfword prev_p
= temp_head
; /* lags one step behind |p|
*/
1586 vlink
(temp_head
) = p
;
1592 /* Insert glue for |split_top_skip| and set~|p
:=null|
*/
1593 q
= new_skip_param
(split_top_skip_code
);
1596 /* now |temp_ptr
=glue_ptr
(q
)|
*/
1597 if
(width
(temp_ptr
) > height
(p
))
1598 width
(temp_ptr
) = width
(temp_ptr
) - height
(p
);
1600 width
(temp_ptr
) = 0;
1618 if
(split_disc
== null
)
1628 confusion
("pruning");
1632 return vlink
(temp_head
);
1635 @ The next subroutine finds the best place to break a given vertical list so as
1636 to obtain a box of height~|h|
, with maximum depth~|d|. A pointer to the beginning
1637 of the vertical list is given
, and a pointer to the optimum breakpoint is
1638 returned. The list is effectively followed by a forced break
, i.e.
, a penalty
1639 node with the |eject_penalty|
; if the best break occurs at this artificial node
,
1640 the value |null| is returned.
1643 scaled active_height
[10]; /* distance from first active node to~|cur_p|
*/
1645 @ An array of six |scaled| distances is used to keep track of the height from the
1646 beginning of the list to the current place
, just as in |line_break|. In fact
, we
1647 use one of the same arrays
, only changing its name to reflect its new
1651 #define do_all_six
(A
) A
(1);A
(2);A
(3);A
(4);A
(5);A
(6);A
(7)
1652 #define set_height_zero
(A
) active_height
[A
]=0 /* initialize the height to zero
*/
1654 @ A global variable |best_height_plus_depth| will be set to the natural size of
1655 the box that corresponds to the optimum breakpoint found by |vert_break|.
(This
1656 value is used by the insertion-splitting algorithm of the page builder.
)
1658 @ height of the best box
, without stretching or shrinking
1661 scaled best_height_plus_depth
;
1663 /* finds optimum page break
*/
1665 halfword vert_break
(halfword p
, scaled h
, scaled d
)
1667 halfword prev_p
= p
; /* if |p| is a glue node
, |type
(prev_p
)| determines whether |p| is a
1668 legal breakpoint
, an initial glue node is not a legal breakpoint
*/
1669 halfword q
, r
; /* glue specifications
*/
1670 int pi
= 0; /* penalty value
*/
1671 int b
; /* badness at a trial breakpoint
*/
1672 int t
; /* |type| of the node following a kern
*/
1673 int least_cost
; /* the smallest badness plus penalties found so far
*/
1674 halfword best_place
= null
; /* the most recent break that leads to |least_cost|
*/
1675 scaled prev_dp
= 0; /* depth of previous box in the list
*/
1676 least_cost
= awful_bad
;
1677 do_all_six
(set_height_zero
);
1679 /* If node |p| is a legal breakpoint
, check if this break is
1680 the best known
, and |goto done| if |p| is null or
1681 if the page-so-far is already too full to accept more stuff
*/
1682 /* A subtle point to be noted here is that the maximum depth~|d| might be
1683 negative
, so |cur_height| and |prev_dp| might need to be corrected even
1684 after a glue or kern node.
*/
1689 /* Use node |p| to update the current height and depth measurements
;
1690 if this node is not a legal breakpoint
, |goto not_found|
1691 or |update_heights|
,
1692 otherwise set |pi| to the associated penalty at the break
*/
1697 cur_height
= cur_height
+ prev_dp
+ height
(p
);
1706 if
(precedes_break
(prev_p
))
1709 goto UPDATE_HEIGHTS
;
1712 if
(vlink
(p
) == null
)
1719 goto UPDATE_HEIGHTS
;
1729 confusion
("vertbreak");
1733 /* Check if node |p| is a new champion breakpoint
; then |goto done|
1734 if |p| is a forced break or if the page-so-far is already too full
*/
1735 if
(pi
< inf_penalty
) {
1736 /* Compute the badness
, |b|
, using |awful_bad| if the box is too full
*/
1737 if
(cur_height
< h
) {
1738 if
((active_height
[3] != 0) ||
(active_height
[4] != 0) ||
1739 (active_height
[5] != 0) ||
(active_height
[6] != 0))
1742 b
= badness
(h
- cur_height
, active_height
[2]);
1743 } else if
(cur_height
- h
> active_height
[7]) {
1746 b
= badness
(cur_height
- h
, active_height
[7]);
1749 if
(b
< awful_bad
) {
1750 if
(pi
<= eject_penalty
)
1752 else if
(b
< inf_bad
)
1757 if
(b
<= least_cost
) {
1760 best_height_plus_depth
= cur_height
+ prev_dp
;
1762 if
((b
== awful_bad
) ||
(pi
<= eject_penalty
))
1766 if
((type
(p
) < glue_node
) ||
(type
(p
) > kern_node
))
1769 /* Update the current height and depth measurements with
1770 respect to a glue or kern node~|p|
*/
1771 /* Vertical lists that are subject to the |vert_break| procedure should not
1772 contain infinite shrinkability
, since that would permit any amount of
1773 information to ``fit'' on one page.
*/
1775 if
(type
(p
) == kern_node
) {
1779 active_height
[2 + stretch_order
(q
)] += stretch
(q
);
1780 active_height
[7] += shrink
(q
);
1781 if
((shrink_order
(q
) != normal
) && (shrink(q) != 0)) {
1782 print_err
("Infinite glue shrinkage found in box being split");
1783 help4
("The box you are \\vsplitting contains some infinitely",
1784 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
1785 "Such glue doesn't belong there; but you can safely proceed,",
1786 "since the offensive shrinkability has been made finite.");
1789 shrink_order
(r
) = normal
;
1795 cur_height
= cur_height
+ prev_dp
+ width
(q
);
1799 cur_height
= cur_height
+ prev_dp
- d
;
1809 @ Now we are ready to consider |vsplit| itself. Most of its work is accomplished
1810 by the two subroutines that we have just considered.
1812 Given the number of a vlist box |n|
, and given a desired page height |h|
, the
1813 |vsplit| function finds the best initial segment of the vlist and returns a box
1814 for a page of height~|h|. The remainder of the vlist
, if any
, replaces the
1815 original box
, after removing glue and penalties and adjusting for
1816 |split_top_skip|. Mark nodes in the split-off box are used to set the values of
1817 |split_first_mark| and |split_bot_mark|
; we use the fact that
1818 |split_first_mark
(x
)=null| if and only if |split_bot_mark
(x
)=null|.
1820 The original box becomes ``void'' if and only if it has been entirely extracted.
1821 The extracted box is ``void'' if and only if the original box was void
(or if it
1822 was
, erroneously
, an hlist box
).
1825 /* extracts a page of height |h| from box |n|
*/
1827 halfword vsplit
(halfword n
, scaled h
, int m
)
1829 halfword v
; /* the box to be split
*/
1830 int vdir
; /* the direction of the box to be split
*/
1831 halfword p
; /* runs through the vlist
*/
1832 halfword q
; /* points to where the break occurs
*/
1833 halfword i
; /* for traversing marks lists
*/
1836 flush_node_list
(split_disc
);
1838 for
(i
= 0; i
<= biggest_used_mark
; i
++) {
1839 delete_split_first_mark
(i
);
1840 delete_split_bot_mark
(i
);
1842 /* Dispense with trivial cases of void or bad boxes
*/
1846 if
(type
(v
) != vlist_node
) {
1847 print_err
("\\vsplit needs a \\vbox");
1848 help2
("The box you are trying to split is an \\hbox.",
1849 "i can't split such a box, so I''ll leave it alone.");
1853 q
= vert_break
(list_ptr
(v
), h
, dimen_par
(split_max_depth_code
));
1855 Look at all the marks in nodes before the break
, and set the final
1856 link to |null| at the break. It's possible that the box begins with
1857 a penalty node that is the ``best'' break
, so we must be careful to
1858 handle this special case correctly.
1865 if
(type
(p
) == mark_node
) {
1866 if
(split_first_mark
(mark_class
(p
)) == null
) {
1867 set_split_first_mark
(mark_class
(p
), mark_ptr
(p
));
1868 set_split_bot_mark
(mark_class
(p
), split_first_mark
(mark_class
(p
)));
1869 set_token_ref_count
(split_first_mark
(mark_class
(p
)),
1870 token_ref_count
(split_first_mark
(mark_class
(p
))) + 2);
1872 delete_token_ref
(split_bot_mark
(mark_class
(p
)));
1873 set_split_bot_mark
(mark_class
(p
), mark_ptr
(p
));
1874 add_token_ref
(split_bot_mark
(mark_class
(p
)));
1877 if
(vlink
(p
) == q
) {
1884 q
= prune_page_top
(q
, int_par
(saving_vdiscards_code
) > 0);
1889 /* the |eq_level| of the box stays the same
*/
1892 box
(n
) = filtered_vpackage
(q
, 0, additional
, dimen_par
(max_depth_code
), split_keep_group
, vdir
, 0);
1895 return filtered_vpackage
(p
, h
, exactly
, dimen_par
(split_max_depth_code
), split_off_group
, vdir
, 0);
1897 return filtered_vpackage
(p
, 0, additional
, dimen_par
(max_depth_code
), split_off_group
, vdir
, 0);
1901 @ Now that we can see what eventually happens to boxes
, we can consider the first
1902 steps in their creation. The |begin_box| routine is called when |box_context| is
1903 a context specification
, |cur_chr| specifies the type of box desired
, and
1907 void begin_box
(int box_context
)
1909 halfword q
; /* run through the current list
*/
1910 halfword k
; /* 0 or |vmode| or |hmode|
*/
1911 int n
; /* a box number
*/
1912 int spec_direction
= -1;
1916 scan_register_num
();
1917 cur_box
= box
(cur_val
);
1918 /* the box becomes void
, at the same level
*/
1919 box
(cur_val
) = null
;
1922 scan_register_num
();
1923 cur_box
= copy_node_list
(box
(cur_val
));
1927 If the current list ends with a box node
, delete it from
1928 the list and make |cur_box| point to it
; otherwise set
1932 if
(abs
(cur_list.mode_field
) == mmode
) {
1934 help1
("Sorry; this \\lastbox will be void.");
1936 } else if
((cur_list.mode_field
== vmode
) && (cur_list.head_field == cur_list.tail_field)) {
1938 help2
("Sorry...I usually can't take things from the current page.",
1939 "This \\lastbox will therefore be void.");
1942 if
(cur_list.head_field
!= cur_list.tail_field
) {
1943 /* todo
: new code
, needs testing
*/
1945 /* maybe
: ((type
(cur_list.tail_field
) == hlist_node
) < rule_node
) */
1947 if
((type
(cur_list.tail_field
) == hlist_node
) ||
(type
(cur_list.tail_field
) == vlist_node
)) {
1948 /* Remove the last box ...
*/
1949 q
= alink
(cur_list.tail_field
);
1950 if
(q
== null || vlink
(q
) != cur_list.tail_field
) {
1951 q
= cur_list.head_field
;
1952 while
(vlink
(q
) != cur_list.tail_field
)
1955 uncouple_node
(cur_list.tail_field
);
1956 cur_box
= cur_list.tail_field
;
1957 shift_amount
(cur_box
) = 0;
1958 cur_list.tail_field
= q
;
1959 vlink
(cur_list.tail_field
) = null
;
1966 Split off part of a vertical box
, make |cur_box| point to it. Here we
1967 deal with things like `\.
{\\vsplit
13 to
100pt
}'.
1969 scan_register_num
();
1971 if
(!scan_keyword
("to")) {
1972 print_err
("Missing `to' inserted");
1973 help2
("I'm working on `\\vsplit<box number> to <dimen>';",
1974 "will look for the <dimen> next.");
1977 scan_normal_dimen
();
1978 cur_box
= vsplit
(n
, cur_val
, additional
);
1982 Initiate the construction of an hbox or vbox
, then |return|. Here is
1983 where we enter restricted horizontal mode or internal vertical mode
,
1984 in order to make a box.
1988 cur_chr
= vtop_code
;
1992 cur_chr
= vtop_code
+ vmode
;
1996 cur_chr
= vtop_code
+ hmode
;
2001 k
= cur_chr
- vtop_code
;
2002 set_saved_record
(0, saved_boxcontext
, 0, box_context
);
2003 switch
(abs
(cur_list.mode_field
)) {
2005 spec_direction
= body_direction
;
2008 spec_direction
= text_direction
;
2011 spec_direction
= math_direction
;
2015 if
((box_context
< box_flag
) && (abs(cur_list.mode_field) == vmode))
2016 scan_full_spec
(adjusted_hbox_group
, spec_direction
,just_pack
);
2018 scan_full_spec
(hbox_group
, spec_direction
,just_pack
);
2021 scan_full_spec
(vbox_group
, spec_direction
,just_pack
);
2023 scan_full_spec
(vtop_group
, spec_direction
,just_pack
);
2029 cur_list.mode_field
= -k
;
2031 prev_depth
= ignore_depth
;
2032 if
(every_vbox
!= null
)
2033 begin_token_list
(every_vbox
, every_vbox_text
);
2035 space_factor
= 1000;
2036 if
(every_hbox
!= null
)
2037 begin_token_list
(every_hbox
, every_hbox_text
);
2042 /* in simple cases
, we use the box immediately
*/
2043 box_end
(box_context
);