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 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
)
355 scaled char_stretch
(halfword p
)
357 internal_font_number f
= font
(p
);
358 int m
= font_max_stretch
(f
);
360 int c
= character
(p
);
361 int ef
= get_ef_code
(f
, c
);
363 scaled dw
= calc_char_width
(f
, c
, m
) - char_width
(f
, c
) - x_advance
(p
);
365 return round_xn_over_d
(dw
, ef
, 1000);
373 scaled char_shrink
(halfword p
)
375 internal_font_number f
= font
(p
);
376 int m
= font_max_shrink
(f
);
378 int c
= character
(p
);
379 int ef
= get_ef_code
(f
, c
);
381 scaled dw
= char_width
(f
, c
) + x_advance
(p
) - calc_char_width
(f
, c
, -m
);
383 return round_xn_over_d
(dw
, ef
, 1000);
392 scaled kern_stretch
(halfword p
)
397 if
((prev_char_p
== null
) ||
(vlink
(prev_char_p
) != p
) ||
(vlink
(p
) == null
))
400 // we need a left char
401 if
(!is_char_node
(l
))
405 if
(!is_char_node
(r
))
407 // and a reason to kern
408 if
((font
(l
) != font
(r
)) ||
(font_max_stretch
(font
(l
)) == 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
)
421 scaled w
= width
(p
) ;
425 /* why bother about zero kerns
*/
429 if
((l
== null
) ||
(vlink
(l
) != p
)) {
430 /* we only care about kerns following a char
*/
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
) */
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;
444 /* nothing to kern
*/
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 ;
453 x
= round_xn_over_d
(d
- w
, e
, 1000);
456 printf
("STRETCH w=%i s=%i x=%i\n",w
,e
+m
,x
);
463 scaled kern_shrink
(halfword p
)
468 if
((prev_char_p
== null
) ||
(vlink
(prev_char_p
) != p
) ||
(vlink
(p
) == null
))
471 // we need a left char
472 if
(!is_char_node
(l
))
476 if
(!is_char_node
(r
))
478 // and a reason to kern
479 if
((font
(l
) != font
(r
)) ||
(font_max_shrink
(font
(l
)) == 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
)
492 scaled w
= width
(p
) ;
496 /* why bother about zero kerns
*/
500 if
((l
== null
) ||
(vlink
(l
) != p
)) {
501 /* we only care about kerns following a char
*/
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
) */
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;
515 /* nothing to kern
*/
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 ;
523 x
= round_xn_over_d
(w
- d
, e
, 1000);
526 printf
("SHRINK w=%i s=%i x=%i\n",w
,e
+m
,x
);
532 void do_subst_font
(halfword p
, int ex_ratio
)
534 if
(type
(p
) == disc_node
) {
535 halfword r
= vlink
(pre_break
(p
));
538 do_subst_font
(r
, ex_ratio
);
541 r
= vlink
(post_break
(p
));
544 do_subst_font
(r
, ex_ratio
);
547 r
= vlink
(no_break
(p
));
550 do_subst_font
(r
, ex_ratio
);
555 if
(! is_char_node
(p
)) {
556 normal_error
("font expansion", "invalid node type");
559 internal_font_number f
= font
(p
);
560 int ef
= get_ef_code
(f
, character
(p
));
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;
574 scaled char_pw
(halfword p
, int side
)
576 internal_font_number f
;
578 if
(side
== left_side
)
579 last_leftmost_char
= null
;
581 last_rightmost_char
= null
;
584 if
(!is_char_node
(p
))
587 if
(side
== left_side
) {
588 c
= get_lp_code
(f
, character
(p
));
589 last_leftmost_char
= p
;
591 c
= get_rp_code
(f
, character
(p
));
592 last_rightmost_char
= p
;
597 return round_xn_over_d
(w
, c
, 1000);
601 halfword new_margin_kern
(scaled w
, halfword p
, int side
)
604 k
= new_node
(margin_kern_node
, side
);
607 normal_error
("margin kerning", "invalid pointer to marginal char node");
608 q
= new_char
(font
(p
), character
(p
));
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
*/
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
*/
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;
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
;
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
)
657 push_dir
(dir_ptr1
,hpack_dir
); /* push null
*/
658 q
= r
+ list_offset
; /* hm
, adding something to a node address?
*/
660 if
(m
== cal_expand_ratio
) {
661 prev_char_p
= null
; /* why not always
*/
663 if
(adjust_spacing
> 2) {
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;
678 while
((p
!= null
) ||
(disc_level
> 0)) {
681 p
= pack_interrupt
[disc_level
];
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
) {
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
);
720 Incorporate box dimensions into the dimensions of the hbox
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.
728 whd
= pack_width_height_depth
(hpack_dir
, box_dir
(p
), p
, false
);
739 if
(type
(p
) >= rule_node
) // always
743 if
(height
(p
) - s
> h
)
745 if
(depth
(p
) + s
> d
)
759 /* Incorporate glue into the horizontal totals
*/
761 o
= stretch_order
(p
);
762 total_stretch
[o
] = total_stretch
[o
] + stretch
(p
);
764 total_shrink
[o
] = total_shrink
[o
] + shrink
(p
);
765 if
(subtype
(p
) >= a_leaders
) {
766 halfword g
= leader_ptr
(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
*/
783 if
(font_expand_ratio
> 0) {
785 } else if
(font_expand_ratio
< 0) {
791 if
(x
!=0) printf
("SET %i %i %i\n",font_expand_ratio
,k
,x
);
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
);
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
);
811 pop_dir_node
(dir_ptr1
);
812 if
(dir_ptr1
!= null
)
813 hpack_dir
= dir_dir
(dir_ptr1
);
817 /* begin mathskip code
*/
818 if
(glue_is_zero
(p
)) {
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
));
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
854 if
(adjust_tail
!= null || pre_adjust_tail
!= null
) {
855 while
(vlink
(q
) != p
)
857 if
(type
(p
) == adjust_node
) {
858 if
(adjust_pre
(p
) != 0)
859 update_adjust_list
(pre_adjust_tail
);
861 update_adjust_list
(adjust_tail
);
863 adjust_ptr
(vlink
(q
)) = null
;
864 flush_node
(vlink
(q
));
866 vlink
(adjust_tail
) = p
;
883 if
(adjust_tail
!= null
)
884 vlink
(adjust_tail
) = null
;
885 if
(pre_adjust_tail
!= null
)
886 vlink
(pre_adjust_tail
) = null
;
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.
900 /* now |x| is the excess to be made up
*/
902 glue_sign
(r
) = normal
;
903 glue_order
(r
) = normal
;
904 set_glue_ratio_zero
(glue_set
(r
));
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
915 if
(total_stretch
[filll
] != 0)
917 else if
(total_stretch
[fill
] != 0)
919 else if
(total_stretch
[fil
] != 0)
921 else if
(total_stretch
[sfi
] != 0)
926 if
((m
== cal_expand_ratio
) && (o == normal) && (font_stretch > 0)) {
927 font_expand_ratio
= divide_scaled_n
(x
, font_stretch
, 1000.0);
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
]);
935 /* there's nothing to stretch
*/
936 glue_sign
(r
) = normal
;
937 set_glue_ratio_zero
(glue_set
(r
));
940 if
(list_ptr
(r
) != null
) {
942 Report an underfull hbox and |goto common_ending|
, if this box
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);
953 run_callback
(callback_id
, "SdNdd->N","loose",last_badness
,r
,abs
(pack_begin_line
),line
,&rule);
956 while
(vlink
(q
) != null
) {
959 couple_nodes
(q
,rule
);
963 if
(last_badness
> 100) {
964 tprint_nl
("Underfull \\hbox (badness ");
966 tprint_nl
("Loose \\hbox (badness ");
968 print_int
(last_badness
);
977 Determine horizontal glue shrink setting
, then |return|
978 or \hbox
{|goto common_ending|
},
980 if
(total_shrink
[filll
] != 0)
982 else if
(total_shrink
[fill
] != 0)
984 else if
(total_shrink
[fil
] != 0)
986 else if
(total_shrink
[sfi
] != 0)
991 if
((m
== cal_expand_ratio
) && (o == normal) && (font_shrink > 0)) {
992 font_expand_ratio
= divide_scaled_n
(x
, font_shrink
, 1000.0);
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
]);
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
);
1024 while
(vlink
(q
) != null
) {
1027 couple_nodes
(q
,rule
);
1029 if
(callback_id
== 0) {
1031 tprint_nl
("Overfull \\hbox (");
1032 print_scaled
(overshoot
);
1033 tprint
("pt too wide");
1037 } else if
(o
== normal
) {
1038 if
(list_ptr
(r
) != null
) {
1040 Report a tight hbox and |goto common_ending|
, if this box is
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);
1050 while
(vlink
(q
) != null
) {
1053 couple_nodes
(q
,rule
);
1057 tprint_nl
("Tight \\hbox (badness ");
1058 print_int
(last_badness
);
1069 Finish issuing a diagnostic message for an overfull or underfull
1072 if
(output_active
) {
1073 tprint
(") has occurred while \\output is active");
1075 if
(pack_begin_line
!= 0) {
1076 if
(pack_begin_line
> 0) {
1077 tprint
(") in paragraph at lines ");
1079 tprint
(") in alignment at lines ");
1081 print_int
(abs
(pack_begin_line
));
1084 tprint
(") detected at line ");
1090 font_in_short_display
= null_font
;
1091 short_display
(list_ptr
(r
));
1095 end_diagnostic
(true
);
1097 if
((m
== cal_expand_ratio
) && (font_expand_ratio != 0)) {
1098 font_expand_ratio
= fix_int
(font_expand_ratio
, -1000, 1000);
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;
1113 halfword filtered_hpack
(halfword p
, halfword qt
, scaled w
, int m
, int grp
, int pac
, int just_pack
, halfword attr
)
1118 } else if
(type
(p
) == temp_node
&& vlink(p) == null) {
1121 q
= new_node
(hlist_node
, min_quarterword
);
1122 box_dir
(q
) = (pac
== -1) ? text_direction
: pac
;
1127 new_hyphenation
(p
, qt
);
1128 (void
) new_ligkern
(p
, qt
); /* we don't care about the tail in this case
*/
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
*/
1145 scaled_whd xx
; /* for recursion
*/
1146 scaled_whd whd
, siz
= { 0, 0, 0 };
1147 if
(pack_direction
== -1) {
1148 hpack_dir
= text_direction
;
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
);
1156 if
(whd.ht
> siz.ht
)
1158 if
(whd.dp
> siz.dp
)
1162 if
(p
!= pp
&& p != null) {
1166 s
= shift_amount
(p
);
1167 whd
= pack_width_height_depth
(hpack_dir
, box_dir
(p
), p
, false
);
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
;
1178 if
(type
(p
) >= rule_node
) // always true
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
;
1191 if
(height
(p
) > siz.ht
)
1193 if
(depth
(p
) > siz.dp
)
1198 /* begin mathskip code
*/
1199 if
(glue_is_zero
(p
)) {
1200 siz.wd
+= surround
(p
);
1203 /* fall through
: mathskip
*/
1205 /* end mathskip code
*/
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
) {
1219 if
(height
(g
) > siz.ht
)
1221 if
(depth
(g
) > siz.dp
)
1225 case margin_kern_node
:
1229 siz.wd
+= width
(p
) + ex_kern
(p
);
1232 xx
= natural_sizes
(no_break
(p
), null
, g_mult
, g_sign
, g_order
, hpack_dir
);
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
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
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
*/
1275 scaled s
; /* shift amount
*/
1276 int o
; /* order of infinity
*/
1278 r
= new_node
(vlist_node
, 0);
1279 if
(pack_direction
== -1) {
1280 box_dir
(r
) = body_direction
;
1282 box_dir
(r
) = pack_direction
;
1284 subtype
(r
) = min_quarterword
;
1285 shift_amount
(r
) = 0;
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;
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
1304 if
(is_char_node
(p
)) {
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
);
1326 if
(type
(p
) >= rule_node
) // always
1329 s
= shift_amount
(p
);
1330 if
(width
(p
) + s
> w
)
1343 /* Incorporate glue into the vertical totals
*/
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
);
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
)
1385 /* now |x| is the excess to be made up
*/
1387 glue_sign
(r
) = normal
;
1388 glue_order
(r
) = normal
;
1389 set_glue_ratio_zero
(glue_set
(r
));
1393 Determine vertical glue stretch setting
, then |return|
1394 or \hbox
{|goto common_ending|
}.
1396 if
(total_stretch
[filll
] != 0)
1398 else if
(total_stretch
[fill
] != 0)
1400 else if
(total_stretch
[fil
] != 0)
1402 else if
(total_stretch
[sfi
] != 0)
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
]);
1412 glue_sign
(r
) = normal
;
1413 set_glue_ratio_zero
(glue_set
(r
)); /* there's nothing to stretch
*/
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
);
1428 run_callback
(callback_id
, "SdNdd->","loose",last_badness
,r
,abs
(pack_begin_line
),line
);
1433 if
(last_badness
> 100) {
1434 tprint_nl
("Underfull \\vbox (badness ");
1436 tprint_nl
("Loose \\vbox (badness ");
1438 print_int
(last_badness
);
1448 Determine vertical glue shrink setting
, then |return|
1449 or \hbox
{|goto common_ending|
}.
1451 if
(total_shrink
[filll
] != 0)
1453 else if
(total_shrink
[fill
] != 0)
1455 else if
(total_shrink
[fil
] != 0)
1457 else if
(total_shrink
[sfi
] != 0)
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
]);
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
);
1487 tprint_nl
("Overfull \\vbox (");
1488 print_scaled
(-x
- total_shrink
[normal
]);
1489 tprint
("pt too high");
1493 } else if
(o
== normal
) {
1494 if
(list_ptr
(r
) != null
) {
1496 Report a tight vbox and |goto common_ending|
, if this box is
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
);
1507 tprint_nl
("Tight \\vbox (badness ");
1508 print_int
(last_badness
);
1518 /* Finish issuing a diagnostic message or an overfull or underfull vbox
*/
1519 if
(output_active
) {
1520 tprint
(") has occurred while \\output is active");
1522 if
(pack_begin_line
!= 0) {
1523 /* it's actually negative
*/
1524 tprint
(") in alignment at lines ");
1525 print_int
(abs
(pack_begin_line
));
1528 tprint
(") detected at line ");
1535 end_diagnostic
(true
);
1541 halfword filtered_vpackage
(halfword p
, scaled h
, int m
, scaled l
, int grp
, int pack_direction
, int just_pack
, halfword attr
)
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
);
1551 void finish_vcenter
(void
)
1556 p
= vpack
(vlink
(cur_list.head_field
), saved_value
(0), saved_level
(0), -1);
1558 p
= math_vcenter_group
(p
);
1565 halfword saved0
, saved2
, saved3
, saved4
;
1566 int grp
= cur_group
;
1567 scaled d
= box_max_depth
; /* max depth
*/
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
;
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.
1589 halfword p
= list_ptr
(cur_box
);
1590 if
((p
!= null
) && (type(p) <= rule_node)) {
1591 /* hlist
, vlist
, rule
*/
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
);
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
);
1628 prev_depth
= next_depth
;
1631 if
(prev_depth
> ignore_depth
) {
1633 d
= width
(glue_par
(baseline_skip_code
)) - prev_depth
- depth
(b
);
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
);
1640 p
= new_skip_param
(baseline_skip_code
);
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
;
1649 prev_depth
= height
(b
);
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
)
1687 halfword prev_p
= temp_head
; /* lags one step behind |p|
*/
1689 vlink
(temp_head
) = p
;
1695 /* Insert glue for |split_top_skip| and set~|p
:=null|
*/
1696 q
= new_skip_param
(split_top_skip_code
);
1699 if
(width
(q
) > height
(p
))
1700 width
(q
) = width
(q
) - height
(p
);
1720 if
(split_disc
== null
)
1730 confusion
("pruning");
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
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
);
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.
*/
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
*/
1798 cur_height
= cur_height
+ prev_dp
+ height
(p
);
1807 if
(precedes_break
(prev_p
))
1810 goto UPDATE_HEIGHTS
;
1813 if
(vlink
(p
) == null
)
1820 goto UPDATE_HEIGHTS
;
1830 confusion
("vertbreak");
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))
1843 b
= badness
(h
- cur_height
, active_height
[2]);
1844 } else if
(cur_height
- h
> active_height
[7]) {
1847 b
= badness
(cur_height
- h
, active_height
[7]);
1850 if
(b
< awful_bad
) {
1851 if
(pi
<= eject_penalty
)
1853 else if
(b
< inf_bad
)
1858 if
(b
<= least_cost
) {
1861 best_height_plus_depth
= cur_height
+ prev_dp
;
1863 if
((b
== awful_bad
) ||
(pi
<= eject_penalty
))
1867 if
((type
(p
) < glue_node
) ||
(type
(p
) > kern_node
))
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.");
1886 shrink_order
(p
) = normal
;
1889 cur_height
= cur_height
+ prev_dp
+ width
(p
);
1893 cur_height
= cur_height
+ prev_dp
- d
;
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
*/
1930 flush_node_list
(split_disc
);
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
*/
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.");
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.
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);
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
) {
1978 q
= prune_page_top
(q
, int_par
(saving_vdiscards_code
) > 0);
1983 /* the |eq_level| of the box stays the same
*/
1986 box
(n
) = filtered_vpackage
(q
, 0, additional
, dimen_par
(max_depth_code
), split_keep_group
, vdir
, 0, 0);
1989 return filtered_vpackage
(p
, h
, exactly
, dimen_par
(split_max_depth_code
), split_off_group
, vdir
, 0, 0);
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
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;
2010 scan_register_num
();
2011 cur_box
= box
(cur_val
);
2012 /* the box becomes void
, at the same level
*/
2013 box
(cur_val
) = null
;
2016 scan_register_num
();
2017 cur_box
= copy_node_list
(box
(cur_val
));
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
2026 if
(abs
(cur_list.mode_field
) == mmode
) {
2028 help1
("Sorry; this \\lastbox will be void.");
2030 } else if
((cur_list.mode_field
== vmode
) && (cur_list.head_field == cur_list.tail_field)) {
2032 help2
("Sorry...I usually can't take things from the current page.",
2033 "This \\lastbox will therefore be void.");
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
)
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
;
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
();
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.");
2071 scan_normal_dimen
();
2072 cur_box
= vsplit
(n
, cur_val
, additional
);
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.
2082 cur_chr
= vtop_code
;
2086 cur_chr
= vtop_code
+ vmode
;
2090 cur_chr
= vtop_code
+ hmode
;
2095 k
= cur_chr
- vtop_code
;
2096 set_saved_record
(0, saved_boxcontext
, 0, box_context
);
2097 switch
(abs
(cur_list.mode_field
)) {
2099 spec_direction
= body_direction
;
2102 spec_direction
= text_direction
;
2105 spec_direction
= math_direction
;
2109 if
((box_context
< box_flag
) && (abs(cur_list.mode_field) == vmode))
2110 scan_full_spec
(adjusted_hbox_group
, spec_direction
,just_pack
);
2112 scan_full_spec
(hbox_group
, spec_direction
,just_pack
);
2115 scan_full_spec
(vbox_group
, spec_direction
,just_pack
);
2117 scan_full_spec
(vtop_group
, spec_direction
,just_pack
);
2123 cur_list.mode_field
= -k
;
2125 prev_depth
= ignore_depth
;
2126 if
(every_vbox
!= null
)
2127 begin_token_list
(every_vbox
, every_vbox_text
);
2129 space_factor
= 1000;
2130 if
(every_hbox
!= null
)
2131 begin_token_list
(every_hbox
, every_hbox_text
);
2136 /* in simple cases
, we use the box immediately
*/
2137 box_end
(box_context
);