summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex/linebreak.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/linebreak.w')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.w2164
1 files changed, 0 insertions, 2164 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.w b/Build/source/texk/web2c/luatexdir/tex/linebreak.w
deleted file mode 100644
index 345a1a42456..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/linebreak.w
+++ /dev/null
@@ -1,2164 +0,0 @@
-% linebreak.w
-%
-% Copyright 2006-2008 Taco Hoekwater <taco@@luatex.org>
-%
-% This file is part of LuaTeX.
-%
-% LuaTeX is free software; you can redistribute it and/or modify it under
-% the terms of the GNU General Public License as published by the Free
-% Software Foundation; either version 2 of the License, or (at your
-% option) any later version.
-%
-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-% License for more details.
-%
-% You should have received a copy of the GNU General Public License along
-% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-@ We come now to what is probably the most interesting algorithm of \TeX:
-the mechanism for choosing the ``best possible'' breakpoints that yield
-the individual lines of a paragraph. \TeX's line-breaking algorithm takes
-a given horizontal list and converts it to a sequence of boxes that are
-appended to the current vertical list. In the course of doing this, it
-creates a special data structure containing three kinds of records that are
-not used elsewhere in \TeX. Such nodes are created while a paragraph is
-being processed, and they are destroyed afterwards; thus, the other parts
-of \TeX\ do not need to know anything about how line-breaking is done.
-
-The method used here is based on an approach devised by Michael F. Plass and
-@^Plass, Michael Frederick@>
-@^Knuth, Donald Ervin@>
-the author in 1977, subsequently generalized and improved by the same two
-people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
-\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
-line-breaking problem can be regarded as a special case of the problem of
-computing the shortest path in an acyclic network. The cited paper includes
-numerous examples and describes the history of line breaking as it has been
-practiced by printers through the ages. The present implementation adds two
-new ideas to the algorithm of 1980: Memory space requirements are considerably
-reduced by using smaller records for inactive nodes than for active ones,
-and arithmetic overflow is avoided by using ``delta distances'' instead of
-keeping track of the total distance from the beginning of the paragraph to the
-current point.
-
-The |line_break| procedure should be invoked only in horizontal mode; it
-leaves that mode and places its output into the current vlist of the
-enclosing vertical mode (or internal vertical mode).
-There is one explicit parameter: |d| is true for partial paragraphs
-preceding display math mode; in this case the amount of additional
-penalty inserted before the final line is |display_widow_penalty|
-instead of |widow_penalty|.
-
-There are also a number of implicit parameters: The hlist to be broken
-starts at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the
-enclosing semantic level tells where the paragraph should begin in the
-sequence of line numbers, in case hanging indentation or \.{\\parshape}
-are in use; |prev_graf| is zero unless this paragraph is being continued
-after a displayed formula. Other implicit parameters, such as the
-|par_shape_ptr| and various penalties to use for hyphenation, etc., appear
-in |eqtb|.
-
-After |line_break| has acted, it will have updated the current vlist and the
-value of |prev_graf|. Furthermore, the global variable |just_box| will
-point to the final box created by |line_break|, so that the width of this
-line can be ascertained when it is necessary to decide whether to use
-|above_display_skip| or |above_display_short_skip| before a displayed formula.
-
-@c
-halfword just_box; /* the |hlist_node| for the last line of the new paragraph */
-
-@ In it's complete form, |line_break| is a rather lengthy
-procedure---sort of a small world unto itself---we must build it up
-little by little. Below you see only the general outline.
-
-The main task performed here is to move the list from |head| to
-|temp_head| and go into the enclosing semantic level. We also append
-the \.{\\parfillskip} glue to the end of the paragraph, removing a
-space (or other glue node) if it was there, since spaces usually
-precede blank lines and instances of `\.{\$\$}'. The |par_fill_skip|
-is preceded by an infinite penalty, so it will never be considered as
-a potential breakpoint.
-
-That code assumes that a |glue_node| and a |penalty_node| occupy the
-same number of |mem|~words.
-@^data structure assumptions@>
-
-Most other processing is delegated to external functions.
-
-@c
-void line_break(boolean d, int line_break_context)
-{
- int paragraph_dir = 0; /* main direction of paragraph */
- halfword final_par_glue;
- halfword start_of_par;
- int callback_id;
- pack_begin_line = cur_list.ml_field; /* this is for over/underfull box messages */
- alink(temp_head) = null; /* hh-ls */
- vlink(temp_head) = vlink(cur_list.head_field);
- new_hyphenation(temp_head, cur_list.tail_field);
- cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field);
- if (is_char_node(cur_list.tail_field)) {
- tail_append(new_penalty(inf_penalty,line_penalty));
- } else if (type(cur_list.tail_field) != glue_node) {
- tail_append(new_penalty(inf_penalty,line_penalty));
- } else {
- halfword t = alink(cur_list.tail_field);
- flush_node(cur_list.tail_field);
- cur_list.tail_field = t;
- tail_append(new_penalty(inf_penalty,line_penalty));
- }
- final_par_glue = new_param_glue(par_fill_skip_code);
- couple_nodes(cur_list.tail_field, final_par_glue);
- cur_list.tail_field = vlink(cur_list.tail_field);
- lua_node_filter(pre_linebreak_filter_callback,
- line_break_context, temp_head,
- addressof(cur_list.tail_field));
- last_line_fill = cur_list.tail_field;
- pop_nest();
- start_of_par = cur_list.tail_field;
- callback_id = callback_defined(linebreak_filter_callback);
- if (callback_id > 0) {
- callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field));
- if (callback_id > 0) {
- /* find the correct value for the |just_box| */
- halfword box_search = cur_list.tail_field;
- just_box = null;
- if (box_search != null) {
- do {
- if (type(box_search) == hlist_node) {
- just_box = box_search;
- }
- box_search = vlink(box_search);
- } while (box_search != null);
- }
- if (just_box == null) {
- help3
- ("A linebreaking routine should return a non-empty list of nodes",
- "and at least one of those has to be a \\hbox.",
- "Sorry, I cannot recover from this.");
- print_err("Invalid linebreak_filter");
- succumb();
- }
- } else {
- if (tracing_paragraphs_par > 0) {
- begin_diagnostic();
- print_int(line);
- end_diagnostic(true);
- }
- }
- }
- if (callback_id == 0) {
- if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) {
- paragraph_dir = local_par_dir(vlink(temp_head));
- } else {
- confusion("weird par dir"); /* assert(0); */ /* |paragraph_dir = 0|; */
- }
- ext_do_line_break(paragraph_dir,
- pretolerance_par,
- tracing_paragraphs_par,
- tolerance_par,
- emergency_stretch_par,
- looseness_par,
- adjust_spacing_par,
- par_shape_par_ptr,
- adj_demerits_par,
- protrude_chars_par,
- line_penalty_par,
- last_line_fit_par,
- double_hyphen_demerits_par,
- final_hyphen_demerits_par,
- hang_indent_par,
- hsize_par,
- hang_after_par,
- left_skip_par,
- right_skip_par,
- inter_line_penalties_par_ptr,
- inter_line_penalty_par,
- club_penalty_par,
- club_penalties_par_ptr,
- (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr),
- (d ? display_widow_penalty_par : widow_penalty_par),
- broken_penalty_par,
- final_par_glue);
- }
- lua_node_filter(post_linebreak_filter_callback,
- line_break_context, start_of_par,
- addressof(cur_list.tail_field));
- pack_begin_line = 0;
-}
-
-@ Glue nodes in a horizontal list that is being paragraphed are not supposed to
- include ``infinite'' shrinkability; that is why the algorithm maintains
- four registers for stretching but only one for shrinking. If the user tries to
- introduce infinite shrinkability, the shrinkability will be reset to finite
- and an error message will be issued. A boolean variable |no_shrink_error_yet|
- prevents this error message from appearing more than once per paragraph.
-
-@c
-#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? */
-
-static halfword finite_shrink(halfword p)
-{ /* recovers from infinite shrinkage */
- const 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);
- }
- shrink_order(p) = normal;
- return p;
-}
-
-@ A pointer variable |cur_p| runs through the given horizontal list as we look
- for breakpoints. This variable is global, since it is used both by |line_break|
- and by its subprocedure |try_break|.
-
- Another global variable called |threshold| is used to determine the feasibility
- of individual lines: breakpoints are feasible if there is a way to reach
- them without creating lines whose badness exceeds |threshold|. (The
- badness is compared to |threshold| before penalties are added, so that
- penalty values do not affect the feasibility of breakpoints, except that
- no break is allowed when the penalty is 10000 or more.) If |threshold|
- is 10000 or more, all legal breaks are considered feasible, since the
- |badness| function specified above never returns a value greater than~10000.
-
- Up to three passes might be made through the paragraph in an attempt to find at
- least one set of feasible breakpoints. On the first pass, we have
- |threshold=pretolerance| and |second_pass=final_pass=false|.
- If this pass fails to find a
- feasible solution, |threshold| is set to |tolerance|, |second_pass| is set
- |true|, and an attempt is made to hyphenate as many words as possible.
- If that fails too, we add |emergency_stretch| to the background
- stretchability and set |final_pass=true|.
-
-@c
-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 int threshold; /* maximum badness on feasible lines */
-
-/* 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 */
-
-/* stack for |find_protchar_left()| and |find_protchar_right()| */
-static halfword hlist_stack[max_hlist_stack];
-
-/* fill level for |hlist_stack| */
-static short hlist_stack_level = 0;
-
-@ @c
-static void push_node(halfword p)
-{
- if (hlist_stack_level >= max_hlist_stack)
- normal_error("push_node","stack overflow");
- hlist_stack[hlist_stack_level++] = p;
-}
-
-static halfword pop_node(void)
-{
- if (hlist_stack_level <= 0) /* would point to some bug */
- normal_error("pop_node","stack underflow (internal error)");
- return hlist_stack[--hlist_stack_level];
-}
-
-@ @c
-static int max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */
-static int max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */
-static int cur_font_step = 0; /*the current step of expanded fonts */
-
-static boolean check_expand_pars(internal_font_number f)
-{
- int m;
-
- if ((font_step(f) == 0)
- || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0)))
- return false;
- if (cur_font_step < 0)
- cur_font_step = font_step(f);
- else if (cur_font_step != font_step(f))
- normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed");
- m = font_max_stretch(f);
- if (m != 0) {
- if (max_stretch_ratio < 0)
- max_stretch_ratio = m;
- else if (max_stretch_ratio != m)
- normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
- }
- m = font_max_shrink(f);
- if (m != 0) {
- if (max_shrink_ratio < 0)
- max_shrink_ratio = -m;
- else if (max_shrink_ratio != -m)
- normal_error("font expansion","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
-
-@c
-/*public*/ halfword find_protchar_left(halfword l, boolean d)
-{
- halfword t;
- boolean run;
- boolean done = false ;
- while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) {
- /*for paragraph start with \.{\\parindent} = 0pt or any empty hbox */
- l = vlink(l);
- done = true ;
- }
- if ((!done) && (type(l) == local_par_node)) {
- l = vlink(l);
- done = true ;
- }
- if ((!done) && d) {
- while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) {
- /* std.\ discardables at line break, \TeX book, p 95 */
- l = vlink(l);
- }
- }
- if (type(l) != glyph_node) {
- 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 */
- run = false;
- }
- if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) &&
- ((boundary_value(l) == 1) || (boundary_value(l) == 3))) {
- /* skip next node */
- l = vlink(l);
- }
- 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
-
-@c
-/*public*/ halfword find_protchar_right(halfword l, halfword r)
-{
- halfword t;
- boolean run = true;
- if (r == null)
- return null;
- hlist_stack_level = 0;
- 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)) {
- if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) &&
- ((boundary_value(r) == 2) || (boundary_value(r) == 3))) {
- /* skip next node */
- r = alink(r);
- }
- if (alink(r) != null) {
- r = alink(r);
- } else { /* this is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268) */
- run = false;
- }
- } else if ((r == l) && (hlist_stack_level == 0))
- run = false;
- }
- } while (t != r);
- return r;
-}
-
-@ @c
-#define left_pw(a) char_pw((a), left_side)
-#define right_pw(a) char_pw((a), right_side)
-
-@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
- each break that is {\sl feasible}, in the sense that there is a way to end
- a line at the given place without requiring any line to stretch more than
- a given tolerance. A break node is characterized by three things: the position
- of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
- or |disc_node|); the ordinal number of the line that will follow this
- breakpoint; and the fitness classification of the line that has just
- ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
-
-@c
-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;
-
-
-@ The algorithm essentially determines the best possible way to achieve
- each feasible combination of position, line, and fitness. Thus, it answers
- questions like, ``What is the best way to break the opening part of the
- paragraph so that the fourth line is a tight line ending at such-and-such
- a place?'' However, the fact that all lines are to be the same length
- after a certain point makes it possible to regard all sufficiently large
- line numbers as equivalent, when the looseness parameter is zero, and this
- makes it possible for the algorithm to save space and time.
-
- An ``active node'' and a ``passive node'' are created in |mem| for each
- feasible breakpoint that needs to be considered. Active nodes are three
- words long and passive nodes are two words long. We need active nodes only
- for breakpoints near the place in the paragraph that is currently being
- examined, so they are recycled within a comparatively short time after
- they are created.
-
-@ An active node for a given breakpoint contains six fields:
-
-|vlink| points to the next node in the list of active nodes; the
-last active node has |vlink=active|.
-
-|break_node| points to the passive node associated with this
-breakpoint.
-
-|line_number| is the number of the line that follows this
-breakpoint.
-
-|fitness| is the fitness classification of the line ending at this
-breakpoint.
-
-|type| is either |hyphenated_node| or |unhyphenated_node|, depending on
-whether this breakpoint is a |disc_node|.
-
-|total_demerits| is the minimum possible sum of demerits over all
-lines leading from the beginning of the paragraph to this breakpoint.
-
-The value of |vlink(active)| points to the first active node on a vlinked list
-of all currently active nodes. This list is in order by |line_number|,
-except that nodes with |line_number>easy_line| may be in any order relative
-to each other.
-
-@c
-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:
-
-|vlink| points to the passive node created just before this one,
-if any, otherwise it is |null|.
-
-|cur_break| points to the position of this breakpoint in the
-horizontal list for the paragraph being broken.
-
-|prev_break| points to the passive node that should precede this
-one in an optimal path to this breakpoint.
-
-|serial| is equal to |n| if this passive node is the |n|th
-one created during the current pass. (This field is used only when
-printing out detailed statistics about the line-breaking calculations.)
-
-|passive_pen_inter| holds the current \.{\\localinterlinepenalty}
-
-|passive_pen_broken| holds the current \.{\\localbrokenpenalty}
-
-There is a global variable called |passive| that points to the most
-recently created passive node. Another global variable, |printed_node|,
-is used to help print out the paragraph when detailed information about
-the line-breaking computation is being displayed.
-
-@c
-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
-compute the badness of individual lines. Such nodes appear only between two
-active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes
-and if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|,
-then |q| tells the space difference between lines in the horizontal list that
-start after breakpoint |p| and lines that start after breakpoint |r|. In
-other words, if we know the length of the line that starts after |p| and
-ends at our current position, then the corresponding length of the line that
-starts after |r| is obtained by adding the amounts in node~|q|. A delta node
-contains seven scaled numbers, since it must record the net change in glue
-stretchability with respect to all orders of infinity. The natural width
-difference appears in |mem[q+1].sc|; the stretch differences in units of
-pt, sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink
-difference appears in |mem[q+7].sc|. The |subtype| field of a delta node
-is not used.
-
-Actually, we have two more fields that are used by |pdftex|.
-
-As the algorithm runs, it maintains a set of seven delta-like registers
-for the length of the line following the first active breakpoint to the
-current position in the given hlist. When it makes a pass through the
-active list, it also maintains a similar set of seven registers for the
-length following the active breakpoint of current interest. A third set
-holds the length of an empty line (namely, the sum of \.{\\leftskip} and
-\.{\\rightskip}); and a fourth set is used to create new delta nodes.
-
-When we pass a delta node we want to do operations like
-$$\hbox{\ignorespaces|for
-k:=1 to 7 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
-want to do this without the overhead of |for| loops. The |do_all_six|
-macro makes such six-tuples convenient.
-
-@c
-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| */
-
-@ 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
- breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
- $\beta(p)$ such that the length of material in a line from breakpoint~|p|
- to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
- Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
- the beginning of the paragraph to a point ``after'' a break at |p| and to a
- point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
- namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
-
- Suppose, for example, that the paragraph consists entirely of alternating
- boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
- let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
- represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
- at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
- x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
- $p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
- -\alpha(p_2)$.
-
- The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
- shrinkability as well as a natural width. If we were to compute $\alpha(p)$
- and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
- the multiprecise numbers would have to be kept in the active nodes.
- \TeX\ avoids this problem by working entirely with relative differences
- or ``deltas.'' Suppose, for example, that the active list contains
- $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
- and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
- and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
- currently positioned at some other breakpoint |p|, the |active_width| array
- contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
- the list of active nodes and considering a tentative line that runs from
- $a_2$ to~|p|, say, the |cur_active_width| array will contain the value
- $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
- we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
- is just $\delta_2$, which appears in the active list between $a_2$ and
- $a_3$. The |background| array contains $\gamma$. The |break_width| array
- will be used to calculate values of new delta nodes when the active
- list is being updated.
-
-@ The heart of the line-breaking procedure is `|try_break|', a subroutine
- that tests if the current breakpoint |cur_p| is feasible, by running
- through the active list to see what lines of text can be made from active
- nodes to~|cur_p|. If feasible breaks are possible, new break nodes are
- created. If |cur_p| is too far from an active node, that node is
- deactivated.
-
- The parameter |pi| to |try_break| is the penalty associated
- with a break at |cur_p|; we have |pi=eject_penalty| if the break is forced,
- and |pi=inf_penalty| if the break is illegal.
-
- The other parameter, |break_type|, is set to |hyphenated_node| or |unhyphenated_node|,
- depending on whether or not the current break is at a |disc_node|. The
- end of a paragraph is also regarded as `|hyphenated_node|'; this case is
- distinguishable by the condition |cur_p=null|.
-
-@c
-static int internal_pen_inter; /* running \.{\\localinterlinepenalty} */
-static int internal_pen_broken; /* running \.{\\localbrokenpenalty} */
-static halfword internal_left_box; /* running \.{\\localleftbox} */
-static int internal_left_box_width; /* running \.{\\localleftbox} width */
-static halfword init_internal_left_box; /* running \.{\\localleftbox} */
-static int init_internal_left_box_width; /* running \.{\\localleftbox} width */
-static halfword internal_right_box; /* running \.{\\localrightbox} */
-static int internal_right_box_width; /* running \.{\\localrightbox} width */
-
-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
- one entry for each of the fitness classifications. For example,
- |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
- line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]|
- points to the passive node for the break before~|cur_p| that achieves such
- an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the
- active node corresponding to |best_place[tight_fit]|. When no feasible break
- sequence is known, the |minimal_demerits| entries will be equal to
- |awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|,
- keeps track of the smallest value in the |minimal_demerits| array.
-
-@c
-static int minimal_demerits[4]; /* best total demerits known for current
- line class and position, given the fitness */
-static int 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 */
-
-@ The length of lines depends on whether the user has specified
-\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it
-points to a $(2n+1)$-word record in |mem|, where the |vinfo| in the first
-word contains the value of |n|, and the other $2n$ words contain the left
-margins and line lengths for the first |n| lines of the paragraph; the
-specifications for line |n| apply to all subsequent lines. If
-|par_shape_ptr=null|, the shape of the paragraph depends on the value of
-|n=hang_after|; if |n>=0|, hanging indentation takes place on lines |n+1|,
-|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
-n\vert$. When hanging indentation is active, the left margin is
-|hang_indent|, if |hang_indent>=0|, else it is 0; the line length is
-$|hsize|-\vert|hang_indent|\vert$. The normal setting is
-|par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|.
-Note that if |hang_indent=0|, the value of |hang_after| is irrelevant.
-@^length of lines@> @^hanging indentation@>
-
-@c
-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 int fewest_demerits; /*the demerits associated with |best_bet| */
-static halfword best_line; /*line number following the last line of the new paragraph */
-static int actual_looseness; /*the difference between |line_number(best_bet)|
- and the optimum |best_line| */
-static int line_diff; /*the difference between the current line number and
- the optimum |best_line| */
-
-@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
- |rule_node|, |ins_node|, |mark_node|, |adjust_node|,
- |disc_node|, |whatsit_node|, and |math_node| are at the low end of the
- type codes, by permitting a break at glue in a list if and only if the
- |type| of the previous node is less than |math_node|. Furthermore, a
- node is discarded after a break if its type is |math_node| or~more.
-
-@c
-#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7)
-#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); }
-#define do_all_eight(a) do_all_six(a); do_seven_eight(a)
-#define do_one_seven_eight(a) a(1); do_seven_eight(a)
-
-#define store_background(a) {active_width[a]=background[a];}
-
-#define kern_break() { \
- if ((!is_char_node(vlink(cur_p))) && auto_breaking) \
- if (type(vlink(cur_p))==glue_node) \
- ext_try_break(0, \
- unhyphenated_node, \
- line_break_dir, \
- adjust_spacing, \
- par_shape_ptr, \
- adj_demerits, \
- tracing_paragraphs, \
- protrude_chars, \
- line_penalty, \
- last_line_fit, \
- double_hyphen_demerits, \
- final_hyphen_demerits, \
- first_p, \
- cur_p); \
- if (type(cur_p)!=math_node) \
- active_width[1] += width(cur_p); \
- else \
- active_width[1] += surround(cur_p); \
-}
-
-#define clean_up_the_memory() { \
- q=vlink(active); \
- while (q!=active) { \
- cur_p = vlink(q); \
- if (type(q)==delta_node) \
- flush_node(q); \
- else \
- flush_node(q); \
- q = cur_p; \
- } \
- q = passive; \
- while (q!=null) { \
- cur_p = vlink(q); \
- flush_node(q); \
- q = cur_p; \
- } \
-}
-
-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 */
-
-#define reset_disc_width(a) disc_width[(a)] = 0
-
-#define add_disc_width_to_break_width(a) break_width[(a)] += disc_width[(a)]
-#define sub_disc_width_from_active_width(a) active_width[(a)] -= disc_width[(a)]
-
-#define add_char_shrink(a,b) a += char_shrink((b))
-#define add_char_stretch(a,b) a += char_stretch((b))
-#define sub_char_shrink(a,b) a -= char_shrink((b))
-#define sub_char_stretch(a,b) a -= char_stretch((b))
-
-#define add_kern_shrink(a,b) a += kern_shrink((b))
-#define add_kern_stretch(a,b) a += kern_stretch((b))
-#define sub_kern_shrink(a,b) a -= kern_shrink((b))
-#define sub_kern_stretch(a,b) a -= kern_stretch((b))
-
-@ This function is used to add the width of a list of nodes
-(from a discretionary) to one of the width arrays.
-
-Replacement texts and discretionary texts are supposed to contain
-only character nodes, kern nodes, and box or rule nodes.
-
-@c
-#define bad_node_in_disc_error(p) { \
- if (type(p) == whatsit_node) { \
- formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \
- } else { \
- formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \
- } \
-}
-
-static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
-{
- while (s != null) {
- if (is_char_node(s)) {
- widths[1] += pack_width(line_break_dir, dir_TRT, s, true);
- if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
- set_prev_char_p(s);
- add_char_stretch(widths[8], s);
- add_char_shrink(widths[9], s);
- };
- } else {
- switch (type(s)) {
- case hlist_node:
- case vlist_node:
- widths[1] += pack_width(line_break_dir, box_dir(s), s, false);
- break;
- case kern_node:
- if ((adjust_spacing == 2) && (subtype(s) == normal)) {
- add_kern_stretch(widths[8], s);
- add_kern_shrink(widths[9], s);
- }
- /* fall through */
- case rule_node:
- widths[1] += width(s);
- break;
- case disc_node: /* TH temp */
- break;
- default:
- bad_node_in_disc_error(s);
- break;
- }
- }
- s = vlink(s);
- }
-}
-
-@ This function is used to substract the width of a list of nodes
-(from a discretionary) from one of the width arrays.
-It is used only once, but deserves it own function because of orthogonality
-with the |add_to_widths| function.
-
-@c
-static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
-{
- while (s != null) {
- /* Subtract the width of node |s| from |break_width|; */
- if (is_char_node(s)) {
- widths[1] -= pack_width(line_break_dir, dir_TRT, s, true);
- if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
- set_prev_char_p(s);
- sub_char_stretch(widths[8], s);
- sub_char_shrink(widths[9], s);
- }
- } else {
- switch (type(s)) {
- case hlist_node:
- case vlist_node:
- widths[1] -= pack_width(line_break_dir, box_dir(s), s, false);
- break;
- case kern_node:
- if ((adjust_spacing == 2) && (subtype(s) == normal)) {
- sub_kern_stretch(widths[8], s);
- sub_kern_shrink(widths[9], s);
- }
- /* fall through */
- case rule_node:
- widths[1] -= width(s);
- break;
- case disc_node: /* TH temp */
- break;
- default:
- bad_node_in_disc_error(s);
- break;
- }
- }
- s = vlink(s);
- }
-}
-
-@ When we insert a new active node for a break at |cur_p|, suppose this
- new node is to be placed just before active node |a|; then we essentially
- want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
- $\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$
- in the notation explained above. The |cur_active_width| array now holds
- $\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by
- subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)-
- \alpha(|cur_p|)$. The latter quantity can be regarded as the length of a
- line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|.
-
- The |break_width| is usually negative, since it consists of the background
- (which is normally zero) minus the width of nodes following~|cur_p| that are
- eliminated after a break. If, for example, node |cur_p| is a glue node, the
- width of this glue is subtracted from the background; and we also look
- ahead to eliminate all subsequent glue and penalty and kern and math
- nodes, subtracting their widths as well.
-
- Kern nodes do not disappear at a line break unless they are |explicit|.
-
-@c
-static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p)
-{
- halfword s = p; /* glue and other 'whitespace' to be skipped after a break;
- used if unhyphenated, or |post_break==empty| */
- 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 {\it this} position.
- */
-
- sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width);
- add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width);
- 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;
- }
- }
- while (s != null) {
- switch (type(s)) {
- case math_node:
- /* begin mathskip code */
- if (glue_is_zero(s)) {
- break_width[1] -= surround(s);
- break;
- } else {
- /* fall through */
- }
- /* end mathskip code */
- case glue_node:
- /*Subtract glue from |break_width|; */
- break_width[1] -= width(s);
- break_width[2 + stretch_order(s)] -= stretch(s);
- break_width[7] -= shrink(s);
- break;
- case penalty_node:
- break;
- case kern_node:
- if (subtype(s) != explicit_kern && subtype(s) != italic_kern)
- return;
- else
- break_width[1] -= width(s);
- break;
- default:
- return;
- };
- s = vlink(s);
- }
-}
-
-@ @c
-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)));
-}
-
-@ @c
-static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi,
- int 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;
- }
- 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);
-}
-
-@ @c
-#define add_disc_width_to_active_width(a) active_width[a] += disc_width[a]
-#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint
-
-#define set_break_width_to_background(a) break_width[a]=background[(a)]
-
-#define convert_to_break_width(a) \
- varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)]
-
-#define store_break_width(a) active_width[(a)]=break_width[(a)]
-
-#define new_delta_to_break_width(a) \
- varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)]
-
-#define new_delta_from_break_width(a) \
- varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)]
-
-#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)]
-
-#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint
-#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint
-#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint
-
-#define total_font_stretch cur_active_width[8]
-#define total_font_shrink cur_active_width[9]
-
-#define cal_margin_kern_var(a) { \
- character(cp) = character((a)); \
- font(cp) = font((a)); \
- do_subst_font(cp, 1000); \
- if (font(cp) != font((a))) \
- 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))); \
-}
-
-static void ext_try_break(int pi,
- quarterword break_type,
- int line_break_dir,
- int adjust_spacing,
- int par_shape_ptr,
- int adj_demerits,
- int tracing_paragraphs,
- int protrude_chars,
- int line_penalty,
- int last_line_fit,
- int double_hyphen_demerits,
- int final_hyphen_demerits, halfword first_p, halfword cur_p)
-{
- /* labels: |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 = active; /* stays a step behind |r| */
- halfword prev_prev_r = null; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */
- halfword old_l = 0; /* maximum line number in current equivalence class of lines */
- boolean no_break_yet = true; /* 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 = 0; /*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 */
- int d; /*demerits of test line */
- boolean artificial_demerits; /*has |d| been forced to zero? */
-
- scaled shortfall; /*used in badness calculations */
- scaled g = 0; /*glue stretch or shrink of test line, adjustment for last line */
- scaled cur_active_width[10] = { 0 }; /*distance from current active node */
-
- /*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 */
- }
-
- 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, line_break_dir, 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));
- } 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 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 {
- 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;
- }
- }
- }
- /* /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];
- if (break_node(r) == null)
- shortfall -= init_internal_left_box_width;
- else
- shortfall -= passive_last_left_box_width(break_node(r));
- shortfall -= internal_right_box_width;
- if (protrude_chars > 1) {
- halfword l1, o;
- l1 = (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);
- }
- /* MAGIC: the disc could be a SELECT subtype, to we might need
- to get the last character as |pre_break| from either the
- |pre_break| list (if the previous INIT disc was taken), or the
- |no_break| (sic) list (if the previous INIT disc was not taken)
-
- BUT:
- the last characters (hyphenation character) if these two list
- should always be the same anyway, so we just look at |pre_break|.
- */
- /* let's look at the right margin first */
- if ((cur_p != 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(l1, o);
- }
- /* now the left margin */
- if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) {
- /* FIXME: first 'char' could be a disc! */
- l1 = vlink_post_break(l1); /* protrude the first char */
- } else {
- l1 = find_protchar_left(l1, true);
- }
- shortfall += (left_pw(l1) + right_pw(o));
- }
- if (shortfall != 0) {
- margin_kern_stretch = 0;
- margin_kern_shrink = 0;
- if (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. */
-
- 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 {
- /* 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 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.
- */
- 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 ((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 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);
- 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);
- }
- }
- }
-}
-
-@ @c
-void ext_do_line_break(int paragraph_dir,
- int pretolerance,
- int tracing_paragraphs,
- int tolerance,
- scaled emergency_stretch,
- int looseness,
- int adjust_spacing,
- halfword par_shape_ptr,
- int adj_demerits,
- int 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,
- halfword inter_line_penalties_ptr,
- int inter_line_penalty,
- int club_penalty,
- halfword club_penalties_ptr,
- halfword widow_penalties_ptr,
- 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 */
- int line_break_dir = paragraph_dir;
-
- /* 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;
-
- fewest_demerits = 0;
- actual_looseness = 0;
-
- /* 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 {
- halfword used_hang_indent = swap_hang_indent(hang_indent);
- /* 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(used_hang_indent);
- if (used_hang_indent >= 0)
- first_indent = used_hang_indent;
- else
- first_indent = 0;
- second_width = hsize;
- second_indent = 0;
- } else {
- first_width = hsize;
- first_indent = 0;
- second_width = hsize - abs(used_hang_indent);
- if (used_hang_indent >= 0)
- second_indent = used_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 + 1))].cint;
- second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint;
- second_indent = swap_parshape_indent(second_indent,second_width);
- }
- 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 (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 = 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);
- }
- }
- }
- /* DIR: Initialize |dir_ptr| for |line_break| */
- if (dir_ptr != null) {
- flush_node_list(dir_ptr);
- dir_ptr = null;
- }
-#if 0
- push_dir(dir_ptr,paragraph_dir); /* TODO what was the point of this? */
-#endif
-
- /* Find optimal breakpoints; */
- threshold = pretolerance;
- if (threshold >= 0) {
- if (tracing_paragraphs > 0) {
- begin_diagnostic();
- tprint_nl("@@firstpass");
- }
- 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);
- /* LOCAL: Initialize with first |local_paragraph| node */
- if ((cur_p != null) && (type(cur_p) == local_par_node)) {
- alink(cur_p) = temp_head; /* this used to be an assert, but may as well force it */
- 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;
- }
- /* /LOCAL: Initialize with first |local_paragraph| node */
- set_prev_char_p(null);
- 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_node|s. */
-
- while (is_char_node(cur_p)) {
- /* Advance |cur_p| to the node following the present string of characters ; */
- /* The code that passes over the characters of words in a paragraph is part of
- \TeX's inner loop, so it has been streamlined for speed. We use the fact that
- `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore
- unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node.
- */
- active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true);
- if ((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];
- }
- }
- if (cur_p == null) {
- normal_error("linebreak","invalid list tail, probably missing glue");
- }
- /* Determine legal breaks: As we move through the hlist, we need to keep
- the |active_width| array up to date, so that the badness of individual
- lines is readily calculated by |try_break|. It is convenient to use the
- short name |active_width[1]| for the component of active width that represents
- real width as opposed to glue. */
-
- switch (type(cur_p)) {
- case hlist_node:
- case vlist_node:
- active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false);
- break;
- case rule_node:
- active_width[1] += 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(dir_ptr,cur_p); /* adds to |dir_ptr| */
- } else {
- pop_dir_node(dir_ptr);
- if (dir_ptr != null) {
- line_break_dir = dir_dir(dir_ptr);
- }
- }
- break;
- 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 math_node:
- auto_breaking = (subtype(cur_p) == after);
- /* begin mathskip code */
- if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) {
- kern_break();
- break;
- } else {
- /* fall through */
- }
- /* end mathskip code */
- 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.
-
- We only break after certain nodes (see texnodes.h), a font related
- kern and a dir node when |\breakafterdirmode=1|.
- */
- if (auto_breaking) {
- halfword prev_p = alink(cur_p);
- if (prev_p != temp_head && (
- is_char_node(prev_p)
- || precedes_break(prev_p)
- || precedes_kern(prev_p)
- || precedes_dir(prev_p)
- )) {
- ext_try_break(0, unhyphenated_node, line_break_dir, adjust_spacing,
- par_shape_ptr, adj_demerits,
- tracing_paragraphs, protrude_chars,
- line_penalty, last_line_fit,
- double_hyphen_demerits,
- final_hyphen_demerits, first_p, cur_p);
- }
- }
- /* *INDENT-ON* */
- check_shrinkage(cur_p);
- active_width[1] += width(cur_p);
- active_width[2 + stretch_order(cur_p)] += stretch(cur_p);
- active_width[7] += shrink(cur_p);
- break;
- case kern_node:
- if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) {
- kern_break();
- } else {
- active_width[1] += width(cur_p);
- if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) {
- add_kern_stretch(active_width[8], cur_p);
- add_kern_shrink(active_width[9], cur_p);
- }
- }
- break;
- case disc_node:
- /* |select_disc|s are handled by the leading |init_disc| */
- if (subtype(cur_p) == select_disc)
- break;
- /* Try to break after a discretionary fragment, then |goto done5|; */
- /* The following code knows that discretionary texts contain
- only character nodes, kern nodes, box nodes, and rule
- nodes. This branch differs a bit from older engines because in LuaTeX we
- already have hyphenated the list. This means that we need to skip
- automatic disc nodes. Of better, we need to treat discretionaries
- and explicit hyphens always, even in the first pass (HH). */
- if (second_pass || subtype(cur_p) <= automatic_disc) {
- /*
- int actual_penalty = hyphen_penalty;
- if (disc_penalty(cur_p) != 0) {
- actual_penalty = (int) disc_penalty(cur_p);
- } else if (subtype(cur_p) == automatic_disc) {
- actual_penalty = ex_hyphen_penalty;
- }
- */
- int actual_penalty = (int) disc_penalty(cur_p);
- 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,
- line_break_dir, adjust_spacing,
- par_shape_ptr, adj_demerits,
- tracing_paragraphs, protrude_chars,
- line_penalty, last_line_fit,
- double_hyphen_demerits,
- final_hyphen_demerits, first_p, cur_p);
- } else {
- add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
- do_one_seven_eight(add_disc_width_to_active_width);
- ext_try_break(actual_penalty, hyphenated_node,
- line_break_dir, adjust_spacing,
- par_shape_ptr, adj_demerits,
- tracing_paragraphs, protrude_chars,
- line_penalty, last_line_fit,
- double_hyphen_demerits,
- final_hyphen_demerits, first_p, cur_p);
- if (subtype(cur_p) == init_disc) {
- /* we should at two break points after the one we
- added above:
- \item1 which does a possible break in INIT's |post_break|
- \item2 which means the |no_break| actually was broken
- just a character later */
- /* do the select-0 case 'f-f-i' */
- s = vlink_pre_break(vlink(cur_p));
- add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
- ext_try_break(actual_penalty, hyphenated_node,
- line_break_dir, adjust_spacing,
- par_shape_ptr, adj_demerits,
- tracing_paragraphs,
- protrude_chars, line_penalty,
- last_line_fit, double_hyphen_demerits,
- final_hyphen_demerits, first_p,
- vlink(cur_p));
-#if 0
- /* TODO this does not work */
- /* go back to the starting situation */
- do_one_seven_eight(sub_disc_width_from_active_width);
- do_one_seven_eight(reset_disc_width);
- /* add select |no_break| to |active_width| */
- s = vlink_no_break(vlink(cur_p));
- add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
- ext_try_break(actual_penalty, hyphenated_node,
- line_break_dir, adjust_spacing,
- par_shape_ptr, adj_demerits,
- tracing_paragraphs,
- protrude_chars, line_penalty,
- last_line_fit, double_hyphen_demerits,
- final_hyphen_demerits, first_p,
- vlink(cur_p));
-#endif
- }
- do_one_seven_eight(sub_disc_width_from_active_width);
- }
- }
- s = vlink_no_break(cur_p);
- add_to_widths(s, line_break_dir, adjust_spacing, active_width);
- break;
- case penalty_node:
- ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir,
- adjust_spacing, par_shape_ptr, adj_demerits,
- tracing_paragraphs, protrude_chars,
- line_penalty, last_line_fit,
- double_hyphen_demerits, final_hyphen_demerits,
- first_p, cur_p);
- break;
- case boundary_node:
- case whatsit_node:
- /* / Advance past a whatsit node in the |line_break| loop/; */
- case mark_node:
- case ins_node:
- case adjust_node:
- break;
- case glue_spec_node:
- normal_warning("parbuilder","found a glue_spec in a paragraph");
- break;
- default:
- formatted_error("parbuilder","weird node %d in paragraph",type(cur_p));
- }
- cur_p = vlink(cur_p);
- while (cur_p == null && nest_index > 0) {
- cur_p = nest_stack[--nest_index];
- }
- }
- 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, line_break_dir,
- adjust_spacing, par_shape_ptr, adj_demerits,
- tracing_paragraphs, 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;
- }
- }
-
- DONE:
- if (tracing_paragraphs > 0) {
- end_diagnostic(true);
- normalize_selector();
- }
- if (do_last_line_fit) {
- /*
- Adjust the final line of the paragraph; here we either reset
- |do_last_line_fit| or adjust the |par_fill_skip| glue.
- */
- if (active_short(best_bet) == 0) {
- do_last_line_fit = false;
- } else {
- width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet));
- stretch(last_line_fill) = 0;
- }
- }
-
- /*
- 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.
- */
-
- /* first thing |ext_post_line_break| does is reset |dir_ptr| */
- flush_node_list(dir_ptr);
- dir_ptr = null;
- ext_post_line_break(paragraph_dir,
- right_skip,
- left_skip,
- protrude_chars,
- par_shape_ptr,
- adjust_spacing,
- inter_line_penalties_par_ptr,
- inter_line_penalty,
- club_penalty,
- club_penalties_ptr,
- widow_penalties_ptr,
- 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();
-}
-
-@ @c
-void get_linebreak_info (int *f, int *a)
-{
- *f = fewest_demerits;
- *a = actual_looseness;
-}