diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/linebreak.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/linebreak.c | 3220 |
1 files changed, 1646 insertions, 1574 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c index 910d26b9f27..768d1999af8 100644 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c @@ -1,4 +1,4 @@ -/* $Id: linebreak.c 1102 2008-03-11 09:52:54Z taco $ */ +/* $Id: linebreak.c 1168 2008-04-15 13:43:34Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -12,29 +12,31 @@ prevents this error message from appearing more than once per paragraph. */ -#define check_shrinkage(a) \ - if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ +#define check_shrinkage(a) \ + if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ a=finite_shrink((a)) -static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage?*/ - -halfword -finite_shrink(halfword p) { /* recovers from infinite shrinkage */ - halfword q; /*new glue specification*/ - char *hlp[] = { - "The paragraph just ended includes some glue that has", - "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", - "Such glue doesn't belong there---it allows a paragraph", - "of any length to fit on one line. But it's safe to proceed,", - "since the offensive shrinkability has been made finite.", - NULL }; - if (no_shrink_error_yet) { - no_shrink_error_yet=false; - tex_error("Infinite glue shrinkage found in a paragraph",hlp); - } - q=new_spec(p); shrink_order(q)=normal; - delete_glue_ref(p); - return q; +static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage? */ + +halfword finite_shrink(halfword p) +{ /* recovers from infinite shrinkage */ + halfword q; /*new glue specification */ + char *hlp[] = { + "The paragraph just ended includes some glue that has", + "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", + "Such glue doesn't belong there---it allows a paragraph", + "of any length to fit on one line. But it's safe to proceed,", + "since the offensive shrinkability has been made finite.", + NULL + }; + if (no_shrink_error_yet) { + no_shrink_error_yet = false; + tex_error("Infinite glue shrinkage found in a paragraph", hlp); + } + q = new_spec(p); + shrink_order(q) = normal; + delete_glue_ref(p); + return q; } /* A pointer variable |cur_p| runs through the given horizontal list as we look @@ -60,168 +62,177 @@ finite_shrink(halfword p) { /* recovers from infinite shrinkage */ stretchability and set |final_pass=true|. */ -static boolean second_pass; /* is this our second attempt to break this paragraph? */ -static boolean final_pass; /*is this our final attempt to break this paragraph? */ -static integer threshold; /* maximum badness on feasible lines */ +static boolean second_pass; /* is this our second attempt to break this paragraph? */ +static boolean final_pass; /*is this our final attempt to break this paragraph? */ +static integer threshold; /* maximum badness on feasible lines */ /* skipable nodes at the margins during character protrusion */ -#define cp_skipable(a) ((! is_char_node((a))) && \ - ((type((a)) == ins_node) \ - || (type((a)) == mark_node) \ - || (type((a)) == adjust_node) \ - || (type((a)) == penalty_node) \ - || ((type((a)) == whatsit_node) && \ - (subtype((a)) != pdf_refximage_node) && \ - (subtype((a)) != pdf_refxform_node)) \ - /* reference to an image or XObject form */ \ - || ((type((a)) == disc_node) && \ - (vlink_pre_break(a) == null) && \ - (vlink_post_break(a) == null) && \ - (vlink_no_break(a) == null)) \ - /* an empty |disc_node| */ \ - || ((type((a)) == math_node) && \ - (surround((a)) == 0)) \ - || ((type((a)) == kern_node) && \ - ((width((a)) == 0) || \ - (subtype((a)) == normal))) \ - || ((type((a)) == glue_node) && \ - (glue_ptr((a)) == zero_glue)) \ - || ((type((a)) == hlist_node) && \ - (width((a)) == 0) && \ - (height((a)) == 0) && \ - (depth((a)) == 0) && \ - (list_ptr((a)) == null)) \ - )) +#define cp_skipable(a) ((! is_char_node((a))) && \ + ((type((a)) == ins_node) \ + || (type((a)) == mark_node) \ + || (type((a)) == adjust_node) \ + || (type((a)) == penalty_node) \ + || ((type((a)) == whatsit_node) && \ + (subtype((a)) != pdf_refximage_node) && \ + (subtype((a)) != pdf_refxform_node)) \ + /* reference to an image or XObject form */ \ + || ((type((a)) == disc_node) && \ + (vlink_pre_break(a) == null) && \ + (vlink_post_break(a) == null) && \ + (vlink_no_break(a) == null)) \ + /* an empty |disc_node| */ \ + || ((type((a)) == math_node) && \ + (surround((a)) == 0)) \ + || ((type((a)) == kern_node) && \ + ((width((a)) == 0) || \ + (subtype((a)) == normal))) \ + || ((type((a)) == glue_node) && \ + (glue_ptr((a)) == zero_glue)) \ + || ((type((a)) == hlist_node) && \ + (width((a)) == 0) && \ + (height((a)) == 0) && \ + (depth((a)) == 0) && \ + (list_ptr((a)) == null)) \ + )) /* maximum fill level for |hlist_stack|*/ -#define max_hlist_stack 512 /* maybe good if larger than |2 * - max_quarterword|, so that box nesting - level would overflow first */ +#define max_hlist_stack 512 /* maybe good if larger than |2 * + max_quarterword|, so that box nesting + level would overflow first */ /* stack for |find_protchar_left()| and |find_protchar_right()| */ -static halfword hlist_stack[max_hlist_stack]; +static halfword hlist_stack[max_hlist_stack]; /* fill level for |hlist_stack| */ -static short hlist_stack_level = 0; +static short hlist_stack_level = 0; -void push_node(halfword p) { - if (hlist_stack_level >= max_hlist_stack) - pdf_error("push_node", "stack overflow"); - hlist_stack[hlist_stack_level++] = p; +void push_node(halfword p) +{ + if (hlist_stack_level >= max_hlist_stack) + pdf_error(maketexstring("push_node"), maketexstring("stack overflow")); + hlist_stack[hlist_stack_level++] = p; } -halfword pop_node (void) { - if (hlist_stack_level <= 0) /* would point to some bug */ - pdf_error("pop_node", "stack underflow (internal error)"); - return hlist_stack[--hlist_stack_level]; +halfword pop_node(void) +{ + if (hlist_stack_level <= 0) /* would point to some bug */ + pdf_error(maketexstring("pop_node"), + maketexstring("stack underflow (internal error)")); + return hlist_stack[--hlist_stack_level]; } #define null_font 0 -static integer max_stretch_ratio=0; /*maximal stretch ratio of expanded fonts*/ -static integer max_shrink_ratio=0; /*maximal shrink ratio of expanded fonts*/ -static integer cur_font_step=0; /*the current step of expanded fonts*/ - - -boolean check_expand_pars(internal_font_number f) { - internal_font_number k; - - if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) && - (pdf_font_shrink(f) == null_font))) - return false; - if (cur_font_step < 0) - cur_font_step = pdf_font_step(f); - else if (cur_font_step != pdf_font_step(f)) - pdf_error("font expansion", - "using fonts with different step of expansion in one paragraph is not allowed"); - k = pdf_font_stretch(f); - if( k != null_font) { - if (max_stretch_ratio < 0) - max_stretch_ratio = pdf_font_expand_ratio(k); - else if (max_stretch_ratio != pdf_font_expand_ratio(k)) - pdf_error("font expansion", - "using fonts with different limit of expansion in one paragraph is not allowed"); - } - k = pdf_font_shrink(f); - if( k != null_font) { - if (max_shrink_ratio < 0) - max_shrink_ratio = -pdf_font_expand_ratio(k); - else if (max_shrink_ratio != -pdf_font_expand_ratio(k)) - pdf_error("font expansion", - "using fonts with different limit of expansion in one paragraph is not allowed"); - } - return true; +static integer max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */ +static integer max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */ +static integer cur_font_step = 0; /*the current step of expanded fonts */ + + +boolean check_expand_pars(internal_font_number f) +{ + internal_font_number k; + + if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) && + (pdf_font_shrink(f) == null_font))) + return false; + if (cur_font_step < 0) + cur_font_step = pdf_font_step(f); + else if (cur_font_step != pdf_font_step(f)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different step of expansion in one paragraph is not allowed")); + k = pdf_font_stretch(f); + if (k != null_font) { + if (max_stretch_ratio < 0) + max_stretch_ratio = pdf_font_expand_ratio(k); + else if (max_stretch_ratio != pdf_font_expand_ratio(k)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different limit of expansion in one paragraph is not allowed")); + } + k = pdf_font_shrink(f); + if (k != null_font) { + if (max_shrink_ratio < 0) + max_shrink_ratio = -pdf_font_expand_ratio(k); + else if (max_shrink_ratio != -pdf_font_expand_ratio(k)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different limit of expansion in one paragraph is not allowed")); + } + return true; } /* searches left to right from list head |l|, returns 1st non-skipable item*/ -/*public*/ halfword find_protchar_left(halfword l, boolean d) { - halfword t; - boolean run; - if ((vlink(l) != null) && (type(l) == hlist_node) && (width(l) == 0) - && (height(l) == 0) && (depth(l) == 0) && (list_ptr(l) == null)) { - l = vlink(l); /*for paragraph start with \.{\\parindent = 0pt*/ - } else if (d) { - while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { - l = vlink(l); /* std.\ discardables at line break, \TeX book, p 95 */ - } - } - hlist_stack_level = 0; - run = true; - do { - t = l; - while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { - push_node(l); - l = list_ptr(l); - } - while (run && cp_skipable(l)) { - while ((vlink(l) == null) && (hlist_stack_level > 0)) { - l = pop_node(); /* don't visit this node again */ - } - if (vlink(l) != null) - l = vlink(l); - else if (hlist_stack_level == 0) - run = false; +/*public*/ halfword find_protchar_left(halfword l, boolean d) +{ + halfword t; + boolean run; + if ((vlink(l) != null) && (type(l) == hlist_node) && (width(l) == 0) + && (height(l) == 0) && (depth(l) == 0) && (list_ptr(l) == null)) { + l = vlink(l); /*for paragraph start with \.{\\parindent = 0pt */ + } else if (d) { + while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { + l = vlink(l); /* std.\ discardables at line break, \TeX book, p 95 */ + } } - } while (t != l); - return l; + hlist_stack_level = 0; + run = true; + do { + t = l; + while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { + push_node(l); + l = list_ptr(l); + } + while (run && cp_skipable(l)) { + while ((vlink(l) == null) && (hlist_stack_level > 0)) { + l = pop_node(); /* don't visit this node again */ + } + if (vlink(l) != null) + l = vlink(l); + else if (hlist_stack_level == 0) + run = false; + } + } while (t != l); + return l; } /* searches right to left from list tail |r| to head |l|, returns 1st non-skipable item */ -/*public*/ halfword find_protchar_right(halfword l, halfword r) { - halfword t; - boolean run; - if (r == null) - return null; - hlist_stack_level = 0; - run = true; - do { - t = r; - while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { - push_node(l); - push_node(r); - l = list_ptr(r); - r = l; - while (vlink(r) != null) { - halfword s = r; - r = vlink(r); - alink(r) = s; - } - } - while (run && cp_skipable(r)) { - while ((r == l) && (hlist_stack_level > 0)) { - r = pop_node(); /* don't visit this node again */ - l = pop_node(); - } - if ((r != l) && (r != null) ) { - assert(vlink(alink(r))==r); - r = alink(r); - } else if ((r == l) && (hlist_stack_level == 0)) - run = false; - } - } while (t != r); - return r; +/*public*/ halfword find_protchar_right(halfword l, halfword r) +{ + halfword t; + boolean run; + if (r == null) + return null; + hlist_stack_level = 0; + run = true; + do { + t = r; + while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { + push_node(l); + push_node(r); + l = list_ptr(r); + r = l; + while (vlink(r) != null) { + halfword s = r; + r = vlink(r); + alink(r) = s; + } + } + while (run && cp_skipable(r)) { + while ((r == l) && (hlist_stack_level > 0)) { + r = pop_node(); /* don't visit this node again */ + l = pop_node(); + } + if ((r != l) && (r != null)) { + assert(vlink(alink(r)) == r); + r = alink(r); + } else if ((r == l) && (hlist_stack_level == 0)) + run = false; + } + } while (t != r); + return r; } #define left_pw(a) char_pw((a), left_side) @@ -232,30 +243,31 @@ boolean check_expand_pars(internal_font_number f) { |cur_break(break_node(q))| and |p| is the leftmost resp. rightmost node in the horizontal list representing the actual line */ -scaled total_pw (halfword first_p, halfword q, halfword p) { - halfword l, r; - l = (break_node(q) == null) ? first_p : cur_break(break_node(q)); - assert(vlink(alink(p))==p); - r = alink(r); - /* let's look at the right margin first */ - if ((p != null) && (type(p) == disc_node) && (vlink(pre_break(p)) != null)) { - /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ - r = vlink(pre_break(p)); - while (vlink(r) != null) - r = vlink(r); - } else { - r = find_protchar_right(l, r); - } - /* now the left margin */ - if ((l != null) && (type(l) == disc_node)) { - if (vlink(post_break(l)) != null) { - l = vlink(post_break(l)); /* protrude the first char */ - goto DONE; +scaled total_pw(halfword first_p, halfword q, halfword p) +{ + halfword l, r; + l = (break_node(q) == null) ? first_p : cur_break(break_node(q)); + assert(vlink(alink(p)) == p); + r = alink(r); + /* let's look at the right margin first */ + if ((p != null) && (type(p) == disc_node) && (vlink(pre_break(p)) != null)) { + /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + r = vlink(pre_break(p)); + while (vlink(r) != null) + r = vlink(r); + } else { + r = find_protchar_right(l, r); } - } - l = find_protchar_left(l, true); -DONE: - return (left_pw(l) + right_pw(r)); + /* now the left margin */ + if ((l != null) && (type(l) == disc_node)) { + if (vlink(post_break(l)) != null) { + l = vlink(post_break(l)); /* protrude the first char */ + goto DONE; + } + } + l = find_protchar_left(l, true); + DONE: + return (left_pw(l) + right_pw(r)); } #endif @@ -271,14 +283,14 @@ DONE: */ typedef enum { - very_loose_fit = 0, /* fitness classification for lines stretching more than - their stretchability */ - loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their - stretchability */ - decent_fit, /* fitness classification for all other lines*/ - tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their - shrinkability */ -} fitness_value ; + very_loose_fit = 0, /* fitness classification for lines stretching more than + their stretchability */ + loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their + stretchability */ + decent_fit, /* fitness classification for all other lines */ + tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their + shrinkability */ +} fitness_value; /* The algorithm essentially determines the best possible way to achieve @@ -324,11 +336,11 @@ except that nodes with |line_number>easy_line| may be in any order relative to each other. */ -void -initialize_active (void) { - type(active)=hyphenated_node; - line_number(active)=max_halfword; - subtype(active)=0; /* the |subtype| is never examined */ +void initialize_active(void) +{ + type(active) = hyphenated_node; + line_number(active) = max_halfword; + subtype(active) = 0; /* the |subtype| is never examined */ } /* The passive node for a given breakpoint contains EIGHT fields: @@ -356,9 +368,9 @@ is used to help print out the paragraph when detailed information about the line-breaking computation is being displayed. */ -static halfword passive; /* most recent node on passive list */ -static halfword printed_node; /*most recent node that has been printed*/ -static halfword pass_number; /*the number of passive nodes allocated on this pass*/ +static halfword passive; /* most recent node on passive list */ +static halfword printed_node; /*most recent node that has been printed */ +static halfword pass_number; /*the number of passive nodes allocated on this pass */ /* @ The active list also contains ``delta'' nodes that help the algorithm @@ -398,15 +410,15 @@ macro makes such six-tuples convenient. #define save_active_width(a) prev_active_width[(a)] = active_width[(a)] #define restore_active_width(a) active_width[(a)] = prev_active_width[(a)] -static scaled active_width[10] = {0}; /*distance from first active node to~|cur_p|*/ -static scaled background[10] = {0}; /*length of an ``empty'' line*/ -static scaled break_width[10] = {0}; /*length being computed after current break*/ +static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */ +static scaled background[10] = { 0 }; /*length of an ``empty'' line */ +static scaled break_width[10] = { 0 }; /*length being computed after current break */ -static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break|*/ -static boolean try_prev_break; /*force break at the previous legal breakpoint?*/ -static halfword prev_legal; /*the previous legal breakpoint*/ -static halfword rejected_cur_p; /*the last |cur_p| that has been rejected*/ -static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|?*/ +static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */ +static boolean try_prev_break; /*force break at the previous legal breakpoint? */ +static halfword prev_legal; /*the previous legal breakpoint */ +static halfword rejected_cur_p; /*the last |cur_p| that has been rejected */ +static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|? */ /* Let's state the principles of the delta nodes more precisely and concisely, so that the following programs will be less obscure. For each legal @@ -467,16 +479,16 @@ static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p| -static integer internal_pen_inter; /* running \.{\\localinterlinepenalty} */ -static integer internal_pen_broken; /* running \.{\\localbrokenpenalty} */ -static halfword internal_left_box; /* running \.{\\localleftbox} */ +static integer internal_pen_inter; /* running \.{\\localinterlinepenalty} */ +static integer internal_pen_broken; /* running \.{\\localbrokenpenalty} */ +static halfword internal_left_box; /* running \.{\\localleftbox} */ static integer internal_left_box_width; /* running \.{\\localleftbox} width */ static halfword init_internal_left_box; /* running \.{\\localleftbox} */ -static integer init_internal_left_box_width; /* running \.{\\localleftbox} width */ -static halfword internal_right_box; /* running \.{\\localrightbox} */ -static integer internal_right_box_width; /* running \.{\\localrightbox} width */ +static integer init_internal_left_box_width; /* running \.{\\localleftbox} width */ +static halfword internal_right_box; /* running \.{\\localrightbox} */ +static integer internal_right_box_width; /* running \.{\\localrightbox} width */ -static scaled disc_width [10] = {0}; /* the length of discretionary material preceding a break*/ +static scaled disc_width[10] = { 0 }; /* the length of discretionary material preceding a break */ /* As we consider various ways to end a line at |cur_p|, in a given line number class, we keep track of the best total demerits known, in an array with @@ -492,12 +504,12 @@ static scaled disc_width [10] = {0}; /* the length of discretionary material pre */ -static integer minimal_demerits[4]; /* best total demerits known for current - line class and position, given the fitness */ -static integer minimum_demerits; /* best total demerits known for current line class - and position */ -static halfword best_place[4]; /* how to achieve |minimal_demerits| */ -static halfword best_pl_line[4]; /*corresponding line number*/ +static integer minimal_demerits[4]; /* best total demerits known for current + line class and position, given the fitness */ +static integer minimum_demerits; /* best total demerits known for current line class + and position */ +static halfword best_place[4]; /* how to achieve |minimal_demerits| */ +static halfword best_pl_line[4]; /*corresponding line number */ /* @@ -518,21 +530,21 @@ Note that if |hang_indent=0|, the value of |hang_after| is irrelevant. @^length of lines@> @^hanging indentation@> */ -static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes*/ -static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ -static scaled first_width; /*the width of all lines |<=last_special_line|, if - no \.{\\parshape} has been specified*/ -static scaled second_width; /*the width of all lines |>last_special_line|*/ -static scaled first_indent; /*left margin to go with |first_width|*/ -static scaled second_indent; /*left margin to go with |second_width|*/ +static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes */ +static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ +static scaled first_width; /*the width of all lines |<=last_special_line|, if + no \.{\\parshape} has been specified */ +static scaled second_width; /*the width of all lines |>last_special_line| */ +static scaled first_indent; /*left margin to go with |first_width| */ +static scaled second_indent; /*left margin to go with |second_width| */ -static halfword best_bet; /*use this passive node and its predecessors */ -static integer fewest_demerits; /*the demerits associated with |best_bet|*/ -static halfword best_line; /*line number following the last line of the new paragraph*/ -static integer actual_looseness; /*the difference between |line_number(best_bet)| - and the optimum |best_line|*/ -static integer line_diff; /*the difference between the current line number and - the optimum |best_line|*/ +static halfword best_bet; /*use this passive node and its predecessors */ +static integer fewest_demerits; /*the demerits associated with |best_bet| */ +static halfword best_line; /*line number following the last line of the new paragraph */ +static integer actual_looseness; /*the difference between |line_number(best_bet)| + and the optimum |best_line| */ +static integer line_diff; /*the difference between the current line number and + the optimum |best_line| */ @@ -551,7 +563,7 @@ static integer line_diff; /*the difference between the current line number and #define store_background(a) {active_width[a]=background[a];} -#define act_width active_width[1] /*length from first active node to current node*/ +#define act_width active_width[1] /*length from first active node to current node */ #define kern_break() { \ if ((!is_char_node(vlink(cur_p))) && auto_breaking) \ @@ -581,17 +593,17 @@ static integer line_diff; /*the difference between the current line number and } \ } -#define inf_bad 10000 /* infinitely bad value */ +#define inf_bad 10000 /* infinitely bad value */ -static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */ -static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */ +static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */ +static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */ static scaled best_pl_short[4]; /* |shortfall| corresponding to |minimal_demerits| */ -static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ +static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ -#define awful_bad 07777777777 /* more than a billion demerits */ +#define awful_bad 07777777777 /* more than a billion demerits */ -#define before 0 /* |subtype| for math node that introduces a formula */ -#define after 1 /* |subtype| for math node that winds up a formula */ +#define before 0 /* |subtype| for math node that introduces a formula */ +#define after 1 /* |subtype| for math node that winds up a formula */ #define reset_disc_width(a) disc_width[(a)] = 0 @@ -636,241 +648,250 @@ static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ disc_width[] line_break_dir */ -static void -compute_break_width (int break_type, int pdf_adjust_spacing, halfword p - /*, halfword s*/) { - halfword s; /* glue and other 'whitespace' to be skipped after a break - * used if unhyphenated, or post_break==empty */ - s=p; - if (break_type>unhyphenated_node && p!=null) { - /*@<Compute the discretionary |break_width| values@>;*/ - /* When |p| is a discretionary break, the length of a line - ``from |p| to |p|'' has to be defined properly so - that the other calculations work out. Suppose that the - pre-break text at |p| has length $l_0$, the post-break - text has length $l_1$, and the replacement text has length - |l|. Suppose also that |q| is the node following the - replacement text. Then length of a line from |p| to |q| - will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where - $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be - the background plus $l_1$, so the length from |p| to - |p| should be $\gamma+l_0+l_1-l$. If the post-break text - of the discretionary is empty, a break may also discard~|q|; - in that unusual case we subtract the length of~|q| and any - other nodes that will be discarded after the discretionary - break. - - TH: I don't quite understand the above remarks. - - The value of $l_0$ need not be computed, since |line_break| - will put it into the global variable |disc_width| before - calling |try_break|. - */ - /* In case of nested discretionaries, we always follow the no_break - path, as we are talking about the breaking on _this_ position. - */ - - halfword v; - for (v=vlink_no_break(p); v!=null; v=vlink(v)) { - /* @<Subtract the width of node |v| from |break_width|@>; */ - /* Replacement texts and discretionary texts are supposed to contain - only character nodes, kern nodes, and box or rule nodes.*/ - if (is_char_node(v)) { - if (is_rotated(line_break_dir)) { - break_width[1] -= (glyph_height(v)+glyph_depth(v)); - } else { - break_width[1] -= glyph_width(v); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { - set_prev_char_p(v); - sub_char_stretch(break_width[8],v); - sub_char_shrink(break_width[9],v); +static void +compute_break_width(int break_type, int pdf_adjust_spacing, halfword p + /*, halfword s */ ) +{ + halfword s; /* glue and other 'whitespace' to be skipped after a break + * used if unhyphenated, or post_break==empty */ + s = p; + if (break_type > unhyphenated_node && p != null) { + /*@<Compute the discretionary |break_width| values@>; */ + /* When |p| is a discretionary break, the length of a line + ``from |p| to |p|'' has to be defined properly so + that the other calculations work out. Suppose that the + pre-break text at |p| has length $l_0$, the post-break + text has length $l_1$, and the replacement text has length + |l|. Suppose also that |q| is the node following the + replacement text. Then length of a line from |p| to |q| + will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where + $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be + the background plus $l_1$, so the length from |p| to + |p| should be $\gamma+l_0+l_1-l$. If the post-break text + of the discretionary is empty, a break may also discard~|q|; + in that unusual case we subtract the length of~|q| and any + other nodes that will be discarded after the discretionary + break. + + TH: I don't quite understand the above remarks. + + The value of $l_0$ need not be computed, since |line_break| + will put it into the global variable |disc_width| before + calling |try_break|. + */ + /* In case of nested discretionaries, we always follow the no_break + path, as we are talking about the breaking on _this_ position. + */ + + halfword v; + for (v = vlink_no_break(p); v != null; v = vlink(v)) { + /* @<Subtract the width of node |v| from |break_width|@>; */ + /* Replacement texts and discretionary texts are supposed to contain + only character nodes, kern nodes, and box or rule nodes. */ + if (is_char_node(v)) { + if (is_rotated(line_break_dir)) { + break_width[1] -= (glyph_height(v) + glyph_depth(v)); + } else { + break_width[1] -= glyph_width(v); + } + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { + set_prev_char_p(v); + sub_char_stretch(break_width[8], v); + sub_char_shrink(break_width[9], v); + } + } else { + switch (type(v)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(v)], + dir_primary[line_break_dir]))) + break_width[1] -= width(v); + else + break_width[1] -= (depth(v) + height(v)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { + sub_kern_stretch(break_width[8], v); + sub_kern_shrink(break_width[9], v); + } + /* fall through */ + case rule_node: + break_width[1] -= width(v); + break; + case disc_node: + assert(vlink(v) == null); /* discs are _always_ last */ + v = no_break(v); + break; + default: + tconfusion("disc1"); + break; + } + } } - } else { - switch (type(v)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(v)], - dir_primary[line_break_dir]))) - break_width[1] -= width(v); - else - break_width[1] -= (depth(v)+height(v)); - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { - sub_kern_stretch(break_width[8],v); - sub_kern_shrink(break_width[9],v); - } - /* fall through */ - case rule_node: - break_width[1] -= width(v); - break; - case disc_node: - assert(vlink(v)==null); /* discs are _always_ last */ - v = no_break(v); - break; - default: - tconfusion("disc1"); - break; + + for (v = vlink_post_break(p); v != null; v = vlink(v)) { + /* @<Add the width of node |v| to |break_width|@>; */ + if (is_char_node(v)) { + if (is_rotated(line_break_dir)) + break_width[1] += (glyph_height(v) + glyph_depth(v)); + else + break_width[1] += glyph_width(v); + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { + set_prev_char_p(v); + add_char_stretch(break_width[8], v); + add_char_shrink(break_width[9], v); + } + } else { + switch (type(v)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(v)], + dir_primary[line_break_dir]))) + break_width[1] += width(v); + else + break_width[1] += (depth(v) + height(v)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { + add_kern_stretch(break_width[8], v); + add_kern_shrink(break_width[9], v); + } + /* fall through */ + case rule_node: + break_width[1] += width(v); + break; + case disc_node: + assert(vlink(v) == null); + v = no_break(v); + break; + default: + tconfusion("disc2"); + } + } } - } - } - for (v=vlink_post_break(p); v!=null; v=vlink(v)) { - /* @<Add the width of node |v| to |break_width|@>; */ - if (is_char_node(v)) { - if (is_rotated(line_break_dir)) - break_width[1] += (glyph_height(v)+glyph_depth(v)); - else - break_width[1] += glyph_width(v); - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { - set_prev_char_p(v); - add_char_stretch(break_width[8],v); - add_char_shrink(break_width[9],v); + do_one_seven_eight(add_disc_width_to_break_width); + if (vlink_post_break(p) == null) { + s = vlink(p); /* no post_break: 'skip' any 'whitespace' following */ + } else { + s = null; } - } else { - switch (type(v)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(v)], - dir_primary[line_break_dir]))) - break_width[1] += width(v); - else - break_width[1] += (depth(v)+height(v)); - break; + } else { + s = p; /* unhyphenated: we need to 'skip' any 'whitespace' following */ + } + while (s != null) { + switch (type(s)) { + case glue_node: + /*@<Subtract glue from |break_width|@>; */ + { + halfword v = glue_ptr(s); + break_width[1] -= width(v); + break_width[2 + stretch_order(v)] -= stretch(v); + break_width[7] -= shrink(v); + } + break; + case penalty_node: + break; + case math_node: + break_width[1] -= surround(s); + break; case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { - add_kern_stretch(break_width[8],v); - add_kern_shrink(break_width[9],v); - } - /* fall through */ - case rule_node: - break_width[1] += width(v); - break; - case disc_node: - assert(vlink(v)==null); - v = no_break(v); - break; + if (subtype(s) != explicit) + return; + else + break_width[1] -= width(s); + break; default: - tconfusion("disc2"); - } - } + return; + }; + s = vlink(s); } - - do_one_seven_eight(add_disc_width_to_break_width); - if (vlink_post_break(p)==null) { - s=vlink(p); /* no post_break: 'skip' any 'whitespace' following */ - } else { - s=null; - } - } else { - s = p; /* unhyphenated: we need to 'skip' any 'whitespace' following */ - } - while (s!=null) { - switch (type(s)) { - case glue_node: - /*@<Subtract glue from |break_width|@>;*/ - {halfword v=glue_ptr(s); - break_width[1] -= width(v); - break_width[2+stretch_order(v)] -= stretch(v); - break_width[7] -= shrink(v); - }break; - case penalty_node: - break; - case math_node: - break_width[1] -= surround(s); - break; - case kern_node: - if (subtype(s)!=explicit) - return; - else - break_width[1] -= width(s); - break; - default: - return; - }; - s=vlink(s); - } } static void print_break_node(halfword q, fitness_value fit_class, - quarterword break_type, halfword cur_p) { - /* @<Print a symbolic description of the new break node@> */ - tprint_nl("@@"); - print_int(serial(passive)); - tprint(": line "); - print_int(line_number(q)-1); - print_char('.'); - print_int(fit_class); - if (break_type==hyphenated_node) - print_char('-'); - tprint(" t="); - print_int(total_demerits(q)); - if (do_last_line_fit) { - /*@<Print additional data in the new active node@>; */ - tprint(" s="); - print_scaled(active_short(q)); - if (cur_p==null) - tprint(" a="); - else - tprint(" g="); - print_scaled(active_glue(q)); - } - tprint(" -> @@"); - if (prev_break(passive)==null) - print_char('0'); - else - print_int(serial(prev_break(passive))); + quarterword break_type, halfword cur_p) +{ + /* @<Print a symbolic description of the new break node@> */ + tprint_nl("@@"); + print_int(serial(passive)); + tprint(": line "); + print_int(line_number(q) - 1); + print_char('.'); + print_int(fit_class); + if (break_type == hyphenated_node) + print_char('-'); + tprint(" t="); + print_int(total_demerits(q)); + if (do_last_line_fit) { + /*@<Print additional data in the new active node@>; */ + tprint(" s="); + print_scaled(active_short(q)); + if (cur_p == null) + tprint(" a="); + else + tprint(" g="); + print_scaled(active_glue(q)); + } + tprint(" -> @@"); + if (prev_break(passive) == null) + print_char('0'); + else + print_int(serial(prev_break(passive))); } -static void +static void print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, - integer d, boolean artificial_demerits) { - /* @<Print a symbolic description of this feasible break@>;*/ - if (printed_node!=cur_p) { - /* @<Print the list between |printed_node| and |cur_p|, then - set |printed_node:=cur_p|@>;*/ - tprint_nl(""); - if (cur_p==null) { - short_display(vlink(printed_node)); - } else { - halfword save_link=vlink(cur_p); - vlink(cur_p)=null; - tprint_nl(""); - short_display(vlink(printed_node)); - vlink(cur_p)=save_link; + integer d, boolean artificial_demerits) +{ + /* @<Print a symbolic description of this feasible break@>; */ + if (printed_node != cur_p) { + /* @<Print the list between |printed_node| and |cur_p|, then + set |printed_node:=cur_p|@>; */ + tprint_nl(""); + if (cur_p == null) { + short_display(vlink(printed_node)); + } else { + halfword save_link = vlink(cur_p); + vlink(cur_p) = null; + tprint_nl(""); + short_display(vlink(printed_node)); + vlink(cur_p) = save_link; + } + printed_node = cur_p; } - printed_node=cur_p; - } - tprint_nl("@"); - if (cur_p==null) { - tprint_esc("par"); - } else if (type(cur_p)!=glue_node) { - if (type(cur_p)==penalty_node) tprint_esc("penalty"); - else if (type(cur_p)==disc_node) tprint_esc("discretionary"); - else if (type(cur_p)==kern_node) tprint_esc("kern"); - else tprint_esc("math"); - } - tprint(" via @@"); - if (break_node(r)==null) - print_char('0'); - else - print_int(serial(break_node(r))); - tprint(" b="); - if (b>inf_bad) - print_char('*'); - else - print_int(b); - tprint(" p="); - print_int(pi); - tprint(" d="); - if (artificial_demerits) - print_char('*'); - else - print_int(d); + tprint_nl("@"); + if (cur_p == null) { + tprint_esc("par"); + } else if (type(cur_p) != glue_node) { + if (type(cur_p) == penalty_node) + tprint_esc("penalty"); + else if (type(cur_p) == disc_node) + tprint_esc("discretionary"); + else if (type(cur_p) == kern_node) + tprint_esc("kern"); + else + tprint_esc("math"); + } + tprint(" via @@"); + if (break_node(r) == null) + print_char('0'); + else + print_int(serial(break_node(r))); + tprint(" b="); + if (b > inf_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" d="); + if (artificial_demerits) + print_char('*'); + else + print_int(d); } #define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] @@ -898,7 +919,7 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, #define total_font_stretch cur_active_width[8] #define total_font_shrink cur_active_width[9] -#define max_dimen 07777777777 /* $2^{30}-1$ */ +#define max_dimen 07777777777 /* $2^{30}-1$ */ #define left_side 0 #define right_side 1 @@ -909,1187 +930,1238 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, font(cp) = font((a)); \ do_subst_font(cp, 1000); \ if (font(cp) != font((a))) \ - margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ + margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ font(cp) = font((a)); \ do_subst_font(cp, -1000); \ if (font(cp) != font((a))) \ - margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ + margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ } -static void +static void ext_try_break(integer pi, quarterword break_type, int pdf_adjust_spacing, - int par_shape_ptr, - int adj_demerits, - int tracing_paragraphs, + int par_shape_ptr, + int adj_demerits, + int tracing_paragraphs, int pdf_protrude_chars, int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, - halfword first_p, - halfword cur_p) { - /* CONTINUE,DEACTIVATE,FOUND,NOT_FOUND; */ - pointer r; /* runs through the active list */ - scaled margin_kern_stretch; - scaled margin_kern_shrink; - halfword lp, rp, cp; - halfword prev_r; /* stays a step behind |r| */ - halfword prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)=delta_node|*/ - halfword old_l; /* maximum line number in current equivalence class of lines */ - boolean no_break_yet; /* have we found a feasible break at |cur_p|? */ - halfword q; /*points to a new node being created*/ - halfword l; /*line number of current active node*/ - boolean node_r_stays_active; /*should node |r| remain in the active list?*/ - scaled line_width; /*the current line will be justified to this width*/ - fitness_value fit_class; /*possible fitness class of test line*/ - halfword b; /*badness of test line*/ - integer d; /*demerits of test line*/ - boolean artificial_demerits; /*has |d| been forced to zero?*/ - - scaled shortfall; /*used in badness calculations*/ - scaled g; /*glue stretch or shrink of test line, adjustment for last line*/ - scaled cur_active_width[10] = {0}; /*distance from current active node*/ - - line_width=0; g=0; prev_prev_r=null; - /*@<Make sure that |pi| is in the proper range@>;*/ - if (pi>=inf_penalty) { - return; /* this breakpoint is inhibited by infinite penalty */ - } else if (pi<=-inf_penalty) { - pi=eject_penalty; /*this breakpoint will be forced*/ - } - - no_break_yet=true; - prev_r=active; - old_l=0; - do_all_eight(copy_to_cur_active); - - while (1) { - r=vlink(prev_r); - /* @<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| */ - if (type(r)==delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - prev_prev_r=prev_r; - prev_r=r; - continue; + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, halfword first_p, halfword cur_p) +{ + /* CONTINUE,DEACTIVATE,FOUND,NOT_FOUND; */ + pointer r; /* runs through the active list */ + scaled margin_kern_stretch; + scaled margin_kern_shrink; + halfword lp, rp, cp; + halfword prev_r; /* stays a step behind |r| */ + halfword prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */ + halfword old_l; /* maximum line number in current equivalence class of lines */ + boolean no_break_yet; /* have we found a feasible break at |cur_p|? */ + halfword q; /*points to a new node being created */ + halfword l; /*line number of current active node */ + boolean node_r_stays_active; /*should node |r| remain in the active list? */ + scaled line_width; /*the current line will be justified to this width */ + fitness_value fit_class; /*possible fitness class of test line */ + halfword b; /*badness of test line */ + integer d; /*demerits of test line */ + boolean artificial_demerits; /*has |d| been forced to zero? */ + + scaled shortfall; /*used in badness calculations */ + scaled g; /*glue stretch or shrink of test line, adjustment for last line */ + scaled cur_active_width[10] = { 0 }; /*distance from current active node */ + + line_width = 0; + g = 0; + prev_prev_r = null; + /*@<Make sure that |pi| is in the proper range@>; */ + if (pi >= inf_penalty) { + return; /* this breakpoint is inhibited by infinite penalty */ + } else if (pi <= -inf_penalty) { + pi = eject_penalty; /*this breakpoint will be forced */ } - /* @<If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|@>; */ - /* The first part of the following code is part of \TeX's inner loop, so - we don't want to waste any time. The current active node, namely node |r|, - contains the line number that will be considered next. At the end of the - list we have arranged the data structure so that |r=active| and - |line_number(active)>old_l|. - */ - l=line_number(r); - if (l>old_l) { /* now we are no longer in the inner loop */ - if ((minimum_demerits<awful_bad)&&((old_l!=easy_line)||(r==active))) { - /*@<Create new active nodes for the best feasible breaks just found@>*/ - /* It is not necessary to create new active nodes having |minimal_demerits| - greater than - |minimum_demerits+abs(adj_demerits)|, since such active nodes will never - be chosen in the final paragraph breaks. This observation allows us to - omit a substantial number of feasible breakpoints from further consideration. - */ - if (no_break_yet) { - no_break_yet=false; - do_all_eight(set_break_width_to_background); - compute_break_width(break_type, pdf_adjust_spacing, cur_p); - } - /* @<Insert a delta node to prepare for breaks at |cur_p|@>;*/ - /* We use the fact that |type(active)<>delta_node|.*/ - if (type(prev_r)==delta_node) {/* modify an existing delta node */ - do_all_eight(convert_to_break_width); /* IMPLICIT prev_r */ - } else if (prev_r==active) { /* no delta node needed at the beginning */ - do_all_eight(store_break_width); - } else { - q=new_node(delta_node,0); - vlink(q)=r; - do_all_eight(new_delta_to_break_width); /* IMPLICIT q */ - vlink(prev_r)=q; - prev_prev_r=prev_r; - prev_r=q; + + no_break_yet = true; + prev_r = active; + old_l = 0; + do_all_eight(copy_to_cur_active); + + while (1) { + r = vlink(prev_r); + /* @<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| */ + if (type(r) == delta_node) { + do_all_eight(update_width); /* IMPLICIT ,r */ + prev_prev_r = prev_r; + prev_r = r; + continue; } + /* @<If a line number class has ended, create new active nodes for + the best feasible breaks in that class; then |return| + if |r=active|, otherwise compute the new |line_width|@>; */ + /* The first part of the following code is part of \TeX's inner loop, so + we don't want to waste any time. The current active node, namely node |r|, + contains the line number that will be considered next. At the end of the + list we have arranged the data structure so that |r=active| and + |line_number(active)>old_l|. + */ + l = line_number(r); + if (l > old_l) { /* now we are no longer in the inner loop */ + if ((minimum_demerits < awful_bad) + && ((old_l != easy_line) || (r == active))) { + /*@<Create new active nodes for the best feasible breaks just found@> */ + /* It is not necessary to create new active nodes having |minimal_demerits| + greater than + |minimum_demerits+abs(adj_demerits)|, since such active nodes will never + be chosen in the final paragraph breaks. This observation allows us to + omit a substantial number of feasible breakpoints from further consideration. + */ + if (no_break_yet) { + no_break_yet = false; + do_all_eight(set_break_width_to_background); + compute_break_width(break_type, pdf_adjust_spacing, cur_p); + } + /* @<Insert a delta node to prepare for breaks at |cur_p|@>; */ + /* We use the fact that |type(active)<>delta_node|. */ + if (type(prev_r) == delta_node) { /* modify an existing delta node */ + do_all_eight(convert_to_break_width); /* IMPLICIT prev_r */ + } else if (prev_r == active) { /* no delta node needed at the beginning */ + do_all_eight(store_break_width); + } else { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_to_break_width); /* IMPLICIT q */ + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } - if (abs(adj_demerits)>=awful_bad-minimum_demerits) - minimum_demerits=awful_bad-1; - else - minimum_demerits += abs(adj_demerits); - for (fit_class=very_loose_fit;fit_class<=tight_fit;fit_class++) { - if (minimal_demerits[fit_class]<=minimum_demerits) { - /* @<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. - */ - q=new_node(passive_node,0); - vlink(q)=passive; - passive=q; - cur_break(q)=cur_p; - incr(pass_number); - serial(q)=pass_number; - prev_break(q)=best_place[fit_class]; - /*Here we keep track of the subparagraph penalties in the break nodes*/ - passive_pen_inter(q)=internal_pen_inter; - passive_pen_broken(q)=internal_pen_broken; - passive_last_left_box(q)=internal_left_box; - passive_last_left_box_width(q)=internal_left_box_width; - if (prev_break(q)!=null) { - passive_left_box(q)=passive_last_left_box(prev_break(q)); - passive_left_box_width(q)=passive_last_left_box_width(prev_break(q)); + if (abs(adj_demerits) >= awful_bad - minimum_demerits) + minimum_demerits = awful_bad - 1; + else + minimum_demerits += abs(adj_demerits); + for (fit_class = very_loose_fit; fit_class <= tight_fit; + fit_class++) { + if (minimal_demerits[fit_class] <= minimum_demerits) { + /* @<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. + */ + q = new_node(passive_node, 0); + vlink(q) = passive; + passive = q; + cur_break(q) = cur_p; + incr(pass_number); + serial(q) = pass_number; + prev_break(q) = best_place[fit_class]; + /*Here we keep track of the subparagraph penalties in the break nodes */ + passive_pen_inter(q) = internal_pen_inter; + passive_pen_broken(q) = internal_pen_broken; + passive_last_left_box(q) = internal_left_box; + passive_last_left_box_width(q) = + internal_left_box_width; + if (prev_break(q) != null) { + passive_left_box(q) = + passive_last_left_box(prev_break(q)); + passive_left_box_width(q) = + passive_last_left_box_width(prev_break(q)); + } else { + passive_left_box(q) = init_internal_left_box; + passive_left_box_width(q) = + init_internal_left_box_width; + } + passive_right_box(q) = internal_right_box; + passive_right_box_width(q) = internal_right_box_width; + q = new_node(break_type, fit_class); + break_node(q) = passive; + line_number(q) = best_pl_line[fit_class] + 1; + total_demerits(q) = minimal_demerits[fit_class]; + if (do_last_line_fit) { + /*@<Store \(a)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_pl_short[fit_class]; + active_glue(q) = best_pl_glue[fit_class]; + } + vlink(q) = r; + vlink(prev_r) = q; + prev_r = q; + if (tracing_paragraphs > 0) + print_break_node(q, fit_class, break_type, cur_p); + } + minimal_demerits[fit_class] = awful_bad; + } + minimum_demerits = awful_bad; + /* @<Insert a delta node to prepare for the next active node@>; */ + /* When the following code is performed, we will have just inserted at + least one active node before |r|, so |type(prev_r)<>delta_node|. + */ + if (r != active) { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_from_break_width); /* IMPLICIT q */ + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + } + if (r == active) + return; + /*@<Compute the new line width@>; */ + /* When we come to the following code, we have just encountered + the first active node~|r| whose |line_number| field contains + |l|. Thus we want to compute the length of the $l\mskip1mu$th + line of the current paragraph. Furthermore, we want to set + |old_l| to the last number in the class of line numbers + equivalent to~|l|. + */ + if (l > easy_line) { + old_l = max_halfword - 1; + line_width = second_width; } else { - passive_left_box(q)=init_internal_left_box; - passive_left_box_width(q)=init_internal_left_box_width; + old_l = l; + if (l > last_special_line) { + line_width = second_width; + } else if (par_shape_ptr == null) { + line_width = first_width; + } else { + line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint; + } } - passive_right_box(q)=internal_right_box; - passive_right_box_width(q)=internal_right_box_width; - q=new_node(break_type,fit_class); - break_node(q)=passive; - line_number(q)=best_pl_line[fit_class]+1; - total_demerits(q)=minimal_demerits[fit_class]; - if (do_last_line_fit) { - /*@<Store \(a)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_pl_short[fit_class]; - active_glue(q)=best_pl_glue[fit_class]; + } + /* /If a line number class has ended, create new active nodes for + the best feasible breaks in that class; then |return| + if |r=active|, otherwise compute the new |line_width|@>; */ + + /* @<Consider the demerits for a line from |r| to |cur_p|; + deactivate node |r| if it should no longer be active; + then |goto continue| if a line from |r| to |cur_p| is infeasible, + otherwise record a new feasible break@>; */ + artificial_demerits = false; + shortfall = line_width - cur_active_width[1] + - ((break_node(r) == null) + ? init_internal_left_box_width + : passive_last_left_box_width(break_node(r))) + - internal_right_box_width; + if (pdf_protrude_chars > 1) { + halfword l, o; + l = (break_node(r) == null) ? first_p : cur_break(break_node(r)); + if (cur_p == null) { + o = null; + } else { /* TODO if (is_character_node(alink(cur_p))) */ + o = alink(cur_p); + assert(vlink(o) == cur_p); + } + /* let's look at the right margin first */ + if ((cur_p != null) && (type(cur_p) == disc_node) + && (vlink_pre_break(cur_p) != null)) { + /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + o = tlink_pre_break(cur_p); + } else { + o = find_protchar_right(l, o); + } + /* now the left margin */ + if ((l != null) && (type(l) == disc_node) + && (vlink_post_break(l) != null)) { + /* FIXME: first 'char' could be a disc! */ + l = vlink_post_break(l); /* protrude the first char */ + } else { + l = find_protchar_left(l, true); } - vlink(q)=r; - vlink(prev_r)=q; - prev_r=q; - if (tracing_paragraphs>0) - print_break_node(q,fit_class,break_type,cur_p); - } - minimal_demerits[fit_class]=awful_bad; + shortfall += (left_pw(l) + right_pw(o)); } - minimum_demerits=awful_bad; - /* @<Insert a delta node to prepare for the next active node@>;*/ - /* When the following code is performed, we will have just inserted at - least one active node before |r|, so |type(prev_r)<>delta_node|. - */ - if (r!=active) { - q=new_node(delta_node,0); - vlink(q)=r; - do_all_eight(new_delta_from_break_width); /* IMPLICIT q */ - vlink(prev_r)=q; - prev_prev_r=prev_r; - prev_r=q; + if ((shortfall != 0) && (pdf_adjust_spacing > 1)) { + margin_kern_stretch = 0; + margin_kern_shrink = 0; + if (pdf_protrude_chars > 1) { + /* @<Calculate variations of marginal kerns@>; */ + lp = last_leftmost_char; + rp = last_rightmost_char; + cp = raw_glyph_node(); + if (lp != null) { + cal_margin_kern_var(lp); + } + if (rp != null) { + cal_margin_kern_var(rp); + } + flush_node(cp); + } + if ((shortfall > 0) + && ((total_font_stretch + margin_kern_stretch) > 0)) { + if ((total_font_stretch + margin_kern_stretch) > shortfall) + shortfall = ((total_font_stretch + margin_kern_stretch) / + (max_stretch_ratio / cur_font_step)) / 2; + else + shortfall -= (total_font_stretch + margin_kern_stretch); + } else if ((shortfall < 0) + && ((total_font_shrink + margin_kern_shrink) > 0)) { + if ((total_font_shrink + margin_kern_shrink) > -shortfall) + shortfall = -((total_font_shrink + margin_kern_shrink) / + (max_shrink_ratio / cur_font_step)) / 2; + else + shortfall += (total_font_shrink + margin_kern_shrink); + } } - } - if (r==active) - return; - /*@<Compute the new line width@>;*/ - /* When we come to the following code, we have just encountered - the first active node~|r| whose |line_number| field contains - |l|. Thus we want to compute the length of the $l\mskip1mu$th - line of the current paragraph. Furthermore, we want to set - |old_l| to the last number in the class of line numbers - equivalent to~|l|. - */ - if (l>easy_line) { - old_l=max_halfword-1; - line_width=second_width; - } else { - old_l=l; - if (l>last_special_line) { - line_width=second_width; - } else if (par_shape_ptr==null) { - line_width=first_width; + if (shortfall > 0) { + /* @<Set the value of |b| to the badness for stretching the line, + and compute the corresponding |fit_class|@> */ + + /* When a line must stretch, the available stretchability can be + found in the subarray |cur_active_width[2..6]|, in units of + points, sfi, fil, fill and filll. + + The present section is part of \TeX's inner loop, and it is + most often performed when the badness is infinite; therefore + it is worth while to make a quick test for large width excess + and small stretchability, before calling the |badness| + subroutine. @^inner loop@> */ + + if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || + (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { + if (do_last_line_fit) { + if (cur_p == null) { /* the last line of a paragraph */ + /* @<Perform computations for last line and |goto found|@>; */ + + /* Here we compute the adjustment |g| and badness |b| for + a line from |r| to the end of the paragraph. When any + of the criteria for adjustment is violated we fall + through to the normal algorithm. + + The last line must be too short, and have infinite + stretch entirely due to |par_fill_skip|. */ + if ((active_short(r) == 0) || (active_glue(r) <= 0)) + /* previous line was neither stretched nor shrunk, or + was infinitely bad */ + goto NOT_FOUND; + if ((cur_active_width[3] != fill_width[0]) || + (cur_active_width[4] != fill_width[1]) || + (cur_active_width[5] != fill_width[2]) || + (cur_active_width[6] != fill_width[3])) + /* infinite stretch of this line not entirely due to + |par_fill_skip| */ + goto NOT_FOUND; + if (active_short(r) > 0) + g = cur_active_width[2]; + else + g = cur_active_width[7]; + if (g <= 0) + /*no finite stretch resp.\ no shrink */ + goto NOT_FOUND; + arith_error = false; + g = fract(g, active_short(r), active_glue(r), + max_dimen); + if (last_line_fit < 1000) + g = fract(g, last_line_fit, 1000, max_dimen); + if (arith_error) { + if (active_short(r) > 0) + g = max_dimen; + else + g = -max_dimen; + } + if (g > 0) { + /*@<Set the value of |b| to the badness of the last line + for stretching, compute the corresponding |fit_class, + and |goto found||@> */ + /* These badness computations are rather similar to + those of the standard algorithm, with the adjustment + amount |g| replacing the |shortfall|. */ + if (g > shortfall) + g = shortfall; + if (g > 7230584) { + if (cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + goto FOUND; + } + } + b = badness(g, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + goto FOUND; + } else if (g < 0) { + /*@<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[7]) + g = -cur_active_width[7]; + b = badness(-g, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + goto FOUND; + } + } + NOT_FOUND: + shortfall = 0; + } + b = 0; + fit_class = decent_fit; /* infinite stretch */ + } else { + if (shortfall > 7230584 && cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + } else { + b = badness(shortfall, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + } + } } else { - line_width=varmem[(par_shape_ptr+2*l+1)].cint; + /* Set the value of |b| to the badness for shrinking the line, + and compute the corresponding |fit_class|@>; */ + /* Shrinkability is never infinite in a paragraph; we can shrink + the line from |r| to |cur_p| by at most + |cur_active_width[7]|. */ + if (-shortfall > cur_active_width[7]) + b = inf_bad + 1; + else + b = badness(-shortfall, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; } - } - } - /* /If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|@>; */ - - /* @<Consider the demerits for a line from |r| to |cur_p|; - deactivate node |r| if it should no longer be active; - then |goto continue| if a line from |r| to |cur_p| is infeasible, - otherwise record a new feasible break@>; */ - artificial_demerits=false; - shortfall = line_width - - cur_active_width[1] - -(( break_node(r)==null) - ? init_internal_left_box_width - : passive_last_left_box_width(break_node(r))) - - internal_right_box_width; - if (pdf_protrude_chars > 1) { - halfword l, o; - l = (break_node(r) == null) ? first_p : cur_break(break_node(r)); - if (cur_p==null) { - o = null; - } else { /* TODO if (is_character_node(alink(cur_p))) */ - o = alink(cur_p); - assert(vlink(o)==cur_p); - } - /* let's look at the right margin first */ - if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) { - /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ - o = tlink_pre_break(cur_p); - } else { - o = find_protchar_right(l, o); - } - /* now the left margin */ - if ((l != null) && (type(l) == disc_node) && (vlink_post_break(l) != null)) { - /* FIXME: first 'char' could be a disc! */ - l = vlink_post_break(l); /* protrude the first char */ - } else { - l = find_protchar_left(l, true); - } - shortfall += (left_pw(l) + right_pw(o)); - } - if ((shortfall != 0) && (pdf_adjust_spacing > 1)) { - margin_kern_stretch = 0; - margin_kern_shrink = 0; - if (pdf_protrude_chars > 1) { - /* @<Calculate variations of marginal kerns@>;*/ - lp = last_leftmost_char; - rp = last_rightmost_char; - cp = raw_glyph_node(); - if (lp != null) { - cal_margin_kern_var(lp); - } - if (rp != null) { - cal_margin_kern_var(rp); - } - flush_node(cp); - } - if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) { - if ((total_font_stretch + margin_kern_stretch) > shortfall) - shortfall = ((total_font_stretch + margin_kern_stretch) / - (max_stretch_ratio / cur_font_step)) / 2; - else - shortfall -= (total_font_stretch + margin_kern_stretch); - } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) { - if ((total_font_shrink + margin_kern_shrink) > -shortfall) - shortfall = -((total_font_shrink + margin_kern_shrink) / - (max_shrink_ratio / cur_font_step)) / 2; - else - shortfall += (total_font_shrink + margin_kern_shrink); - } - } - if (shortfall>0) { - /* @<Set the value of |b| to the badness for stretching the line, - and compute the corresponding |fit_class|@> */ - - /* When a line must stretch, the available stretchability can be - found in the subarray |cur_active_width[2..6]|, in units of - points, sfi, fil, fill and filll. - - The present section is part of \TeX's inner loop, and it is - most often performed when the badness is infinite; therefore - it is worth while to make a quick test for large width excess - and small stretchability, before calling the |badness| - subroutine. @^inner loop@> */ - - if ((cur_active_width[3]!=0)||(cur_active_width[4]!=0)|| - (cur_active_width[5]!=0)||(cur_active_width[6]!=0)) { if (do_last_line_fit) { - if (cur_p==null) { /* the last line of a paragraph */ - /* @<Perform computations for last line and |goto found|@>;*/ - - /* Here we compute the adjustment |g| and badness |b| for - a line from |r| to the end of the paragraph. When any - of the criteria for adjustment is violated we fall - through to the normal algorithm. - - The last line must be too short, and have infinite - stretch entirely due to |par_fill_skip|.*/ - if ((active_short(r)==0)||(active_glue(r)<=0)) - /* previous line was neither stretched nor shrunk, or - was infinitely bad*/ - goto NOT_FOUND; - if ((cur_active_width[3]!=fill_width[0])|| - (cur_active_width[4]!=fill_width[1])|| - (cur_active_width[5]!=fill_width[2])|| - (cur_active_width[6]!=fill_width[3])) - /* infinite stretch of this line not entirely due to - |par_fill_skip| */ - goto NOT_FOUND; - if (active_short(r)>0) - g=cur_active_width[2]; - else - g=cur_active_width[7]; - if (g<=0) - /*no finite stretch resp.\ no shrink*/ - goto NOT_FOUND; - arith_error=false; - g=fract(g,active_short(r),active_glue(r),max_dimen); - if (last_line_fit<1000) - g=fract(g,last_line_fit,1000,max_dimen); - if (arith_error) { - if (active_short(r)>0) - g=max_dimen; - else - g=-max_dimen; + /* @<Adjust \(t)the additional data for last line@>; */ + if (cur_p == null) + shortfall = 0; + if (shortfall > 0) { + g = cur_active_width[2]; + } else if (shortfall < 0) { + g = cur_active_width[7]; + } else { + g = 0; } - if (g>0) { - /*@<Set the value of |b| to the badness of the last line - for stretching, compute the corresponding |fit_class, - and |goto found||@>*/ - /* These badness computations are rather similar to - those of the standard algorithm, with the adjustment - amount |g| replacing the |shortfall|.*/ - if (g>shortfall) - g=shortfall; - if (g>7230584) { - if (cur_active_width[2]<1663497) { - b=inf_bad; - fit_class=very_loose_fit; - goto FOUND; - } - } - b=badness(g,cur_active_width[2]); - if (b>99) { fit_class=very_loose_fit; } - else if (b>12) { fit_class=loose_fit; } - else { fit_class=decent_fit; } - goto FOUND; - } else if (g<0) { - /*@<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[7]) - g=-cur_active_width[7]; - b=badness(-g,cur_active_width[7]); - if (b>12) fit_class=tight_fit; - else fit_class=decent_fit; - goto FOUND; + } + FOUND: + if ((b > inf_bad) || (pi == eject_penalty)) { + /* @<Prepare to deactivate node~|r|, and |goto deactivate| unless + there is a reason to consider lines of text from |r| to |cur_p|@> */ + /* During the final pass, we dare not lose all active nodes, lest we lose + touch with the line breaks already found. The code shown here makes + sure that such a catastrophe does not happen, by permitting overfull + boxes as a last resort. This particular part of \TeX\ was a source of + several subtle bugs before the correct program logic was finally + discovered; readers who seek to ``improve'' \TeX\ should therefore + think thrice before daring to make any changes here. + @^overfull boxes@> + */ + if (final_pass && (minimum_demerits == awful_bad) && + (vlink(r) == active) && (prev_r == active)) { + artificial_demerits = true; /* set demerits zero, this break is forced */ + } else if (b > threshold) { + goto DEACTIVATE; } - } - NOT_FOUND: - shortfall=0; + node_r_stays_active = false; + } else { + prev_r = r; + if (b > threshold) + continue; + node_r_stays_active = true; } - b=0; - fit_class=decent_fit; /* infinite stretch */ - } else { - if (shortfall>7230584 && cur_active_width[2]<1663497) { - b=inf_bad; - fit_class=very_loose_fit; + /* @<Record a new feasible break@>; */ + /* When we get to this part of the code, the line from |r| to |cur_p| is + feasible, its badness is~|b|, and its fitness classification is + |fit_class|. We don't want to make an active node for this break yet, + but we will compute the total demerits and record them in the + |minimal_demerits| array, if such a break is the current champion among + all ways to get to |cur_p| in a given line-number class and fitness + class. + */ + if (artificial_demerits) { + d = 0; } else { - b=badness(shortfall,cur_active_width[2]); - if (b>99) { fit_class=very_loose_fit; } - else if (b>12) { fit_class=loose_fit; } - else { fit_class=decent_fit; } + /* @<Compute the demerits, |d|, from |r| to |cur_p|@>; */ + d = line_penalty + b; + if (abs(d) >= 10000) + d = 100000000; + else + d = d * d; + if (pi != 0) { + if (pi > 0) { + d += (pi * pi); + } else if (pi > eject_penalty) { + d -= (pi * pi); + } + } + if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) { + if (cur_p != null) + d += double_hyphen_demerits; + else + d += final_hyphen_demerits; + } + if (abs(fit_class - fitness(r)) > 1) + d = d + adj_demerits; } - } - } else { - /* Set the value of |b| to the badness for shrinking the line, - and compute the corresponding |fit_class|@>; */ - /* Shrinkability is never infinite in a paragraph; we can shrink - the line from |r| to |cur_p| by at most - |cur_active_width[7]|.*/ - if (-shortfall>cur_active_width[7]) - b=inf_bad+1; - else - b=badness(-shortfall,cur_active_width[7]); - if (b>12) - fit_class=tight_fit; - else - fit_class=decent_fit; - } - if (do_last_line_fit) { - /* @<Adjust \(t)the additional data for last line@>; */ - if (cur_p==null) shortfall=0; - if (shortfall>0) { g=cur_active_width[2]; } - else if (shortfall<0) { g=cur_active_width[7]; } - else { g=0;} - } - FOUND: - if ((b>inf_bad)||(pi==eject_penalty)) { - /* @<Prepare to deactivate node~|r|, and |goto deactivate| unless - there is a reason to consider lines of text from |r| to |cur_p|@> */ - /* During the final pass, we dare not lose all active nodes, lest we lose - touch with the line breaks already found. The code shown here makes - sure that such a catastrophe does not happen, by permitting overfull - boxes as a last resort. This particular part of \TeX\ was a source of - several subtle bugs before the correct program logic was finally - discovered; readers who seek to ``improve'' \TeX\ should therefore - think thrice before daring to make any changes here. - @^overfull boxes@> - */ - if (final_pass && (minimum_demerits==awful_bad) && - (vlink(r)==active) && (prev_r==active)) { - artificial_demerits=true; /* set demerits zero, this break is forced */ - } else if (b>threshold) { - goto DEACTIVATE; - } - node_r_stays_active=false; - } else { - prev_r=r; - if (b>threshold) - continue; - node_r_stays_active=true; - } - /* @<Record a new feasible break@>;*/ - /* When we get to this part of the code, the line from |r| to |cur_p| is - feasible, its badness is~|b|, and its fitness classification is - |fit_class|. We don't want to make an active node for this break yet, - but we will compute the total demerits and record them in the - |minimal_demerits| array, if such a break is the current champion among - all ways to get to |cur_p| in a given line-number class and fitness - class. - */ - if (artificial_demerits) { - d=0; - } else { - /* @<Compute the demerits, |d|, from |r| to |cur_p|@>;*/ - d=line_penalty+b; - if (abs(d)>=10000) d=100000000; else d=d*d; - if (pi!=0) { - if (pi>0) { - d += (pi*pi); - } else if (pi>eject_penalty) { - d -= (pi*pi); + if (tracing_paragraphs > 0) + print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); + d += total_demerits(r); /*this is the minimum total demerits + from the beginning to |cur_p| via |r| */ + if (d <= minimal_demerits[fit_class]) { + minimal_demerits[fit_class] = d; + best_place[fit_class] = break_node(r); + best_pl_line[fit_class] = l; + if (do_last_line_fit) { + /* Store \(a)additional data for this feasible break@>; */ + /* For each feasible break we record the shortfall and glue stretch or + shrink (or adjustment). */ + best_pl_short[fit_class] = shortfall; + best_pl_glue[fit_class] = g; + } + if (d < minimum_demerits) + minimum_demerits = d; } - } - if ((break_type==hyphenated_node)&&(type(r)==hyphenated_node)) { - if (cur_p!=null) - d += double_hyphen_demerits; - else - d += final_hyphen_demerits; - } - if (abs(fit_class-fitness(r))>1) d=d+adj_demerits; - } - if (tracing_paragraphs>0) - print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); - d += total_demerits(r); /*this is the minimum total demerits - from the beginning to |cur_p| via |r| */ - if (d<=minimal_demerits[fit_class]) { - minimal_demerits[fit_class]=d; - best_place[fit_class]=break_node(r); - best_pl_line[fit_class]=l; - if (do_last_line_fit) { - /* Store \(a)additional data for this feasible break@>; */ - /* For each feasible break we record the shortfall and glue stretch or - shrink (or adjustment).*/ - best_pl_short[fit_class]=shortfall; - best_pl_glue[fit_class]=g; - } - if (d<minimum_demerits) - minimum_demerits=d; - } - /* /Record a new feasible break */ - if (node_r_stays_active) - continue; /*|prev_r| has been set to |r| */ - DEACTIVATE: - /* @<Deactivate node |r|@>; */ - /* When an active node disappears, we must delete an adjacent delta node if - the active node was at the beginning or the end of the active list, or - if it was surrounded by delta nodes. We also must preserve the property - that |cur_active_width| represents the length of material from - |vlink(prev_r)| to~|cur_p|.*/ - - vlink(prev_r)=vlink(r); - flush_node(r); - if (prev_r==active) { - /*@<Update the active widths, since the first active node has been - deleted@>*/ - /* The following code uses the fact that |type(active)<>delta_node|. - If the active list has just become empty, we do not need to update the - |active_width| array, since it will be initialized when an active - node is next inserted. - */ - r=vlink(active); - if (type(r)==delta_node) { - do_all_eight(update_active); /* IMPLICIT r */ - do_all_eight(copy_to_cur_active); - vlink(active)=vlink(r); + /* /Record a new feasible break */ + if (node_r_stays_active) + continue; /*|prev_r| has been set to |r| */ + DEACTIVATE: + /* @<Deactivate node |r|@>; */ + /* When an active node disappears, we must delete an adjacent delta node if + the active node was at the beginning or the end of the active list, or + if it was surrounded by delta nodes. We also must preserve the property + that |cur_active_width| represents the length of material from + |vlink(prev_r)| to~|cur_p|. */ + + vlink(prev_r) = vlink(r); flush_node(r); - } - } else if (type(prev_r)==delta_node){ - r=vlink(prev_r); - if (r==active) { - do_all_eight(downdate_width); /* IMPLICIT prev_r */ - vlink(prev_prev_r)=active; - flush_node(prev_r); - prev_r=prev_prev_r; - } else if (type(r)==delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - do_all_eight(combine_two_deltas); /* IMPLICIT r prev_r */ - vlink(prev_r)=vlink(r); - flush_node(r); - } - } - } + if (prev_r == active) { + /*@<Update the active widths, since the first active node has been + deleted@> */ + /* The following code uses the fact that |type(active)<>delta_node|. + If the active list has just become empty, we do not need to update the + |active_width| array, since it will be initialized when an active + node is next inserted. + */ + r = vlink(active); + if (type(r) == delta_node) { + do_all_eight(update_active); /* IMPLICIT r */ + do_all_eight(copy_to_cur_active); + vlink(active) = vlink(r); + flush_node(r); + } + } else if (type(prev_r) == delta_node) { + r = vlink(prev_r); + if (r == active) { + do_all_eight(downdate_width); /* IMPLICIT prev_r */ + vlink(prev_prev_r) = active; + flush_node(prev_r); + prev_r = prev_prev_r; + } else if (type(r) == delta_node) { + do_all_eight(update_width); /* IMPLICIT ,r */ + do_all_eight(combine_two_deltas); /* IMPLICIT r prev_r */ + vlink(prev_r) = vlink(r); + flush_node(r); + } + } + } } void -ext_do_line_break (boolean d, - int pretolerance, - int tracing_paragraphs, - int tolerance, - scaled emergency_stretch, - int looseness, - int hyphen_penalty, - int ex_hyphen_penalty, - int pdf_adjust_spacing, - halfword par_shape_ptr, - int adj_demerits, - int pdf_protrude_chars, - int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, - int hang_indent, - int hsize, - int hang_after, - halfword left_skip, - halfword right_skip, - int pdf_each_line_height, - int pdf_each_line_depth, - int pdf_first_line_height, - int pdf_last_line_depth, - halfword inter_line_penalties_ptr, - int inter_line_penalty, - int club_penalty, - halfword club_penalties_ptr, - halfword display_widow_penalties_ptr, - halfword widow_penalties_ptr, - int display_widow_penalty, - int widow_penalty, - int broken_penalty, - halfword final_par_glue) { - /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE;*/ - halfword cur_p,q,r,s; /* miscellaneous nodes of temporary interest */ - - /* Get ready to start ... */ - minimum_demerits=awful_bad; - minimal_demerits[tight_fit]=awful_bad; - minimal_demerits[decent_fit]=awful_bad; - minimal_demerits[loose_fit]=awful_bad; - minimal_demerits[very_loose_fit]=awful_bad; - - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - if (par_shape_ptr==null) { - if (hang_indent==0) { - last_special_line=0; - second_width=hsize; - second_indent=0; - } else { - /* @<Set line length parameters in preparation for hanging indentation@>*/ - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - last_special_line=abs(hang_after); - if (hang_after<0) { - first_width=hsize-abs(hang_indent); - if (hang_indent>=0) - first_indent=hang_indent; - else - first_indent=0; - second_width=hsize; - second_indent=0; - } else { - first_width=hsize; - first_indent=0; - second_width=hsize-abs(hang_indent); - if (hang_indent>=0) - second_indent=hang_indent; - else - second_indent=0; - } +ext_do_line_break(boolean d, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + int hyphen_penalty, + int ex_hyphen_penalty, + int pdf_adjust_spacing, + halfword par_shape_ptr, + int adj_demerits, + int pdf_protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, + int hang_indent, + int hsize, + int hang_after, + halfword left_skip, + halfword right_skip, + int pdf_each_line_height, + int pdf_each_line_depth, + int pdf_first_line_height, + int pdf_last_line_depth, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword display_widow_penalties_ptr, + halfword widow_penalties_ptr, + int display_widow_penalty, + int widow_penalty, + int broken_penalty, halfword final_par_glue) +{ + /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */ + halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */ + + /* Get ready to start ... */ + minimum_demerits = awful_bad; + minimal_demerits[tight_fit] = awful_bad; + minimal_demerits[decent_fit] = awful_bad; + minimal_demerits[loose_fit] = awful_bad; + minimal_demerits[very_loose_fit] = awful_bad; + + /* We compute the values of |easy_line| and the other local variables relating + to line length when the |line_break| procedure is initializing itself. */ + if (par_shape_ptr == null) { + if (hang_indent == 0) { + last_special_line = 0; + second_width = hsize; + second_indent = 0; + } else { + /* @<Set line length parameters in preparation for hanging indentation@> */ + /* We compute the values of |easy_line| and the other local variables relating + to line length when the |line_break| procedure is initializing itself. */ + last_special_line = abs(hang_after); + if (hang_after < 0) { + first_width = hsize - abs(hang_indent); + if (hang_indent >= 0) + first_indent = hang_indent; + else + first_indent = 0; + second_width = hsize; + second_indent = 0; + } else { + first_width = hsize; + first_indent = 0; + second_width = hsize - abs(hang_indent); + if (hang_indent >= 0) + second_indent = hang_indent; + else + second_indent = 0; + } + } + } else { + last_special_line = vinfo(par_shape_ptr + 1) - 1; + second_indent = varmem[(par_shape_ptr + (2 * last_special_line))].cint; + second_width = + varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint; } - } else { - last_special_line=vinfo(par_shape_ptr+1)-1; - second_indent=varmem[(par_shape_ptr+(2*last_special_line))].cint; - second_width=varmem[(par_shape_ptr+2*(last_special_line+1)+1)].cint; - } - if (looseness==0) - easy_line=last_special_line; - else - easy_line=max_halfword; - - no_shrink_error_yet=true; - check_shrinkage(left_skip); - check_shrinkage(right_skip); - q=left_skip; - r=right_skip; - background[1]=width(q)+width(r); - background[2]=0; - background[3]=0; - background[4]=0; - background[5]=0; - background[6]=0; - background[2+stretch_order(q)] = stretch(q); - background[2+stretch_order(r)] += stretch(r); - background[7]=shrink(q)+shrink(r); - if (pdf_adjust_spacing > 1) { - background[8] = 0; - background[9] = 0; - max_stretch_ratio = -1; - max_shrink_ratio = -1; - cur_font_step = -1; - set_prev_char_p(null); - } - /* @<Check for special treatment of last line of paragraph@>;*/ - /* The new algorithm for the last line requires that the stretchability - |par_fill_skip| is infinite and the stretchability of |left_skip| plus - |right_skip| is finite. - */ - do_last_line_fit=false; - if (last_line_fit>0) { - q=glue_ptr(last_line_fill); - if ((stretch(q)>0)&&(stretch_order(q)>normal)) { - if ((background[3]==0)&&(background[4]==0)&& - (background[5]==0)&&(background[6]==0)) { - do_last_line_fit=true; - fill_width[0]=0; - fill_width[1]=0; - fill_width[2]=0; - fill_width[3]=0; - fill_width[stretch_order(q)-1]=stretch(q); - } + if (looseness == 0) + easy_line = last_special_line; + else + easy_line = max_halfword; + + no_shrink_error_yet = true; + check_shrinkage(left_skip); + check_shrinkage(right_skip); + q = left_skip; + r = right_skip; + background[1] = width(q) + width(r); + background[2] = 0; + background[3] = 0; + background[4] = 0; + background[5] = 0; + background[6] = 0; + background[2 + stretch_order(q)] = stretch(q); + background[2 + stretch_order(r)] += stretch(r); + background[7] = shrink(q) + shrink(r); + if (pdf_adjust_spacing > 1) { + background[8] = 0; + background[9] = 0; + max_stretch_ratio = -1; + max_shrink_ratio = -1; + cur_font_step = -1; + set_prev_char_p(null); } - } - /* @<DIR: Initialize |dir_ptr| for |line_break|@> */ - if (dir_ptr!=null) { - fprintf(stdout,"-dir_node s %d\n",dir_ptr); - flush_node_list(dir_ptr); - dir_ptr=null; - } - /*push_dir(paragraph_dir);*/ /* TODO what was the point of this? */ - - /* @<Find optimal breakpoints@>;*/ - threshold=pretolerance; - if (threshold>=0) { - if (tracing_paragraphs>0) { - begin_diagnostic(); - tprint_nl("@firstpass"); + /* @<Check for special treatment of last line of paragraph@>; */ + /* The new algorithm for the last line requires that the stretchability + |par_fill_skip| is infinite and the stretchability of |left_skip| plus + |right_skip| is finite. + */ + do_last_line_fit = false; + if (last_line_fit > 0) { + q = glue_ptr(last_line_fill); + if ((stretch(q) > 0) && (stretch_order(q) > normal)) { + if ((background[3] == 0) && (background[4] == 0) && + (background[5] == 0) && (background[6] == 0)) { + do_last_line_fit = true; + fill_width[0] = 0; + fill_width[1] = 0; + fill_width[2] = 0; + fill_width[3] = 0; + fill_width[stretch_order(q) - 1] = stretch(q); + } + } } - second_pass=false; - final_pass=false; - } else { - threshold=tolerance; - second_pass=true; - final_pass=(emergency_stretch<=0); - if (tracing_paragraphs>0) - begin_diagnostic(); - } - - while (1) { - halfword first_p; - halfword nest_stack[10]; - int nest_index = 0; - if (threshold>inf_bad) - threshold=inf_bad; - /* Create an active breakpoint representing the beginning of the paragraph */ - q=new_node(unhyphenated_node,decent_fit); - vlink(q)=active; - break_node(q)=null; - line_number(q)=cur_list.pg_field+1; - total_demerits(q)=0; - active_short(q)=0; - active_glue(q)=0; - vlink(active)=q; - do_all_eight(store_background); - passive=null; - printed_node=temp_head; - pass_number=0; - font_in_short_display=null_font; - /* /Create an active breakpoint representing the beginning of the paragraph */ - auto_breaking=true; - cur_p=vlink(temp_head); - assert(alink(cur_p)==temp_head); - /* LOCAL: Initialize with first |local_paragraph| node */ - if ((type(cur_p)==whatsit_node)&&(subtype(cur_p)==local_par_node)) { - internal_pen_inter=local_pen_inter(cur_p); - internal_pen_broken=local_pen_broken(cur_p); - init_internal_left_box=local_box_left(cur_p); - init_internal_left_box_width=local_box_left_width(cur_p); - internal_left_box=init_internal_left_box; - internal_left_box_width=init_internal_left_box_width; - internal_right_box=local_box_right(cur_p); - internal_right_box_width=local_box_right_width(cur_p); - } else { - internal_pen_inter=0; - internal_pen_broken=0; - init_internal_left_box=null; - init_internal_left_box_width=0; - internal_left_box=init_internal_left_box; - internal_left_box_width=init_internal_left_box_width; - internal_right_box=null; - internal_right_box_width=0; + /* @<DIR: Initialize |dir_ptr| for |line_break|@> */ + if (dir_ptr != null) { + flush_node_list(dir_ptr); + dir_ptr = null; } - /* /LOCAL: Initialize with first |local_paragraph| node */ - set_prev_char_p (null); - prev_legal = null; - rejected_cur_p = null; - try_prev_break = false; - before_rejected_cur_p = false; - first_p = cur_p; - /* to access the first node of paragraph as the first active node - has |break_node=null| */ - while ((cur_p!=null)&&(vlink(active)!=active)) { - /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at disc_nodes. */ - - while (is_char_node(cur_p)) { - /* Advance \(c)|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. - */ - act_width += (is_rotated(line_break_dir)) - ? (glyph_height(cur_p)+glyph_depth(cur_p)) - : glyph_width(cur_p); - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(cur_p))) { - set_prev_char_p (cur_p); - add_char_stretch(active_width[8],cur_p); - add_char_shrink(active_width[9],cur_p); - } - cur_p=vlink(cur_p); - while (cur_p==null && nest_index>0) { - cur_p = nest_stack[--nest_index]; - /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ + /*push_dir(paragraph_dir); *//* TODO what was the point of this? */ + + /* @<Find optimal breakpoints@>; */ + threshold = pretolerance; + if (threshold >= 0) { + if (tracing_paragraphs > 0) { + begin_diagnostic(); + tprint_nl("@firstpass"); } - } - if (cur_p==null) { /* TODO */ - tconfusion("linebreak_tail"); - } - /* 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 |act_width| for the component of active width that represents - real width as opposed to glue. */ - - switch (type(cur_p)) { - - case hlist_node: - case vlist_node: - act_width += (dir_orthogonal(dir_primary[box_dir(cur_p)], dir_primary[line_break_dir])) - ? (depth(cur_p)+height(cur_p)) - : width(cur_p); - break; - case rule_node: - act_width += width(cur_p); - break; - case whatsit_node: - /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>;*/ - switch (subtype(cur_p)) { - case local_par_node: /* @<LOCAL: Advance past a |local_paragraph| node@>;*/ - internal_pen_inter=local_pen_inter(cur_p); - internal_pen_broken=local_pen_broken(cur_p); - internal_left_box=local_box_left(cur_p); - internal_left_box_width=local_box_left_width(cur_p); - internal_right_box=local_box_right(cur_p); - internal_right_box_width=local_box_right_width(cur_p); - break; - case dir_node: /* @<DIR: Adjust the dir stack for the |line_break| routine@>;*/ - if (dir_dir(cur_p)>=0) { - line_break_dir=dir_dir(cur_p); - push_dir_node(cur_p); - } else { - pop_dir_node(); - if (dir_ptr!=null) - line_break_dir=dir_dir(dir_ptr); - } - break; - case pdf_refxform_node: - case pdf_refximage_node: - act_width += pdf_width(cur_p); - } - /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */ - break; - case glue_node: - /* @<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. */ - if (auto_breaking) { - halfword prev_p = alink(cur_p); - if (prev_p!=temp_head && - (is_char_node(prev_p) || - precedes_break(prev_p)|| - ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit)))) { - ext_try_break(0,unhyphenated_node, pdf_adjust_spacing, par_shape_ptr, - adj_demerits, tracing_paragraphs, pdf_protrude_chars, - line_penalty, last_line_fit, double_hyphen_demerits, - final_hyphen_demerits,first_p,cur_p); - } - } - check_shrinkage(glue_ptr(cur_p)); - q=glue_ptr(cur_p); - act_width += width(q); - active_width[2+stretch_order(q)] += stretch(q); - active_width[7] += shrink(q); - break; - case kern_node: - if (subtype(cur_p)==explicit) { - kern_break(); + second_pass = false; + final_pass = false; + } else { + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + if (tracing_paragraphs > 0) + begin_diagnostic(); + } + + while (1) { + halfword first_p; + halfword nest_stack[10]; + int nest_index = 0; + if (threshold > inf_bad) + threshold = inf_bad; + /* Create an active breakpoint representing the beginning of the paragraph */ + q = new_node(unhyphenated_node, decent_fit); + vlink(q) = active; + break_node(q) = null; + line_number(q) = cur_list.pg_field + 1; + total_demerits(q) = 0; + active_short(q) = 0; + active_glue(q) = 0; + vlink(active) = q; + do_all_eight(store_background); + passive = null; + printed_node = temp_head; + pass_number = 0; + font_in_short_display = null_font; + /* /Create an active breakpoint representing the beginning of the paragraph */ + auto_breaking = true; + cur_p = vlink(temp_head); + assert(alink(cur_p) == temp_head); + /* LOCAL: Initialize with first |local_paragraph| node */ + if ((type(cur_p) == whatsit_node) && (subtype(cur_p) == local_par_node)) { + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + init_internal_left_box = local_box_left(cur_p); + init_internal_left_box_width = local_box_left_width(cur_p); + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); } else { - act_width += width(cur_p); - if ((pdf_adjust_spacing > 1) && (subtype(cur_p) == normal)) { - add_kern_stretch(active_width[8],cur_p); - add_kern_shrink(active_width[9],cur_p); - } + internal_pen_inter = 0; + internal_pen_broken = 0; + init_internal_left_box = null; + init_internal_left_box_width = 0; + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = null; + internal_right_box_width = 0; } - break; - case disc_node: - /* @<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. */ - if (second_pass) { - int actual_penalty = hyphen_penalty; - if (subtype(cur_p)==automatic_disc) - actual_penalty = ex_hyphen_penalty; - s=vlink_pre_break(cur_p); - do_one_seven_eight(reset_disc_width); - if (s==null) { /* trivial pre-break */ - ext_try_break(actual_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - } else { - do { - /* @<Add the width of node |s| to |disc_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - disc_width[1] += (glyph_height(s)+glyph_depth(s)); - } else { - disc_width[1] += glyph_width(s); + /* /LOCAL: Initialize with first |local_paragraph| node */ + set_prev_char_p(null); + prev_legal = null; + rejected_cur_p = null; + try_prev_break = false; + before_rejected_cur_p = false; + first_p = cur_p; + /* to access the first node of paragraph as the first active node + has |break_node=null| */ + while ((cur_p != null) && (vlink(active) != active)) { + /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at disc_nodes. */ + + while (is_char_node(cur_p)) { + /* Advance \(c)|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. + */ + act_width += (is_rotated(line_break_dir)) + ? (glyph_height(cur_p) + glyph_depth(cur_p)) + : glyph_width(cur_p); + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(cur_p))) { + set_prev_char_p(cur_p); + add_char_stretch(active_width[8], cur_p); + add_char_shrink(active_width[9], cur_p); } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(disc_width[8],s); - add_char_shrink(disc_width[9],s); + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ } - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) { - disc_width[1] += width(s); - } else { - disc_width[1] += (depth(s)+height(s)); - } - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(disc_width[8],s); - add_kern_shrink(disc_width[9],s); - } - /* fall through */ - case rule_node: - disc_width[1] += width(s); - break; - case disc_node: - tconfusion("pre_break_disc"); - break; - default: - tconfusion("disc3"); - } - } - /* /Add the width of node |s| to |disc_width| */ - s=vlink(s); - } while (s!=null); - do_one_seven_eight(add_disc_width_to_active_width); - ext_try_break(actual_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits, - first_p,cur_p); - do_one_seven_eight(sub_disc_width_from_active_width); - } -#define FOO 1 -#if FOO -#if 0 - if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p); - cur_p = no_break(cur_p); -#else - s=vlink_no_break(cur_p); - while (s!=null) { - /* @<Add the width of node |s| to |act_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - act_width += glyph_height(s)+glyph_depth(s); - } else { - act_width += glyph_width(s); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(active_width[8],s); - add_char_shrink(active_width[9],s); - }; - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - act_width += width(s); - else - act_width += (depth(s)+height(s)); + } + if (cur_p == null) { /* TODO */ + tconfusion("linebreak_tail"); + } + /* 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 |act_width| for the component of active width that represents + real width as opposed to glue. */ + + switch (type(cur_p)) { + + case hlist_node: + case vlist_node: + act_width += + (dir_orthogonal + (dir_primary[box_dir(cur_p)], dir_primary[line_break_dir])) + ? (depth(cur_p) + height(cur_p)) + : width(cur_p); break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(active_width[8],s); - add_kern_shrink(active_width[9],s); + case rule_node: + act_width += width(cur_p); + break; + case whatsit_node: + /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>; */ + switch (subtype(cur_p)) { + case local_par_node: /* @<LOCAL: Advance past a |local_paragraph| node@>; */ + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + internal_left_box = local_box_left(cur_p); + internal_left_box_width = local_box_left_width(cur_p); + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + break; + case dir_node: /* @<DIR: Adjust the dir stack for the |line_break| routine@>; */ + if (dir_dir(cur_p) >= 0) { + line_break_dir = dir_dir(cur_p); + push_dir_node(cur_p); + } else { + pop_dir_node(); + if (dir_ptr != null) + line_break_dir = dir_dir(dir_ptr); + } + break; + case pdf_refxform_node: + case pdf_refximage_node: + act_width += pdf_width(cur_p); } - /* fall through */ - case rule_node: - act_width += width(s); + /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */ break; - case disc_node: - /* what now */ - assert(vlink(s)==null); /* discs are _always_ last */ - /* NB: temporary solution: not good, but not bad either */ - s = no_break(s); + case glue_node: + /* @<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. */ + /* *INDENT-OFF* */ + if (auto_breaking) { + halfword prev_p = alink(cur_p); + if (prev_p != temp_head && + (is_char_node(prev_p) || + precedes_break(prev_p) || + ((type(prev_p) == kern_node) + && (subtype(prev_p) != explicit)))) { + ext_try_break(0, unhyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, pdf_protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } + } + /* *INDENT-ON* */ + check_shrinkage(glue_ptr(cur_p)); + q = glue_ptr(cur_p); + act_width += width(q); + active_width[2 + stretch_order(q)] += stretch(q); + active_width[7] += shrink(q); break; - default: - tconfusion("disc5"); - } - } - /* /Add the width of node |s| to |act_width|;*/ - s=vlink(s); - } -#endif - } else { /* first pass, just take the no_break path */ -#else /* FOO */ - } -#endif /* FOO */ + case kern_node: + if (subtype(cur_p) == explicit) { + kern_break(); + } else { + act_width += width(cur_p); + if ((pdf_adjust_spacing > 1) && (subtype(cur_p) == normal)) { + add_kern_stretch(active_width[8], cur_p); + add_kern_shrink(active_width[9], cur_p); + } + } + break; + case disc_node: + /* @<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. */ + if (second_pass) { + int actual_penalty = hyphen_penalty; + if (subtype(cur_p) == automatic_disc) + actual_penalty = ex_hyphen_penalty; + s = vlink_pre_break(cur_p); + do_one_seven_eight(reset_disc_width); + if (s == null) { /* trivial pre-break */ + ext_try_break(actual_penalty, hyphenated_node, + pdf_adjust_spacing, par_shape_ptr, + adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } else { + do { + /* @<Add the width of node |s| to |disc_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + disc_width[1] += + (glyph_height(s) + glyph_depth(s)); + } else { + disc_width[1] += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(disc_width[8], s); + add_char_shrink(disc_width[9], s); + } + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (! + (dir_orthogonal + (dir_primary[box_dir(s)], + dir_primary[line_break_dir]))) { + disc_width[1] += width(s); + } else { + disc_width[1] += (depth(s) + height(s)); + } + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(disc_width[8], s); + add_kern_shrink(disc_width[9], s); + } + /* fall through */ + case rule_node: + disc_width[1] += width(s); + break; + case disc_node: + tconfusion("pre_break_disc"); + break; + default: + tconfusion("disc3"); + } + } + /* /Add the width of node |s| to |disc_width| */ + s = vlink(s); + } while (s != null); + do_one_seven_eight(add_disc_width_to_active_width); + ext_try_break(actual_penalty, hyphenated_node, + pdf_adjust_spacing, par_shape_ptr, + adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + do_one_seven_eight(sub_disc_width_from_active_width); + } +#define FOO 1 +#if FOO +# if 0 + if (vlink(cur_p) != null) + nest_stack[nest_index++] = vlink(cur_p); + cur_p = no_break(cur_p); +# else + s = vlink_no_break(cur_p); + while (s != null) { + /* @<Add the width of node |s| to |act_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + act_width += glyph_height(s) + glyph_depth(s); + } else { + act_width += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(active_width[8], s); + add_char_shrink(active_width[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(s)], + dir_primary + [line_break_dir]))) + act_width += width(s); + else + act_width += (depth(s) + height(s)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(active_width[8], s); + add_kern_shrink(active_width[9], s); + } + /* fall through */ + case rule_node: + act_width += width(s); + break; + case disc_node: + /* what now */ + assert(vlink(s) == null); /* discs are _always_ last */ + /* NB: temporary solution: not good, but not bad either */ + s = no_break(s); + break; + default: + tconfusion("disc5"); + } + } + /* /Add the width of node |s| to |act_width|; */ + s = vlink(s); + } +# endif + } else { /* first pass, just take the no_break path */ +#else /* FOO */ + } +#endif /* FOO */ #if 0 - if (vlink_no_break(cur_p)!=null) { - if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p); - /* fprintf(stderr,"Node Push %d [%d]->[%d] / [%d]\n",(nest_index-1),(int)cur_p,(int)vlink(cur_p),(int)vlink_no_break(cur_p));*/ - cur_p = no_break(cur_p); - } + if (vlink_no_break(cur_p) != null) { + if (vlink(cur_p) != null) + nest_stack[nest_index++] = vlink(cur_p); + /* fprintf(stderr,"Node Push %d [%d]->[%d] / [%d]\n",(nest_index-1),(int)cur_p,(int)vlink(cur_p),(int)vlink_no_break(cur_p)); */ + cur_p = no_break(cur_p); + } #else - s=vlink_no_break(cur_p); - while (s!=null) { - /* @<Add the width of node |s| to |act_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - act_width += glyph_height(s)+glyph_depth(s); - } else { - act_width += glyph_width(s); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(active_width[8],s); - add_char_shrink(active_width[9],s); - }; - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - act_width += width(s); - else - act_width += (depth(s)+height(s)); - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(active_width[8],s); - add_kern_shrink(active_width[9],s); + s = vlink_no_break(cur_p); + while (s != null) { + /* @<Add the width of node |s| to |act_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + act_width += glyph_height(s) + glyph_depth(s); + } else { + act_width += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(active_width[8], s); + add_char_shrink(active_width[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(s)], + dir_primary[line_break_dir]))) + act_width += width(s); + else + act_width += (depth(s) + height(s)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(active_width[8], s); + add_kern_shrink(active_width[9], s); + } + /* fall through */ + case rule_node: + act_width += width(s); + break; + case disc_node: + assert(vlink(s) == null); /* discs are _always_ last */ + s = no_break(s); + break; + default: + tconfusion("disc4"); + } + } + /* /Add the width of node |s| to |act_width|; */ + s = vlink(s); } - /* fall through */ - case rule_node: - act_width += width(s); - break; - case disc_node: - assert(vlink(s)==null); /* discs are _always_ last */ - s = no_break(s); - break; - default: - tconfusion("disc4"); - } - } - /* /Add the width of node |s| to |act_width|;*/ - s=vlink(s); - } #endif #if FOO + } +#endif /* FOO */ + break; +case math_node: + auto_breaking = (subtype(cur_p) == after); + kern_break(); + break; +case penalty_node: + ext_try_break(penalty(cur_p), unhyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, + first_p, cur_p); + break; +case mark_node: +case ins_node: +case adjust_node: + break; +case glue_spec_node: + fprintf(stdout, "\nfound a glue_spec in a paragraph!"); + break; +default: + fprintf(stdout, "\ntype=%d", type(cur_p)); + tconfusion("paragraph"); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ } -#endif /* FOO */ - break; - case math_node: - auto_breaking=(subtype(cur_p)==after); - kern_break(); - break; - case penalty_node: - ext_try_break(penalty(cur_p),unhyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - break; - case mark_node: - case ins_node: - case adjust_node: - break; - case glue_spec_node: - fprintf(stdout, "\nfound a glue_spec in a paragraph!"); - break; - default: - fprintf(stdout, "\ntype=%d",type(cur_p)); - tconfusion("paragraph") ; - } - cur_p=vlink(cur_p); - while (cur_p==null && nest_index>0) { - cur_p = nest_stack[--nest_index]; - /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p);*/ - } } - if (cur_p==null) { - /* 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 ``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. - - 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. - */ - - ext_try_break(eject_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars,line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - if (vlink(active)!=active) { - /* @<Find an active node with fewest demerits@>;*/ - r=vlink(active); - fewest_demerits=awful_bad; - do { - if (type(r)!=delta_node) { - if (total_demerits(r)<fewest_demerits) { - fewest_demerits=total_demerits(r); - best_bet=r; - } - } - r=vlink(r); - } while (r!=active); - best_line=line_number(best_bet); - - /* /Find an active node with fewest demerits;*/ - if (looseness==0) - goto DONE; - /*@<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=vlink(active); - actual_looseness=0; - do { - if (type(r)!=delta_node) { - line_diff=line_number(r)-best_line; - if (((line_diff<actual_looseness)&&(looseness<=line_diff))|| - ((line_diff>actual_looseness)&&(looseness>=line_diff))) { - best_bet=r; - actual_looseness=line_diff; - fewest_demerits=total_demerits(r); - } else if ((line_diff==actual_looseness)&& - (total_demerits(r)<fewest_demerits)) { - best_bet=r; - fewest_demerits=total_demerits(r); - } - } - r=vlink(r); - } while (r!=active); - best_line=line_number(best_bet); - - /* /Find the best active node for the desired looseness;*/ - if ((actual_looseness==looseness)|| final_pass) - goto DONE; - } + if (cur_p == null) { + /* 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 ``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. + + 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. + */ + + ext_try_break(eject_penalty, hyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, first_p, + cur_p); + if (vlink(active) != active) { + /* @<Find an active node with fewest demerits@>; */ + r = vlink(active); + fewest_demerits = awful_bad; + do { + if (type(r) != delta_node) { + if (total_demerits(r) < fewest_demerits) { + fewest_demerits = total_demerits(r); + best_bet = r; + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + + /* /Find an active node with fewest demerits; */ + if (looseness == 0) + goto DONE; + /*@<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 = vlink(active); + actual_looseness = 0; + do { + if (type(r) != delta_node) { + line_diff = line_number(r) - best_line; + if (((line_diff < actual_looseness) + && (looseness <= line_diff)) + || ((line_diff > actual_looseness) + && (looseness >= line_diff))) { + best_bet = r; + actual_looseness = line_diff; + fewest_demerits = total_demerits(r); + } else if ((line_diff == actual_looseness) && + (total_demerits(r) < fewest_demerits)) { + best_bet = r; + fewest_demerits = total_demerits(r); + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + + /* /Find the best active node for the desired looseness; */ + if ((actual_looseness == looseness) || final_pass) + goto DONE; + } } - + /* Clean up the memory by removing the break nodes; */ clean_up_the_memory(); /* /Clean up the memory by removing the break nodes; */ - if (!second_pass) { - if (tracing_paragraphs>0) - tprint_nl("@secondpass"); - threshold=tolerance; - second_pass=true; - final_pass=(emergency_stretch<=0); - } else { /* if at first you do not succeed, \dots */ - if (tracing_paragraphs>0) - tprint_nl("@emergencypass"); - background[2] += emergency_stretch; - final_pass=true; + if (!second_pass) { + if (tracing_paragraphs > 0) + tprint_nl("@secondpass"); + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + } else { /* if at first you do not succeed, \dots */ + if (tracing_paragraphs > 0) + tprint_nl("@emergencypass"); + background[2] += emergency_stretch; + final_pass = true; } - } - DONE: - if (tracing_paragraphs>0) { - end_diagnostic(true); - normalize_selector(); - } - if (do_last_line_fit) { - /* Adjust \(t)the final line of the paragraph;*/ +} + +DONE: +if (tracing_paragraphs > 0) { + end_diagnostic(true); + normalize_selector(); +} +if (do_last_line_fit) { + /* Adjust \(t)the final line of the paragraph; */ /* Here we either reset |do_last_line_fit| or adjust the |par_fill_skip| glue. */ - if (active_short(best_bet)==0) { - do_last_line_fit=false; - } else { - q=new_spec(glue_ptr(last_line_fill)); - delete_glue_ref(glue_ptr(last_line_fill)); - width(q) += (active_short(best_bet)-active_glue(best_bet)); - stretch(q)=0; - glue_ptr(last_line_fill)=q; + if (active_short(best_bet) == 0) { + do_last_line_fit = false; + } else { + q = new_spec(glue_ptr(last_line_fill)); + delete_glue_ref(glue_ptr(last_line_fill)); + width(q) += (active_short(best_bet) - active_glue(best_bet)); + stretch(q) = 0; + glue_ptr(last_line_fill) = q; } - /* /Adjust \(t)the final line of the paragraph;*/ - } + /* /Adjust \(t)the final line of the paragraph; */ +} /* @<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.) - */ - ext_post_line_break(d, - right_skip, - left_skip, - pdf_protrude_chars, - par_shape_ptr, - pdf_adjust_spacing, - pdf_each_line_height, - pdf_each_line_depth, - pdf_first_line_height, - pdf_last_line_depth, - inter_line_penalties_ptr, - inter_line_penalty, - club_penalty, - club_penalties_ptr, - display_widow_penalties_ptr, - widow_penalties_ptr, - display_widow_penalty, - widow_penalty, - broken_penalty, - final_par_glue, - best_bet, - last_special_line, - second_width, - second_indent, - first_width, - first_indent, - best_line - ); + */ +ext_post_line_break(d, + right_skip, + left_skip, + pdf_protrude_chars, + par_shape_ptr, + pdf_adjust_spacing, + pdf_each_line_height, + pdf_each_line_depth, + pdf_first_line_height, + pdf_last_line_depth, + inter_line_penalties_ptr, + inter_line_penalty, + club_penalty, + club_penalties_ptr, + display_widow_penalties_ptr, + widow_penalties_ptr, + display_widow_penalty, + widow_penalty, + broken_penalty, + final_par_glue, + best_bet, + last_special_line, + second_width, + second_indent, first_width, first_indent, best_line); /* /Break the paragraph at the chosen... */ /* Clean up the memory by removing the break nodes; */ - clean_up_the_memory(); +clean_up_the_memory(); /* - flush_node_list(dir_ptr); - dir_ptr=null; - */ + flush_node_list(dir_ptr); + dir_ptr=null; + */ } - - |