[main]Notes on TeXmacs

Mathematical typesetting in TeXmacs

This document describes how TeXmacs deals with the typesetting of mathematics, and in particular with the low level implementation of the typesetting primitives relative to mathematical documents. It should be useful if you are interested in understanding the C++ sources. We describe the state of facts as per svn revision r14561 (December 2024, TeXmacs 2.14+). Excerpts from the official documentation are included in this document for completeness.

1.Overview

In this chapter we describe the algorithms used by TeXmacs in order to typeset mathematical formulas. This is a difficult subject, because esthetics and effectiveness do not always go hand in hand. Until now, TeX is widely accepted for having achieved an optimal compromise in this respect. Nevertheless, we thought that several improvements could still be made, which have now been implemented in TeXmacs. We will shortly describe the motivations behind them.

In order to obtain esthetic formulas, what criteria should we use? It is often stressed that good typesetting allows the reader to concentrate on what he reads, without being distracted by ugly typesetting details. Such distracting details arise when distinct, though similar parts of text are typeset in a non uniform way:

Different base lines

The eye expects text of a similar nature to be typeset with respect to a same base line. For instance, in , the bottoms of the and should be at the same height as the bottom of the -part in the . This should again be the case in .

Unequal spacing

Different components of text with approximately the same function should be separated by equal amounts of space. For instance, in , the typesetter should notice the hangover of the . This should again be the case in . Similarly, the distance between the baselines of the and the in should not be disproportionally large with respect to the height of an .

Additional difficulties may arise when considering automatically generated formulas, in which case line breaking has to be dealt with in a satisfactory way.

Unfortunately, the different esthetic criteria may enter into conflict with each other. For instance, consider the formula . On the one hand, the baselines of the scripts should be the same, but the other hand, the first subscript should not be “disproportionally low” with respect to the . Unfortunately, this dilemma can not been solved in a completely satisfactory way without the help of a human for the simple reason that the computer has no way to know whether the and are “related”. Indeed, if the and are close (like in ), then it is natural to opt for a common base line. However, if they are further away from each other (like in ), then we might want to opt for different base lines and locally optimize the rendering of the first .

Consequently, TeXmacs should offer a reasonable compromise for the most frequent cases, while offering methods for the user to make finer adjustments in the remaining ones. We provide the constructs FormatAdjustMove and FormatAdjustResize to move and resize boxes in order to perform such adjustments. For instance, if the brackets around the two sums

have different sizes, then one may resize the bottom of the subscript of the second sum to 0fn. Alternatively, one may resize the bottoms of both the and subscripts to (say) -0.3fn. For easier adjustments you may use FormatAdjustSmash and FormatAdjustInflate to automatically adjust the size of the contents to the height of the character “x” and the largest one in the font respectively.

Notice that one should adjust by preference in a structural and not visual way. For instance, one should prefer -0.3fn to -2mm in the above example, because the second option disallows you to switch to another font size for your document. Similarly, you should try not change the semantics of the formula. For instance, in the above example, you might have added a “dummy subscript” to the subscript of the sum. However, this would alter the meaning of the formula (whence make it non suitable as input to a computer algebra system) In the future, we plan to provide additional constructs in order to facilitate structural adjusting. For instance, in the case of a formula like

one might think of a construct to enclose the entire formula into an area, where all scripts are forced to be double (using dummy superscripts wherever necessary).

2.Mathematical primitives

<left|large-delimiter>

<left|large-delimiter|size>

<left|large-delimiter|bottom|top>

<mid|large-delimiter|>

<right|large-delimiter|>
(large delimiters)

These primitives are used for producing large delimiters, like in the formula

Matching left and right delimiters are automatically sized so as contain the enclosed expression. Between matching left and right delimiters, the formula may contain an arbitrary number of middle delimiters, which are sized in a similar way. Contrary to TeX, the depth of a large delimiter is not necessarily equal to its height, so as to correctly render formulas like

The user may override the automatically determined size by specifying additional length parameters size or bottom and top. For instance,

f<left|(|-8mm|4mm>x<mid|||8mm>y<right|)|-4mm|8mm>

is rendered as

The size may also be a number , in which case the -th available size for the delimiter is taken. For instance,

g<left|(|0><left|(|1><left|(|2><left|(|3>z<right|)|3><right|)|2><right|)|1><right|)|0>

is rendered as

<big|big-symbol>
(big symbols)

This primitive is used in order to produce big operators as in

(1)

The size of the operator depends on whether the formula is rendered in “display style” or not. Formulas in separate equations, like (1), are said to be rendered in display style, contrary to formulas which occur in the main text, like . The user may use FormatDisplay style to override the current settings.

Notice that the formula (1) is internally represented as

<big|sum><rsub|i=0><rsup|>a<rsub|i>*z<rsup|i><big|.>

The invisible big operator <big|.> is used to indicate the end of the scope of <big|sum>.

<frac|num|den>
(fractions)

The frac primitive is used in order to render fractions like . In display style, the numerator num and denominator den are rendered in the normal size, but display style is turned of when typesetting num and den. When the display style is turned of, then the arguments are rendered in script size. For instance, the content

<frac|1|a<rsub|0>+<frac|1|a<rsub|1>+<frac|1|a<rsub|2>+⋱>>>

is rendered in display style as

<sqrt|content>

<sqrt|content|n>
(roots)

The sqrt primitive is used in order to render square roots like or n-th roots like . The root symbol is automatically sized so as to encapsulate the content:

<lsub|script>

<lsup|script>

<rsub|script>

<rsup|script>
(scripts)

These primitives are used in order to attach a script to the preceding box in a horizontal concatenation (in the case of right scripts) or the next one (in the case of left scripts). When there is no such box, then the script is attached to an empty box. Moreover, when both a subscript and a superscript are specified on the same side, then they are merged together. For instance, the expression

<rsub|a><rsup|b>+<lsub|1><lsup|2>x<rsub|3><rsup|4>=y<rsub|1>+<lsub|c>

is rendered as

When a right script is attached to an operator (or symbol) which accepts limits, then it is rendered below or above instead of beside the operator:

Scripts are rendered in a smaller font in non-display style. Nevertheless, in order to keep formulas readable, the size is not reduced below script-script-size.

<lprime|prime-symbols>

<rprime|prime-symbols>
(primes)

Left and right primes are similar to left and right superscripts, except that they behave in a different way when being edited. For instance, when your cursor is behind the prime symbol in and you press backspace, then the prime is removed. If you are behind and you press backspace several times, then you first enter the superscript, next remove and finally remove the superscript. Notice also that prime-symbols is necessarily a string of concatenated prime symbols. For instance, is represented by f<rprime|'†>.

<below|content|script>

<above|content|script>
(scripts above and below)

The below and above tags are used to explicitly attach a script below or above a given content. Both can be mixed in order to produce content with both a script below and above:

can be produced using

<above|<below|xor|i=1>|> x<rsub|i>

<wide|content|wide-symbol>

<wide*|content|wide-symbol>
(wide symbols)

These primitives can be used in order to produce wide accents above or below some mathematical content. For instance corresponds to the markup <wide|x+y|¯>.

<neg|content>
(negations)

This primitive is mainly used for producing negated symbols or expressions, such as or .

<tree|root|child-1||child-n>
(trees)

This primitive is used to produce a tree with a given root and children child-1 until child-n. The primitive should be used recursively in order to produce trees. For instance,

corresponds to the markup

<tree|+|x|y|<tree|×|2|y|z>>

In the future, we plan to provide further style parameters in order to control the rendering.

3.The font parameters

Several font parameters are crucial for the correct positioning of the different components. The following are often needed:

quad

The main font reference space 1fn, which can be taken as the distance between successive lines of text.

y1 and y2

The bottom and top level for the font (we have y2-y1=quad).

sep

The reference minimal space between distinct components, like the minimal distance between a subscript and a superscript. In fact, sep=quad/10.

wline

The width of several types of lines, like the fraction and square root bars, wide accents, etc.

yfrac

The height of the fraction bar, which is needed for the positioning of fractions and big delimiters. Usually, yfrac is almost equal to yx/2 below.

The following parameters are mainly needed in order to deal with scripts:

yx

The height of the character, which is needed for the positioning of scripts. All the remaining parameters are actually computed as a function of yx.

ysub lo base

Logical base line for subscripts.

ysub hi lim

Subscripts may never physically exceed this top height.

ysup lo base

Logical base line for superscripts.

ysup lo lim

Superscripts may never physically exceed this bottom height.

ysup hi lim

Suggestion for a physical top line for superscripts.

yshift

Possible shift of the base lines when we are inside fractions or scripts.

The individual strings in a font also have several important positioning properties. First of all, they always admit left and right slopes. Furthermore, they admit left and right italic corrections, which are needed for the positioning of scripts or when passing from text in upright to text in italics (or vice versa).

4.Implementation of the mathematical primitives

The typesetting semantics of TeXmacs documents is implemented by

void concater_rep::typeset (tree t, path ip);

in src/Typeset/Concat/concat_math.cpp which dispatch according to the current tree label to more specialized routines, which we will describe below.

Tree labels for mathematical typesettings are (in scr/Kernel/Types/tree_label.hpp)

enum tree_label {
  // [… other labels …]
  
  // mathematics
  AROUND, VAR_AROUND, BIG_AROUND,
  LEFT, MID, RIGHT, BIG, LONG_ARROW,
  LPRIME, RPRIME, BELOW, ABOVE,
  LSUB, LSUP, RSUB, RSUP,
  FRAC, SQRT, WIDE, VAR_WIDE, NEG, TREE,
  SYNTAX,

