diff options
Diffstat (limited to 'source/luametatex/source/tex/texlinebreak.c')
-rw-r--r-- | source/luametatex/source/tex/texlinebreak.c | 1310 |
1 files changed, 742 insertions, 568 deletions
diff --git a/source/luametatex/source/tex/texlinebreak.c b/source/luametatex/source/tex/texlinebreak.c index af60f1c40..e009d3e50 100644 --- a/source/luametatex/source/tex/texlinebreak.c +++ b/source/luametatex/source/tex/texlinebreak.c @@ -53,6 +53,14 @@ understand. (Remark for myself: the lua variant that i use for playing around occasionally is not in sync with the code here!) + I played a bit with prerolling: make a copy, run the par builder, afterwards collect the + result in a box that then can be consulted: wd, ht, dp, quality, hyphens, and especially + shape fitting (which was the reason, because |\hangafter| assumes lines and esp with math a + line is somewhat unpredictable so we get bad fitting). In the end we decided that it was kind + of useless because of the unlikely usage scenario. But I might pick up on it. Of course it can + be done in \LUA\ but we don't want the associated performance hit (management overhead) and + dealing with (progressive) solution oscillating is also an issue. + */ linebreak_state_info lmt_linebreak_state = { @@ -859,12 +867,14 @@ static halfword tex_aux_clean_up_the_memory(halfword p) halfword q = node_next(active_head); while (q != active_head) { p = node_next(q); + // tex_free_node(q, get_node_size(node_type(q))); // less overhead & testing tex_flush_node(q); q = p; } q = lmt_linebreak_state.passive; while (q) { p = node_next(q); + // tex_free_node(q, get_node_size(node_type(q))); // less overhead & testing tex_flush_node(q); q = p; } @@ -907,12 +917,13 @@ inline static void tex_aux_reset_disc_target(halfword adjust_spacing, scaled *ta inline static void tex_aux_set_target_to_source(halfword adjust_spacing, scaled target[], const scaled source[]) { + // memcpy(&target[total_glue_amount], &source[total_glue_amount], font_shrink_amount * sizeof(halfword)); for (int i = total_glue_amount; i <= total_shrink_amount; i++) { target[i] = source[i]; } if (adjust_spacing) { - target[font_shrink_amount] = source[font_shrink_amount]; target[font_stretch_amount] = source[font_stretch_amount]; + target[font_shrink_amount] = source[font_shrink_amount]; } } @@ -1040,7 +1051,7 @@ static void tex_aux_add_to_widths(halfword s, int adjust_spacing, int adjust_spa while (s) { switch (node_type(s)) { case glyph_node: - widths[total_glue_amount] += tex_glyph_width(s); + widths[total_glue_amount] += tex_glyph_width_ex(s); // ex if (adjust_spacing && ! tex_has_glyph_option(s, glyph_option_no_expansion) && tex_aux_check_expand_pars(adjust_spacing_step, glyph_font(s))) { lmt_packaging_state.previous_char_ptr = s; widths[font_stretch_amount] += tex_char_stretch(s); @@ -1056,7 +1067,7 @@ static void tex_aux_add_to_widths(halfword s, int adjust_spacing, int adjust_spa break; case glue_node: widths[total_glue_amount] += glue_amount(s); - widths[2 + glue_stretch_order(s)] += glue_stretch(s); + widths[total_stretch_amount + glue_stretch_order(s)] += glue_stretch(s); widths[total_shrink_amount] += glue_shrink(s); break; case kern_node: @@ -1093,7 +1104,7 @@ static void tex_aux_sub_from_widths(halfword s, int adjust_spacing, int adjust_s /*tex Subtract the width of node |s| from |break_width|; */ switch (node_type(s)) { case glyph_node: - widths[total_glue_amount] -= tex_glyph_width(s); + widths[total_glue_amount] -= tex_glyph_width_ex(s); // ex if (adjust_spacing && ! tex_has_glyph_option(s, glyph_option_no_expansion) && tex_aux_check_expand_pars(adjust_spacing_step, glyph_font(s))) { lmt_packaging_state.previous_char_ptr = s; widths[font_stretch_amount] -= tex_char_stretch(s); @@ -1108,9 +1119,9 @@ static void tex_aux_sub_from_widths(halfword s, int adjust_spacing, int adjust_s widths[total_glue_amount] -= rule_width(s); break; case glue_node: - widths[total_glue_amount] -= glue_amount(s); - widths[2 + glue_stretch_order(s)] -= glue_stretch(s); - widths[total_shrink_amount] -= glue_shrink(s); + widths[total_glue_amount] -= glue_amount(s); + widths[total_stretch_amount + glue_stretch_order(s)] -= glue_stretch(s); + widths[total_shrink_amount] -= glue_shrink(s); break; case kern_node: widths[total_glue_amount] -= kern_amount(s); @@ -1205,7 +1216,7 @@ static void tex_aux_compute_break_width(int break_type, int adjust_spacing, int case glue_node: /*tex Subtract glue from |break_width|; */ lmt_linebreak_state.break_width[total_glue_amount] -= glue_amount(s); - lmt_linebreak_state.break_width[2 + glue_stretch_order(s)] -= glue_stretch(s); + lmt_linebreak_state.break_width[total_stretch_amount + glue_stretch_order(s)] -= glue_stretch(s); lmt_linebreak_state.break_width[total_shrink_amount] -= glue_shrink(s); break; case penalty_node: @@ -1222,7 +1233,7 @@ static void tex_aux_compute_break_width(int break_type, int adjust_spacing, int lmt_linebreak_state.break_width[total_glue_amount] -= math_surround(s); } else { lmt_linebreak_state.break_width[total_glue_amount] -= math_amount(s); - lmt_linebreak_state.break_width[2 + math_stretch_order(s)] -= math_stretch(s); + lmt_linebreak_state.break_width[total_stretch_amount + math_stretch_order(s)] -= math_stretch(s); lmt_linebreak_state.break_width[total_shrink_amount] -= math_shrink(s); } break; @@ -1233,30 +1244,94 @@ static void tex_aux_compute_break_width(int break_type, int adjust_spacing, int } } -static void tex_aux_print_break_node(halfword q, halfword fit_class, halfword break_type, halfword cur_p, const line_break_properties *properties) +static void tex_aux_initialize_show_break_node(int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", initialize_show_breaks_context); +} + +static void tex_aux_start_show_break_node(int callback_id, int pass) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->", start_show_breaks_context, pass); +} + +static void tex_aux_stop_show_break_node(int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", stop_show_breaks_context); +} + +static void tex_aux_collect_show_break_node(int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", collect_show_breaks_context); +} + +static void tex_aux_line_show_break_node(int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNdddd->", line_show_breaks_context, + lmt_linebreak_state.just_box, lmt_packaging_state.last_badness, lmt_packaging_state.last_overshoot, + lmt_packaging_state.total_shrink[normal_glue_order], lmt_packaging_state.total_stretch[normal_glue_order] + ); +} + +static void tex_aux_delete_break_node(halfword active, halfword passive, int callback_id) +{ + (void) active; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->", delete_show_breaks_context, + passive_serial(passive) + ); +} + +static void tex_aux_wrapup_show_break_node(int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", wrapup_show_breaks_context); +} + +static void tex_aux_show_break_node(halfword active, halfword passive, int callback_id, int pass, halfword *demerits) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddddddddNdd->r", report_show_breaks_context, + pass, + passive_serial(passive), + passive_prev_break(passive) ? passive_serial(passive_prev_break(passive)) : 0, + active_line_number(active) - 1, + node_type(active), + active_fitness(active), + active_total_demerits(active), /* demerits */ + passive_cur_break(passive), + lmt_linebreak_state.do_last_line_fit ? active_short(active) : 0, + lmt_linebreak_state.do_last_line_fit ? active_glue(active) : 0, + demerits /* optionally changed */ + ); +} + +static void tex_aux_list_break_node(halfword passive, int callback_id) +{ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->", list_show_breaks_context, + passive_serial(passive) + ); +} + +static void tex_aux_print_break_node(halfword active, halfword passive) { - (void) properties; /*tex Print a symbolic description of the new break node. */ tex_print_format( "%l[break: serial %i, line %i.%i,%s demerits %i, ", - passive_serial(lmt_linebreak_state.passive), - active_line_number(q) - 1, - fit_class, - break_type == hyphenated_node ? " hyphenated, " : "", - active_total_demerits(q) + passive_serial(passive), + active_line_number(active) - 1, + active_fitness(active), + node_type(active) == hyphenated_node ? " hyphenated, " : "", + active_total_demerits(active) ); if (lmt_linebreak_state.do_last_line_fit) { /*tex Print additional data in the new active node. */ tex_print_format( " short %D, %s %D, ", - active_short(q), pt_unit, - cur_p ? "glue" : "active", - active_glue(q), pt_unit + active_short(active), pt_unit, + passive_cur_break(passive) ? "glue" : "active", + active_glue(active), pt_unit ); } tex_print_format( "previous %i]", - passive_prev_break(lmt_linebreak_state.passive) ? passive_serial(passive_prev_break(lmt_linebreak_state.passive)) : 0 + passive_prev_break(passive) ? passive_serial(passive_prev_break(passive)) : null ); } @@ -1303,9 +1378,6 @@ static void tex_aux_print_feasible_break(halfword cur_p, halfword r, halfword b, ); } -# define total_font_stretch cur_active_width[font_stretch_amount] -# define total_font_shrink cur_active_width[font_shrink_amount] - /*tex We implement this one later on. */ /* @@ -1313,7 +1385,7 @@ static void tex_aux_print_feasible_break(halfword cur_p, halfword r, halfword b, trickery depending on it. */ -static void tex_aux_post_line_break(const line_break_properties *properties, halfword line_break_dir); +static void tex_aux_post_line_break(const line_break_properties *properties, halfword line_break_dir, int callback_id); /*tex @@ -1363,28 +1435,52 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal good estimates from Don Knuth here, it would be pretentious to suggest that I really did research this fuzzy topic (if it was worth the effort at all). + Here |large_width_excess| is 110.32996pt while |small_stretchability| equals 25.38295pt. + +*/ + +/*tex + Around 2023-05-24 Mikael Sundqvist and I did numerous tests with the badness function below in + comparison with the variant mentioned in Digital Typography (DEK) and we observed that indeed + both functions behave pretty close (emulations with lua, mathematica etc). In practice one can + get different badness values (especially low numbers). We ran some test on documents and on + hundreds of pages one can get a few different decisions. The main reason for looking into this + was that we were exploring a bit more visual approach to deciding on what penalties to use in + the math inter-atom spacing in \CONTEXT\ (where we use a more granular class model). In the end + the magic criteria became even more magic (and impressive). BTW, indeed we could get these 1095 + different badness cases with as maximum calculated one 8189. */ halfword tex_badness(scaled t, scaled s) { - /*tex Approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$ */ + /*tex Approximation to $\alpha t/s$, where $\alpha^3 \approx 100 \cdot 2^{18}$ */ if (t == 0) { return 0; } else if (s <= 0) { return infinite_bad; } else { - /*tex $297^3=99.94\times2^{18}$ */ - if (t <= large_width_excess) { - t = (t * 297) / s; - } else if (s >= small_stretchability) { - t = t / (s / 297); - } - if (t > 1290) { - /*tex $1290^3<2^{31}<1291^3$ */ + /*tex $297^3 = 99.94 \times 2^{18}$ */ + if (t <= large_width_excess) { + t = (t * 297) / s; /* clipping by integer division */ + } else if (s >= small_stretchability) { + t = t / (s / 297); /* clipping by integer division */ + } else { + /*tex + When we end up here |t| is pretty large so we can as well save a test and return + immediately. (HH & MS: we tested this while cheating a bit because this function + is seldom entered with values that make us end up here.) + */ + return infinite_bad; + } + if (t > 1290) { + /*tex As $1290^3 < 2^{31} < 1291^3$ we catch an overflow here. */ /* actually badness 8189 */ return infinite_bad; } else { - /*tex This is $t^3/2^{18}$, rounded to the nearest integer. */ - return ((t * t * t + 0400000) / 01000000); + /*tex 297*297*297 == 26198073 / 100 => 261981 */ + /*tex This is $t^3 / 2^{18}$, rounded to the nearest integer */ + return (t * t * t + 0400000) / 01000000; /* 0400000/01000000 == 1/2 */ + // return (t * t * t + 0x20000) / 0x40000; + // return (t * t * t + 131072) / 262144; } } } @@ -1410,13 +1506,6 @@ inline static void tex_split_line_break_criterium(halfword criterium, halfword * inline static halfword tex_normalized_loose_badness(halfword b, halfword loose, halfword semi_loose, halfword decent) { - // if (b > loose_criterium) { - // return very_loose_fit; - // } else if (b > decent_criterium) { - // return loose_fit; - // } else { - // return decent_fit; - // } if (b > loose) { return very_loose_fit; } else if (b > semi_loose) { @@ -1430,11 +1519,6 @@ inline static halfword tex_normalized_loose_badness(halfword b, halfword loose, inline static halfword tex_normalized_tight_badness(halfword b, halfword decent, halfword semi_tight) { - // if (b > decent_criterium) { - // return tight_fit; - // } else { - // return decent_fit; - // } if (b > semi_tight) { return semi_tight_fit; } else if (b > decent) { @@ -1444,78 +1528,134 @@ inline static halfword tex_normalized_tight_badness(halfword b, halfword decent, } } +static void tex_check_protrusion_shortfall(halfword r, halfword first_p, halfword cur_p, halfword *shortfall) +{ + // if (line_break_dir == dir_righttoleft) { + // /*tex Not now, we need to keep more track. */ + // } else { + halfword o = null; + halfword l = active_break_node(r) ? passive_cur_break(active_break_node(r)) : first_p; + if (cur_p) { + o = node_prev(cur_p); + if (node_next(o) != cur_p) { + tex_normal_error("linebreak", "the node list is messed up"); + } + } + /*tex + + The last characters (hyphenation character) if these two list should always be + the same anyway, so we just look at |pre_break|. Let's look at the right margin + first. + + */ + if (cur_p && node_type(cur_p) == disc_node && disc_pre_break_head(cur_p)) { + /*tex + A |disc_node| with non-empty |pre_break|, protrude the last char of + |pre_break|: + */ + o = disc_pre_break_tail(cur_p); + } else { + o = tex_aux_find_protchar_right(l, o); + } + if (o && node_type(o) == glyph_node) { + shortfall += tex_char_protrusion(o, right_margin_kern_subtype); + // char_pw_kern(o, right_margin_kern, &margin_kern_stretch, &margin_kern_shrink); + } + /*tex now the left margin */ + if (l && (node_type(l) == disc_node) && (disc_post_break_head(l))) { + /*tex The first char could be a disc! Protrude the first char. */ + o = disc_post_break_head(l); + } else { + o = tex_aux_find_protchar_left(l, 1); + } + if (o && node_type(o) == glyph_node) { + shortfall += tex_char_protrusion(o, left_margin_kern_subtype); + // char_pw_kern(o, left_margin_kern, &margin_kern_stretch, &margin_kern_shrink); + } + // } +} + static void tex_aux_try_break( const line_break_properties *properties, - halfword pi, /* a penalty */ + halfword penalty, halfword break_type, halfword first_p, - halfword cur_p + halfword cur_p, + int callback_id, + int pass ) { - /*tex runs through the active list */ - halfword r; /*tex stays a step behind |r| */ halfword prev_r = active_head; /*tex a step behind |prev_r|, if |type(prev_r) = delta_node| */ halfword prev_prev_r = null; + /*tex distance from current active node */ + scaled cur_active_width[n_of_glue_amounts] = { 0 }; + /*tex + These status arrays are global to the main loop and will be initialized as we go. + */ + halfword best_place[n_of_finess_values]; + halfword best_place_line[n_of_finess_values]; + scaled best_place_short[n_of_finess_values]; + scaled best_place_glue[n_of_finess_values]; + /* + These are more local but we keep them here because of readability. + */ + /*tex badness of test line */ + halfword badness = 0; + /*tex demerits of test line */ + int demerits = 0; + /*tex glue stretch or shrink of test line, adjustment for last line */ + scaled glue = 0; + /*tex used in badness calculations */ + scaled shortfall = 0; /*tex maximum line number in current equivalence class of lines */ - halfword old_l = 0; + halfword old_line = 0; /*tex have we found a feasible break at |cur_p|? */ int no_break_yet = 1; - /*tex line number of current active node */ - halfword l; /*tex should node |r| remain in the active list? */ int node_r_stays_active; - /*tex the current line will be justified to this width */ - scaled line_width = 0; /*tex possible fitness class of test line */ halfword fit_class; - /*tex badness of test line */ - halfword b; - /*tex demerits of test line */ - int d; /*tex has |d| been forced to zero? */ int artificial_demerits; - /*tex used in badness calculations */ - scaled shortfall = 0; - /*tex glue stretch or shrink of test line, adjustment for last line */ - scaled g = 0; - /*tex distance from current active node */ - scaled cur_active_width[10] = { 0 }; - halfword best_place[n_of_finess_values]; - halfword best_place_line[n_of_finess_values]; - scaled best_place_short[n_of_finess_values]; - scaled best_place_glue[n_of_finess_values]; - /*tex Experiment */ + /*tex the current line will be justified to this width */ + scaled line_width = 0; + /*tex line number of current active node */ + halfword line = 0; + /*tex + We have added an extra category, just as experiment. In practice there is very little + to gain here as it becomes kind of fuzzy and DEK values are quite okay. + */ halfword semi_tight, decent, semi_loose, loose; - /* in par node */ + /*tex in par node */ tex_split_line_break_criterium(line_break_criterium_par, &semi_tight, &decent, &semi_loose, &loose); /*tex Make sure that |pi| is in the proper range; */ - if (pi >= infinite_penalty) { + if (penalty >= infinite_penalty) { /*tex this breakpoint is inhibited by infinite penalty */ return; - } else if (pi <= -infinite_penalty) { + } else if (penalty <= -infinite_penalty) { /*tex this breakpoint will be forced */ - pi = eject_penalty; + penalty = eject_penalty; } tex_aux_set_target_to_source(properties->adjust_spacing, cur_active_width, lmt_linebreak_state.active_width); while (1) { - r = node_next(prev_r); + /*tex Here |r| runs through the active list: */ + halfword r = node_next(prev_r); /*tex If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r| and |prev_prev_r|, then |goto continue|. The following code uses the fact that |type (active) <> delta_node|. - Here we get: |unhyphenated_node|, |hyphenated_node, |delta_node|, |passive_node| - */ if (node_type(r) == delta_node) { - /*tex implicit */ tex_aux_add_to_target_from_delta(properties->adjust_spacing, cur_active_width, r); prev_prev_r = prev_r; prev_r = r; continue; + } else { + /*tex We have an |unhyphenated_node| or |hyphenated_node|. */ } /*tex @@ -1528,10 +1668,10 @@ static void tex_aux_try_break( that |r = active| and |line_number (active) > old_l|. */ - l = active_line_number(r); - if (l > old_l) { - /*tex now we are no longer in the inner loop */ - if ((lmt_linebreak_state.minimum_demerits < awful_bad) && ((old_l != lmt_linebreak_state.easy_line) || (r == active_head))) { + line = active_line_number(r); + if (line > old_line) { + /*tex Now we are no longer in the inner loop (well ...). */ + if ((lmt_linebreak_state.minimum_demerits < awful_bad) && ((old_line != lmt_linebreak_state.easy_line) || (r == active_head))) { /*tex Create new active nodes for the best feasible breaks just found. It is not @@ -1578,53 +1718,55 @@ static void tex_aux_try_break( Insert a new active node from |best_place [fit_class]| to |cur_p|. When we create an active node, we also create the corresponding passive node. + In the passive node we also keep track of the subparagraph penalties. */ - halfword q = tex_new_node(passive_node, (quarterword) very_loose_fit); - node_next(q) = lmt_linebreak_state.passive; - lmt_linebreak_state.passive = q; - passive_cur_break(q) = cur_p; - ++lmt_linebreak_state.pass_number; - passive_serial(q) = lmt_linebreak_state.pass_number; - passive_prev_break(q) = best_place[fit_class]; - /*tex - - Here we keep track of the subparagraph penalties in the break nodes. - - */ - passive_pen_inter(q) = lmt_linebreak_state.internal_penalty_interline; - passive_pen_broken(q) = lmt_linebreak_state.internal_penalty_broken; - passive_last_left_box(q) = lmt_linebreak_state.internal_left_box; - passive_last_left_box_width(q) = lmt_linebreak_state.internal_left_box_width; - if (passive_prev_break(q)) { - passive_left_box(q) = passive_last_left_box(passive_prev_break(q)); - passive_left_box_width(q) = passive_last_left_box_width(passive_prev_break(q)); + halfword passive = tex_new_node(passive_node, (quarterword) very_loose_fit); + halfword active = tex_new_node((quarterword) break_type, (quarterword) fit_class); + halfword prev_break = best_place[fit_class]; + /*tex Initialize the passive node: */ + passive_cur_break(passive) = cur_p; + passive_serial(passive) = ++lmt_linebreak_state.pass_number; + passive_prev_break(passive) = prev_break; + passive_pen_inter(passive) = lmt_linebreak_state.internal_penalty_interline; + passive_pen_broken(passive) = lmt_linebreak_state.internal_penalty_broken; + passive_last_left_box(passive) = lmt_linebreak_state.internal_left_box; + passive_last_left_box_width(passive) = lmt_linebreak_state.internal_left_box_width; + if (prev_break) { + passive_left_box(passive) = passive_last_left_box(prev_break); + passive_left_box_width(passive) = passive_last_left_box_width(prev_break); } else { - passive_left_box(q) = lmt_linebreak_state.init_internal_left_box; - passive_left_box_width(q) = lmt_linebreak_state.init_internal_left_box_width; + passive_left_box(passive) = lmt_linebreak_state.init_internal_left_box; + passive_left_box_width(passive) = lmt_linebreak_state.init_internal_left_box_width; } - passive_right_box(q) = lmt_linebreak_state.internal_right_box; - passive_right_box_width(q) = lmt_linebreak_state.internal_right_box_width; - passive_middle_box(q) = lmt_linebreak_state.internal_middle_box; - q = tex_new_node((quarterword) break_type, (quarterword) fit_class); - active_break_node(q) = lmt_linebreak_state.passive; - active_line_number(q) = best_place_line[fit_class] + 1; - active_total_demerits(q) = lmt_linebreak_state.minimal_demerits[fit_class]; + passive_right_box(passive) = lmt_linebreak_state.internal_right_box; + passive_right_box_width(passive) = lmt_linebreak_state.internal_right_box_width; + passive_middle_box(passive) = lmt_linebreak_state.internal_middle_box; + /*tex Initialize the active node: */ + active_break_node(active) = passive; + active_line_number(active) = best_place_line[fit_class] + 1; + active_total_demerits(active) = lmt_linebreak_state.minimal_demerits[fit_class]; + // active_reserved(active) = lmt_linebreak_state.pass_number; if (lmt_linebreak_state.do_last_line_fit) { - /*tex - - Store additional data in the new active node. Here we save these - data in the active node representing a potential line break. - - */ - active_short(q) = best_place_short[fit_class]; - active_glue(q) = best_place_glue[fit_class]; + /*tex Store additional data in the new active node. */ + active_short(active) = best_place_short[fit_class]; + active_glue(active) = best_place_glue[fit_class]; + } + /*tex Append the passive node. */ + node_next(passive) = lmt_linebreak_state.passive; + lmt_linebreak_state.passive = passive; + /*tex Append the active node. */ + node_next(active) = r; + node_next(prev_r) = active; + prev_r = active; + /* */ + if (callback_id) { + halfword demerits = active_total_demerits(active); + tex_aux_show_break_node(active, passive, callback_id, pass, &demerits); + active_total_demerits(active) = demerits; } - node_next(q) = r; - node_next(prev_r) = q; - prev_r = q; if (properties->tracing_paragraphs > 0) { - tex_aux_print_break_node(q, fit_class, break_type, cur_p, properties); + tex_aux_print_break_node(active, passive); } } lmt_linebreak_state.minimal_demerits[fit_class] = awful_bad; @@ -1638,12 +1780,12 @@ static void tex_aux_try_break( */ if (r != active_head) { - halfword q = tex_new_node(delta_node, (quarterword) very_loose_fit); - node_next(q) = r; - tex_aux_set_delta_from_difference(properties->adjust_spacing, q, cur_active_width, lmt_linebreak_state.break_width); - node_next(prev_r) = q; + halfword delta = tex_new_node(delta_node, (quarterword) very_loose_fit); + node_next(delta) = r; + tex_aux_set_delta_from_difference(properties->adjust_spacing, delta, cur_active_width, lmt_linebreak_state.break_width); + node_next(prev_r) = delta; prev_prev_r = prev_r; - prev_r = q; + prev_r = delta; } } /*tex @@ -1657,17 +1799,17 @@ static void tex_aux_try_break( */ if (r == active_head) { return; - } else if (l > lmt_linebreak_state.easy_line) { - old_l = max_halfword - 1; + } else if (line > lmt_linebreak_state.easy_line) { + old_line = max_halfword - 1; line_width = lmt_linebreak_state.second_width; } else { - old_l = l; + old_line = line; /* if (properties->par_shape && specification_repeat(properties->par_shape)) { line_width = get_specification_width(properties->par_shape, l); - } else */ if (l > lmt_linebreak_state.last_special_line) { + } else */ if (line > lmt_linebreak_state.last_special_line) { line_width = lmt_linebreak_state.second_width; } else if (properties->par_shape) { - line_width = tex_get_specification_width(properties->par_shape, l); + line_width = tex_get_specification_width(properties->par_shape, line); } else { line_width = lmt_linebreak_state.first_width; } @@ -1694,49 +1836,7 @@ static void tex_aux_try_break( // halfword margin_kern_stretch = 0; // halfword margin_kern_shrink = 0; if (properties->protrude_chars) { - // if (line_break_dir == dir_righttoleft) { - // /*tex Not now, we need to keep more track. */ - // } else { - halfword o = null; - halfword l1 = active_break_node(r) ? passive_cur_break(active_break_node(r)) : first_p; - if (cur_p) { - o = node_prev(cur_p); - if (node_next(o) != cur_p) { - tex_normal_error("linebreak", "the node list is messed up"); - } - } - /*tex - - The last characters (hyphenation character) if these two list should always be - the same anyway, so we just look at |pre_break|. Let's look at the right margin - first. - - */ - if (cur_p && node_type(cur_p) == disc_node && disc_pre_break_head(cur_p)) { - /*tex - A |disc_node| with non-empty |pre_break|, protrude the last char of - |pre_break|: - */ - o = disc_pre_break_tail(cur_p); - } else { - o = tex_aux_find_protchar_right(l1, o); - } - if (o && node_type(o) == glyph_node) { - shortfall += tex_char_protrusion(o, right_margin_kern_subtype); - // char_pw_kern(o, right_margin_kern, &margin_kern_stretch, &margin_kern_shrink); - } - /*tex now the left margin */ - if (l1 && (node_type(l1) == disc_node) && (disc_post_break_head(l1))) { - /*tex The first char could be a disc! Protrude the first char. */ - o = disc_post_break_head(l1); - } else { - o = tex_aux_find_protchar_left(l1, 1); - } - if (o && node_type(o) == glyph_node) { - shortfall += tex_char_protrusion(o, left_margin_kern_subtype); - // char_pw_kern(o, left_margin_kern, &margin_kern_stretch, &margin_kern_shrink); - } - // } + tex_check_protrusion_shortfall(r, first_p, cur_p, &shortfall); } /*tex The only reason why we have a shared ratio is that we need to calculate the shortfall @@ -1745,8 +1845,8 @@ static void tex_aux_try_break( if (shortfall == 0) { /*tex We're okay. */ } else if (shortfall > 0) { - halfword total_stretch = total_font_stretch; - // halfword total_stretch = total_font_stretch + margin_kern_stretch; + halfword total_stretch = cur_active_width[font_stretch_amount]; + // halfword total_stretch = cur_active_width[font_stretch_amount] + margin_kern_stretch; if (total_stretch > 0) { if (total_stretch > shortfall) { shortfall = (total_stretch / (lmt_linebreak_state.max_stretch_ratio / lmt_linebreak_state.current_font_step)) / 2; @@ -1755,11 +1855,11 @@ static void tex_aux_try_break( } } } else if (shortfall < 0) { - halfword total_shrink = total_font_shrink; - // halfword total_shrink = total_font_shrink + margin_kern_shrink; + halfword total_shrink = cur_active_width[font_shrink_amount]; + // halfword total_shrink = cur_active_width[font_shrink_amount] + margin_kern_shrink; if (total_shrink > 0) { if (total_shrink > -shortfall) { - shortfall = - (total_shrink / (lmt_linebreak_state.max_shrink_ratio / lmt_linebreak_state.current_font_step)) / 2; + shortfall = - (total_shrink / (lmt_linebreak_state.max_shrink_ratio / lmt_linebreak_state.current_font_step)) / 2; } else { shortfall += total_shrink; } @@ -1809,23 +1909,23 @@ static void tex_aux_try_break( goto NOT_FOUND; } if (active_short(r) > 0) { - g = cur_active_width[total_stretch_amount]; + glue = cur_active_width[total_stretch_amount]; } else { - g = cur_active_width[total_shrink_amount]; + glue = cur_active_width[total_shrink_amount]; } - if (g <= 0) { + if (glue <= 0) { /*tex No finite stretch resp.\ no shrink. */ goto NOT_FOUND; } lmt_scanner_state.arithmic_error = 0; - g = tex_fract(g, active_short(r), active_glue(r), max_dimen); + glue = tex_fract(glue, active_short(r), active_glue(r), max_dimen); if (properties->last_line_fit < 1000) { - g = tex_fract(g, properties->last_line_fit, 1000, max_dimen); + glue = tex_fract(glue, properties->last_line_fit, 1000, max_dimen); } if (lmt_scanner_state.arithmic_error) { - g = (active_short(r) > 0) ? max_dimen : -max_dimen; + glue = (active_short(r) > 0) ? max_dimen : -max_dimen; } - if (g > 0) { + if (glue > 0) { /*tex Set the value of |b| to the badness of the last line for stretching, @@ -1834,44 +1934,44 @@ static void tex_aux_try_break( algorithm, with the adjustment amount |g| replacing the |shortfall|. */ - if (g > shortfall) { - g = shortfall; + if (glue > shortfall) { + glue = shortfall; } - if (g > large_width_excess && (cur_active_width[total_stretch_amount] < small_stretchability)) { - b = infinite_bad; + if (glue > large_width_excess && (cur_active_width[total_stretch_amount] < small_stretchability)) { + badness = infinite_bad; fit_class = very_loose_fit; - goto FOUND; + } else { + badness = tex_badness(glue, cur_active_width[total_stretch_amount]); + fit_class = tex_normalized_loose_badness(badness, loose, semi_loose, decent); } - b = tex_badness(g, cur_active_width[total_stretch_amount]); - fit_class = tex_normalized_loose_badness(b, loose, semi_loose, decent); goto FOUND; - } else if (g < 0) { + } else if (glue < 0) { /*tex Set the value of |b| to the badness of the last line for shrinking, compute the corresponding |fit_class, and |goto found||. */ - if (-g > cur_active_width[total_shrink_amount]) { - g = -cur_active_width[total_shrink_amount]; + if (-glue > cur_active_width[total_shrink_amount]) { + glue = -cur_active_width[total_shrink_amount]; } - b = tex_badness(-g, cur_active_width[total_shrink_amount]); - fit_class = tex_normalized_tight_badness(b, decent, semi_tight); + badness = tex_badness(-glue, cur_active_width[total_shrink_amount]); + fit_class = tex_normalized_tight_badness(badness, decent, semi_tight); goto FOUND; } } NOT_FOUND: shortfall = 0; } - b = 0; + badness = 0; /*tex Infinite stretch. */ fit_class = decent_fit; } else if (shortfall > large_width_excess && cur_active_width[total_stretch_amount] < small_stretchability) { - b = infinite_bad; + badness = infinite_bad; fit_class = very_loose_fit; } else { - b = tex_badness(shortfall, cur_active_width[total_stretch_amount]); - fit_class = tex_normalized_loose_badness(b, loose, semi_loose, decent); + badness = tex_badness(shortfall, cur_active_width[total_stretch_amount]); + fit_class = tex_normalized_loose_badness(badness, loose, semi_loose, decent); } } else { /*tex @@ -1883,27 +1983,27 @@ static void tex_aux_try_break( */ if (-shortfall > cur_active_width[total_shrink_amount]) { - b = infinite_bad + 1; + badness = infinite_bad + 1; } else { - b = tex_badness(-shortfall, cur_active_width[total_shrink_amount]); + badness = tex_badness(-shortfall, cur_active_width[total_shrink_amount]); } - fit_class = tex_normalized_tight_badness(b, decent, semi_tight); + fit_class = tex_normalized_tight_badness(badness, decent, semi_tight); } if (lmt_linebreak_state.do_last_line_fit) { /*tex Adjust the additional data for last line; */ if (! cur_p) { shortfall = 0; - g = 0; + glue = 0; } else if (shortfall > 0) { - g = cur_active_width[total_stretch_amount]; + glue = cur_active_width[total_stretch_amount]; } else if (shortfall < 0) { - g = cur_active_width[total_shrink_amount]; + glue = cur_active_width[total_shrink_amount]; } else { - g = 0; + glue = 0; } } FOUND: - if ((b > infinite_bad) || (pi == eject_penalty)) { + if ((badness > infinite_bad) || (penalty == eject_penalty)) { /*tex Prepare to deactivate node~|r|, and |goto deactivate| unless there is a reason to @@ -1916,17 +2016,16 @@ static void tex_aux_try_break( changes here. */ - if (lmt_linebreak_state.final_pass && (lmt_linebreak_state.minimum_demerits == awful_bad) && - (node_next(r) == active_head) && (prev_r == active_head)) { + if (lmt_linebreak_state.final_pass && (lmt_linebreak_state.minimum_demerits == awful_bad) && (node_next(r) == active_head) && (prev_r == active_head)) { /*tex Set demerits zero, this break is forced. */ artificial_demerits = 1; - } else if (b > lmt_linebreak_state.threshold) { + } else if (badness > lmt_linebreak_state.threshold) { goto DEACTIVATE; } node_r_stays_active = 0; } else { prev_r = r; - if (b > lmt_linebreak_state.threshold) { + if (badness > lmt_linebreak_state.threshold) { continue; } else { node_r_stays_active = 1; @@ -1942,27 +2041,27 @@ static void tex_aux_try_break( */ if (artificial_demerits) { - d = 0; + demerits = 0; } else { /*tex Compute the demerits, |d|, from |r| to |cur_p|. */ - d = properties->line_penalty + b; - if (abs(d) >= 10000) { - d = 100000000; + demerits = properties->line_penalty + badness; + if (abs(demerits) >= 10000) { + demerits = 100000000; } else { - d = d * d; + demerits = demerits * demerits; } - if (pi != 0) { - if (pi > 0) { - d += (pi * pi); - } else if (pi > eject_penalty) { - d -= (pi * pi); + if (penalty != 0) { + if (penalty > 0) { + demerits += (penalty * penalty); + } else if (penalty > eject_penalty) { + demerits -= (penalty * penalty); } } if (break_type == hyphenated_node && node_type(r) == hyphenated_node) { if (cur_p) { - d += properties->double_hyphen_demerits; + demerits += properties->double_hyphen_demerits; } else { - d += properties->final_hyphen_demerits; + demerits += properties->final_hyphen_demerits; } } /*tex @@ -1972,18 +2071,18 @@ static void tex_aux_try_break( used. */ if (abs(fit_class - (halfword) active_fitness(r)) > 1) { - d = d + properties->adj_demerits; + demerits = demerits + properties->adj_demerits; } } if (properties->tracing_paragraphs > 0) { - tex_aux_print_feasible_break(cur_p, r, b, pi, d, artificial_demerits, properties); + tex_aux_print_feasible_break(cur_p, r, badness, penalty, demerits, artificial_demerits, properties); } /*tex This is the minimum total demerits from the beginning to |cur_p| via |r|. */ - d += active_total_demerits(r); - if (d <= lmt_linebreak_state.minimal_demerits[fit_class]) { - lmt_linebreak_state.minimal_demerits[fit_class] = d; + demerits += active_total_demerits(r); + if (demerits <= lmt_linebreak_state.minimal_demerits[fit_class]) { + lmt_linebreak_state.minimal_demerits[fit_class] = demerits; best_place[fit_class] = active_break_node(r); - best_place_line[fit_class] = l; + best_place_line[fit_class] = line; if (lmt_linebreak_state.do_last_line_fit) { /*tex @@ -1992,10 +2091,10 @@ static void tex_aux_try_break( */ best_place_short[fit_class] = shortfall; - best_place_glue[fit_class] = g; + best_place_glue[fit_class] = glue; } - if (d < lmt_linebreak_state.minimum_demerits) { - lmt_linebreak_state.minimum_demerits = d; + if (demerits < lmt_linebreak_state.minimum_demerits) { + lmt_linebreak_state.minimum_demerits = demerits; } } /*tex Record a new feasible break. */ @@ -2013,6 +2112,9 @@ static void tex_aux_try_break( */ node_next(prev_r) = node_next(r); + if (callback_id) { + tex_aux_delete_break_node(r, active_break_node(r), callback_id); + } tex_flush_node(r); if (prev_r == active_head) { /*tex @@ -2054,9 +2156,10 @@ static halfword tex_aux_inject_orphan_penalty(halfword current, halfword amount) halfword penalty = tex_new_penalty_node(amount, orphan_penalty_subtype); tex_couple_nodes(previous, penalty); tex_couple_nodes(penalty, current); - current = previous; + return previous; + } else { + return current; } - return current; } inline static int tex_aux_valid_glue_break(halfword p) @@ -2065,13 +2168,47 @@ inline static int tex_aux_valid_glue_break(halfword p) return (prv && prv != temp_head && (node_type(prv) == glyph_node || precedes_break(prv) || precedes_kern(prv) || precedes_dir(prv))); } +inline static halfword tex_aux_upcoming_penalty(halfword p) { + halfword n = node_next(p); + return (n && node_type(n) == math_node && node_subtype(n) == begin_inline_math) ? math_penalty(n) : 0; +} + +/*tex + + I played a bit with a height driven hanging indentation. One can store |cur_p| in the active + node and progressively calculate the height + depth and then act on that but in the end + interline space, adjustsm etc. also have to be taken into account and that all happens later + so in the end it makes no sense. There are valdi reasons why \TEX\ can't do some things + reliable: user demands are unpredictable. + +*/ + +/*tex + + Here we pickup the line number from |prev_graf| which relates to display math inside a + paragraph. A display formula is then considered to span three lines. Of course this also + assume a constant baseline distance with lines heigths not exceeding that amount. It also + assumes that the shape and hang are not reset. We check the prevgraf for a large value + because when we're close to |max_integer| we can wrap around due to addition beyond that + and negative values has side effects (see musings-sideffects) but it's optional so that we + can actually use these side effects. + +*/ + +# define max_prev_graf (max_integer/2) + void tex_do_line_break(line_break_properties *properties) { /*tex Miscellaneous nodes of temporary interest. */ - halfword cur_p, l, r; int line_break_dir = properties->paragraph_dir; + int callback_id = lmt_callback_defined(show_break_callback); int force_check_hyphenation = hyphenation_permitted(properties->hyphenation_mode, force_check_hyphenation_mode); (void) (properties->inter_line_penalties); /* avoid not used message */ + /*tex Fix a buglet that probably is a feature. */ + if ((cur_list.prev_graf > max_prev_graf || cur_list.prev_graf < 0) && normalize_par_mode_permitted(normalize_par_mode_par, limit_prev_graf_mode)) { + tex_formatted_warning("tex", "clipping prev_graf %i to %i", cur_list.prev_graf, max_prev_graf); + cur_list.prev_graf = max_prev_graf; + } /*tex Get ready to start */ lmt_linebreak_state.fewest_demerits = 0; lmt_linebreak_state.actual_looseness = 0; @@ -2228,17 +2365,19 @@ void tex_do_line_break(line_break_properties *properties) lmt_linebreak_state.easy_line = max_halfword; } lmt_linebreak_state.no_shrink_error_yet = 1; - l = properties->left_skip; - r = properties->right_skip; - lmt_linebreak_state.background[total_glue_amount] = glue_amount(l) + glue_amount(r); - lmt_linebreak_state.background[total_stretch_amount] = 0; - lmt_linebreak_state.background[total_fi_amount] = 0; - lmt_linebreak_state.background[total_fil_amount] = 0; - lmt_linebreak_state.background[total_fill_amount] = 0; - lmt_linebreak_state.background[total_filll_amount] = 0; - lmt_linebreak_state.background[total_stretch_amount + glue_stretch_order(l)] = glue_stretch(l); - lmt_linebreak_state.background[total_stretch_amount + glue_stretch_order(r)] += glue_stretch(r); - lmt_linebreak_state.background[total_shrink_amount] = tex_aux_checked_shrink(l) + tex_aux_checked_shrink(r); + { + halfword l = properties->left_skip; + halfword r = properties->right_skip; + lmt_linebreak_state.background[total_glue_amount] = glue_amount(l) + glue_amount(r); + lmt_linebreak_state.background[total_stretch_amount] = 0; + lmt_linebreak_state.background[total_fi_amount] = 0; + lmt_linebreak_state.background[total_fil_amount] = 0; + lmt_linebreak_state.background[total_fill_amount] = 0; + lmt_linebreak_state.background[total_filll_amount] = 0; + lmt_linebreak_state.background[total_stretch_amount + glue_stretch_order(l)] = glue_stretch(l); + lmt_linebreak_state.background[total_stretch_amount + glue_stretch_order(r)] += glue_stretch(r); + lmt_linebreak_state.background[total_shrink_amount] = tex_aux_checked_shrink(l) + tex_aux_checked_shrink(r); + } if (properties->adjust_spacing) { lmt_linebreak_state.background[font_stretch_amount] = 0; lmt_linebreak_state.background[font_shrink_amount] = 0; @@ -2283,7 +2422,6 @@ void tex_do_line_break(line_break_properties *properties) tex_short_display(node_next(temp_head)); tex_end_diagnostic(); } - if (lmt_linebreak_state.threshold >= 0) { if (properties->tracing_paragraphs > 0) { tex_begin_diagnostic(); @@ -2299,354 +2437,373 @@ void tex_do_line_break(line_break_properties *properties) tex_begin_diagnostic(); } } - while (1) { - halfword first_p, q; - halfword nest_stack[10]; - int nest_index = 0; - if (lmt_linebreak_state.threshold > infinite_bad) { - lmt_linebreak_state.threshold = infinite_bad; - } - /*tex Create an active breakpoint representing the beginning of the paragraph. */ - q = tex_new_node(unhyphenated_node, (quarterword) decent_fit); - node_next(q) = active_head; - active_break_node(q) = null; - active_line_number(q) = cur_list.prev_graf + 1; - active_total_demerits(q) = 0; - active_short(q) = 0; - active_glue(q) = 0; - node_next(active_head) = q; /* we create a cycle */ - tex_aux_set_target_to_source(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.background); - lmt_linebreak_state.passive = null; - lmt_linebreak_state.printed_node = temp_head; - lmt_linebreak_state.pass_number = 0; - lmt_print_state.font_in_short_display = null_font; - /*tex Create an active breakpoint representing the beginning of the paragraph. */ - /* lmt_linebreak_state.auto_breaking = 1; */ /* gone */ - cur_p = node_next(temp_head); - /*tex Initialize with first (or current) |par| node. */ - if (cur_p && node_type(cur_p) == par_node) { - node_prev(cur_p) = temp_head; - lmt_linebreak_state.internal_penalty_interline = tex_get_local_interline_penalty(cur_p); - lmt_linebreak_state.internal_penalty_broken = tex_get_local_broken_penalty(cur_p); - lmt_linebreak_state.init_internal_left_box = par_box_left(cur_p); - lmt_linebreak_state.init_internal_left_box_width = tex_get_local_left_width(cur_p); - lmt_linebreak_state.internal_right_box = par_box_right(cur_p); - lmt_linebreak_state.internal_right_box_width = tex_get_local_right_width(cur_p); - lmt_linebreak_state.internal_middle_box = par_box_middle(cur_p); - } else { - lmt_linebreak_state.internal_penalty_interline = 0; - lmt_linebreak_state.internal_penalty_broken = 0; - lmt_linebreak_state.init_internal_left_box = null; - lmt_linebreak_state.init_internal_left_box_width = 0; - lmt_linebreak_state.internal_right_box = null; - lmt_linebreak_state.internal_right_box_width = 0; - lmt_linebreak_state.internal_middle_box = null; - } - lmt_linebreak_state.internal_left_box = lmt_linebreak_state.init_internal_left_box; - lmt_linebreak_state.internal_left_box_width = lmt_linebreak_state.init_internal_left_box_width; - lmt_packaging_state.previous_char_ptr = null; - first_p = cur_p; - /*tex + if (callback_id) { + tex_aux_initialize_show_break_node(callback_id); + } + { + halfword cur_p = null; + int pass = 0; + while (++pass) { + halfword first_p = node_next(temp_head); + cur_p = first_p; + if (lmt_linebreak_state.threshold > infinite_bad) { + lmt_linebreak_state.threshold = infinite_bad; + } + if (callback_id) { + tex_aux_start_show_break_node(callback_id, pass); + } + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + { + halfword initial = tex_new_node(unhyphenated_node, (quarterword) decent_fit); + node_next(initial) = active_head; + active_break_node(initial) = null; + active_line_number(initial) = cur_list.prev_graf + 1; + active_total_demerits(initial) = 0; // default + active_short(initial) = 0; // default + active_glue(initial) = 0; // default + // active_reserved(initial) = 0; // default + node_next(active_head) = initial; + } + /*tex We now have created a cycle. */ + tex_aux_set_target_to_source(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.background); + lmt_linebreak_state.passive = null; + lmt_linebreak_state.printed_node = temp_head; + lmt_linebreak_state.pass_number = 0; + lmt_print_state.font_in_short_display = null_font; + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + /* lmt_linebreak_state.auto_breaking = 1; */ /* gone */ + // cur_p = node_next(temp_head); + /*tex Initialize with first (or current) |par| node. */ + if (cur_p && node_type(cur_p) == par_node) { + node_prev(cur_p) = temp_head; + lmt_linebreak_state.internal_penalty_interline = tex_get_local_interline_penalty(cur_p); + lmt_linebreak_state.internal_penalty_broken = tex_get_local_broken_penalty(cur_p); + lmt_linebreak_state.init_internal_left_box = par_box_left(cur_p); + lmt_linebreak_state.init_internal_left_box_width = tex_get_local_left_width(cur_p); + lmt_linebreak_state.internal_right_box = par_box_right(cur_p); + lmt_linebreak_state.internal_right_box_width = tex_get_local_right_width(cur_p); + lmt_linebreak_state.internal_middle_box = par_box_middle(cur_p); + } else { + lmt_linebreak_state.internal_penalty_interline = 0; + lmt_linebreak_state.internal_penalty_broken = 0; + lmt_linebreak_state.init_internal_left_box = null; + lmt_linebreak_state.init_internal_left_box_width = 0; + lmt_linebreak_state.internal_right_box = null; + lmt_linebreak_state.internal_right_box_width = 0; + lmt_linebreak_state.internal_middle_box = null; + } + lmt_linebreak_state.internal_left_box = lmt_linebreak_state.init_internal_left_box; + lmt_linebreak_state.internal_left_box_width = lmt_linebreak_state.init_internal_left_box_width; + lmt_packaging_state.previous_char_ptr = null; + // first_p = cur_p; + /*tex - To access the first node of paragraph as the first active node has |break_node = null|. + To access the first node of paragraph as the first active node has |break_node = null|. - Determine legal breaks: As we move through the hlist, we need to keep the |active_width| - array up to date, so that the badness of individual lines is readily calculated by - |try_break|. It is convenient to use the short name |active_width [1]| for the component - of active width that represents real width as opposed to glue. + Determine legal breaks: As we move through the hlist, we need to keep the |active_width| + array up to date, so that the badness of individual lines is readily calculated by + |try_break|. It is convenient to use the short name |active_width [1]| for the component + of active width that represents real width as opposed to glue. - Advance |cur_p| to the node following the present string of characters. The code that - passes over the characters of words in a paragraph is part of \TEX's inner loop, so it - has been streamlined for speed. We use the fact that |\parfillskip| glue appears at the - end of each paragraph; it is therefore unnecessary to check if |vlink (cur_p) = null| - when |cur_p| is a character node. + Advance |cur_p| to the node following the present string of characters. The code that + passes over the characters of words in a paragraph is part of \TEX's inner loop, so it + has been streamlined for speed. We use the fact that |\parfillskip| glue appears at the + end of each paragraph; it is therefore unnecessary to check if |vlink (cur_p) = null| + when |cur_p| is a character node. - */ - while (cur_p && (node_next(active_head) != active_head)) { /* we check the cycle */ - switch (node_type(cur_p)) { - case glyph_node: - lmt_linebreak_state.active_width[total_glue_amount] += tex_glyph_width_ex(cur_p); - if (properties->adjust_spacing && tex_aux_check_expand_pars(properties->adjust_spacing_step, glyph_font(cur_p))) { - lmt_packaging_state.previous_char_ptr = cur_p; - lmt_linebreak_state.active_width[font_stretch_amount] += tex_char_stretch(cur_p); - lmt_linebreak_state.active_width[font_shrink_amount] += tex_char_shrink(cur_p); - } - break; - case hlist_node: - case vlist_node: - lmt_linebreak_state.active_width[total_glue_amount] += box_width(cur_p); - break; - case rule_node: - lmt_linebreak_state.active_width[total_glue_amount] += rule_width(cur_p); - break; - case dir_node: - /*tex Adjust the dir stack for the |line_break| routine. */ - line_break_dir = tex_update_dir_state(cur_p, properties->paragraph_dir); - break; - case par_node: - /*tex Advance past a |par| node. */ - lmt_linebreak_state.internal_penalty_interline = tex_get_local_interline_penalty(cur_p); - lmt_linebreak_state.internal_penalty_broken = tex_get_local_broken_penalty(cur_p); - lmt_linebreak_state.internal_left_box = par_box_left(cur_p); - lmt_linebreak_state.internal_left_box_width = tex_get_local_left_width(cur_p); - lmt_linebreak_state.internal_right_box = par_box_right(cur_p); - lmt_linebreak_state.internal_right_box_width = tex_get_local_right_width(cur_p); - lmt_linebreak_state.internal_middle_box = par_box_middle(cur_p); - break; - case glue_node: - /*tex + */ + while (cur_p && (node_next(active_head) != active_head)) { /* we check the cycle */ + switch (node_type(cur_p)) { + case glyph_node: + /* why ex here and not in add/sub disc glyphs */ + lmt_linebreak_state.active_width[total_glue_amount] += tex_glyph_width_ex(cur_p); // ex + if (properties->adjust_spacing && tex_aux_check_expand_pars(properties->adjust_spacing_step, glyph_font(cur_p))) { + lmt_packaging_state.previous_char_ptr = cur_p; + lmt_linebreak_state.active_width[font_stretch_amount] += tex_char_stretch(cur_p); + lmt_linebreak_state.active_width[font_shrink_amount] += tex_char_shrink(cur_p); + } + break; + case hlist_node: + case vlist_node: + lmt_linebreak_state.active_width[total_glue_amount] += box_width(cur_p); + break; + case rule_node: + lmt_linebreak_state.active_width[total_glue_amount] += rule_width(cur_p); + break; + case dir_node: + /*tex Adjust the dir stack for the |line_break| routine. */ + line_break_dir = tex_update_dir_state(cur_p, properties->paragraph_dir); + break; + case par_node: + /*tex Advance past a |par| node. */ + lmt_linebreak_state.internal_penalty_interline = tex_get_local_interline_penalty(cur_p); + lmt_linebreak_state.internal_penalty_broken = tex_get_local_broken_penalty(cur_p); + lmt_linebreak_state.internal_left_box = par_box_left(cur_p); + lmt_linebreak_state.internal_left_box_width = tex_get_local_left_width(cur_p); + lmt_linebreak_state.internal_right_box = par_box_right(cur_p); + lmt_linebreak_state.internal_right_box_width = tex_get_local_right_width(cur_p); + lmt_linebreak_state.internal_middle_box = par_box_middle(cur_p); + break; + case glue_node: + /*tex - If node |cur_p| is a legal breakpoint, call |try_break|; then update the - active widths by including the glue in |glue_ptr(cur_p)|. + If node |cur_p| is a legal breakpoint, call |try_break|; then update the + active widths by including the glue in |glue_ptr(cur_p)|. - When node |cur_p| is a glue node, we look at the previous to see whether or - not a breakpoint is legal at |cur_p|, as explained above. + When node |cur_p| is a glue node, we look at the previous to see whether + or not a breakpoint is legal at |cur_p|, as explained above. - We only break after certain nodes (see texnodes.h), a font related kern and - a dir node when |\breakafterdirmode = 1|. + We only break after certain nodes (see texnodes.h), a font related kern + and a dir node when |\breakafterdirmode = 1|. - */ - if (tex_has_glue_option(cur_p, glue_option_no_auto_break)) { - /*tex Glue in math is not a valid breakpoint. */ - } else if (tex_is_par_init_glue(cur_p)) { - /*tex Of course we don't break here. */ - } else if (tex_aux_valid_glue_break(cur_p)) { - tex_aux_try_break(properties, 0, unhyphenated_node, first_p, cur_p); - } - lmt_linebreak_state.active_width[total_glue_amount] += glue_amount(cur_p); - lmt_linebreak_state.active_width[2 + glue_stretch_order(cur_p)] += glue_stretch(cur_p); - lmt_linebreak_state.active_width[total_shrink_amount] += tex_aux_checked_shrink(cur_p); - break; - case kern_node: - switch (node_subtype(cur_p)) { - case explicit_kern_subtype: - case italic_kern_subtype: - { - /* there used to a ! is_char_node(node_next(cur_p)) test */ - halfword nxt = node_next(cur_p); - if (nxt && node_type(nxt) == glue_node && ! tex_has_glue_option(nxt, glue_option_no_auto_break)) { - tex_aux_try_break(properties, 0, unhyphenated_node, first_p, cur_p); + */ + if (tex_has_glue_option(cur_p, glue_option_no_auto_break)) { + /*tex Glue in math is not a valid breakpoint, unless we permit it. */ + } else if (tex_is_par_init_glue(cur_p)) { + /*tex Of course we don't break here. */ + } else if (tex_aux_valid_glue_break(cur_p)) { + tex_aux_try_break(properties, tex_aux_upcoming_penalty(cur_p), unhyphenated_node, first_p, cur_p, callback_id, pass); + } + lmt_linebreak_state.active_width[total_glue_amount] += glue_amount(cur_p); + lmt_linebreak_state.active_width[total_stretch_amount + glue_stretch_order(cur_p)] += glue_stretch(cur_p); + lmt_linebreak_state.active_width[total_shrink_amount] += tex_aux_checked_shrink(cur_p); + break; + case kern_node: + switch (node_subtype(cur_p)) { + case explicit_kern_subtype: + case italic_kern_subtype: + { + /* there used to a ! is_char_node(node_next(cur_p)) test */ + halfword nxt = node_next(cur_p); + if (nxt && node_type(nxt) == glue_node && ! tex_has_glue_option(nxt, glue_option_no_auto_break)) { + tex_aux_try_break(properties, 0, unhyphenated_node, first_p, cur_p, callback_id, pass); + } } - } - break; - case font_kern_subtype: - if (properties->adjust_spacing == adjust_spacing_full) { - lmt_linebreak_state.active_width[font_stretch_amount] += tex_kern_stretch(cur_p); - lmt_linebreak_state.active_width[font_shrink_amount] += tex_kern_shrink(cur_p); - } - break; - } - lmt_linebreak_state.active_width[total_glue_amount] += kern_amount(cur_p); - break; - case disc_node: - /*tex - - Try to break after a discretionary fragment, then |goto done5|. The - following code knows that discretionary texts contain only character - nodes, kern nodes, box nodes, and rule nodes. This branch differs a bit - from older engines because in \LUATEX\ we already have hyphenated the list. - This means that we need to skip automatic disc nodes. Or better, we need - to treat discretionaries and explicit hyphens always, even in the first - pass. - - We used to have |init_disc| followed by |select disc| variants where the - |select_disc|s were handled by the leading |init_disc|. The question is: should - we bother about select nodes? Knuth indicates in the original source that only - a very few cases need hyphenation so the exceptional case of >2 char ligatures - having hyphenation points in between is rare. We'd better have proper compound - word handling. Keep in mind that these (old) init and select subtypes always - came in isolated pairs and that they only were meant for the simple (enforced) - hyphenation discretionaries. - - Therefore, this feature has been dropped from \LUAMETATEX. It not only makes - the code simpler, it also avoids having code on board for border cases that - even when dealt with are suboptimal. It's better to have nothing that something - fuzzy. It also makes dealing with (intermediate) node lists easier. If I want - something like this it should be okay for any situation. - - */ - if (force_check_hyphenation || lmt_linebreak_state.second_pass || (node_subtype(cur_p) != syllable_discretionary_code)) { - halfword actual_penalty = disc_penalty(cur_p); - halfword s = disc_pre_break_head(cur_p); - tex_aux_reset_disc_target(properties->adjust_spacing, lmt_linebreak_state.disc_width); - if (s) { - tex_aux_add_to_widths(s, properties->adjust_spacing, properties->adjust_spacing_step, lmt_linebreak_state.disc_width); - tex_aux_add_disc_source_to_target(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.disc_width); - tex_aux_try_break(properties, actual_penalty, hyphenated_node, first_p, cur_p); - tex_aux_sub_disc_target_from_source(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.disc_width); - } else { - /*tex trivial pre-break */ - tex_aux_try_break(properties, actual_penalty, hyphenated_node, first_p, cur_p); + break; + case font_kern_subtype: + if (properties->adjust_spacing == adjust_spacing_full) { + lmt_linebreak_state.active_width[font_stretch_amount] += tex_kern_stretch(cur_p); + lmt_linebreak_state.active_width[font_shrink_amount] += tex_kern_shrink(cur_p); + } + break; } - } - tex_aux_add_to_widths(disc_no_break_head(cur_p), properties->adjust_spacing, properties->adjust_spacing_step, lmt_linebreak_state.active_width); - break; - case penalty_node: - tex_aux_try_break(properties, penalty_amount(cur_p), unhyphenated_node, first_p, cur_p); - break; - case math_node: - { - /* there used to a ! is_char_node(node_next(cur_p)) test */ - int finishing = node_subtype(cur_p) == end_inline_math; - // lmt_linebreak_state.auto_breaking = finishing; - if (tex_math_glue_is_zero(cur_p) || tex_ignore_math_skip(cur_p)) { - /*tex - When we end up here we assume |\mathsurround| but we only check for - a break when we're ending math. Maybe this is something we need to - open up. The math specific penalty only kicks in when we break. - */ - if (finishing && node_type(node_next(cur_p)) == glue_node) { - tex_aux_try_break(properties, math_penalty(cur_p), unhyphenated_node, first_p, cur_p); + lmt_linebreak_state.active_width[total_glue_amount] += kern_amount(cur_p); + break; + case disc_node: + /*tex + + Try to break after a discretionary fragment, then |goto done5|. The + following code knows that discretionary texts contain only character + nodes, kern nodes, box nodes, and rule nodes. This branch differs a bit + from older engines because in \LUATEX\ we already have hyphenated the list. + This means that we need to skip automatic disc nodes. Or better, we need + to treat discretionaries and explicit hyphens always, even in the first + pass. + + We used to have |init_disc| followed by |select disc| variants where the + |select_disc|s were handled by the leading |init_disc|. The question is: should + we bother about select nodes? Knuth indicates in the original source that only + a very few cases need hyphenation so the exceptional case of >2 char ligatures + having hyphenation points in between is rare. We'd better have proper compound + word handling. Keep in mind that these (old) init and select subtypes always + came in isolated pairs and that they only were meant for the simple (enforced) + hyphenation discretionaries. + + Therefore, this feature has been dropped from \LUAMETATEX. It not only makes + the code simpler, it also avoids having code on board for border cases that + even when dealt with are suboptimal. It's better to have nothing that something + fuzzy. It also makes dealing with (intermediate) node lists easier. If I want + something like this it should be okay for any situation. + + */ + if (force_check_hyphenation || lmt_linebreak_state.second_pass || (node_subtype(cur_p) != syllable_discretionary_code)) { + halfword actual_penalty = disc_penalty(cur_p); + halfword pre = disc_pre_break_head(cur_p); + tex_aux_reset_disc_target(properties->adjust_spacing, lmt_linebreak_state.disc_width); + if (pre) { + tex_aux_add_to_widths(pre, properties->adjust_spacing, properties->adjust_spacing_step, lmt_linebreak_state.disc_width); + tex_aux_add_disc_source_to_target(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.disc_width); + tex_aux_try_break(properties, actual_penalty, hyphenated_node, first_p, cur_p, callback_id, pass); + tex_aux_sub_disc_target_from_source(properties->adjust_spacing, lmt_linebreak_state.active_width, lmt_linebreak_state.disc_width); + } else { + /*tex trivial pre-break */ + tex_aux_try_break(properties, actual_penalty, hyphenated_node, first_p, cur_p, callback_id, pass); } - lmt_linebreak_state.active_width[total_glue_amount] += math_surround(cur_p); - } else { - /*tex - This one does quite some testing, is that still needed? - */ - if (finishing && tex_aux_valid_glue_break(cur_p)) { - tex_aux_try_break(properties, math_penalty(cur_p), unhyphenated_node, first_p, cur_p); + } + tex_aux_add_to_widths(disc_no_break_head(cur_p), properties->adjust_spacing, properties->adjust_spacing_step, lmt_linebreak_state.active_width); + break; + case penalty_node: + tex_aux_try_break(properties, penalty_amount(cur_p), unhyphenated_node, first_p, cur_p, callback_id, pass); + break; + case math_node: + { + /* there used to a ! is_char_node(node_next(cur_p)) test */ + int finishing = node_subtype(cur_p) == end_inline_math; + // lmt_linebreak_state.auto_breaking = finishing; + if (tex_math_glue_is_zero(cur_p) || tex_ignore_math_skip(cur_p)) { + /*tex + When we end up here we assume |\mathsurround| but we only check for + a break when we're ending math. Maybe this is something we need to + open up. The math specific penalty only kicks in when we break. + */ + if (finishing && node_type(node_next(cur_p)) == glue_node) { + tex_aux_try_break(properties, math_penalty(cur_p), unhyphenated_node, first_p, cur_p, callback_id, pass); + } + lmt_linebreak_state.active_width[total_glue_amount] += math_surround(cur_p); + } else { + /*tex + This one does quite some testing, is that still needed? + */ + if (finishing && tex_aux_valid_glue_break(cur_p)) { + tex_aux_try_break(properties, math_penalty(cur_p), unhyphenated_node, first_p, cur_p, callback_id, pass); + } + lmt_linebreak_state.active_width[total_glue_amount] += math_amount(cur_p); + lmt_linebreak_state.active_width[total_stretch_amount + math_stretch_order(cur_p)] += math_stretch(cur_p); + lmt_linebreak_state.active_width[total_shrink_amount] += tex_aux_checked_shrink(cur_p); } - lmt_linebreak_state.active_width[total_glue_amount] += math_amount(cur_p); - lmt_linebreak_state.active_width[2 + math_stretch_order(cur_p)] += math_stretch(cur_p); - lmt_linebreak_state.active_width[total_shrink_amount] += tex_aux_checked_shrink(cur_p); } - } - break; - case boundary_node: - case whatsit_node: - case mark_node: - case insert_node: - case adjust_node: - /*tex Advance past these nodes in the |line_break| loop. */ - break; - default: - tex_formatted_error("parbuilder", "weird node %d in paragraph", node_type(cur_p)); - } - cur_p = node_next(cur_p); - while (! cur_p && nest_index > 0) { - cur_p = nest_stack[--nest_index]; + break; + case boundary_node: + case whatsit_node: + case mark_node: + case insert_node: + case adjust_node: + /*tex Advance past these nodes in the |line_break| loop. */ + break; + default: + tex_formatted_error("parbuilder", "weird node %d in paragraph", node_type(cur_p)); + } + cur_p = node_next(cur_p); } - } - if (! cur_p) { - /*tex - - Try the final line break at the end of the paragraph, and |goto done| if the desired - breakpoints have been found. - - The forced line break at the paragraph's end will reduce the list of breakpoints so - that all active nodes represent breaks at |cur_p = null|. On the first pass, we - insist on finding an active node that has the correct \quote {looseness.} On the - final pass, there will be at least one active node, and we will match the desired - looseness as well as we can. + if (! cur_p) { + /*tex - The global variable |best_bet| will be set to the active node for the best way to - break the paragraph, and a few other variables are used to help determine what is - best. + Try the final line break at the end of the paragraph, and |goto done| if the desired + breakpoints have been found. - */ - tex_aux_try_break(properties, eject_penalty, hyphenated_node, first_p, cur_p); - if (node_next(active_head) != active_head) { - /*tex Find an active node with fewest demerits. */ - r = node_next(active_head); - lmt_linebreak_state.fewest_demerits = awful_bad; - do { - if ((node_type(r) != delta_node) && (active_total_demerits(r) < lmt_linebreak_state.fewest_demerits)) { - lmt_linebreak_state.fewest_demerits = active_total_demerits(r); - lmt_linebreak_state.best_bet = r; - } - r = node_next(r); - } while (r != active_head); - lmt_linebreak_state.best_line = active_line_number(lmt_linebreak_state.best_bet); - /*tex Find an active node with fewest demerits. */ - if (properties->looseness == 0) { - goto DONE; - } else { - /*tex + The forced line break at the paragraph's end will reduce the list of breakpoints so + that all active nodes represent breaks at |cur_p = null|. On the first pass, we + insist on finding an active node that has the correct \quote {looseness.} On the + final pass, there will be at least one active node, and we will match the desired + looseness as well as we can. - Find the best active node for the desired looseness. The adjustment for a - desired looseness is a slightly more complicated version of the loop just - considered. Note that if a paragraph is broken into segments by displayed - equations, each segment will be subject to the looseness calculation, - independently of the other segments. + The global variable |best_bet| will be set to the active node for the best way to + break the paragraph, and a few other variables are used to help determine what is + best. - */ - r = node_next(active_head); // can be local - lmt_linebreak_state.actual_looseness = 0; + */ + tex_aux_try_break(properties, eject_penalty, hyphenated_node, first_p, cur_p, callback_id, pass); + if (node_next(active_head) != active_head) { + /*tex Find an active node with fewest demerits. */ + halfword r = node_next(active_head); + lmt_linebreak_state.fewest_demerits = awful_bad; do { - if (node_type(r) != delta_node) { - lmt_linebreak_state.line_difference = active_line_number(r) - lmt_linebreak_state.best_line; - if (((lmt_linebreak_state.line_difference < lmt_linebreak_state.actual_looseness) && (properties->looseness <= lmt_linebreak_state.line_difference)) - || ((lmt_linebreak_state.line_difference > lmt_linebreak_state.actual_looseness) && (properties->looseness >= lmt_linebreak_state.line_difference))) { - lmt_linebreak_state.best_bet = r; - lmt_linebreak_state.actual_looseness = lmt_linebreak_state.line_difference; - lmt_linebreak_state.fewest_demerits = active_total_demerits(r); - } else if ((lmt_linebreak_state.line_difference == lmt_linebreak_state.actual_looseness) && (active_total_demerits(r) < lmt_linebreak_state.fewest_demerits)) { - lmt_linebreak_state.best_bet = r; - lmt_linebreak_state.fewest_demerits = active_total_demerits(r); - } + if ((node_type(r) != delta_node) && (active_total_demerits(r) < lmt_linebreak_state.fewest_demerits)) { + lmt_linebreak_state.fewest_demerits = active_total_demerits(r); + lmt_linebreak_state.best_bet = r; } r = node_next(r); } while (r != active_head); lmt_linebreak_state.best_line = active_line_number(lmt_linebreak_state.best_bet); - /*tex - Find the best active node for the desired looseness. - */ - if ((lmt_linebreak_state.actual_looseness == properties->looseness) || lmt_linebreak_state.final_pass) { + /*tex Find an active node with fewest demerits. */ + if (properties->looseness == 0) { goto DONE; + } else { + /*tex + + Find the best active node for the desired looseness. The adjustment for a + desired looseness is a slightly more complicated version of the loop just + considered. Note that if a paragraph is broken into segments by displayed + equations, each segment will be subject to the looseness calculation, + independently of the other segments. + + */ + r = node_next(active_head); // can be local + lmt_linebreak_state.actual_looseness = 0; + do { + if (node_type(r) != delta_node) { + lmt_linebreak_state.line_difference = active_line_number(r) - lmt_linebreak_state.best_line; + if (((lmt_linebreak_state.line_difference < lmt_linebreak_state.actual_looseness) && (properties->looseness <= lmt_linebreak_state.line_difference)) + || ((lmt_linebreak_state.line_difference > lmt_linebreak_state.actual_looseness) && (properties->looseness >= lmt_linebreak_state.line_difference))) { + lmt_linebreak_state.best_bet = r; + lmt_linebreak_state.actual_looseness = lmt_linebreak_state.line_difference; + lmt_linebreak_state.fewest_demerits = active_total_demerits(r); + } else if ((lmt_linebreak_state.line_difference == lmt_linebreak_state.actual_looseness) && (active_total_demerits(r) < lmt_linebreak_state.fewest_demerits)) { + lmt_linebreak_state.best_bet = r; + lmt_linebreak_state.fewest_demerits = active_total_demerits(r); + } + } + r = node_next(r); + } while (r != active_head); + lmt_linebreak_state.best_line = active_line_number(lmt_linebreak_state.best_bet); + /*tex + Find the best active node for the desired looseness. + */ + if ((lmt_linebreak_state.actual_looseness == properties->looseness) || lmt_linebreak_state.final_pass) { + goto DONE; + } } } + } else { + /*tex So we have cycled: |node_next(active_head) == active_head|. */ + } + /*tex Clean up the memory by removing the break nodes. */ + cur_p = tex_aux_clean_up_the_memory(cur_p); + if (! lmt_linebreak_state.second_pass) { + if (properties->tracing_paragraphs > 0) { + tex_print_format("%l[linebreak: second pass]"); /* @secondpass */; + } + lmt_linebreak_state.threshold = properties->tolerance; + lmt_linebreak_state.second_pass = 1; + lmt_linebreak_state.final_pass = (properties->emergency_stretch <= 0); + } else { + /*tex If at first you do not succeed, then: */ + if (properties->tracing_paragraphs > 0) { + tex_print_format("%l[linebreak: emergency pass]"); /* @emergencypass */ + } + lmt_linebreak_state.background[total_stretch_amount] += properties->emergency_stretch; + lmt_linebreak_state.final_pass = 1; } - } - /*tex Clean up the memory by removing the break nodes. */ - cur_p = tex_aux_clean_up_the_memory(cur_p); - if (! lmt_linebreak_state.second_pass) { - if (properties->tracing_paragraphs > 0) { - tex_print_str("%l[linebreak: second pass]"); /* @secondpass */; + if (callback_id) { + tex_aux_stop_show_break_node(callback_id); } - lmt_linebreak_state.threshold = properties->tolerance; - lmt_linebreak_state.second_pass = 1; - lmt_linebreak_state.final_pass = (properties->emergency_stretch <= 0); - } else { - /*tex If at first you do not succeed, then: */ - if (properties->tracing_paragraphs > 0) { - tex_print_str("%l[linebreak: emergency pass]"); /* @emergencypass */ + } + DONE: + if (properties->tracing_paragraphs > 0) { + tex_end_diagnostic(); + /*tex + This is a bit weird, as only here: |normalize_selector()| while we have diagnostics + all over the place. + */ + } + if (lmt_linebreak_state.do_last_line_fit) { + /*tex + Adjust the final line of the paragraph; here we either reset |do_last_line_fit| or + adjust the |par_fill_skip| glue. + */ + if (active_short(lmt_linebreak_state.best_bet) == 0) { + lmt_linebreak_state.do_last_line_fit = 0; + } else { + glue_amount(lmt_linebreak_state.last_line_fill) += (active_short(lmt_linebreak_state.best_bet) - active_glue(lmt_linebreak_state.best_bet)); + glue_stretch(lmt_linebreak_state.last_line_fill) = 0; } - lmt_linebreak_state.background[total_stretch_amount] += properties->emergency_stretch; - lmt_linebreak_state.final_pass = 1; } - } - DONE: - if (properties->tracing_paragraphs > 0) { - tex_end_diagnostic(); /*tex - This is a bit weird, as only here: |normalize_selector()| while we have diagnostics - all over the place. + Break the paragraph at the chosen. Once the best sequence of breakpoints has been found + (hurray), we call on the procedure |post_line_break| to finish the remainder of the work. + By introducing this subprocedure, we are able to keep |line_break| from getting extremely + long. The first thing |ext_post_line_break| does is reset |dir_ptr|. */ + tex_flush_node_list(lmt_linebreak_state.dir_ptr); + lmt_linebreak_state.dir_ptr = null; + /*tex Here we still have a temp node as head. */ + tex_aux_post_line_break(properties, line_break_dir, callback_id); + /*tex Clean up memory by removing the break nodes (maybe: |tex_flush_node_list(cur_p);|). */ + tex_aux_clean_up_the_memory(cur_p); } - if (lmt_linebreak_state.do_last_line_fit) { - /*tex - Adjust the final line of the paragraph; here we either reset |do_last_line_fit| or - adjust the |par_fill_skip| glue. - */ - if (active_short(lmt_linebreak_state.best_bet) == 0) { - lmt_linebreak_state.do_last_line_fit = 0; - } else { - glue_amount(lmt_linebreak_state.last_line_fill) += (active_short(lmt_linebreak_state.best_bet) - active_glue(lmt_linebreak_state.best_bet)); - glue_stretch(lmt_linebreak_state.last_line_fill) = 0; - } + if (callback_id) { + tex_aux_wrapup_show_break_node(callback_id); } - /*tex - Break the paragraph at the chosen. Once the best sequence of breakpoints has been found - (hurray), we call on the procedure |post_line_break| to finish the remainder of the work. - By introducing this subprocedure, we are able to keep |line_break| from getting extremely - long. The first thing |ext_post_line_break| does is reset |dir_ptr|. - */ - tex_flush_node_list(lmt_linebreak_state.dir_ptr); - lmt_linebreak_state.dir_ptr = null; - /*tex Here we still have a temp node as head. */ - tex_aux_post_line_break(properties, line_break_dir); - /*tex Clean up the memory by removing the break nodes. */ - cur_p = tex_aux_clean_up_the_memory(cur_p); } void tex_get_linebreak_info(int *f, int *a) @@ -2698,7 +2855,7 @@ static void tex_aux_trace_penalty(const char *what, int line, int index, halfwor } } -static void tex_aux_post_line_break(const line_break_properties *properties, halfword line_break_dir) +static void tex_aux_post_line_break(const line_break_properties *properties, halfword line_break_dir, int callback_id) { /*tex temporary registers for list manipulation */ halfword q, r; @@ -2733,6 +2890,9 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal and having |next_break| fields. Node |r| is the passive node being moved from stack to stack. */ + if (callback_id) { + tex_aux_collect_show_break_node(callback_id); + } q = active_break_node(lmt_linebreak_state.best_bet); do { r = q; @@ -2740,6 +2900,13 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal passive_next_break(r) = cur_p; cur_p = r; } while (q); + if (callback_id) { + halfword p = cur_p; + while (p) { + tex_aux_list_break_node(p, callback_id); + p = passive_next_break(p); + } + } /*tex prevgraf + 1 */ cur_line = cur_list.prev_graf + 1; do { @@ -3310,6 +3477,9 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal } /*tex Call the packaging subroutine, setting |just_box| to the justified box. */ node_subtype(lmt_linebreak_state.just_box) = line_list; + if (callback_id) { + tex_aux_line_show_break_node(callback_id); + } /*tex Pending content (callback). */ if (node_next(contribute_head)) { if (! lmt_page_builder_state.output_active) { @@ -3504,6 +3674,10 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal while (1) { q = node_next(r); if (node_type(q) == math_node) { + if (node_subtype(q) == begin_inline_math) { + /*tex We keep it for tracing. */ + break; + } /*tex begin mathskip code */ math_surround(q) = 0 ; tex_reset_math_glue_to_zero(q); @@ -3517,7 +3691,7 @@ static void tex_aux_post_line_break(const line_break_properties *properties, hal /*tex Keep it. Can be tricky after a |\break| with no follow up (loops). */ break; } else if (node_type(q) == par_node && node_subtype(q) == local_box_par_subtype) { - /*tex weird, in the middle somewhere .. these local penalties do this */ + /*tex Weird, in the middle somewhere .. these local penalties do this. */ break; /* if not we leak, so maybe this needs more testing */ } else if (non_discardable(q)) { break; |