summaryrefslogtreecommitdiff
path: root/source/luametatex/source/tex/texlinebreak.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/tex/texlinebreak.c')
-rw-r--r--source/luametatex/source/tex/texlinebreak.c1310
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;