  // [… other labels …]
};

They provide labels for the primitives to typeset brackets (around, var_around, big_around, left, mid, right), sub/super-scripts (lsup, lsub, rsup, rsub), accents (lprime, rprime), above and below formulas (below, above) fractions (frac), roots (sqrt), wide under and over braces (wide, var_wide), negations (neg), and trees (tree) or syntax (syntax) constructions.

4.1.Primed expressions

Primed expressions are encoded in TeXmacs as

<math|a<rprime|'>,a<rprime|>,<lprime|>s>

and are typeset via the following code (the code for rprime is analogous and is not shown):

void
concater_rep::typeset_lprime (tree t, path ip) {
  if ((N(t) == 1) && is_atomic (t[0])) {
    string s= t[0]->label;
    bool flag= (env->fn->type == FONT_TYPE_UNICODE);
    if (flag)
      for (int i=0; i<N(s); i++)
        flag= flag && (s[i] == '\'' || s[i] == '‘');
    if (env->fn->type == FONT_TYPE_TEX ||
        env->fn->math_type != MATH_TYPE_NORMAL)
      s= replace_primes (s);
    tree old_il;
    if (!flag) old_il= env->local_begin_script ();
    path sip= descend (ip, 0);
    box b1, b2;
    b2= typeset_as_concat (env, s /*t[0]*/, sip);
    b2= symbol_box (sip, b2, N(t[0]->label));
    if (flag || env->fn->math_type != MATH_TYPE_TEX_GYRE)
      b2= move_box (sip, b2,
                    flag? 0: env->as_length (string ("-0.05fn")),
                    flag? env->as_length ("-0.75ex"): 0,
                    false, true);
    if (!flag) env->local_end_script (old_il);
    print (LSUP_ITEM, OP_SKIP, script_box (ip, b1, b2, env->fn));
    penalty_max (HYPH_INVALID);
  }
  else typeset_error (t, ip);
}

Note that we output an LSUP_ITEM, since later on, in a second pass, we need to reposition these boxes as we will do for sub, superscripts.

The replace_primes function:

string
replace_primes (string s) {
  string r;
  int i, n= N(s);
  for (i=0; i<n; i++)
    if (s[i] == '\'') r << "<prime>";
    else if (s[i] == '‘') r << "<backprime>";
    else r << s[i];
  return r;
}

4.2.Fractions

Fractions can be inline or in display style and have different variants (standard frac, display dfrac, small inline tfrac, continued cfrac and slashed frac*). For example

<dfrac|\<mathd\>x|\<mathd\>y>,<space|2em>

<frac*|\<mathd\>x|\<mathd\>y>,

<math|<dfrac|dx|dy>,<space|2em><frac*|dx|dy>,<space|2em><tfrac|dx|dy>,>

<cfrac|1|1+<cfrac|1|1+<cfrac|1|1+x>>>

Apart from frac, the others tags are implemented in std-maths.ts as:

<document|<assign|tfrac|<macro|x|y|<with|mode|math|<with|math-display|false|<frac|x|y>>>>>||<assign|dfrac|<macro|x|y|<with|mode|math|<with|math-display|true|<frac|x|y>>>>>||<assign|cfrac|<macro|x|y|<with|mode|math|<dfrac|x|<resize|y|||<plus|1r|-1sep>|>>>>>||<assign|frac*|<macro|x|y|<move|<lsup|x><resize|/|<plus|1l|0.15em>|<plus|1b|0.5em>|<minus|1r|0.15em>|<minus|1t|0.5em>><rsub|y>||0.05em>>>||<drd-props|frac*|arity|2|syntax|<macro|x|y|x/y>>>

The frac tag is primitive and typesetted by concater_rep::typeset_frac :

void
concater_rep::typeset_frac (tree t, path ip) {
  if (N(t) != 2) { typeset_error (t, ip); return; }
  bool disp= env->display_style;
  tree old;
  if (disp) old= env->local_begin (MATH_DISPLAY, "false");
  else old= env->local_begin_script ();
  tree old_vp= env->local_begin (MATH_VPOS, "1");
  box num= typeset_as_concat (env, t[0], descend (ip, 0));
  env->local_end (MATH_VPOS, "-1");
  box den= typeset_as_concat (env, t[1], descend (ip, 1));
  env->local_end (MATH_VPOS, old_vp);
  font sfn= env->fn;
  if (disp) env->local_end (MATH_DISPLAY, old);
  else env->local_end_script (old);
  if (num->w() <= env->frac_max && den->w () <= env->frac_max)
    print (frac_box (ip, num, den, env->fn, sfn, env->pen));
  else typeset_wide_frac (t, ip);
}

The "math-vpos" environment variable is set to depending on where we are in the typesetting of fractions. This is used in the finalization routines. [Add more?]

The function frac_box inserts an instance of frac_box_rep

frac_box_rep::frac_box_rep (
  path ip, box b1, box b2, font fn2, font sfn2, pencil pen2):
    composite_box_rep (ip), fn (fn2), sfn (sfn2), pen (pen2)
{
  // Italic correction does not lead to nicer results,
  // because right correction is not equilibrated w.r.t. left correction

  SI bar_y = fn->yfrac;
  SI bar_w = fn->wline;
  SI sep   = fn->sep;
  SI b1_y  = min (b1->y1, sfn->y1);
  SI b2_y  = max (b2->y2, sfn->y2);
  SI w     = max (b1->w (), b2->w()) + 2*sep;
  SI d     = sep >> 1;

  pencil bar_pen= pen->set_width (bar_w);
  insert (b1, (w>>1) - (b1->x2>>1), bar_y+ sep+ (bar_w>>1)- b1_y);
  insert (b2, (w>>1) - (b2->x2>>1), bar_y- sep- (bar_w>>1)- b2_y);
  insert (line_box (decorate_middle (ip), d, 0, w-d, 0, bar_pen), 0, bar_y);

  italic_correct (b1);
  italic_correct (b2);
  position ();
  italic_restore (b1);
  italic_restore (b2);
  x1= min (0, x1);
  x2= max (w, x2);
  left_justify ();
  finalize ();
}

The following heuristics are used:

The italic corrections are not taken into account during the positioning algorithms, because this may create the impression that the numerator and denominator are not correctly centered with respect to each other. Nevertheless, the italic corrections are taken into account in order to compute the logical bounding box of the fraction (whose has italic slopes vanish at both sides).

In the case the fraction's numerator or denominator are very wide a fallback typesetting strategy is used, according to:

