1
4
5# include "luametatex.h"
6
7
42
43page_builder_state_info lmt_page_builder_state = {
44 .page_tail = null,
45 .contents = 0,
46 .max_depth = 0,
47 .best_break = null,
48 .least_cost = 0,
49 .best_size = 0,
50 .goal = 0,
51 .vsize = 0,
52 .total = 0,
53 .depth = 0,
54 .excess = 0,
55 .page_so_far = { 0 },
56 .insert_penalties = 0,
57 .insert_heights = 0,
58 .last_glue = max_halfword,
59 .last_penalty = 0,
60 .last_kern = 0,
61 .last_node_type = unknown_node_type,
62 .last_node_subtype= unknown_node_subtype,
63 .last_extra_used = 0,
64 .last_boundary = 0,
65 .output_active = 0,
66 .dead_cycles = 0,
67 .current_state = 0
68};
69
70
74
75# define page_stretched(order) lmt_page_builder_state.page_so_far[page_stretch_state+order]
76
77static void tex_aux_fire_up (halfword c);
78
79
127
128void tex_initialize_pagestate(void)
129{
130 lmt_page_builder_state.page_tail = page_head;
131 lmt_page_builder_state.contents = contribute_nothing;
132 lmt_page_builder_state.max_depth = 0;
133 lmt_page_builder_state.best_break = null;
134 lmt_page_builder_state.least_cost = 0;
135 lmt_page_builder_state.best_size = 0;
136 lmt_page_builder_state.goal = 0;
137 lmt_page_builder_state.vsize = 0;
138 lmt_page_builder_state.total = 0;
139 lmt_page_builder_state.depth = 0;
140 for (int i = page_initial_state; i <= page_shrink_state; i++) {
141 lmt_page_builder_state.page_so_far[i] = 0;
142 }
143 lmt_page_builder_state.insert_penalties = 0;
144 lmt_page_builder_state.insert_heights = 0;
145 lmt_page_builder_state.last_glue = max_halfword;
146 lmt_page_builder_state.last_penalty = 0;
147 lmt_page_builder_state.last_kern = 0;
148 lmt_page_builder_state.last_extra_used = 0;
149 lmt_page_builder_state.last_boundary = 0;
150 lmt_page_builder_state.last_node_type = unknown_node_type;
151 lmt_page_builder_state.last_node_subtype = unknown_node_subtype;
152 lmt_page_builder_state.output_active = 0;
153 lmt_page_builder_state.dead_cycles = 0;
154 lmt_page_builder_state.current_state = 0;
155}
156
157void tex_initialize_buildpage(void)
158{
159 node_type(page_insert_head) = split_node;
160 node_subtype(page_insert_head) = insert_split_subtype;
161 insert_index(page_insert_head) = 65535;
162 node_next(page_insert_head) = page_insert_head;
163 node_type(page_head) = glue_node;
164 node_subtype(page_head) = page_glue;
165}
166
167
189
190void tex_additional_page_skip(void)
191{
192 if (page_total > 0 && additional_page_skip_par) {
193 page_stretch += glue_stretch(additional_page_skip_par);
194 page_shrink += glue_shrink(additional_page_skip_par);
195 page_total += glue_amount(additional_page_skip_par);
196 update_tex_additional_page_skip(null);
197 }
198}
199
200static void tex_aux_save_best_page_specs(void)
201{
202 page_last_depth = page_depth;
203 page_last_height = page_total;
204 page_last_stretch = page_stretch;
205 page_last_fistretch = page_fistretch;
206 page_last_filstretch = page_filstretch;
207 page_last_fillstretch = page_fillstretch;
208 page_last_filllstretch = page_filllstretch;
209 page_last_shrink = page_shrink;
210}
211
212static void tex_aux_freeze_page_specs(int s)
213{
214 lmt_page_builder_state.contents = s;
215 lmt_page_builder_state.max_depth = max_depth_par;
216 lmt_page_builder_state.least_cost = awful_bad;
217
218 for (int i = page_initial_state; i <= page_shrink_state; i++) {
219 lmt_page_builder_state.page_so_far[i] = 0;
220 lmt_page_builder_state.page_last_so_far[i] = 0;
221 }
222 page_goal = vsize_par;
223 page_vsize = vsize_par;
224 page_depth = 0;
225 page_total = 0;
226 page_excess = 0;
227 page_last_depth = 0;
228 page_last_height = 0;
229 if (initial_page_skip_par) {
230 page_stretch = glue_stretch(initial_page_skip_par);
231 page_shrink = glue_shrink(initial_page_skip_par);
232 page_total = glue_amount(initial_page_skip_par);
233 }
234 if (additional_page_skip_par) {
235 page_stretch += glue_stretch(additional_page_skip_par);
236 page_shrink += glue_shrink(additional_page_skip_par);
237 page_total += glue_amount(additional_page_skip_par);
238 update_tex_additional_page_skip(null);
239 }
240 if (tracing_pages_par > 0) {
241 tex_begin_diagnostic();
242 tex_print_format(
243 "[page: frozen state, goal=%p, maxdepth=%p, contribution=%s, insertheights=%p]",
244 page_goal,
245 lmt_page_builder_state.max_depth,
246 lmt_interface.page_contribute_values[s].name,
247 lmt_page_builder_state.insert_heights
248 );
249 tex_end_diagnostic();
250 }
251}
252
253static void update_page_goal(halfword index, scaled total, scaled delta)
254{
255 page_goal -= delta;
256 lmt_page_builder_state.insert_heights += total;
257 if (lmt_page_builder_state.insert_heights > max_dimension) {
258 lmt_page_builder_state.insert_heights = max_dimension;
259 }
260 if (tracing_inserts_par > 0) {
261 tex_begin_diagnostic();
262 tex_print_format(
263 "[page: update page goal for insert, index=%i, total=%p, insertheights=%p, vsize=%p, delta=%p, goal=%p]",
264 index, total, lmt_page_builder_state.insert_heights, page_vsize, delta, page_goal
265 );
266 tex_end_diagnostic();
267 }
268}
269
270
277
278static void tex_aux_start_new_page(void)
279{
280 lmt_page_builder_state.contents = contribute_nothing;
281 lmt_page_builder_state.page_tail = page_head;
282 node_next(page_head) = null;
283 lmt_page_builder_state.last_glue = max_halfword;
284 lmt_page_builder_state.last_penalty = 0;
285 lmt_page_builder_state.last_kern = 0;
286 lmt_page_builder_state.last_boundary = 0;
287 lmt_page_builder_state.last_node_type = unknown_node_type;
288 lmt_page_builder_state.last_node_subtype = unknown_node_subtype;
289 page_depth = 0;
290 lmt_page_builder_state.max_depth = 0;
291}
292
293
300
301static halfword tex_aux_delete_box_content(int n)
302{
303 tex_begin_diagnostic();
304 tex_print_format("[page: deleting box]");
305 tex_show_box(n);
306 tex_end_diagnostic();
307 tex_flush_node_list(n);
308 return null;
309}
310
311
317
318static int tex_aux_valid_insert_content(halfword content)
319{
320 if (content && node_type(content) == hlist_node) {
321
322 tex_handle_error(
323 normal_error_type,
324 "Insertions can only be added to a vbox",
325 "Tut tut: You're trying to \\insert into a \\box register that now contains an\n"
326 "\\hbox. Proceed, and I'll discard its present contents."
327 );
328 return 0;
329 } else {
330 return 1;
331 }
332}
333
334
344
345static void tex_aux_display_page_break_cost(halfword badness, halfword penalty, halfword cost, int moveon, int fireup)
346{
347 tex_begin_diagnostic();
348 tex_print_format("[page: break, total %P, goal %p, badness %B, penalty %i, cost %B%s, moveon %s, fireup %s]",
349 page_total, page_stretch, page_fistretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
350 page_goal, badness, penalty, cost, cost < lmt_page_builder_state.least_cost ? "#" : "",
351 moveon ? "yes" : "no", fireup ? "yes" : "no"
352 );
353 tex_end_diagnostic();
354}
355
356static void tex_aux_display_insertion_split_cost(halfword index, scaled height, halfword penalty)
357{
358
359 tex_begin_diagnostic();
360 tex_print_format("[page: split insert %i: height %p, depth %p, penalty %i]",
361 index, height, lmt_packaging_state.best_height_plus_depth, penalty
362 );
363 tex_end_diagnostic();
364}
365
366static halfword tex_aux_page_badness(scaled goal)
367{
368 if (page_total < goal) {
369 if (page_fistretch || page_filstretch || page_fillstretch || page_filllstretch) {
370 return 0;
371 } else {
372 return tex_badness(goal - page_total, page_stretch);
373 }
374 } else if (page_total - goal > page_shrink) {
375 return awful_bad;
376 } else {
377 return tex_badness(page_total - goal, page_shrink);
378 }
379}
380
381inline static halfword tex_aux_page_costs(halfword badness, halfword penalty)
382{
383 if (lmt_page_builder_state.insert_penalties >= infinite_penalty) {
384 return awful_bad;
385 } else if (badness >= awful_bad) {
386 return badness;
387 } else if (penalty <= eject_penalty) {
388 return penalty;
389 } else if (badness < infinite_bad) {
390 return badness + penalty + lmt_page_builder_state.insert_penalties;
391 } else {
392 return deplorable;
393 }
394}
395
396static halfword tex_aux_insert_topskip(halfword height, int contribution)
397{
398 if (lmt_page_builder_state.contents != contribute_nothing) {
399 lmt_page_builder_state.contents = contribution;
400 } else {
401 tex_aux_freeze_page_specs(contribution);
402 }
403 {
404 halfword glue = tex_new_param_glue_node(tex_glue_is_zero(initial_top_skip_par) ? top_skip_code : initial_top_skip_code, top_skip_glue);
405 if (glue_amount(glue) > height) {
406 glue_amount(glue) -= height;
407 } else {
408 glue_amount(glue) = 0;
409 }
410 return glue;
411 }
412}
413
414
418
419static void tex_aux_append_insert(halfword current)
420{
421 halfword index = insert_index(current);
422 halfword location = page_insert_head;
423 halfword multiplier = tex_get_insert_multiplier(index);
424 halfword content = tex_get_insert_content(index);
425 scaled limit = tex_get_insert_limit(index);
426 int slot = 1;
427 if (lmt_page_builder_state.contents == contribute_nothing) {
428 tex_aux_freeze_page_specs(contribute_insert);
429 }
430 while (index >= insert_index(node_next(location))) {
431 location = node_next(location);
432 slot += 1 ;
433 }
434 if (insert_index(location) != index) {
435
456 halfword splitnode = tex_new_node(split_node, normal_split_subtype);
457 scaled advance = 0;
458 halfword distance = lmt_get_insert_distance(index, slot);
459 split_insert_index(splitnode) = index;
460 tex_try_couple_nodes(splitnode, node_next(location));
461 tex_couple_nodes(location, splitnode);
462 location = splitnode;
463 if (! tex_aux_valid_insert_content(content)) {
464 content = tex_aux_delete_box_content(content);
465 tex_set_insert_content(index, content);
466 };
467 if (content) {
468 box_height(location) = box_total(content);
469 } else {
470 box_height(location) = 0;
471 }
472 split_best_insert(location) = null;
473 if (multiplier == scaling_factor) {
474 advance = box_height(location);
475 } else {
476 advance = tex_x_over_n(box_height(location), scaling_factor) * multiplier;
477 }
478 advance += glue_amount(distance);
479 update_page_goal(index, 0, advance);
480 page_stretched(glue_stretch_order(distance)) += glue_stretch(distance);
481 page_shrink += glue_shrink(distance);
482 if (glue_shrink_order(distance) != normal_glue_order && glue_shrink(distance)) {
483 tex_handle_error(
484 normal_error_type,
485 "Infinite glue shrinkage inserted from \\skip%i",
486 index,
487 "The correction glue for page breaking with insertions must have finite\n"
488 "shrinkability. But you may proceed, since the offensive shrinkability has been\n"
489 "made finite."
490 );
491 }
492 tex_flush_node(distance);
493 }
494
495 if (node_type(location) == split_node && node_subtype(location) == insert_split_subtype) {
496 lmt_page_builder_state.insert_penalties += insert_float_cost(current);
497 } else {
498 scaled delta = page_goal - page_total - page_depth + page_shrink;
499 scaled needed = insert_total_height(current);
500 split_last_insert(location) = current;
501
502 if (multiplier != scaling_factor) {
503
504 needed = tex_x_over_n(needed, scaling_factor) * multiplier;
505 }
506 if ((needed <= 0 || needed <= delta) && (insert_total_height(current) + box_height(location) <= limit)) {
507 update_page_goal(index, insert_total_height(current), needed);
508 box_height(location) += insert_total_height(current);
509 } else {
510
526 scaled height;
527 halfword breaknode, penalty;
528 if (multiplier <= 0) {
529 height = max_dimension;
530 } else {
531 height = page_goal - page_total - page_depth;
532 if (multiplier != scaling_factor) {
533 height = tex_x_over_n(height, multiplier) * scaling_factor;
534 }
535 }
536 if (height > limit - box_height(location)) {
537 height = limit - box_height(location);
538 }
539 breaknode = tex_vert_break(insert_list(current), height, insert_max_depth(current));
540 box_height(location) += lmt_packaging_state.best_height_plus_depth;
541 penalty = breaknode ? (node_type(breaknode) == penalty_node ? penalty_amount(breaknode) : 0) : eject_penalty;
542 if (tracing_pages_par > 0) {
543 tex_aux_display_insertion_split_cost(index, height, penalty);
544 }
545 if (multiplier != scaling_factor) {
546 lmt_packaging_state.best_height_plus_depth = tex_x_over_n(lmt_packaging_state.best_height_plus_depth, scaling_factor) * multiplier;
547 }
548 update_page_goal(index, lmt_packaging_state.best_height_plus_depth, lmt_packaging_state.best_height_plus_depth);
549 node_subtype(location) = insert_split_subtype;
550 split_broken(location) = breaknode;
551 split_broken_insert(location) = current;
552 lmt_page_builder_state.insert_penalties += penalty;
553 }
554 }
555}
556
557inline static int tex_aux_get_penalty_option(halfword current)
558{
559 while (1) {
560 current = node_prev(current);
561 if (current && current != contribute_head) {
562 switch (node_type(current)) {
563 case glue_node:
564 break;
565 case penalty_node:
566 if (penalty_amount(current) >= infinite_penalty) {
567 if (tex_has_penalty_option(current, penalty_option_widowed)) {
568 if (tracing_pages_par > 1) {
569 tex_begin_diagnostic();
570 tex_print_format("[page: widowed]");
571 tex_end_diagnostic();
572 }
573 return penalty_option_widowed;
574 } else if (tex_has_penalty_option(current, penalty_option_clubbed)) {
575 if (tracing_pages_par > 1) {
576 tex_begin_diagnostic();
577 tex_print_format("[page: clubbed]");
578 tex_end_diagnostic();
579 }
580 return penalty_option_clubbed;
581 }
582 }
583 return 0;
584 default:
585 return 0;
586 }
587 } else {
588 return 0;
589 }
590 }
591
592}
593
594
595static void tex_aux_initialize_show_build_node(int callback_id)
596{
597 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", initialize_show_build_context);
598}
599
600static void tex_aux_step_show_build_node(int callback_id, halfword current)
601{
602 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNdd->", step_show_build_context,
603 current,
604 page_goal,
605 page_total
606 );
607}
608
609static void tex_aux_check_show_build_node(int callback_id, halfword current, int badness, int costs, int penalty, int *moveon, int *fireup)
610{
611 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNbbddd->bb", check_show_build_context,
612 current,
613 *moveon,
614 *fireup,
615 badness,
616 costs,
617 penalty,
618 moveon,
619 fireup
620 );
621}
622
623static void tex_aux_skip_show_build_node(int callback_id, halfword current)
624{
625 (void) current;
626 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dN->", skip_show_build_context);
627}
628
629static void tex_aux_move_show_build_node(int callback_id, halfword current)
630{
631 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNddddb->", move_show_build_context,
632 current,
633 page_last_height,
634 page_last_depth,
635 page_last_stretch,
636 page_last_shrink,
637 (page_last_fistretch || page_last_filstretch || page_last_fillstretch || page_last_filllstretch) ? 1 : 0
638 );
639}
640
641static void tex_aux_fireup_show_build_node(int callback_id, halfword current)
642{
643 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dN->", fireup_show_build_context,
644 current
645 );
646}
647
648static void tex_aux_wrapup_show_build_node(int callback_id)
649{
650 lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", wrapup_show_build_context);
651}
652
653static int tex_aux_topskip_restart(halfword current, int where, scaled height, scaled depth, int tracing)
654{
655 if (lmt_page_builder_state.contents < contribute_box) {
656
660 halfword gluenode = tex_aux_insert_topskip(height, where);
661 tex_couple_nodes(gluenode, current);
662 tex_couple_nodes(contribute_head, gluenode);
663 if (tracing > 1) {
664 tex_begin_diagnostic();
665 tex_print_format("[page: initialize, topskip at %s]", where == contribute_box ? "box" : "rule");
666 tex_end_diagnostic();
667 }
668 return 1;
669 } else {
670
671 page_total += page_depth + height;
672 page_depth = depth;
673 return 0;
674 }
675}
676
677static int tex_aux_migrating_restart(halfword current, int tracing)
678{
679 if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_post)) {
680 halfword head = box_post_migrated(current);
681 if (head) {
682 halfword tail = tex_tail_of_node_list(head);
683 if (tracing > 1 || tracing_adjusts_par > 1) {
684 tex_begin_diagnostic();
685 tex_print_format("[adjust: post, mvl]");
686 tex_print_node_list(head, "post", show_box_depth_par, show_box_breadth_par);
687 tex_end_diagnostic();
688 }
689 if (node_next(current)) {
690 tex_couple_nodes(tail, node_next(current));
691 } else {
692 contribute_tail = tail;
693 }
694 tex_couple_nodes(current, head);
695 box_post_migrated(current) = null;
696 }
697 }
698 if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_pre)) {
699 halfword head = box_pre_migrated(current);
700 if (head) {
701 halfword tail = tex_tail_of_node_list(head);
702 if (tracing > 1 || tracing_adjusts_par > 1) {
703 tex_begin_diagnostic();
704 tex_print_format("[adjust: pre, mvl]");
705 tex_print_node_list(head, "pre", show_box_depth_par, show_box_breadth_par);
706 tex_end_diagnostic();
707 }
708 tex_couple_nodes(tail, current);
709 tex_couple_nodes(contribute_head, current);
710
711
712
713 box_pre_migrated(current) = null;
714 return 1;
715 }
716 }
717 return 0;
718}
719
720
726
727static halfword tex_aux_process_boundary(halfword current)
728{
729 halfword penaltynode = tex_new_node(penalty_node, user_penalty_subtype);
730
731 tex_page_boundary_message("processed as penalty", 0);
732 tex_try_couple_nodes(node_prev(current), penaltynode);
733 tex_try_couple_nodes(penaltynode, node_next(current));
734 tex_flush_node(current);
735 penalty_amount(penaltynode) = boundary_data(current);
736 current = penaltynode;
737 node_next(contribute_head) = current;
738 return current;
739}
740
741static void tex_aux_reconsider_goal(halfword current, halfword *badness, halfword *costs, halfword *penalty, int tracing)
742{
743 (void) current;
744 if (*badness >= awful_bad && page_extra_goal_par) {
745 switch (tex_aux_get_penalty_option(lmt_page_builder_state.page_tail)) {
746 case penalty_option_widowed:
747 if (page_total <= (page_goal + page_extra_goal_par)) {
748 halfword extrabadness = tex_aux_page_badness(page_goal + page_extra_goal_par);
749 halfword extracosts = tex_aux_page_costs(extrabadness, *penalty);
750 if (tracing > 0) {
751 tex_begin_diagnostic();
752 tex_print_format(
753 "[page: extra check, total=%P, goal=%p, extragoal=%p, badness=%B, costs=%i, extrabadness=%B, extracosts=%i]",
754 page_total, page_stretch, page_filstretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
755 page_goal, page_extra_goal_par,
756 *badness, *costs, extrabadness, extracosts
757 );
758 tex_end_diagnostic();
759 }
760
761 *badness = extrabadness;
762 *costs = extracosts;
763
764 lmt_page_builder_state.last_extra_used = 1;
765 if (tracing > 1) {
766 tex_begin_diagnostic();
767 tex_print_format("[page: widowed]");
768 tex_end_diagnostic();
769 }
770 }
771 break;
772 case penalty_option_clubbed:
773 if (page_total >= (page_goal - page_extra_goal_par)) {
774
775 *penalty = eject_penalty;
776
777 if (tracing > 1) {
778 tex_begin_diagnostic();
779 tex_print_format("[page: clubbed]");
780 tex_end_diagnostic();
781 }
782 }
783 break;
784 }
785 }
786}
787
788static void tex_aux_contribute_glue(halfword current)
789{
790 page_stretched(glue_stretch_order(current)) += glue_stretch(current);
791 page_shrink += glue_shrink(current);
792 if (glue_shrink_order(current) != normal_glue_order && glue_shrink(current)) {
793 tex_handle_error(
794 normal_error_type,
795 "Infinite glue shrinkage found on current page",
796 "The page about to be output contains some infinitely shrinkable glue, e.g.,\n"
797 "'\\vss' or '\\vskip 0pt minus 1fil'. Such glue doesn't belong there; but you can\n"
798 "safely proceed, since the offensive shrinkability has been made finite."
799 );
800 tex_reset_glue_to_zero(current);
801 glue_shrink_order(current) = normal_glue_order;
802 }
803}
804
805
809
810void tex_build_page(halfword context, halfword boundary)
811{
812 if (! lmt_page_builder_state.output_active) {
813 lmt_page_filter_callback(context, boundary);
814 }
815 if (node_next(contribute_head) && ! lmt_page_builder_state.output_active) {
816
817 halfword penalty = 0;
818 int callback_id = lmt_callback_defined(show_build_callback);
819 int tracing = tracing_pages_par;
820 if (callback_id) {
821 tex_aux_initialize_show_build_node(callback_id);
822 }
823 do {
824
828 halfword current = node_next(contribute_head);
829 halfword type = node_type(current);
830 quarterword subtype = node_subtype(current);
831 if (callback_id) {
832 tex_aux_step_show_build_node(callback_id, current);
833 }
834 if (lmt_page_builder_state.last_glue != max_halfword) {
835 tex_flush_node(lmt_page_builder_state.last_glue);
836 lmt_page_builder_state.last_glue = max_halfword;
837 }
838 lmt_page_builder_state.last_penalty = 0;
839 lmt_page_builder_state.last_kern = 0;
840 lmt_page_builder_state.last_boundary = 0;
841 lmt_page_builder_state.last_node_type = type;
842 lmt_page_builder_state.last_node_subtype = subtype;
843 lmt_page_builder_state.last_extra_used = 0;
844
866 switch (type) {
867 case hlist_node:
868 case vlist_node:
869 if (tex_aux_migrating_restart(current, tracing)) {
870 continue;
871 } else if (tex_aux_topskip_restart(current, contribute_box, box_height(current), box_depth(current), tracing)) {
872 continue;
873 } else {
874 goto CONTRIBUTE;
875 }
876 case rule_node:
877 if (tex_aux_topskip_restart(current, contribute_rule, rule_height(current), rule_depth(current), tracing)) {
878 continue;
879 } else {
880 goto CONTRIBUTE;
881 }
882 case boundary_node:
883 if (subtype == page_boundary) {
884 lmt_page_builder_state.last_boundary = boundary_data(current);
885 }
886 if (lmt_page_builder_state.contents < contribute_box) {
887 goto DISCARD;
888 } else if (subtype == page_boundary) {
889 current = tex_aux_process_boundary(current);
890 penalty = 0;
891 break;
892 } else {
893 goto DISCARD;
894 }
895 case whatsit_node:
896 goto CONTRIBUTE;
897 case glue_node:
898 lmt_page_builder_state.last_glue = tex_new_glue_node(current, subtype);
899 if (lmt_page_builder_state.contents < contribute_box) {
900 goto DISCARD;
901 } else if (precedes_break(lmt_page_builder_state.page_tail)) {
902 penalty = 0;
903 break;
904 } else {
905 goto UPDATEHEIGHTS;
906 }
907 case kern_node:
908 lmt_page_builder_state.last_kern = kern_amount(current);
909 if (lmt_page_builder_state.contents < contribute_box) {
910 goto DISCARD;
911 } else if (! node_next(current)) {
912 return;
913 } else if (node_type(node_next(current)) == glue_node) {
914 penalty = 0;
915 break;
916 } else {
917 goto UPDATEHEIGHTS;
918 }
919 case penalty_node:
920 lmt_page_builder_state.last_penalty = penalty_amount(current);
921 if (lmt_page_builder_state.contents < contribute_box) {
922 goto DISCARD;
923 } else {
924 penalty = penalty_amount(current);
925 break;
926 }
927 case mark_node:
928 goto CONTRIBUTE;
929 case insert_node:
930 tex_aux_append_insert(current);
931 goto CONTRIBUTE;
932 default:
933 tex_formatted_error("pagebuilder", "invalid %N node in vertical mode", current);
934 break;
935 }
936
941 if (tracing > 1) {
942 tex_begin_diagnostic();
943 tex_print_format("[page: compute: %N, %d, penalty=%i]", current, current, penalty);
944 tex_end_diagnostic();
945 }
946 if (penalty < infinite_penalty) {
947
951 halfword badness = tex_aux_page_badness(page_goal);
952 halfword costs = tex_aux_page_costs(badness, penalty);
953 lmt_page_builder_state.last_extra_used = 0;
954 if (tracing > 1) {
955 tex_begin_diagnostic();
956 tex_print_format("[page: calculate, %N, %d, total=%P, goal=%p, badness=%B, costs=%i]",
957 current, current,
958 page_total, page_stretch, page_fistretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
959 page_goal,
960 badness, costs
961 );
962 tex_end_diagnostic();
963 }
964 tex_aux_reconsider_goal(current, &badness, &costs, &penalty, tracing);
965 {
966 int moveon = costs <= lmt_page_builder_state.least_cost;
967 int fireup = costs == awful_bad || penalty <= eject_penalty;
968 if (callback_id) {
969 tex_aux_check_show_build_node(callback_id, current, badness, lmt_page_builder_state.last_penalty, costs, &moveon, &fireup);
970 }
971 if (tracing > 0) {
972 tex_aux_display_page_break_cost(badness, penalty, costs, moveon, fireup);
973 }
974 if (moveon) {
975 halfword insert = node_next(page_insert_head);
976 lmt_page_builder_state.best_break = current;
977 lmt_page_builder_state.best_size = page_goal;
978 lmt_page_builder_state.insert_penalties = 0;
979 lmt_page_builder_state.least_cost = costs;
980 while (insert != page_insert_head) {
981 split_best_insert(insert) = split_last_insert(insert);
982 insert = node_next(insert);
983 }
984 tex_aux_save_best_page_specs();
985 if (callback_id) {
986 tex_aux_move_show_build_node(callback_id, current);
987 }
988 }
989 if (fireup) {
990 if (tracing > 1) {
991 tex_begin_diagnostic();
992 tex_print_format("[page: fireup: %N, %d]", current, current);
993 tex_end_diagnostic();
994 }
995 if (callback_id) {
996 tex_aux_fireup_show_build_node(callback_id, current);
997 }
998
999 tex_aux_fire_up(current);
1000 if (lmt_page_builder_state.output_active) {
1001
1002 if (callback_id) {
1003 tex_aux_wrapup_show_build_node(callback_id);
1004 }
1005 return;
1006 } else {
1007
1008 continue;
1009 }
1010 }
1011 }
1012 } else {
1013 if (callback_id) {
1014 tex_aux_skip_show_build_node(callback_id, current);
1015 }
1016 }
1017 UPDATEHEIGHTS:
1018
1022 if (tracing > 1) {
1023 tex_begin_diagnostic();
1024 tex_print_format("[page: update, %N, %d]", current, current);
1025 tex_end_diagnostic();
1026 }
1027 switch(node_type(current)) {
1028 case kern_node:
1029 page_total += page_depth + kern_amount(current);
1030 page_depth = 0;
1031 goto APPEND;
1032 case glue_node:
1033 tex_aux_contribute_glue(current);
1034 page_total += page_depth + glue_amount(current);
1035 page_depth = 0;
1036 goto APPEND;
1037 }
1038 CONTRIBUTE:
1039
1043 if (tracing > 1) {
1044 tex_begin_diagnostic();
1045 tex_print_format("[page: contribute, %N, %d]", current, current);
1046 tex_end_diagnostic();
1047 }
1048 if (page_depth > lmt_page_builder_state.max_depth) {
1049 page_total += page_depth - lmt_page_builder_state.max_depth;
1050 page_depth = lmt_page_builder_state.max_depth;
1051 }
1052 APPEND:
1053 if (tracing > 1) {
1054 tex_begin_diagnostic();
1055 tex_print_format("[page: append, %N, %d]", current, current);
1056 tex_end_diagnostic();
1057 }
1058
1059 tex_couple_nodes(lmt_page_builder_state.page_tail, current);
1060 lmt_page_builder_state.page_tail = current;
1061 tex_try_couple_nodes(contribute_head, node_next(current));
1062 node_next(current) = null;
1063 continue;
1064 DISCARD:
1065 if (tracing > 1) {
1066 tex_begin_diagnostic();
1067 tex_print_format("[page: discard, %N, %d]", current, current);
1068 tex_end_diagnostic();
1069 }
1070
1071 tex_try_couple_nodes(contribute_head, node_next(current));
1072 node_next(current) = null;
1073 if (saving_vdiscards_par > 0) {
1074 if (lmt_packaging_state.page_discards_head) {
1075 tex_couple_nodes(lmt_packaging_state.page_discards_tail, current);
1076 } else {
1077 lmt_packaging_state.page_discards_head = current;
1078 }
1079 lmt_packaging_state.page_discards_tail = current;
1080 } else {
1081 tex_flush_node_list(current);
1082 }
1083 } while (node_next(contribute_head));
1084
1085 contribute_tail = contribute_head;
1086 }
1087}
1088
1089
1105
1106static void tex_aux_fire_up(halfword c)
1107{
1108
1109 halfword current, previous, lastinsert;
1110
1111 if (node_type(lmt_page_builder_state.best_break) == penalty_node) {
1112 update_tex_output_penalty(penalty_amount(lmt_page_builder_state.best_break));
1113 penalty_amount(lmt_page_builder_state.best_break) = infinite_penalty;
1114 } else {
1115 update_tex_output_penalty(infinite_penalty);
1116 }
1117 tex_update_top_marks();
1118
1127 if (c == lmt_page_builder_state.best_break) {
1128
1129 lmt_page_builder_state.best_break = null;
1130 }
1131
1132 if (box_register(output_box_par)) {
1133 tex_handle_error(
1134 normal_error_type,
1135 "\\box%i is not void",
1136 output_box_par,
1137 "You shouldn't use \\box\\outputbox except in \\output routines. Proceed, and I'll\n"
1138 "discard its present contents."
1139 );
1140 box_register(output_box_par) = tex_aux_delete_box_content(box_register(output_box_par));
1141 }
1142
1151
1152 {
1153 halfword save_split_top_skip = split_top_skip_par;
1154 lmt_page_builder_state.insert_penalties = 0;
1155 if (holding_inserts_par <= 0) {
1156
1187 halfword insert = node_next(page_insert_head);
1188 while (insert != page_insert_head) {
1189 if (split_best_insert(insert)) {
1190 halfword index = insert_index(insert);
1191 halfword content = tex_get_insert_content(index);
1192 if (! tex_aux_valid_insert_content(content)) {
1193 content = tex_aux_delete_box_content(content);
1194 }
1195 if (! content) {
1196
1201 content = tex_new_null_box_node(vlist_node, insert_result_list);
1202 tex_set_insert_content(index, content);
1203 }
1204
1209 split_last_insert(insert) = tex_tail_of_node_list(insert_first_box(content));
1210 }
1211 insert = node_next(insert);
1212 }
1213 }
1214 previous = page_head;
1215 current = node_next(previous);
1216 lastinsert = hold_head;
1217 node_next(lastinsert) = null;
1218 while (current != lmt_page_builder_state.best_break) {
1219 switch (node_type(current)) {
1220 case insert_node:
1221 if (holding_inserts_par <= 0) {
1222
1231
1232 int wait = 0;
1233 halfword insert = node_next(page_insert_head);
1234 while (insert_index(insert) != insert_index(current)) {
1235 insert = node_next(insert);
1236 }
1237 if (split_best_insert(insert)) {
1238 halfword split = split_last_insert(insert);
1239 tex_try_couple_nodes(split, insert_list(current));
1240 if (split_best_insert(insert) == current) {
1241
1245 if (node_type(insert) == split_node && node_subtype(insert) == insert_split_subtype && (split_broken_insert(insert) == current) && split_broken(insert)) {
1246 while (node_next(split) != split_broken(insert)) {
1247 split = node_next(split);
1248 }
1249 node_next(split) = null;
1250 split_top_skip_par = insert_split_top(current);
1251 insert_list(current) = tex_prune_page_top(split_broken(insert), 0);
1252 if (insert_list(current)) {
1253
1257 halfword list = insert_list(current);
1258 halfword result = tex_vpack(list, 0, packing_additional, max_dimension, direction_unknown, holding_none_option, NULL);
1259 insert_total_height(current) = box_total(result);
1260 box_list(result) = null;
1261 tex_flush_node(result);
1262 wait = 1;
1263 }
1264 }
1265 split_best_insert(insert) = null;
1266 {
1267
1271 halfword index = insert_index(insert);
1272 halfword content = tex_get_insert_content(index);
1273 halfword list = box_list(content);
1274 halfword result = tex_vpack(list, 0, packing_additional, max_dimension, dir_lefttoright, holding_none_option, NULL);
1275 tex_set_insert_content(index, result);
1276 box_list(content) = null;
1277 tex_flush_node(content);
1278 }
1279 } else {
1280 split_last_insert(insert) = tex_tail_of_node_list(split);
1281 }
1282 } else {
1283 wait = 1;
1284 }
1285
1289 tex_try_couple_nodes(previous, node_next(current));
1290 node_next(current) = null;
1291 if (wait) {
1292 tex_couple_nodes(lastinsert, current);
1293 lastinsert = current;
1294 ++lmt_page_builder_state.insert_penalties;
1295 } else {
1296 insert_list(current) = null;
1297 tex_flush_node(current);
1298 }
1299 current = previous;
1300 }
1301 break;
1302 case mark_node:
1303 tex_update_first_and_bot_mark(current);
1304 break;
1305 }
1306 previous = current;
1307 current = node_next(current);
1308 }
1309 split_top_skip_par = save_split_top_skip;
1310 }
1311
1324 if (current) {
1325 if (! node_next(contribute_head)) {
1326 contribute_tail = lmt_page_builder_state.page_tail;
1327 }
1328 tex_couple_nodes(lmt_page_builder_state.page_tail, node_next(contribute_head));
1329 tex_couple_nodes(contribute_head, current);
1330 node_next(previous) = null;
1331 }
1332
1333 {
1334 halfword save_vbadness = vbadness_par;
1335 halfword save_vfuzz = vfuzz_par;
1336 vbadness_par = infinite_bad;
1337 vfuzz_par = max_dimension;
1338 tex_show_marks();
1339
1340 box_register(output_box_par) = tex_filtered_vpack(node_next(page_head), lmt_page_builder_state.best_size, packing_exactly, lmt_page_builder_state.max_depth, output_group, dir_lefttoright, 0, 0, 0, holding_none_option, &lmt_page_builder_state.excess);
1341
1342
1343
1344
1345 vbadness_par = save_vbadness;
1346 vfuzz_par = save_vfuzz;
1347 }
1348 if (lmt_page_builder_state.last_glue != max_halfword) {
1349 tex_flush_node(lmt_page_builder_state.last_glue);
1350 }
1351
1352 tex_aux_start_new_page();
1353
1354 if (lastinsert != hold_head) {
1355 node_next(page_head) = node_next(hold_head);
1356 lmt_page_builder_state.page_tail = lastinsert;
1357 }
1358
1359 {
1360 halfword r = node_next(page_insert_head);
1361 while (r != page_insert_head) {
1362 lastinsert = node_next(r);
1363 tex_flush_node(r);
1364 r = lastinsert;
1365 }
1366 }
1367 node_next(page_insert_head) = page_insert_head;
1368 tex_update_first_marks();
1369 if (output_routine_par) {
1370 if (lmt_page_builder_state.dead_cycles >= max_dead_cycles_par) {
1371
1372 tex_handle_error(
1373 normal_error_type,
1374 "Output loop --- %i consecutive dead cycles",
1375 lmt_page_builder_state.dead_cycles,
1376 "I've concluded that your \\output is awry; it never does a \\shipout, so I'm\n"
1377 "shipping \\box\\outputbox out myself. Next time increase \\maxdeadcycles if you\n"
1378 "want me to be more patient!"
1379 );
1380 } else {
1381
1382 lmt_page_builder_state.output_active = 1;
1383 ++lmt_page_builder_state.dead_cycles;
1384 tex_push_nest();
1385 cur_list.mode = internal_vmode;
1386 cur_list.prev_depth = ignore_depth_criterion_par;
1387 cur_list.mode_line = -lmt_input_state.input_line;
1388 tex_begin_token_list(output_routine_par, output_text);
1389 tex_new_save_level(output_group);
1390 tex_normal_paragraph(output_par_context);
1391 tex_scan_left_brace();
1392 return;
1393 }
1394 }
1395
1400
1401
1402
1403 if (node_next(page_head)) {
1404 if (node_next(contribute_head)) {
1405 node_next(lmt_page_builder_state.page_tail) = node_next(contribute_head);
1406 }
1407 else {
1408 contribute_tail = lmt_page_builder_state.page_tail;
1409 }
1410 node_next(contribute_head) = node_next(page_head);
1411 node_next(page_head) = null;
1412 lmt_page_builder_state.page_tail = page_head;
1413 }
1414 if (lmt_packaging_state.page_discards_head) {
1415 tex_flush_node_list(lmt_packaging_state.page_discards_head);
1416 lmt_packaging_state.page_discards_head = null;
1417 }
1418 if (box_register(output_box_par)) {
1419 tex_flush_node_list(box_register(output_box_par));
1420 box_register(output_box_par) = null;
1421 }
1422}
1423
1424
1430
1431void tex_resume_after_output(void)
1432{
1433 if (lmt_input_state.cur_input.loc || ((lmt_input_state.cur_input.token_type != output_text) && (lmt_input_state.cur_input.token_type != backed_up_text))) {
1434
1435 tex_handle_error(
1436 normal_error_type,
1437 "Unbalanced output routine",
1438 "Your sneaky output routine has problematic {'s and/or }'s. I can't handle that\n"
1439 "very well; good luck."
1440 );
1441
1442 do {
1443 tex_get_token();
1444 } while (lmt_input_state.cur_input.loc);
1445 }
1446
1447 tex_end_token_list();
1448 tex_end_paragraph(bottom_level_group, output_par_context);
1449 tex_unsave();
1450 lmt_page_builder_state.output_active = 0;
1451 lmt_page_builder_state.insert_penalties = 0;
1452
1453 if (box_register(output_box_par)) {
1454 tex_handle_error(
1455 normal_error_type,
1456 "Output routine didn't use all of \\box%i", output_box_par,
1457 "Your \\output commands should empty \\box\\outputbox, e.g., by saying\n"
1458 "'\\shipout\\box\\outputbox'. Proceed; I'll discard its present contents."
1459 );
1460 box_register(output_box_par) = tex_aux_delete_box_content(box_register(output_box_par));;
1461 }
1462 if (lmt_insert_state.storing == insert_storage_delay && tex_insert_stored()) {
1463 if (tracing_inserts_par > 0) {
1464 tex_print_levels();
1465 tex_print_str(lmt_insert_state.head ? "<delaying inserts>" : "<no inserts to delay>");
1466 if (lmt_insert_state.head && tracing_inserts_par > 1) {
1467 tex_show_node_list(lmt_insert_state.head, max_integer, max_integer);
1468 }
1469 }
1470 tex_try_couple_nodes(lmt_page_builder_state.page_tail, lmt_insert_state.head);
1471 lmt_page_builder_state.page_tail = lmt_insert_state.tail;
1472 lmt_insert_state.head = null;
1473 lmt_insert_state.tail = null;
1474 }
1475 if (cur_list.tail != cur_list.head) {
1476
1477 tex_try_couple_nodes(lmt_page_builder_state.page_tail, node_next(cur_list.head));
1478 lmt_page_builder_state.page_tail = cur_list.tail;
1479 }
1480 if (node_next(page_head)) {
1481
1482 if (! node_next(contribute_head)) {
1483 contribute_tail = lmt_page_builder_state.page_tail;
1484 }
1485 tex_try_couple_nodes(lmt_page_builder_state.page_tail, node_next(contribute_head));
1486 tex_try_couple_nodes(contribute_head, node_next(page_head));
1487 node_next(page_head) = null;
1488 lmt_page_builder_state.page_tail = page_head;
1489 }
1490 if (lmt_insert_state.storing == insert_storage_inject) {
1491 halfword h = node_next(contribute_head);
1492 while (h) {
1493 halfword n = node_next(h);
1494 if (node_type(h) == insert_node) {
1495 tex_try_couple_nodes(node_prev(h), n);
1496 tex_insert_restore(h);
1497 }
1498 h = n;
1499 }
1500 if (tracing_inserts_par > 0) {
1501 tex_print_levels();
1502 tex_print_str(lmt_insert_state.head ? "<storing inserts>" : "<no inserts to store>");
1503 if (lmt_insert_state.head && tracing_inserts_par > 1) {
1504 tex_show_node_list(lmt_insert_state.head, max_integer, max_integer);
1505 }
1506 }
1507 }
1508 lmt_insert_state.storing = insert_storage_ignore;
1509 tex_flush_node_list(lmt_packaging_state.page_discards_head);
1510 lmt_packaging_state.page_discards_head = null;
1511 tex_pop_nest();
1512 tex_build_page(after_output_page_context, 0);
1513}
1514 |