void
concater_rep::typeset_wide_frac (tree t, path ip) {
  bool numb= needs_brackets (t[0], "Product");
  bool denb= needs_brackets (t[1], "Power");
  pencil old_pen= env->pen;
  marker (descend (ip, 0));
  typeset_large (tree (LEFT, "."), decorate_left (descend (ip, 0)),
                 LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
  env->pen= env->flatten_pen;
  if (numb)
    typeset_large (tree (LEFT, "("), decorate_left (descend (ip, 0)),
                   LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
  env->pen= old_pen;
  typeset (t[0], descend (ip, 0));
  env->pen= env->flatten_pen;
  if (numb)
    typeset_large (tree (RIGHT, ")"), decorate_right (descend (ip, 0)),
                   RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
  typeset_large (tree (MID, "/"), decorate_middle (ip),
                 MIDDLE_BRACKET_ITEM, OP_MIDDLE_BRACKET, "<mid-");
  if (denb)
    typeset_large (tree (LEFT, "("), decorate_left (descend (ip, 1)),
                   LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
  env->pen= old_pen;
  typeset (t[1], descend (ip, 1));
  env->pen= env->flatten_pen;
  if (denb)
    typeset_large (tree (RIGHT, ")"), decorate_right (descend (ip, 1)),
                   RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
  env->pen= old_pen;
  typeset_large (tree (RIGHT, "."), decorate_right (descend (ip, 1)),
                 RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
  marker (descend (ip, 1));
}

4.3.Roots

The following heuristics are used:

We take the logical right border plus the italic correction of the main argument in order to determine the right hand limit of the upper bar. The left italic correction is not needed.

<sqrt|1+<frac|3|4>+\<cdots\>+d>

Typesetting goes as follows:

void
concater_rep::typeset_sqrt (tree t, path ip) {
  if (N(t) != 1 && N(t) != 2) { typeset_error (t, ip); return; }
  box b= typeset_as_concat (env, t[0], descend (ip, 0));
  if (b->w () > env->frac_max) { typeset_wide_sqrt (t, ip); return; }
  box ind;
  if (N(t)==2) {
    bool disp= env->display_style;
    tree old;
    if (disp) old= env->local_begin (MATH_DISPLAY, "false");
    tree old_il= env->local_begin_script ();
    ind= typeset_as_concat (env, t[1], descend (ip, 1));
    env->local_end_script (old_il);
    if (disp) env->local_end (MATH_DISPLAY, old);
  }
  SI sep= env->fn->sep;
  font lfn= env->fn;
  bool stix= starts (lfn->res_name, "stix-");
  if (stix) lfn= rubber_font (lfn);
  box sqrtb= delimiter_box (decorate_left (ip), "<large-sqrt>",
                            lfn, env->pen, b->y1, b->y2 + (3*sep >> 1));
  if (stix) sqrtb= shift_box (decorate_left (ip), sqrtb,
                              -env->fn->wline/2, -env->fn->wline/3,
                              false, true);
  print (sqrt_box (ip, b, ind, sqrtb, env->fn, env->pen));
}

sqrt_box_rep::sqrt_box_rep (
  path ip, box b1, box b2, box sqrtb, font fn2, pencil pen2):
    composite_box_rep (ip), fn (fn2), pen (pen2)
{
  right_italic_correct (b1);

  SI sep  = fn->sep;
  SI wline= fn->wline;
  SI dx   = -fn->wfn/36, dy= -fn->wfn/36; // correction
  SI by   = sqrtb->y2+ dy;
  if (sqrtb->x2 - sqrtb->x4 > wline) dx -= (sqrtb->x2 - sqrtb->x4);
  
  pencil rpen= pen->set_width (wline);
  insert (b1, 0, 0);
  if (!is_nil (b2)) {
    SI X = - sqrtb->w();
    SI M = X / 3;
    SI Y = sqrtb->y1;
    SI bw= sqrtb->w();
    SI bh= sqrtb->h();
    if (fn->math_type == MATH_TYPE_TEX_GYRE) {
      if (2*bh < 9*bw) Y += bh >> 1;
      else if (occurs ("ermes", fn->res_name)) Y += (19*bw) >> 3;
      else if (occurs ("agella", fn->res_name)) Y += (16*bw) >> 3;
      else Y += (15*bw) >> 3;
    }
    else {
      if (bh < 3*bw) Y += bh >> 1;
      else Y += (bw*3) >> 1;
    }
    insert (b2, min (X, M- b2->x2), Y- b2->y1+ sep);
  }
  insert (sqrtb, -sqrtb->x2, 0);
  insert (line_box (decorate_middle (ip), dx, by, b1->x2, by, rpen), 0, 0);
  
  position ();
  left_justify ();
  y1 -= wline;
  y2 += wline;
  x2 += sep >> 1;

  right_italic_restore (b1);
  finalize ();
}

Wide versions of the square root goes as follows:

void
concater_rep::typeset_wide_sqrt (tree t, path ip) {
  bool br= needs_brackets (t[0], "Postfixed");
  pencil old_pen= env->pen;
  marker (descend (ip, 0));
  typeset_large (tree (LEFT, "."), decorate_left (descend (ip, 0)),
                 LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
  env->pen= env->flatten_pen;
  if (br)
    typeset_large (tree (LEFT, "("), decorate_left (descend (ip, 0)),
                   LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
  env->pen= old_pen;
  typeset (t[0], descend (ip, 0));
  env->pen= env->flatten_pen;
  if (br)
    typeset_large (tree (RIGHT, ")"), decorate_right (descend (ip, 0)),
                   RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
  env->pen= old_pen;

  bool disp= env->display_style;
  tree old;
  if (disp) old= env->local_begin (MATH_DISPLAY, "false");
  tree old_il= env->local_begin_script ();
  env->pen= env->flatten_pen;
  box num= typeset_as_concat (env, "1", decorate_middle (ip));
  box den;
  if (N(t) >= 2) {
    env->pen= old_pen;
    den= typeset_as_concat (env, t[1], descend (ip, 1));
    env->pen= env->flatten_pen;
  }
  else den= typeset_as_concat (env, "2", decorate_middle (ip));
  box fr= frac_box (decorate_middle (ip), num, den, env->fn, env->fn, env->pen);
  env->pen= old_pen;
  env->local_end_script (old_il);
  if (disp) env->local_end (MATH_DISPLAY, old);
  penalty_max (HYPH_INVALID);
  a << line_item (RSUP_ITEM, OP_SKIP,
                  script_box (ip, box (), fr, env->fn), HYPH_INVALID);

  typeset_large (tree (RIGHT, "."), decorate_right (descend (ip, 1)),
                 RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
  marker (descend (ip, 1));
}

4.4.Negations

Negations are barred expressions:

<neg|abc>

The following heuristics are used:

Typesetting of negation tags is very elementary

void
concater_rep::typeset_neg (tree t, path ip) {
  if (N(t) != 1) { typeset_error (t, ip); return; }
  box b= typeset_as_concat (env, t[0], descend (ip, 0));
  print_semantic (neg_box (ip, b, env->fn, env->pen), t[0]);
}

and give rise to negation boxes:

neg_box_rep::neg_box_rep (path ip, box b, font fn2, pencil pen2):
  composite_box_rep (ip), fn (fn2), pen (pen2)
{
  SI wline= fn->wline;
  SI delta= fn->wfn/6;
  SI X    = (b->x1 + b->x2) >> 1;
  SI Y    = (b->y1 + b->y2) >> 1;
  SI DX, DY;

  pencil npen= pen->set_width (wline);
  insert (b, 0, 0);
  if ((3*(b->x2-b->x1)) > (2*(b->y2-b->y1))) {
    DY= delta + ((b->y2 - b->y1)>>1);
    DX= DY>>1;
  }
  else {
    DX= delta + ((b->x2 - b->x1)>>1);
    DY= DX;
  }
  insert (line_box (decorate_middle (ip), X+DX, Y+DY, X-DX, Y-DY, npen), 0, 0);
  
  italic_correct (b);
  position ();
  italic_restore (b);
  finalize ();
}

4.5.Wide boxes

Wide boxes are used for under and over-braces and wide accents:

<wide*|x+y|\<wide-underbrace\>><rsub|s>

<wide|x+\<cdots\>+y|\<wide-overbrace\>><rsup|s>

The following heuristics are used:

void
concater_rep::typeset_wide (tree t, path ip, bool above) {
  if (N(t) != 2) { typeset_error (t, ip); return; }
  box b= typeset_as_concat (env, t[0], descend (ip, 0));
  string s= env->exec_string (t[1]);
  if (s == "^") s= "<hat>";
  if (s == "~") s= "<tilde>";
  bool request_wide= false;
  if (starts (s, "<wide-")) {
    s= "<" * s (6, N(s));
    request_wide= true;
  }
  if (ends (s, "brace>") || ends (s, "brace*>"))
    b= move_box (decorate_middle (descend (ip, 0)), b, 0, 0, true);
  box wb= wide_box (ip, b, s, env->fn, env->pen, request_wide, above);
  print_semantic (wb, t[0]);
  if (ends (s, "brace>")) with_limits (LIMITS_ALWAYS);
}

wide_box produces a wide_box_rep whose constructor perform further computations and handling of special cases:

wide_box_rep::wide_box_rep (
  path ip, box ref2, string s2, font fn2, pencil pen2,
  bool request_wide2, bool above2):
    composite_box_rep (ip), ref (ref2), s (s2), fn (fn2), pen (pen2),
    request_wide (request_wide2), above (above2)
{
  box hi;
  wide= compute_wide_accent (ip, ref, s, fn, pen, request_wide, above, hi, sep);
  SI X, Y, dx;
  SI hw= max (ref->w(), hi->w()) >> 1;
  SI m = (ref->x1 + ref->x2) >> 1;
  insert (ref, 0, 0);
  if (above) {
    Y= ref->y2;
    X= m;
    if (ref->right_slope () != 0)
      X += ref->rsup_correction() + ((SI) (ref->right_slope() * fn->yx * 0.5));
    X += ref->wide_correction (1);
    //X= ((SI) (ref->right_slope () * (Y - fn->yx))) + m;
    insert (hi, X- ((hi->x1 + hi->x2)>>1), Y+ sep);
  }
  else {
    Y= ref->y1 - hi->y2;
    X= m - ((SI) (ref->right_slope () * sep));
    X += ref->wide_correction (-1);
    //X= ((SI) (ref->right_slope () * (Y - sep))) + m;
    insert (hi, X- ((hi->x1 + hi->x2)>>1), Y- sep);
  }
  position ();
  dx= x1;
  left_justify ();

  dh= hi->y2+ sep;
  dw= (SI) (dh * ref->right_slope ());
  dd= fn->sep;
  x1= m- hw- dx;
  x2= m+ hw- dx;
  x1= min (x1, ref->x1);
  x2= max (x2, ref->x2);
  if (!above) y1 += fn->sep - sep;
  finalize ();
}

The computation of the wide accent is more involved and divided in several cases.

bool
compute_wide_accent (path ip, box b, string s,
                     font fn, pencil pen, bool request_wide, bool above,
                     box& wideb, SI& sep) {
  bool unicode= (fn->type == FONT_TYPE_UNICODE);
  bool stix= (fn->math_type == MATH_TYPE_STIX);
  bool tex_gyre= (fn->math_type == MATH_TYPE_TEX_GYRE);
  bool wide= (b->w() > (fn->wquad)) || request_wide;
  if (ends (s, "dot>") || (s == "<acute>") ||
      (s == "<grave>") || (s == "<abovering>")) wide= false;
  if (wide && !request_wide && b->wide_correction (0) != 0) wide= false;
  bool very_wide= false;
  SI   accw= fn->wfn;
  if (wide) {
    if (tex_gyre) {
      if (s == "^" || s == "<hat>" ||
          s == "~" || s == "<tilde>" ||
          s == "<check>")
        very_wide= (b->w() >= ((8*fn->wfn) >> 2));
      else if (ends (s, "brace>") || ends (s, "brace*>")) {
        if (starts (s, "<sq"))
          very_wide= (b->w() >= ((11*fn->wfn) >> 2));
        else very_wide= (b->w() >= ((15*fn->wfn) >> 2));
      }
      else very_wide= true;
    }
    else if (!unicode) {
      if (s == "^" || s == "<hat>" || s == "~" || s == "<tilde>")
        very_wide= (b->w() >= ((9*fn->wfn) >> 2));
      else very_wide= true;
    }
    else if (stix) very_wide= true;
    /*
    else if (s == "^" || s == "<hat>" || s == "~" || s == "<tilde>" ||
             s == "<bar>" || s == "<vect>" || s == "<check>" ||
             s == "<breve>" || s == "<invbreve>") {
      box wb= text_box (decorate_middle (ip), 0, s, fn, pen);
      accw= wb->x4 - wb->x3;
      if (b->w() >= 16*accw) very_wide= true;
    }
    */
    else very_wide= true;
  }
  if (wide && stix) {
    if (s == "^") s= "<hat>";
    if (s == "~") s= "<tilde>";
    if (s == "<hat>" || s == "<tilde>" || s == "<check>" ||
        ends (s, "brace>") || ends (s, "brace*>")) {
      font rfn= rubber_font (fn);
      SI width= b->x2- b->x1 - fn->wfn/4;
      wideb= wide_stix_box (decorate_middle (ip),
                            "<rubber-" * s (1, N(s)-1) * ">",
                            rfn, pen, width);
      if (wideb->w() >= width) {
        if (b->right_slope () != 0)
          wideb= shift_box (decorate_middle (ip), wideb,
                            (SI) (-0.5 * b->right_slope () * fn->yx), 0);
        sep= above? -fn->yx: fn->sep;
        if (above) {
          if (s == "<overbrace>" || s == "<squnderbrace*>") sep= 2 * fn->sep;
          if (s == "<poverbrace>") sep= 3 * fn->sep;
        }
        return wide;
      }
    }
  }
  if (very_wide) {
    SI w= fn->wline;
    if (stix) w= (SI) (1.189 * w);
    pencil wpen= pen->set_width (w);
    if ((s == "^") || (s == "<hat>"))
      wideb= wide_hat_box   (decorate_middle (ip), b->x1, b->x2, wpen);
    else if ((s == "~") || (s == "<tilde>"))
      wideb= wide_tilda_box (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<bar>")
      wideb= wide_bar_box   (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<vect>")
      wideb= wide_vect_box  (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<check>")
      wideb= wide_check_box (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<breve>" || s == "<punderbrace>" || s == "<punderbrace*>")
      wideb= wide_breve_box (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<invbreve>" || s == "<poverbrace>" || s == "<poverbrace*>")
      wideb= wide_invbreve_box(decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<squnderbrace>" || s == "<squnderbrace*>")
      wideb= wide_squbr_box (decorate_middle (ip), b->x1, b->x2, wpen);
    else if (s == "<sqoverbrace>" || s == "<sqoverbrace*>")
      wideb= wide_sqobr_box (decorate_middle (ip), b->x1, b->x2, wpen);
    else wideb= wide_box (decorate_middle (ip),
                          "<rubber-" * s (1, N(s)-1) * ">",
                          fn, pen, b->x2- b->x1);
    sep= fn->sep;
    if (stix || !unicode) sep= (SI) (1.5 * sep);
  }
  else if (wide && tex_gyre) {
    string ws= "<wide-" * s (1, N(s)-1) * ">";
    SI width= b->x2- b->x1 - fn->wfn/4;
    wideb= wide_box (decorate_middle (ip), ws, fn, pen, width);
    if (b->right_slope () != 0) {
      bool times= stix || (tex_gyre && occurs ("ermes", fn->res_name));
      double factor= ((times || !above)? 0.2: 0.5);
      wideb= shift_box (decorate_middle (ip), wideb,
                        (SI) (-factor * b->right_slope () * fn->yx), 0);
    }
    sep= above? -fn->yx: fn->sep;
  }
  else if (wide && !unicode) {
    string ss= s (1, N(s)-1);
    if (ss == "^") ss= "hat";
    if (ss == "~") ss= "tilde";
    string ws= "<wide-" * ss * ">";
    SI width= b->x2- b->x1 - fn->wfn/4;
    wideb= wide_box (decorate_middle (ip), ws, fn, pen, width);
    if (b->right_slope () != 0) {
      double factor= (above? 0.5: 0.2);
      wideb= shift_box (decorate_middle (ip), wideb,
                        (SI) (-factor * b->right_slope () * fn->yx), 0);
    }
    sep= above? -fn->yx: fn->sep;
  }
  else if (wide) {
    SI pad= fn->wfn - accw;
    pad= (SI) ((0.75 * accw * pad) / (b->w() - pad));
    double sx= ((double) (b->w() - pad)) / ((double) accw);
    sx= floor (4.0*sx) / 4.0;
    double sy= sqrt (sqrt (sx));
    font sfn= fn->magnify (sx, sy);
    wideb= text_box (decorate_middle (ip), 0, s, sfn, pen);
    wideb= resize_box (decorate_middle (ip), wideb,
                       max (wideb->x1, wideb->x3), wideb->y1,
                       min (wideb->x2, wideb->x4), wideb->y2);
    if (unicode && b->right_slope () != 0)
      wideb= shift_box (decorate_middle (ip), wideb,
                        (SI) (-0.5 * b->right_slope () * fn->yx), 0);
    sep= above? -fn->yx: fn->sep;
    if (above) sep -= 3 * (sy - 1.0) * fn->sep;
  }
  else {
    wideb= text_box (decorate_middle (ip), 0, s, fn, pen);
    if (unicode && b->right_slope () != 0) {
      bool times= stix || (tex_gyre && occurs ("ermes", fn->res_name));
      double factor= ((times || !above)? 0.2: 0.5);
      wideb= shift_box (decorate_middle (ip), wideb,
                        (SI) (-factor * b->right_slope () * fn->yx), 0);
    }
    sep= above? -fn->yx: fn->sep;
  }
  if (above && unicode) {
    SI min_d= fn->yx / 8;
    SI max_d= fn->yx / 3;
    if (wideb->y1 + sep <  min_d) sep= min_d - wideb->y1;
    if (wideb->y1 + sep >= max_d) sep= max_d - wideb->y1;
  }
  if (!unicode && !wide && !above)
    wideb= vresize_box (wideb->ip, wideb, wideb->y1 + fn->yx, wideb->y2);
  else if (unicode && s == "<vect>") {
    if (wide);
    else if (above) sep -= fn->yx + (fn->sep >> 1);
    else wideb= vresize_box (wideb->ip, wideb, wideb->y1 + fn->yx, wideb->y2);
  }
  else if (stix || tex_gyre) sep += fn->sep >> 1;
  return wide;
}

To complete the description we discuss wide_box and wide_stix_box which are variants of the same logic:

box
wide_box (path ip, string s, font fn, pencil pen, SI width) {
  string r= get_wide (s, fn, width);
  metric ex;
  fn->get_extents (r, ex);
  box b= text_box (ip, 0, r, fn, pen);
  return macro_box (ip, b, fn);
}

box
wide_stix_box (path ip, string s, font fn, pencil pen, SI width) {
  string r= get_wide_stix (s, fn, width);
  metric ex;
  fn->get_extents (r, ex);
  box b= text_box (ip, 0, r, fn, pen);
  return macro_box (ip, b, fn);
}

the concrete selection of the appropriate glyph which fits the horizontal size of wide construction is performed by get_wide (or get_stix_wide):

static string
get_wide (string s, font fn, SI width) {
  ASSERT (N(s) >= 2 && s[0] == '<' && s[N(s)-1] == '>',
          "invalid rubber character");
  string radical= s (0, N(s)-1) * "-";
  string first  = radical * "0>";
  metric ex;
  fn->get_extents (first, ex);
  if ((ex->x2- ex->x1) >= width) return first;

  string second = radical * "1>";
  metric ey;
  fn->get_extents (second, ey);
  SI w1= ex->x2- ex->x1;
  SI w2= ey->x2- ey->x1;
  if ((w2 <= w1) || (w2 > width)) return first;
  SI  d= w2- w1;
  int n= (width-w1) / (d+1);

  int credit= 20;
  while (true) {
    string test= radical * as_string (n+1) * ">";
    fn->get_extents (test, ey);
    if (ey->x2- ey->x1 > width || credit <= 0)
      return radical * as_string (n) * ">";
    n++;
    credit--;
  }
}

static string
get_wide_stix (string s, font fn, SI width) {
  ASSERT (N(s) >= 2 && s[0] == '<' && s[N(s)-1] == '>',
          "invalid rubber character");
  string radical= s (0, N(s)-1) * "-";
  metric ex;
  int n= 0;
  while (true) {
    string test= radical * as_string (n) * ">";
    fn->get_extents (test, ex);
    if (ex->x2- ex->x1 > width || n >= 6)
      return radical * as_string (n) * ">";
    n++;
  }
}

4.6.Subscripts and superscripts

The positioning of subscripts and superscripts is a complicated affair, due to the conflict between locally and globally optimal esthetics mentioned above. The base line for a subscript is determined as follows:

  1. Always pretend that the subscript has height at least y2-yshift in the script font (actually we should use the height of an instead).

  2. Try to position the script at the base line given by the main argument.

  3. If the top limit (given by the main argument) is physically exceeded by the subscript, then the base line is moved further down accordingly.

The base line for a superscript is determined as follows:

  1. Try to physically position the superscript beneath the suggested top line. Usually, this will place the superscript to far down.

  2. Move the superscript up to the logical base line if necessary. This will usually occur: most of the time, the logical base line is the just the height of an -script below the suggested top line.

  3. If the superscript physically descends below the physical under limit given by the main box, then we move the superscript further upwards.

If both a subscript and a superscript were present, then we still have to adjust the base lines: if the top of the subscript and the bottom of the superscript are not physically separated by sep, then we both move the subscript and the superscript by the same amount away from each other. Because of step 1 in the positioning of the subscript, the base lines of double scripts will usually be the same in formulas with several of them.

The right slope and italic correction of a script box may be non trivial. In order to compute them, we first determine the script (or main argument), whose right limit (taking into account its italic correction) is furthest to the right (this may be the main box, in the case of a big integral with a tiny subscript). Then the right slope of the main box is inherited by the right slope of this script (or main argument). As to the italic correction, it is precisely the difference between the right offset of the script plus its italic correction minus the logical right coordinate of the entire box. The italic correction should be at least zero though. The left slope and italic correction are computed in a similar way.

[Explain the code below]

void
concater_rep::typeset_script (tree t, path ip, bool right) {
  if (N(t) != 1) { typeset_error (t, ip); return; }
  int type= RSUP_ITEM;
  box b1, b2;
  tree old_ds= env->local_begin (MATH_DISPLAY, "false");
  tree old_mc= env->local_begin (MATH_CONDENSED, "true");
  tree old_il= env->local_begin_script ();
  if (is_func (t, SUB (right))) {
    tree old_vp= env->local_begin (MATH_VPOS, "-1");
    b1= typeset_as_concat (env, t[0], descend (ip, 0));
    type= right? RSUB_ITEM: LSUB_ITEM;
    env->local_end (MATH_VPOS, old_vp);
  }
  if (is_func (t, SUP (right))) {
    tree old_vp= env->local_begin (MATH_VPOS, "1");
    b2= typeset_as_concat (env, t[0], descend (ip, 0));
    type= right? RSUP_ITEM: LSUP_ITEM;
    env->local_end (MATH_VPOS, old_vp);
  }
  env->local_end_script (old_il);
  env->local_end (MATH_CONDENSED, old_mc);
  env->local_end (MATH_DISPLAY, old_ds);
  if (right) penalty_max (HYPH_INVALID);
  a << line_item (type, OP_SKIP,
                  script_box (ip, b1, b2, env->fn), HYPH_INVALID);
  // do not use print, because of italic space
  if (!right) penalty_max (HYPH_INVALID);
}

dummy_script_box_rep::dummy_script_box_rep (path ip, box b1, box b2, font fn2):
  composite_box_rep (ip), fn (fn2)
{
  SI sep  = fn->sep;
  SI lo_y = fn->ysub_lo_base;
  SI hi_y = fn->ysup_lo_base;
  SI miny2= (fn->y2 - fn->yshift) * script (fn->size, 1) / fn->size;

  type= 0;
  if (!is_nil (b1)) type += 1;
  if (!is_nil (b2)) type += 2;

  if ((!is_nil (b1)) && (!is_nil (b2))) {
    SI y= max (b1->y2, miny2);
    SI d= lo_y + y + sep - hi_y - b2->y1;
    if (d > 0) {
      lo_y -= (d>>1);
      hi_y += (d>>1);
    }
  }
  if (!is_nil (b1)) {
    insert (b1, 0, lo_y);
    italic_correct (b1);
  }
  if (!is_nil (b2)) {
    insert (b2, 0, hi_y);
    italic_correct (b2);
  }
  position ();
  if (!is_nil (b1)) italic_restore (b1);
  if (!is_nil (b2)) italic_restore (b2);
  left_justify ();
  y1= min (y1, fn->ysub_lo_base);
  y2= max (y2, fn->ysup_lo_base + fn->yx);
  finalize ();
}

4.7.Big operators

Big operators, like the big sum, products or integrals

<big|sum><rsub|n=1><rsup|N>a<rsub|n>

<big|prod><rsub|n=1><rsup|\<infty\>>

<around*|(|1-<frac|1|n>|)>

<big|int>x<rsup|2>\<mathd\>x

It has two basic shapes, either in display style as above or inline as , , .

They are typeset by:

void
concater_rep::typeset_bigop (tree t, path ip) {
  if ((N(t) == 1) && is_atomic (t[0])) {
    space spc= env->fn->spc;
    string l= t[0]->label;
    string s= "<big-" * l * ">";
    bool flag= (!env->math_condensed) && (l != ".");
    box b;
    if (env->fn->type == FONT_TYPE_UNICODE) {
      font mfn= rubber_font (env->fn);
      b= big_operator_box (ip, s, mfn, env->pen,
                           env->display_style? 2: 1);
    }
    else b= big_operator_box (ip, s, env->fn, env->pen,
                              env->display_style? 2: 1);
    print (STD_ITEM, OP_BIG, b);
    penalty_min (HYPH_PANIC);
    bool int_flag= false, it_flag= false, lim_flag= true;
    get_big_flags (l, int_flag, it_flag, lim_flag);
    if (lim_flag) with_limits (LIMITS_DISPLAY);
    if (flag) {
      if (int_flag) {
        if (env->fn->math_type == MATH_TYPE_STIX)
          print (env->display_style? (spc / 2): (spc / 4));
        else if (env->fn->math_type == MATH_TYPE_TEX_GYRE)
          print (env->display_style? (spc / 2): (spc / 4));
        else if (it_flag)
          print (env->display_style? 0: (spc / 4));
        else print (spc / 4);
      }
      else print (env->display_style? spc: (spc / 2));
    }
    // FIXME: we should use parameters from operator-big class in std-math.syx
    // FIXME: in concat_post, we add some more space behind big operators
    //        with scripts; this should be understood better and formalized
  }
  else typeset_error (t, ip);
}

where we note the handling of limits and of the correct spacing after the symbol. The relevant glyph for <big|sum> is <big-sum-N> where N=1,2 according to the appropriate size. Note that for Unicode fonts (type == FONT_TYPE_UNICODE) we dispatch the selection of the glyph to a rubber obtained via rubber_font.

The function big_operator_box takes care of the proper vertical placement of the glyph:

box
big_operator_box (path ip, string s, font fn, pencil pen, int n) {
  ASSERT (N(s) >= 2 && s[0] == '<' && s[N(s)-1] == '>',
          "invalid rubber character");
  string r= s (0, N(s)-1) * "-" * as_string (n) * ">";
  metric ex;
  fn->get_extents (r, ex);
  SI y= fn->yfrac - ((ex->y1 + ex->y2) >> 1);
  box mvb= move_box (ip, text_box (ip, 0, r, fn, pen), 0, y, false, true);
  return macro_box (ip, mvb, fn, BIG_OP_BOX);
}

4.8.Big delimiters

The automatic positioning and computation of sizes of big delimiters is again complicated because of potential conflicts between locally and globally optimal esthetics.

First of all, TeX fonts come only with a discrete set of possible sizes for large delimiters. This is an advantage from the point of view that it favorites delimiters around slightly different expressions to have the same baselines. However, it has the disadvantage that delimiters are easily made “one size to large”. For this reason, we actually diminish the height and the depth of the delimited expression by the small amount sep, before computing the sizes of the delimiters.

Secondly, it is best when the vertical middles of big delimiters occur at the height of fraction bars. However, in a formula like

it may be worth it to descend the delimiters a bit. On the other hand, slight vertical shifts in the middles of the delimiters potentially have a bad effect on base lines, like in

In TeXmacs, we use the following compromise: we start with the middle of the delimited expression as a first approximation to the middle of the delimiters. The real middle is obtained by shifting this middle towards the height of fraction bars by an amount which cannot exceed sep.

From a horizontal point of view, we finally have to notice that we adapted the metrics of the big delimiters in a way that potential scripts are positioned in a better way. For instance, according to the TeX tfm file, in a formula like

the square rather seems to be a left superscript of the second closing bracket than a right superscript of the first one. This is particularly annoying in the case of automatically generated formulas, where this situation occurs quite often.

Markup looks like

<around*|[|

<around*|{|

<frac|1|2>+\<cdots\>+<frac|3|4>

|}>+\<alpha\>

|]>

for automatic sizing of the brackets and like

<around*|<left|[|5>|

<around*|<left|{|2>|

1+\<cdots\>+3

|<right|}|2>>+\<alpha\>

|<right|]|5>>

for manual sizing. The typesetting of big delimiters proceeds in various phases. A first phase fixes the basic structure and horizontal positioning of the three arguments of the around tag. If requested via an appropriate environment variable, we use different colors to emphasize the matching brackets.

void
concater_rep::typeset_around (tree t, path ip, bool colored) {
  tree old_nl=
    env->local_begin (MATH_NESTING_LEVEL, as_string (env->nesting_level + 1));
  if (colored) {
    tree old_col= env->local_begin (COLOR, bracket_color (env->nesting_level));
    typeset_around (t, ip, false);
    env->local_end (COLOR, old_col);
  }
  else {
    marker (descend (ip, 0));
    switch (L(t)) {
    case AROUND:
      if (N(t) == 3) {
        box br1= typeset_as_concat (env, t[0], descend (ip, 0));
        print (STD_ITEM, OP_OPENING_BRACKET, br1);
        typeset (t[1], descend (ip, 1));
        box br2= typeset_as_concat (env, t[2], descend (ip, 2));
        print (STD_ITEM, OP_CLOSING_BRACKET, br2);
      }
      else typeset_error (t, ip);
      break;
    case VAR_AROUND:
      if (N(t) == 3) {
        font old_fn= env->fn;
        font new_fn= env->fn;
        if (starts (new_fn->res_name, "stix-"))
          //if (new_fn->type == FONT_TYPE_UNICODE)
          new_fn= rubber_font (new_fn);
        env->fn= new_fn;
        typeset (make_large (LEFT, t[0]),
                 decorate_middle (descend (ip, 0)));
        env->fn= old_fn;
        typeset (t[1], descend (ip, 1));
        env->fn= new_fn;
        typeset (make_large (RIGHT, t[2]),
                 decorate_middle (descend (ip, 2)));
        env->fn= old_fn;
      }
      else typeset_error (t, ip);
      break;
    case BIG_AROUND:
      if (N(t) == 2) {
        typeset (make_large (BIG, t[0]),
                 decorate_middle (descend (ip, 0)));
        typeset (t[1], descend (ip, 1));
      }
      else typeset_error (t, ip);
      break;
    default:
      break;
    }
    marker (descend (ip, 1));
  }
  env->local_end (MATH_NESTING_LEVEL, old_nl);
}

The expression make_large (LEFT, t[0]) ensures that the first and third subtrees have a specific structure, of the form <left|[|5> for a fixed size or <left|[> if there is automatic sizing.

static tree
make_large (tree_label l, tree t) {
  if (!is_atomic (t)) {
    if (is_func (t, l)) {
      if (N(t) == 2 && is_atomic (t[0]) && is_int (t[1])) {
        string s= t[0]->label;
        if (N(s) >= 3 && s[0] == '<' && s[N(s)-1] == '>') s= s (1, N(s)-1);
        return tree (l, s * "-" * t[1]->label);
      }
      else return t;
    }
    else return tree (l, ".");
  }
  string s= t->label;
  if (N(s) <= 1) return tree (l, s);
  if (s[0] != '<' || s[N(s)-1] != '>' || s == "<nobracket>")
    return tree (l, ".");
  return tree (l, s (1, N(s)-1));
}

The typesetting of LEFT, RIGHT and MID markup is dispatched as follows in concater_rep::typeset:

  case LEFT:
    typeset_large (t, ip, LEFT_BRACKET_ITEM, OP_OPENING_BRACKET, "<left-");
    break;
  case MID:
    typeset_wide_middle (t, ip);
    break;
  case RIGHT:
    typeset_large (t, ip, RIGHT_BRACKET_ITEM, OP_CLOSING_BRACKET, "<right-");
    break;

and realised as follows:

void
concater_rep::typeset_large (tree t, path ip, int tp, int otp, string prefix) {
  font old_fn= env->fn;
  if (starts (old_fn->res_name, "stix-"))
    //if (old_fn->type == FONT_TYPE_UNICODE)
    env->fn= rubber_font (old_fn);
  
  if (N(t) < 1 || !is_atomic (t[0]))
    typeset_error (t, ip);
  else {
    string br= t[0]->label;
    if (N(br) > 2 && br[0] == '<' && br[N(br)-1] == '>')
      br= br (1, N(br) - 1);
    if (N(t) == 1) {
      string s= prefix * br * ">";
      box b= text_box (ip, 0, s, env->fn, env->pen);
      print (tp, otp, b);
      // temporarary: use parameters from group-open class in std-math.syx
      // bug: allow hyphenation after ) and before *
    }
    else if (N(t) == 2 && is_int (t[1])) {
      int nr= max (as_int (t[1]->label), 0);
      string s= prefix * br * "-" * as_string (nr) * ">";
      box b= text_box (ip, 0, s, env->fn, env->pen);
      SI dy= env->fn->yfrac - ((b->y1 + b->y2) >> 1);
      box mvb= move_box (ip, b, 0, dy, false, true);
      print (STD_ITEM, otp, macro_box (ip, mvb, env->fn));
    }
    else {
      SI y1, y2;
      if (N(t) == 2) {
        SI l= env->as_length (t[1]) >> 1;
        y1= env->fn->yfrac - l;
        y2= env->fn->yfrac + l;
      }
      else {
        y1= env->as_length (t[1]);
        y2= env->as_length (t[2]);
      }
      string s= prefix * br * ">";
      box b= delimiter_box (ip, s, env->fn, env->pen, y1, y2);
      print (STD_ITEM, otp, b);
    }
  }

  env->fn= old_fn;
}

Note that the main branch for markup like <left|[> produces only a text box with an appropriate glyph. The vertical size of the glyph is still not correct at this point. Since it depends on the globality of the current expression and not known when typesetting the left bracket. It will be determined later on. See Section 4.11.

4.9.Long arrows

Long arrows refers to the following markup:

A<long-arrow|<rubber-rightarrow>||<text|a long arrow>>B

allowing for material above and below. The typesetting of long arrows do not require further mechanisms to those already put in place for wide boxes:

void
concater_rep::typeset_long_arrow (tree t, path ip) {
  if (N(t) != 2 && N(t) != 3) { typeset_error (t, ip); return; }
  tree old_ds= env->local_begin (MATH_DISPLAY, "false");
  tree old_mc= env->local_begin (MATH_CONDENSED, "true");
  tree old_il= env->local_begin_script ();
  box sup_b, sub_b;
  if (N(t) >= 2) {
    tree old_vp= env->local_begin (MATH_VPOS, "-1");
    sup_b= typeset_as_concat (env, t[1], descend (ip, 1));
    env->local_end (MATH_VPOS, old_vp);
  }
  if (N(t) >= 3) {
    tree old_vp= env->local_begin (MATH_VPOS, "1");
    sub_b= typeset_as_concat (env, t[2], descend (ip, 2));
    env->local_end (MATH_VPOS, old_vp);
  }
  env->local_end_script (old_il);
  env->local_end (MATH_CONDENSED, old_mc);
  env->local_end (MATH_DISPLAY, old_ds);

  string s= env->exec_string (t[0]);
  SI w= sup_b->w();
  if (N(t) == 3) w= max (w, sub_b->w());
  w += env->fn->wquad;
  box arrow= wide_box (decorate (descend (ip, 0)), s, env->fn, env->pen, w);

  space spc= env->fn->spc;
  if (env->math_condensed) spc= space (spc->min>>3, spc->def>>3, spc->max>>2);
  else spc= space (spc->min>>1, spc->def>>1, spc->max);
  print (spc);
  print (limit_box (ip, arrow, sub_b, sup_b, env->fn, false));
  print (spc);
}

It introduces lim_box_rep which takes care of the relative positioning of the various subboxes (limit_box returns a lim_box_rep):

lim_box_rep::lim_box_rep (path ip, box r2, box lo, box hi, font fn2, bool gl):
  composite_box_rep (ip), ref (r2), fn (fn2), glued (gl)
{
  SI sep_lo= fn->sep + fn->yshift;
  SI sep_hi= fn->sep + (fn->yshift >> 1);
  SI X, Y;
  insert (ref, 0, 0);
  type= 0;
  if (!is_nil (lo)) type += 1;
  if (!is_nil (hi)) type += 2;
  if (!is_nil (lo)) {
    SI top= max (lo->y2, fn->y2 * script (fn->size, 1) / fn->size) + sep_lo;
    Y= ref->y1;
    X= ((SI) (ref->right_slope ()* (Y+top-lo->y1))) + ((ref->x1+ref->x2)>>1);
    insert (lo, X- (lo->x2 >> 1), Y-top);
    italic_correct (lo);
  }
  if (!is_nil (hi)) {
    SI bot= min (hi->y1, fn->y1 * script (fn->size, 1) / fn->size) - sep_hi;
    Y= ref->y2;
    X= ((SI) (ref->right_slope ()*(Y+hi->y2-bot))) + ((ref->x1+ref->x2)>>1);
    insert (hi, X- (hi->x2 >> 1), Y-bot);
    italic_correct (hi);
  }
  italic_correct (ref);
  position ();
  italic_restore (ref);
  if (!is_nil (lo)) italic_restore (lo);
  if (!is_nil (hi)) italic_restore (hi);
  left_justify ();
  finalize ();
}

4.10.Above and below boxes

The below and above tags are used to explicitly attach a script below or above a given content. Both can be mixed in order to produce content with both a script below and above:

<math| <above|<below|xor|i=1>|> x<rsub|i>>

Like long arrows, above and below tags also rely on lim_box_rep for their graphical rendering:

void
concater_rep::typeset_below (tree t, path ip) {
  if (N(t) != 2) { typeset_error (t, ip); return; }
  box b1= typeset_as_concat (env, t[0], descend (ip, 0));
  tree old_ds= env->local_begin (MATH_DISPLAY, "false");
  tree old_mc= env->local_begin (MATH_CONDENSED, "true");
  tree old_il= env->local_begin_script ();
  box b2= typeset_as_concat (env, t[1], descend (ip, 1));
  env->local_end_script (old_il);
  env->local_end (MATH_CONDENSED, old_mc);
  env->local_end (MATH_DISPLAY, old_ds);
  print (limit_box (ip, b1, b2, box (), env->fn, false));
}
void
concater_rep::typeset_above (tree t, path ip) {
  if (N(t) != 2) { typeset_error (t, ip); return; }
  box b1= typeset_as_concat (env, t[0], descend (ip, 0));
  tree old_ds= env->local_begin (MATH_DISPLAY, "false");
  tree old_mc= env->local_begin (MATH_CONDENSED, "true");
  tree old_il= env->local_begin_script ();
  box b2= typeset_as_concat (env, t[1], descend (ip, 1));
  env->local_end_script (old_il);
  env->local_end (MATH_CONDENSED, old_mc);
  env->local_end (MATH_DISPLAY, old_ds);
  // NOTE: start dirty hack to get scripts above … right
  if ((t[0] == "<ldots>" && env->read ("low-dots") != UNINIT) ||
      (t[0] == "<cdots>" && env->read ("center-dots") != UNINIT)) {
    string s= (t[0] == "<ldots>"? ",": "<cdot>");
    box tb= typeset_as_concat (env, s, decorate_middle (descend (ip, 0)));
    b1= resize_box (descend (ip, 0), b1, b1->x1, b1->y1, b1->x2, tb->y2);
  }
  // NOTE: end dirty hack to get scripts above … right
  print (limit_box (ip, b1, box (), b2, env->fn, false));
}

4.11.Finalization

The computation of the correct size of extensible brackets and the proper placement of super/sub-scripts require a second pass through the line_item array that concater is currently typesetting. This triggered by concater_rep::finish ():

void
concater_rep::finish () {
  kill_spaces ();
  pre_glue ();
  handle_brackets ();
  clean_and_correct ();
}

kill_spaces and pre_glue simplify the line_items array:

/******************************************************************************
* Kill invalid spaces
******************************************************************************/

void
concater_rep::kill_spaces () {
  int i;
  for (i=N(a)-1; (i>0) && (a[i]->type == CONTROL_ITEM); i--)
    a[i-1]->spc= space (0);
  for (i=0; (i<N(a)) && (a[i]->type == CONTROL_ITEM); i++)
    a[i]->spc= space (0);

  for (i=0; i<N(a); i++)
    if (a[i]->type==CONTROL_ITEM) {
      if (is_formatting (a[i]->t)) {
        tree_label lab= L(a[i]->t);
        if ((lab==NEXT_LINE) || (lab==LINE_BREAK) || (lab==NEW_LINE))
          {
            if (i>0) a[i-1]->spc= space (0);
            a[i]->spc= space (0);
          }
      }

      if (is_tuple (a[i]->t, "env_par") ||
          is_tuple (a[i]->t, "env_page"))
        a[i]->spc= space (0);
    }
}

while pre_glue put together neightbor sub and super scripts on the same side labelling them with the line items type GLUE_LSUBS_ITEM or GLUE_RSUBS_ITEM:


void
concater_rep::pre_glue () {
  int i=0;
  while (true) {
    int j= succ(i);
    if (j >= N(a)) break;
    line_item item1= a[i];
    line_item item2= a[j];
    int t1= item1->type;
    int t2= item2->type;
    if (((t1 == RSUB_ITEM) && (t2 == RSUP_ITEM)) ||
        ((t1 == RSUP_ITEM) && (t2 == RSUB_ITEM)) ||
        ((t1 == LSUB_ITEM) && (t2 == LSUP_ITEM)) ||
        ((t1 == LSUP_ITEM) && (t2 == LSUB_ITEM)))
      {
        bool  flag1 = (t1 == LSUB_ITEM) || (t1 == RSUB_ITEM);
        bool  flag2 = (t1 == LSUB_ITEM) || (t1 == LSUP_ITEM);
        int   type  = flag2? GLUE_LSUBS_ITEM: GLUE_RSUBS_ITEM;
        box   b1    = flag1? item1->b[0]: item2->b[0];
        box   b2    = flag1? item2->b[0]: item1->b[0];
        box   b     = script_box (b1->ip, b1, b2, env->fn);
        int   pen   = item2->penalty;
        space spc   = max (item1->spc, item2->spc);

        a[i]= line_item (type, OP_SKIP, b, pen);
        a[i]->spc = spc;
        for (int k=i+1; k<j; k++)
          if (a[k]->type == MARKER_ITEM)
            a[k]= line_item (OBSOLETE_ITEM, OP_SKIP, a[k]->b, a[k]->penalty);
        a[j]= line_item (OBSOLETE_ITEM, OP_SKIP, item2->b, pen);
      }
    i++;
  }
}

The function handle_brackets fixes the size of the extensible brackets and middle marks and also the global placement of scripts :

void
concater_rep::handle_brackets () {
  int first=-1, start=0, i=0;
  while (i<N(a)) {
    if (a[i]->type==LEFT_BRACKET_ITEM) {
      if (first==-1) first= i;
      start= i;
    }
    if (a[i]->type==RIGHT_BRACKET_ITEM) {
      handle_scripts  (succ (start), prec (i));
      handle_matching (start, i);
      if (first!=-1) i=first-1;
      start= 0;
      first= -1;
    }
    i++;
  }
  if (N(a)>0) {
    handle_scripts  (0, N(a)-1);
    handle_matching (0, N(a)-1);
  }
}

The placement of scripts is the job of handle_scripts:

void
concater_rep::handle_scripts (int start, int end) {
  int i;
  for (i=start; i<=end; ) {
    if ((a[i]->type == OBSOLETE_ITEM) ||
        (a[i]->type == LSUB_ITEM) ||
        (a[i]->type == LSUP_ITEM) ||
        (a[i]->type == GLUE_LSUBS_ITEM) ||
        (a[i]->type == RSUB_ITEM) ||
        (a[i]->type == RSUP_ITEM) ||
        (a[i]->type == GLUE_RSUBS_ITEM) ||
        (a[i]->type == CONTROL_ITEM && L(a[i]->t) == DATOMS)) {
      i++; continue; }

    path sip;
    int l= prec (i);
    box lb1, lb2;
    if (l < start) l= -1;
    else switch (a[l]->type) {
    case LSUB_ITEM:
      lb1= a[l]->b[0]; sip= lb1->ip;
      break;
    case LSUP_ITEM:
      lb2= a[l]->b[0]; sip= lb2->ip;
      break;
    case GLUE_LSUBS_ITEM:
      lb1= a[l]->b[0]; lb2= a[l]->b[1];
      sip= lb2->ip;
      break;
    default:
      l = -1;
    }

    int r= succ (i);
    box rb1, rb2;
    if (r > end) r= N(a);
    else switch (a[r]->type) {
    case RSUB_ITEM:
      rb1= a[r]->b[0]; sip= rb1->ip;
      break;
    case RSUP_ITEM:
      rb2= a[r]->b[0]; sip= rb2->ip;
      break;
    case GLUE_RSUBS_ITEM:
      rb1= a[r]->b[0]; rb2= a[r]->b[1];
      sip= rb2->ip;
      break;
    default:
      r = N(a);
    }

    box b;
    if (l==-1) {
      if (r==N(a)) { i++; continue; }
      else {
        font ref_fn= get_reference_font (a[i]->b, env->fn);
        box mb= glue_right_markers (a[i]->b, i, r, false);
        if (a[i]->limits)
          b= limit_box (sip, mb, rb1, rb2, ref_fn, true);
        else
          b= right_script_box (sip, mb, rb1, rb2, ref_fn, env->vert_pos);
        glue (b, i, r);
      }
    }
    else {
      font ref_fn= get_reference_font (a[i]->b, env->fn);
      box mb= glue_left_markers (a[i]->b, i, l);
      if (r==N(a)) {
        b= left_script_box (sip, mb, lb1, lb2, ref_fn, env->vert_pos);
        glue (b, i, l);
      }
      else {
        mb= glue_right_markers (mb, i, r, true);
        b = side_box (sip, mb, lb1, lb2, rb1, rb2, ref_fn, env->vert_pos);
        glue (b, i, l, r);
      }
    }
  }
}

with the help of the following subsidiary routines: [explain more]

box
concater_rep::glue_left_markers (box b, int ref, int arg) {
  int i= arg+1;
  while (i < ref && a[i]->type == OBSOLETE_ITEM) i++;
  if (i >= ref) return b;
  array<box> bs;
  array<SI>  spc;
  while (i < ref) {
    if (a[i]->type == MARKER_ITEM) {
      bs  << a[i]->b;
      spc << 0;
      a[i]->type= OBSOLETE_ITEM;
    }
    i++;
  }
  bs  << b;
  spc << 0;
  return concat_box (b->ip, bs, spc);
}

box
concater_rep::glue_right_markers (box b, int ref, int arg, bool flag) {
  int i= ref+1;
  while (i < arg && a[i]->type == OBSOLETE_ITEM) i++;
  if (i >= arg) return b;
  array<box> bs;
  array<SI>  spc;
  if (flag) {
    for (int j=0; j<N(b); j++) {
      bs  << b[j];
      spc << 0;
    }
  }
  else {
    bs  << b;
    spc << 0;
  }
  while (i < arg) {
    if (a[i]->type == MARKER_ITEM) {
      bs  << a[i]->b;
      spc << 0;
      a[i]->type= OBSOLETE_ITEM;
    }
    i++;
  }
  return concat_box (b->ip, bs, spc);
}

void
concater_rep::glue (box b, int ref, int arg) {
  if (a[ref]->op_type == OP_BIG && arg >= ref && !a[ref]->limits) {
    font ref_fn= get_reference_font (a[ref]->b, env->fn);
    if (ref_fn->math_type != MATH_TYPE_NORMAL)
      if (a[ref]->spc->def > 0) {
        space spc= ref_fn->spc;
        a[ref]->spc += space (spc->min/3, spc->def/3, spc->def/3);
      }
  }
  
  space spc = max (a[ref]->spc, a[arg]->spc);

  a[arg]  = line_item (OBSOLETE_ITEM, OP_SKIP, a[arg]->b, a[arg]->penalty);
  a[ref]  = line_item (arg<ref? GLUE_LEFT_ITEM: GLUE_RIGHT_ITEM,
                       a[ref]->op_type, b,
                       min (a[ref]->penalty, a[arg]->penalty));
  a[ref]->spc = spc;
}

void
concater_rep::glue (box b, int ref, int arg1, int arg2) {
  if (a[ref]->op_type == OP_BIG && !a[ref]->limits) {
    font ref_fn= get_reference_font (a[ref]->b, env->fn);
    if (ref_fn->math_type != MATH_TYPE_NORMAL)
      if (a[ref]->spc->def > 0) {
        space spc= ref_fn->spc;
        a[ref]->spc += space (spc->min/3, spc->def/3, spc->def/3);
      }
  }

  space spc = max (a[ref]->spc, max (a[arg1]->spc, a[arg2]->spc));
  int   pen = min (a[ref]->penalty, min (a[arg1]->penalty, a[arg2]->penalty));

  space ref_spc= a[ref]->spc;
  a[arg1]= line_item (OBSOLETE_ITEM, OP_SKIP, a[arg1]->b, a[arg1]->penalty);
  a[arg2]= line_item (OBSOLETE_ITEM, OP_SKIP, a[arg2]->b, a[arg2]->penalty);
  a[ref]= line_item (GLUE_BOTH_ITEM, a[ref]->op_type, b, pen);
  a[ref]->spc = spc;
}

Finally, the sizing of the brackets is the job of handle_matching:

void
concater_rep::handle_matching (int start, int end) {
  //cout << "matching " << start << " -- " << end << "\n";
  //cout << a << "\n\n";
  int i;
  SI y1=  MAX_SI;
  SI y2= -MAX_SI;
  bool uninit= true;
  a[start]->penalty++;
  a[end]->penalty++;
  for (i=start+1; i<end; i++) {
    if (a[i]->type == OBSOLETE_ITEM) continue;
    // cout << "  " << a[i] << ": " << (a[i]->b->y2- a[i]->b->y1) << "\n";
    // y1= min (y1, a[i]->b->sub_base());
    // y2= max (y2, a[i]->b->sup_base());
    SI lo, hi;
    a[i]->b->get_bracket_extents (lo, hi);
    y1= min (y1, lo);
    y2= max (y2, hi);
    a[i]->penalty++;
    uninit= false;
  }
  if (uninit) {
    y1= min (a[start]->b->y1, a[end]->b->y2);
    y2= max (a[start]->b->y1, a[end]->b->y2);
  }

  for (i=start; i<=end; i++) {
    int tp= a[i]->type;
    if (tp == LEFT_BRACKET_ITEM ||
        tp == MIDDLE_BRACKET_ITEM ||
        tp == RIGHT_BRACKET_ITEM)
      {
        string ls= a[i]->b->get_leaf_string ();
        pencil lp= a[i]->b->get_leaf_pencil ();
        font   fn= a[i]->b->get_leaf_font ();

        // find the middle of the bracket, around where to center
        SI mid= (a[i]->b->y1 + a[i]->b->y2) >> 1;
        bool custom=
          N(ls) > 2 && is_digit (ls[N(ls)-2]) && !ends (ls, "-0>");
        if (custom) {
          int pos= N(ls)-1;
          while (pos > 0 && ls[pos] != '-') pos--;
          if (pos > 0 && ls[pos-1] == '-') pos--;
          string ss= ls (0, pos) * ">";
          box auxb= text_box (a[i]->b->ip, 0, ss, fn, lp);
          mid= (auxb->y1 + auxb->y2) >> 1;
        }

        // make symmetric and prevent from too large delimiters if possible
        SI Y1   = y1 + (fn->sep >> 1);
        SI Y2   = y2 - (fn->sep >> 1);
        SI tol  = fn->sep << 1;
        SI drift= ((Y1 + Y2) >> 1) - mid; // fn->yfrac;
        if (drift < 0) Y2 += min (-drift, tol) << 1;
        else Y1 -= min (drift, tol) << 1;

        // further adjustments when the enclosed expression is not very high
        // and for empty brackets
        SI h= y2 - y1 - fn->sep;
        SI d= 5 * fn->yx - h;
        if (d > 0) { Y1 += d/12; Y2 -= d/12; }
        if (N(ls) >= 8 && (ls[6] == '.' || ls[7] == '.'))
          if (starts (ls, "<left-.") || starts (ls, "<right-.")) {
            Y1 += d/6; Y2 -= d/12; }

        // replace item by large or small delimiter
        if (Y1 < fn->y1 || Y2 > fn->y2 || custom || use_poor_rubber (fn))
          a[i]->b= delimiter_box (a[i]->b->ip, ls, fn, lp, Y1, Y2, mid, y1, y2);
        else {
          string s= "<nobracket>";
          int j;
          for (j=0; j<N(ls); j++)
            if (ls[j] == '-') break;
          if (j<N(ls) && ls[N(ls)-1] == '>') s= ls (j+1, N(ls)-1);
          if (N(s) > 1 && s[0] != '<') s= "<" * s * ">";
          else if (N(s) == 0 || s == ".") s= "<nobracket>";
          a[i]->b= text_box (a[i]->b->ip, 0, s, fn, lp);
          tp= STD_ITEM;
        }
        a[i]->type= STD_ITEM;
      }
    if (tp == LEFT_BRACKET_ITEM)
      for (int j= i-1; j>=0; j--) {
        if (a[j]->type == MARKER_ITEM) {
          SI Y1= a[i]->b->y1;
          SI Y2= a[i]->b->y2;
          //a[j]->b = marker_box (a[j]->b->find_lip (), 0, Y1, 0, Y2, a[j]->b);
          a[j]->b   = marker_box (a[j]->b->find_lip (), 0, Y1, 0, Y2, a[i]->b);
          a[j]->type= STD_ITEM;
        }
        else if (a[j]->type != CONTROL_ITEM) break;
      }
    if (tp == RIGHT_BRACKET_ITEM)
      for (int j= i+1; j<N(a); j++) {
        if (a[j]->type == MARKER_ITEM) {
          SI Y1= a[i]->b->y1;
          SI Y2= a[i]->b->y2;
          //a[j]->b = marker_box (a[j]->b->find_lip (), 0, Y1, 0, Y2, a[j]->b);
          a[j]->b   = marker_box (a[j]->b->find_lip (), 0, Y1, 0, Y2, a[i]->b);
          a[j]->type= STD_ITEM;
        }
        else if (a[j]->type != CONTROL_ITEM) break;
      }
  }
}

Again, a key point of this procedure is the call to delimiter_box (in src/Typeset/Boxes/Basic/text_boxes.cpp) in order to replace the “temporary” vertical delimiter, with a correctly sized one (be it a left, right or middle delimiter).

box
delimiter_box (path ip, string s, font fn, pencil pen,
               SI bot, SI top, SI mid, SI real_bot, SI real_top)
{
  SI h= top - bot;
  string r= get_delimiter (s, fn, h);
  box b= text_box (ip, 0, r, fn, pen);
  SI x= -b->x1;
  SI y= (top + bot - b->y1 - b->y2) >> 1;
  if (b->y2 - b->y1 < h) {
    y= (mid - b->y1 - b->y2) >> 1;
    y= min (top - b->y2, y);
    y= max (bot - b->y1, y);
  }
  //cout << s << ", " << bot/PIXEL << " -- " << top/PIXEL
  //     << " -> " << r << "; " << x/PIXEL << ", " << y/PIXEL << "\n";
  //cout << "  extents: " << b->x1/PIXEL << ", " << b->y1/PIXEL
  //     << "; " << b->x2/PIXEL << ", " << b->y2/PIXEL << "\n";
  box mvb= move_delimiter_box (ip, b, x, y, real_bot, real_top);
  if (ends (r, "-0>")) return mvb;
  SI dy= ((mvb->y1 + mvb->y2)>>1) - fn->yfrac;
  return macro_delimiter_box (ip, mvb, fn, dy);
}

The function get_delimiter queries the font for a sequence of glyphs in the form <left-[-N> (for example) with N=0,1,2,3,4,... until we obtain the required height.

/******************************************************************************
* Computing right size for rubber characters
******************************************************************************/

static int
get_number (string s, int& pos) {
  int n= N(s);
  pos= n-1;
  while (pos > 0 && s[pos] != '-') pos--;
  if (pos > 0 && s[pos-1] == '-') pos--;
  return as_int (s (pos+1, n-1));
}

static string
get_delimiter (string s, font fn, SI height) {
  int ns= N(s);
  ASSERT (ns >= 2 && s[0] == '<' && s[ns-1] == '>',
          "invalid rubber character");
  if (is_digit (s[ns-2])) {
    int pos;
    int plus= get_number (s, pos);
    if (pos > 0) {
      string s2= s (0, pos) * ">";
      string r2= get_delimiter (s2, fn, height);
      int pos2;
      int nr2= get_number (r2, pos2);
      if (pos2 > 0) {
        int nr= max (nr2 + plus, 0);
        return r2 (0, pos2) * "-" * as_string (nr) * ">";
      }
    }
  }
  height -= PIXEL;
  string radical= s (0, N(s)-1) * "-";
  string best= radical * "0>";
  SI best_h= 0;
  int n= 0;
  SI last= 0;
  int credit= 20;
  while (credit > 0) {
    metric ex;
    string test= radical * as_string (n) * ">";
    fn->get_extents (test, ex);
    SI h= ex->y2 - ex->y1;
    if (h >= (height - (n==1? PIXEL: 0))) return test;
    if (h > best_h) { best_h= h; best= test; }
    int d= h - last;
    if (last > 0 && d > 0) {
      int plus= (height - h - 1) / d;
      if (plus <= 1 || n <= 4) { n++; last= h; }
      else {
        int n2= n + plus;
        metric ex2;
        string test2= radical * as_string (n2) * ">";
        fn->get_extents (test2, ex2);
        SI h2= ex2->y2 - ex2->y1;
        if (h2 >= height || h2 < h) { n++; last= h; }
        else { n= n2; last= 0; }
      }
    }
    else if (last <= 0 || n < 10) { n++; last= h; }
    else return best;
    credit--;
  }
  return best;
}

5.Final remarks

A list of possible improvements to the code reviewed in this document: