fix getsup (HH)
[luatex.git] / source / texk / web2c / luatexdir / tex / mlist.w
blob32fd32d4c0eae605ea5fa47629073b45721fc4a9
1 % mlist.w
3 % Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
5 % This file is part of LuaTeX.
7 % LuaTeX is free software; you can redistribute it and/or modify it under
8 % the terms of the GNU General Public License as published by the Free
9 % Software Foundation; either version 2 of the License, or (at your
10 % option) any later version.
12 % LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13 % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 % FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 % License for more details.
17 % You should have received a copy of the GNU General Public License along
18 % with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
20 % (HH / 0.82+):
22 @ In traditional \TeX\ the italic correction is added to the width of the glyph. This
23 is part of the engine design and related font design. In opentype math this is
24 different. There the italic correction had more explicit usage. The 1.7 spec
25 says:
27 italic correction:
29 When a run of slanted characters is followed by a straight character (such as
30 an operator or a delimiter), the italics correction of the last glyph is added
31 to its advance width.
33 When positioning limits on an N-ary operator (e.g., integral sign), the horizontal
34 position of the upper limit is moved to the right by ½ of the italics correction,
35 while the position of the lower limit is moved to the left by the same distance.
37 When positioning superscripts and subscripts, their default horizontal positions are
38 also different by the amount of the italics correction of the preceding glyph.
40 math kerning:
42 Set the default horizontal position for the superscript as shifted relative to the
43 position of the subscript by the italics correction of the base glyph.
45 Before this was specified we had to gamble a bit and assume that cambria was the font
46 benchmark and trust our eyes (and msword) for the logic. I must admit that I have been
47 fighting these italics in fonts (and the heuristics that Lua\TeX\ provided) right from
48 the start (e.g. using Lua based postprocessing) but by now we know more and have more
49 fonts to test with. More fonts are handy because not all fonts are alike when it comes
50 to italics. Axis are another area of concern, as it looks like opentype math fonts often
51 already apply that shift.
53 @ @c
54 #define is_new_mathfont(A) ((font_math_params(A) >0) && (math_old_par == 0))
55 #define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B)))
56 #define do_new_math(A) ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0))
59 \def\LuaTeX{Lua\TeX}
61 @ @c
63 #include "ptexlib.h"
64 #include "lua/luatex-api.h"
66 @ @c
67 #define nDEBUG
69 #define reset_attributes(p,newatt) do { \
70 delete_attribute_ref(node_attr(p)); \
71 node_attr(p) = newatt; \
72 if (newatt!=null) { \
73 add_node_attr_ref(node_attr(p)); \
74 } \
75 } while (0)
77 #define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \
78 if (B==text_size) { \
79 def_math_param(A, text_style, (C),D); \
80 def_math_param(A, cramped_text_style, (C),D); \
81 } else if (B==script_size) { \
82 def_math_param(A, script_style, (C),D); \
83 def_math_param(A, cramped_script_style, (C),D); \
84 } else if (B==script_script_size) { \
85 def_math_param(A, script_script_style, (C),D); \
86 def_math_param(A, cramped_script_script_style, (C),D); \
87 } \
88 } while (0)
90 #define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \
91 if (B==text_size) { \
92 def_math_param(A, display_style,(C),D); \
93 def_math_param(A, cramped_display_style,(C),D); \
94 } \
95 } while (0)
97 #define font_MATH_par(a,b) \
98 (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
100 @ here are the math parameters that are font-dependant
102 @ Before an mlist is converted to an hlist, \TeX\ makes sure that
103 the fonts in family~2 have enough parameters to be math-symbol
104 fonts, and that the fonts in family~3 have enough parameters to be
105 math-extension fonts. The math-symbol parameters are referred to by using the
106 following macros, which take a size code as their parameter; for example,
107 |num1(cur_size)| gives the value of the |num1| parameter for the current size.
108 @^parameters for symbols@>
109 @^font parameters@>
112 #define total_mathsy_params 22
113 #define total_mathex_params 13
115 #define mathsy(A,B) font_param(fam_fnt(2,A),B)
117 #define math_x_height(A) mathsy(A,5) /* height of `\.x' */
118 #define math_quad(A) mathsy(A,6) /* \.{18mu} */
119 #define num1(A) mathsy(A,8) /* numerator shift-up in display styles */
120 #define num2(A) mathsy(A,9) /* numerator shift-up in non-display, non-\.{\\atop} */
121 #define num3(A) mathsy(A,10) /* numerator shift-up in non-display \.{\\atop} */
122 #define denom1(A) mathsy(A,11) /* denominator shift-down in display styles */
123 #define denom2(A) mathsy(A,12) /* denominator shift-down in non-display styles */
124 #define sup1(A) mathsy(A,13) /* superscript shift-up in uncramped display style */
125 #define sup2(A) mathsy(A,14) /* superscript shift-up in uncramped non-display */
126 #define sup3(A) mathsy(A,15) /* superscript shift-up in cramped styles */
127 #define sub1(A) mathsy(A,16) /* subscript shift-down if superscript is absent */
128 #define sub2(A) mathsy(A,17) /* subscript shift-down if superscript is present */
129 #define sup_drop(A) mathsy(A,18) /* superscript baseline below top of large box */
130 #define sub_drop(A) mathsy(A,19) /* subscript baseline below bottom of large box */
131 #define delim1(A) mathsy(A,20) /* size of \.{\\atopwithdelims} delimiters in display styles */
132 #define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */
133 #define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */
135 @ The math-extension parameters have similar macros, but the size code is
136 omitted (since it is always |cur_size| when we refer to such parameters).
137 @^parameters for symbols@>
138 @^font parameters@>
141 #define mathex(A,B) font_param(fam_fnt(3,A),B)
142 #define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */
143 #define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */
144 #define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
145 #define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
146 #define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
147 #define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
149 @ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
150 the MathConstants values have no matching usage in \LuaTeX\ right now.
152 ScriptPercentScaleDown,
153 ScriptScriptPercentScaleDown:
154 These should be handled by the macro package, on the engine
155 side there are three separate fonts
157 DelimitedSubFormulaMinHeight:
158 This is perhaps related to word's natural math input? I have
159 no idea what to do about it
161 MathLeading:
162 LuaTeX does not currently handle multi-line displays, and
163 the parameter does not seem to make much sense elsewhere
165 FlattenedAccentBaseHeight:
166 This is based on the 'flac' GSUB feature. It would not be hard
167 to support that, but proper math accent placements cf. MATH
168 needs support for MathTopAccentAttachment table to be
169 implemented first
171 Also still TODO for OpenType Math:
172 * prescripts
174 @ this is not really a math parameter at all
177 static void math_param_error(const char *param, int style)
179 char s[256];
180 const char *hlp[] = {
181 "Sorry, but I can't typeset math unless various parameters have",
182 "been set. This is normally done by loading special math fonts",
183 "into the math family slots. Your font set is lacking at least",
184 "the parameter mentioned earlier.",
185 NULL
187 snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
188 param, math_style_names[style]);
189 tex_error(s, hlp);
190 #if 0
191 flush_math();
192 #endif
193 return;
196 @ @c
197 static scaled accent_base_height(int f)
199 scaled a;
200 if (do_new_math(f)) {
201 a = font_MATH_par(f, AccentBaseHeight);
202 if (a == undefined_math_parameter)
203 a = x_height(f);
204 } else {
205 a = x_height(f);
207 return a;
210 @ The non-staticness of this function is for the benefit of |texmath.w|. Watch out,
211 this one uses the style! The style and size numbers don't match because we have
212 cramped styles.
215 scaled get_math_quad_style(int var)
217 scaled a = get_math_param(math_param_quad, var);
218 if (a == undefined_math_parameter) {
219 math_param_error("quad", var);
220 return 0;
221 } else {
222 return a;
226 @ For this reason the next one is different because it is called with a size
227 specifier instead of a style specifier.
230 static scaled math_axis_size(int b)
232 scaled a;
233 int var;
234 if (b == script_size)
235 var = script_style;
236 else if (b == script_script_size)
237 var = script_script_style;
238 else
239 var = text_style;
240 a = get_math_param(math_param_axis, var);
241 if (a == undefined_math_parameter) {
242 math_param_error("axis", var);
243 return 0;
244 } else {
245 return a;
249 @ @c
250 scaled get_math_quad_size(int b)
252 int var;
253 if (b == script_size)
254 var = script_style;
255 else if (b == script_script_size)
256 var = script_script_style;
257 else
258 var = text_style;
259 return get_math_param(math_param_quad, var);
262 @ @c
263 static scaled minimum_operator_size(int var)
265 scaled a = get_math_param(math_param_operator_size, var);
266 return a;
269 @ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select
270 the backward compatibility code, and it means that we can't raise an error here.
273 static scaled radical_rule_par(int var)
275 scaled a = get_math_param(math_param_radical_rule, var);
276 return a;
279 @ now follow all the trivial math parameters
282 #define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
283 #define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b)
285 static scaled do_get_math_param_or_error(int var, int param, const char *name)
287 scaled a = get_math_param(param, var);
288 if (a == undefined_math_parameter) {
289 math_param_error(name, var);
290 a = 0;
292 return a;
295 static scaled do_get_math_param_or_zero(int var, int param, const char *name)
297 scaled a = get_math_param(param, var);
298 if (a == undefined_math_parameter) {
299 a = 0;
301 return a;
304 @ A variant on a suggestion on the list based on analysis by UV.
307 static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
308 scaled delta, delta1, delta2;
309 if (axis) {
310 delta2 = max_d + math_axis_size(cur_size);
311 } else {
312 delta2 = max_d;
314 delta1 = max_h + max_d - delta2;
315 if (delta2 > delta1) {
316 /* |delta1| is max distance from axis */
317 delta1 = delta2;
319 delta = (delta1 / 500) * delimiter_factor_par;
320 delta2 = delta1 + delta1 - delimiter_shortfall_par;
321 if (delta < delta2) {
322 return delta2;
323 } else {
324 return delta;
328 @ @c
329 #define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
330 #define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after)
331 #define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise)
333 #define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
335 #define overbar_rule(a) get_math_param_or_error(a, overbar_rule)
336 #define overbar_kern(a) get_math_param_or_error(a, overbar_kern)
337 #define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap)
339 #define underbar_rule(a) get_math_param_or_error(a, underbar_rule)
340 #define underbar_kern(a) get_math_param_or_error(a, underbar_kern)
341 #define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap)
343 #define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap)
344 #define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap)
346 #define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap)
347 #define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap)
349 #define radical_vgap(a) get_math_param_or_error(a, radical_vgap)
350 #define radical_kern(a) get_math_param_or_error(a, radical_kern)
352 #define stack_vgap(a) get_math_param_or_error(a, stack_vgap)
353 #define stack_num_up(a) get_math_param_or_error(a, stack_num_up)
354 #define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down)
356 #define fraction_rule(a) get_math_param_or_error(a, fraction_rule)
357 #define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap)
358 #define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap)
359 #define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up)
360 #define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down)
361 #define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
363 #define fraction_del_size_old(a) get_math_param(a, math_param_fraction_del_size)
365 #define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size)
367 #define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap)
368 #define skewed_fraction_vgap(a) get_math_param_or_error(a, skewed_fraction_vgap)
370 #define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap)
371 #define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap)
372 #define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern)
374 #define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap)
375 #define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap)
376 #define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern)
378 #define nolimit_sub_factor(a) get_math_param_or_zero(a, nolimit_sub_factor)
379 #define nolimit_sup_factor(a) get_math_param_or_zero(a, nolimit_sup_factor)
381 #define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop)
382 #define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop)
383 #define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down)
384 #define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down)
385 #define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up)
386 #define sub_top_max(a) get_math_param_or_error(a, sub_top_max)
387 #define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min)
388 #define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max)
389 #define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap)
391 #define space_after_script(a) get_math_param_or_error(a, space_after_script)
393 @ @c
394 void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
397 if (is_new_mathfont(f)) { /* fix all known parameters */
399 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
400 font_size(f), lvl);
401 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
402 font_size(f), lvl);
403 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
404 font_MATH_par(f, AxisHeight), lvl);
405 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
406 font_MATH_par(f, AxisHeight), lvl);
407 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
408 font_MATH_par(f, OverbarExtraAscender), lvl);
409 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
410 font_MATH_par(f, OverbarExtraAscender), lvl);
411 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
412 font_MATH_par(f, OverbarRuleThickness), lvl);
413 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
414 font_MATH_par(f, OverbarRuleThickness), lvl);
415 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
416 font_MATH_par(f, OverbarVerticalGap), lvl);
417 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
418 font_MATH_par(f, OverbarVerticalGap), lvl);
419 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
420 font_MATH_par(f, UnderbarExtraDescender), lvl);
421 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
422 font_MATH_par(f, UnderbarExtraDescender), lvl);
423 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
424 font_MATH_par(f, UnderbarRuleThickness), lvl);
425 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
426 font_MATH_par(f, UnderbarRuleThickness), lvl);
427 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
428 font_MATH_par(f, UnderbarVerticalGap), lvl);
429 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
430 font_MATH_par(f, UnderbarVerticalGap), lvl);
432 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
433 font_MATH_par(f, StretchStackGapAboveMin), lvl);
434 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
435 font_MATH_par(f, StretchStackGapAboveMin), lvl);
436 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
437 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
438 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
439 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
441 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
442 font_MATH_par(f, StretchStackGapBelowMin), lvl);
443 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
444 font_MATH_par(f, StretchStackGapBelowMin), lvl);
445 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
446 font_MATH_par(f, StretchStackTopShiftUp), lvl);
447 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
448 font_MATH_par(f, StretchStackTopShiftUp), lvl);
450 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
451 font_MATH_par(f, StackTopShiftUp), lvl);
452 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
453 font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
454 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
455 font_MATH_par(f, StackBottomShiftDown), lvl);
456 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
457 font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
458 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
459 font_MATH_par(f, StackGapMin), lvl);
460 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
461 font_MATH_par(f, StackDisplayStyleGapMin), lvl);
463 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
464 font_MATH_par(f, RadicalExtraAscender), lvl);
465 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
466 font_MATH_par(f, RadicalExtraAscender), lvl);
468 DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
469 font_MATH_par(f, DisplayOperatorMinHeight), lvl);
471 DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
472 font_MATH_par(f, RadicalRuleThickness), lvl);
473 DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
474 font_MATH_par(f, RadicalRuleThickness), lvl);
475 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
476 font_MATH_par(f, RadicalVerticalGap), lvl);
477 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
478 font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
479 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
480 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
481 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
482 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
483 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
484 font_MATH_par(f, RadicalKernAfterDegree), lvl);
485 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
486 font_MATH_par(f, RadicalKernAfterDegree), lvl);
487 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
488 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
489 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
490 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
492 if (size_id == text_size) {
493 def_math_param(math_param_sup_shift_up, display_style,
494 font_MATH_par(f, SuperscriptShiftUp), lvl);
495 def_math_param(math_param_sup_shift_up, cramped_display_style,
496 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
497 def_math_param(math_param_sup_shift_up, text_style,
498 font_MATH_par(f, SuperscriptShiftUp), lvl);
499 def_math_param(math_param_sup_shift_up, cramped_text_style,
500 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
501 } else if (size_id == script_size) {
502 def_math_param(math_param_sup_shift_up, script_style,
503 font_MATH_par(f, SuperscriptShiftUp), lvl);
504 def_math_param(math_param_sup_shift_up, cramped_script_style,
505 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
506 } else if (size_id == script_script_size) {
507 def_math_param(math_param_sup_shift_up, script_script_style,
508 font_MATH_par(f, SuperscriptShiftUp), lvl);
509 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
510 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
513 DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
514 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
515 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
516 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
517 DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
518 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
519 DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
520 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
521 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
522 font_MATH_par(f, SubscriptShiftDown), lvl);
523 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
524 font_MATH_par(f, SubscriptShiftDown), lvl);
526 if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
527 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
528 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
529 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
530 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
531 } else {
532 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
533 font_MATH_par(f, SubscriptShiftDown), lvl);
534 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
535 font_MATH_par(f, SubscriptShiftDown), lvl);
538 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
539 font_MATH_par(f, SubscriptTopMax), lvl);
540 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
541 font_MATH_par(f, SubscriptTopMax), lvl);
542 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
543 font_MATH_par(f, SuperscriptBottomMin), lvl);
544 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
545 font_MATH_par(f, SuperscriptBottomMin), lvl);
546 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
547 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
548 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
549 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
550 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
551 font_MATH_par(f, SubSuperscriptGapMin), lvl);
552 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
553 font_MATH_par(f, SubSuperscriptGapMin), lvl);
555 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
556 font_MATH_par(f, UpperLimitGapMin), lvl);
557 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
558 font_MATH_par(f, UpperLimitGapMin), lvl);
559 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
560 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
561 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
562 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
563 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
564 0, lvl);
565 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
566 0, lvl);
567 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
568 font_MATH_par(f, LowerLimitGapMin), lvl);
569 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
570 font_MATH_par(f, LowerLimitGapMin), lvl);
571 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
572 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
573 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
574 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
575 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
576 0, lvl);
577 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
578 0, lvl);
579 DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
580 font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
581 DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
582 font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
583 DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
584 font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
585 DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
586 font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
588 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
589 font_MATH_par(f, FractionRuleThickness), lvl);
590 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
591 font_MATH_par(f, FractionRuleThickness), lvl);
592 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
593 font_MATH_par(f, FractionNumeratorGapMin), lvl);
594 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
595 font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
596 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
597 font_MATH_par(f, FractionNumeratorShiftUp), lvl);
598 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
599 font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
600 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
601 font_MATH_par(f, FractionDenominatorGapMin), lvl);
602 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
603 font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
604 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
605 font_MATH_par(f, FractionDenominatorShiftDown), lvl);
606 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
607 font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
609 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
610 font_MATH_par(f, FractionDelimiterSize), lvl);
611 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
612 font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
614 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
615 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
616 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
617 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
618 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
619 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
620 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
621 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
623 DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
624 font_MATH_par(f, SpaceAfterScript), lvl);
625 DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
626 font_MATH_par(f, SpaceAfterScript), lvl);
628 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
629 font_MATH_par(f, MinConnectorOverlap), lvl);
630 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
631 font_MATH_par(f, MinConnectorOverlap), lvl);
633 } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
635 /* fix old-style |sy| parameters */
637 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
638 math_quad(size_id), lvl);
639 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
640 math_quad(size_id), lvl);
641 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
642 axis_height(size_id), lvl);
643 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
644 axis_height(size_id), lvl);
645 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
646 num3(size_id), lvl);
647 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
648 num1(size_id), lvl);
649 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
650 denom2(size_id), lvl);
651 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
652 denom1(size_id), lvl);
653 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
654 num2(size_id), lvl);
655 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
656 num1(size_id), lvl);
657 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
658 denom2(size_id), lvl);
659 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
660 denom1(size_id), lvl);
661 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
662 delim2(size_id), lvl);
663 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
664 delim1(size_id), lvl);
666 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
667 0, lvl);
668 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
669 0, lvl);
670 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
671 0, lvl);
672 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
673 0, lvl);
675 if (size_id == text_size) {
676 def_math_param(math_param_sup_shift_up, display_style,
677 sup1(size_id), lvl);
678 def_math_param(math_param_sup_shift_up, cramped_display_style,
679 sup3(size_id), lvl);
680 def_math_param(math_param_sup_shift_up, text_style,
681 sup2(size_id), lvl);
682 def_math_param(math_param_sup_shift_up, cramped_text_style,
683 sup3(size_id), lvl);
684 } else if (size_id == script_size) {
685 def_math_param(math_param_sub_shift_drop, display_style,
686 sub_drop(size_id), lvl);
687 def_math_param(math_param_sub_shift_drop, cramped_display_style,
688 sub_drop(size_id), lvl);
689 def_math_param(math_param_sub_shift_drop, text_style,
690 sub_drop(size_id), lvl);
691 def_math_param(math_param_sub_shift_drop, cramped_text_style,
692 sub_drop(size_id), lvl);
693 def_math_param(math_param_sup_shift_drop, display_style,
694 sup_drop(size_id), lvl);
695 def_math_param(math_param_sup_shift_drop, cramped_display_style,
696 sup_drop(size_id), lvl);
697 def_math_param(math_param_sup_shift_drop, text_style,
698 sup_drop(size_id), lvl);
699 def_math_param(math_param_sup_shift_drop, cramped_text_style,
700 sup_drop(size_id), lvl);
701 def_math_param(math_param_sup_shift_up, script_style,
702 sup2(size_id), lvl);
703 def_math_param(math_param_sup_shift_up, cramped_script_style,
704 sup3(size_id), lvl);
705 } else if (size_id == script_script_size) {
706 def_math_param(math_param_sub_shift_drop, script_style,
707 sub_drop(size_id), lvl);
708 def_math_param(math_param_sub_shift_drop, cramped_script_style,
709 sub_drop(size_id), lvl);
710 def_math_param(math_param_sub_shift_drop, script_script_style,
711 sub_drop(size_id), lvl);
712 def_math_param(math_param_sub_shift_drop,
713 cramped_script_script_style, sub_drop(size_id), lvl);
714 def_math_param(math_param_sup_shift_drop, script_style,
715 sup_drop(size_id), lvl);
716 def_math_param(math_param_sup_shift_drop, cramped_script_style,
717 sup_drop(size_id), lvl);
718 def_math_param(math_param_sup_shift_drop, script_script_style,
719 sup_drop(size_id), lvl);
720 def_math_param(math_param_sup_shift_drop,
721 cramped_script_script_style, sup_drop(size_id), lvl);
722 def_math_param(math_param_sup_shift_up, script_script_style,
723 sup2(size_id), lvl);
724 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
725 sup3(size_id), lvl);
728 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
729 sub1(size_id), lvl);
730 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
731 sub1(size_id), lvl);
732 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
733 sub2(size_id), lvl);
734 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
735 sub2(size_id), lvl);
736 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
737 (abs(math_x_height(size_id) * 4) / 5), lvl);
738 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
739 (abs(math_x_height(size_id) * 4) / 5), lvl);
740 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
741 (abs(math_x_height(size_id)) / 4), lvl);
742 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
743 (abs(math_x_height(size_id)) / 4), lvl);
744 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
745 (abs(math_x_height(size_id) * 4) / 5), lvl);
746 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
747 (abs(math_x_height(size_id) * 4) / 5), lvl);
750 The display-size |radical_vgap| is done twice because it needs
751 values from both the sy and the ex font.
754 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
755 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
756 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
757 60, lvl);
758 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
759 60, lvl);
760 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
761 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
762 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
763 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
764 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
765 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
766 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
767 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
769 } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
771 /* fix old-style |ex| parameters */
773 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
774 default_rule_thickness(size_id), lvl);
775 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
776 default_rule_thickness(size_id), lvl);
777 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
778 3 * default_rule_thickness(size_id), lvl);
779 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
780 default_rule_thickness(size_id), lvl);
781 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
782 default_rule_thickness(size_id), lvl);
783 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
784 3 * default_rule_thickness(size_id), lvl);
785 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
786 default_rule_thickness(size_id), lvl);
787 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
788 default_rule_thickness(size_id), lvl);
789 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
790 3 * default_rule_thickness(size_id), lvl);
791 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
792 default_rule_thickness(size_id), lvl);
793 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
794 default_rule_thickness(size_id), lvl);
795 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
796 3 * default_rule_thickness(size_id), lvl);
797 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
798 default_rule_thickness(size_id), lvl);
799 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
800 default_rule_thickness(size_id), lvl);
801 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
802 (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
803 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
804 3 * default_rule_thickness(size_id), lvl);
805 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
806 7 * default_rule_thickness(size_id), lvl);
807 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
808 default_rule_thickness(size_id), lvl);
809 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
810 default_rule_thickness(size_id), lvl);
811 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
812 default_rule_thickness(size_id), lvl);
813 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
814 3 * default_rule_thickness(size_id), lvl);
815 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
816 default_rule_thickness(size_id), lvl);
817 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
818 3 * default_rule_thickness(size_id), lvl);
819 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
820 big_op_spacing1(size_id), lvl);
821 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
822 big_op_spacing1(size_id), lvl);
823 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
824 big_op_spacing3(size_id), lvl);
825 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
826 big_op_spacing3(size_id), lvl);
827 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
828 big_op_spacing5(size_id), lvl);
829 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
830 big_op_spacing5(size_id), lvl);
831 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
832 big_op_spacing2(size_id), lvl);
833 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
834 big_op_spacing2(size_id), lvl);
835 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
836 big_op_spacing4(size_id), lvl);
837 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
838 big_op_spacing4(size_id), lvl);
839 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
840 big_op_spacing5(size_id), lvl);
841 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
842 big_op_spacing5(size_id), lvl);
843 DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
844 font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
845 DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
846 font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
847 DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
848 font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
849 DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
850 font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
851 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
852 4 * default_rule_thickness(size_id), lvl);
853 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
854 4 * default_rule_thickness(size_id), lvl);
857 All of the |space_after_script|s are done in |finalize_math_parameters|
858 because the \.{\\scriptspace} may have been altered by the user
861 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
862 0, lvl);
863 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
864 0, lvl);
866 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
867 big_op_spacing2(size_id), lvl);
868 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
869 big_op_spacing2(size_id), lvl);
870 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
871 big_op_spacing4(size_id), lvl);
872 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
873 big_op_spacing4(size_id), lvl);
874 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
875 big_op_spacing1(size_id), lvl);
876 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
877 big_op_spacing1(size_id), lvl);
878 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
879 big_op_spacing3(size_id), lvl);
880 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
881 big_op_spacing3(size_id), lvl);
884 The display-size |radical_vgap| is done twice because it needs
885 values from both the sy and the ex font.
888 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
889 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
894 @ This needs to be called just at the start of |mlist_to_hlist|, for
895 backward compatibility with \.{\\scriptspace}.
898 static void finalize_math_parameters(void)
900 int saved_trace = tracing_assigns_par;
901 tracing_assigns_par = 0;
902 if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
903 def_math_param(math_param_space_after_script, display_style,
904 script_space_par, level_one);
905 def_math_param(math_param_space_after_script, text_style,
906 script_space_par, level_one);
907 def_math_param(math_param_space_after_script, script_style,
908 script_space_par, level_one);
909 def_math_param(math_param_space_after_script, script_script_style,
910 script_space_par, level_one);
911 def_math_param(math_param_space_after_script, cramped_display_style,
912 script_space_par, level_one);
913 def_math_param(math_param_space_after_script, cramped_text_style,
914 script_space_par, level_one);
915 def_math_param(math_param_space_after_script, cramped_script_style,
916 script_space_par, level_one);
917 def_math_param(math_param_space_after_script, cramped_script_script_style,
918 script_space_par, level_one);
920 tracing_assigns_par = saved_trace;
923 @ In order to convert mlists to hlists, i.e., noads to nodes, we need several
924 subroutines that are conveniently dealt with now.
926 Let us first introduce the macros that make it easy to get at the parameters and
927 other font information. A size code, which is a multiple of 256, is added to a
928 family number to get an index into the table of internal font numbers
929 for each combination of family and size. (Be alert: Size codes get
930 larger as the type gets smaller.)
933 static const char *math_size_string(int s)
935 if (s == text_size)
936 return "textfont";
937 else if (s == script_size)
938 return "scriptfont";
939 else
940 return "scriptscriptfont";
943 @ When the style changes, the following piece of program computes associated
944 information:
947 #define setup_cur_size(a) do { \
948 if (a==script_style || a==cramped_script_style) \
949 cur_size = script_size; \
950 else if (a==script_script_style || a==cramped_script_script_style) \
951 cur_size = script_script_size; \
952 else \
953 cur_size = text_size; \
954 } while (0)
957 @ a simple routine that creates a flat copy of a nucleus
959 static pointer math_clone(pointer q)
961 pointer x;
962 if (q == null)
963 return null;
964 x = new_node(type(q), 0);
965 reset_attributes(x, node_attr(q));
966 if (type(q) == math_char_node) {
967 math_fam(x) = math_fam(q);
968 math_character(x) = math_character(q);
969 } else {
970 math_list(x) = math_list(q);
972 return x;
975 @ Here is a function that returns a pointer to a rule node having a given
976 thickness |t|. The rule will extend horizontally to the boundary of the vlist
977 that eventually contains it.
980 static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam)
982 pointer p; /* the new node */
983 if (math_rules_mode_par) {
984 p = new_rule(some_rule);
985 rule_math_size(p) = cur_size;
986 rule_math_font(p) = fam_fnt(cur_fam, cur_size);
987 } else {
988 p = new_rule(normal_rule);
990 rule_dir(p) = math_direction_par;
991 height(p) = t;
992 depth(p) = 0;
993 reset_attributes(p, att);
994 return p;
997 @ The |overbar| function returns a pointer to a vlist box that consists of
998 a given box |b|, above which has been placed a kern of height |k| under a
999 fraction rule of thickness |t| under additional space of height |ht|.
1002 static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam)
1004 pointer p, q; /* nodes being constructed */
1005 p = new_kern(k);
1006 reset_attributes(p, att);
1007 couple_nodes(p,b);
1008 q = do_fraction_rule(t, att, index, cur_size, cur_fam);
1009 couple_nodes(q,p);
1010 p = new_kern(ht);
1011 reset_attributes(p, att);
1012 couple_nodes(p,q);
1013 q = vpackage(p, 0, additional, max_dimen, math_direction_par);
1014 reset_attributes(q, att);
1015 return q;
1018 @ Here is a subroutine that creates a new box, whose list contains a
1019 single character, and whose width includes the italic correction for
1020 that character. The height or depth of the box will be negative, if
1021 the height or depth of the character is negative; thus, this routine
1022 may deliver a slightly different result than |hpack| would produce.
1025 static pointer char_box(internal_font_number f, int c, pointer bb)
1027 pointer b, p; /* the new box and its character node */
1028 b = new_null_box();
1029 if (do_new_math(f))
1030 width(b) = char_width(f, c);
1031 else
1032 width(b) = char_width(f, c) + char_italic(f, c);
1033 height(b) = char_height(f, c);
1034 depth(b) = char_depth(f, c);
1035 reset_attributes(b, bb);
1036 p = new_char(f, c);
1037 reset_attributes(p, bb);
1038 list_ptr(b) = p;
1039 return b;
1042 @ Another handy subroutine computes the height plus depth of
1043 a given character:
1046 static scaled height_plus_depth(internal_font_number f, int c)
1048 return (char_height(f, c) + char_depth(f, c));
1051 @ When we build an extensible character, it's handy to have the
1052 following subroutine, which puts a given character on top
1053 of the characters already in box |b|:
1056 static scaled stack_into_box(pointer b, internal_font_number f, int c)
1058 pointer p, q; /* new node placed into |b| */
1059 p = char_box(f, c, node_attr(b)); /* italic gets added to width */
1060 if (type(b) == vlist_node) {
1061 try_couple_nodes(p,list_ptr(b));
1062 list_ptr(b) = p;
1063 height(b) = height(p);
1064 if (width(b) < width(p))
1065 width(b) = width(p);
1066 return height_plus_depth(f, c);
1067 } else {
1068 q = list_ptr(b);
1069 if (q == null) {
1070 list_ptr(b) = p;
1071 } else {
1072 while (vlink(q) != null)
1073 q = vlink(q);
1074 couple_nodes(q,p);
1076 if (height(b) < height(p))
1077 height(b) = height(p);
1078 if (depth(b) < depth(p))
1079 depth(b) = depth(p);
1080 return char_width(f, c);
1084 static void stack_glue_into_box(pointer b, scaled min, scaled max) {
1085 halfword p = new_glue(zero_glue);
1086 width(p) = min;
1087 stretch(p) = max - min;
1088 reset_attributes(p, node_attr(b));
1089 if (type(b) == vlist_node) {
1090 try_couple_nodes(p,list_ptr(b));
1091 list_ptr(b) = p;
1092 } else {
1093 halfword q = list_ptr(b);
1094 if (q == null) {
1095 list_ptr(b) = p;
1096 } else {
1097 while (vlink(q) != null)
1098 q = vlink(q);
1099 couple_nodes(q,p);
1104 @ \TeX's most important routine for dealing with formulas is called
1105 |mlist_to_hlist|. After a formula has been scanned and represented
1106 as an mlist, this routine converts it to an hlist that can be placed
1107 into a box or incorporated into the text of a paragraph. The
1108 explicit parameter |cur_mlist| points to the first node or noad in
1109 the given mlist (and it might be |null|); the parameter |penalties|
1110 is |true| if penalty nodes for potential line breaks are to be
1111 inserted into the resulting hlist, the parameter |cur_style| is a
1112 style code. After |mlist_to_hlist| has acted, |vlink(temp_head)|
1113 points to the translated hlist.
1115 Since mlists can be inside mlists, the procedure is recursive. And since this
1116 is not part of \TeX's inner loop, the program has been written in a manner
1117 that stresses compactness over efficiency.
1118 @^recursion@>
1121 int cur_size; /* size code corresponding to |cur_style| */
1123 @ @c
1124 static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v,
1125 pointer att, int cur_style, int boxtype)
1127 pointer b; /* new box */
1128 scaled b_max; /* natural (maximum) size of the stack */
1129 scaled s_max; /* amount of possible shrink in the stack */
1130 extinfo *cur;
1131 scaled min_overlap, prev_overlap;
1132 int i; /* a temporary counter number of extensible pieces */
1133 int with_extenders; /* number of times to repeat each repeatable item in |ext| */
1134 int num_extenders, num_normal;
1135 scaled a, c, d;
1137 assert(ext != NULL);
1138 b = new_null_box();
1139 type(b) = (quarterword) boxtype;
1140 reset_attributes(b, att);
1141 min_overlap = connector_overlap_min(cur_style);
1142 assert(min_overlap >= 0);
1143 with_extenders = -1;
1144 num_extenders = 0;
1145 num_normal = 0;
1147 cur = ext;
1148 while (cur != NULL) {
1149 if (!char_exists(f, cur->glyph)) {
1150 const char *hlp[] = {
1151 "Each glyph part in an extensible item should exist in the font.",
1152 "I will give up trying to find a suitable size for now. Fix your font!",
1153 NULL
1155 tex_error("Variant part doesn't exist.", hlp);
1156 width(b) = null_delimiter_space_par;
1157 return b;
1159 if (cur->extender > 0)
1160 num_extenders++;
1161 else
1162 num_normal++;
1163 /* no negative overlaps or advances are allowed */
1164 if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
1165 const char *hlp[] = {
1166 "All measurements in extensible items should be positive.",
1167 "To get around this problem, I have changed the font metrics.",
1168 "Fix your font!",
1169 NULL
1171 tex_error("Extensible recipe has negative fields.", hlp);
1172 if (cur->start_overlap < 0)
1173 cur->start_overlap = 0;
1174 if (cur->end_overlap < 0)
1175 cur->end_overlap = 0;
1176 if (cur->advance < 0)
1177 cur->advance = 0;
1179 cur = cur->next;
1181 if (num_normal == 0) {
1182 const char *hlp[] = {
1183 "Each extensible recipe should have at least one non-repeatable part.",
1184 "To get around this problem, I have changed the first part to be",
1185 "non-repeatable. Fix your font!",
1186 NULL
1188 tex_error("Extensible recipe has no fixed parts.", hlp);
1189 ext->extender = 0;
1190 num_normal = 1;
1191 num_extenders--;
1194 |ext| holds a linked list of numerous items that may or may not be
1195 repeatable. For the total height, we have to figure out how many items
1196 are needed to create a stack of at least |v|.
1198 The next |while| loop does that. It has two goals: it finds out
1199 the natural height |b_max| of the all the parts needed to reach
1200 at least |v|, and it sets |with_extenders| to the number of times
1201 each of the repeatable items in |ext| has to be repeated to reach
1202 that height.
1205 cur = ext;
1206 b_max = 0;
1207 while (b_max < v && num_extenders > 0) {
1208 b_max = 0;
1209 prev_overlap = 0;
1210 with_extenders++;
1211 for (cur = ext; cur != NULL; cur = cur->next) {
1212 if (cur->extender == 0) {
1213 c = cur->start_overlap;
1214 if (min_overlap < c)
1215 c = min_overlap;
1216 if (prev_overlap < c)
1217 c = prev_overlap;
1218 a = cur->advance;
1219 if (a == 0) {
1220 /* for tfm fonts */
1221 if (boxtype == vlist_node)
1222 a = height_plus_depth(f, cur->glyph);
1223 else
1224 a = char_width(f, cur->glyph);
1225 assert(a >= 0);
1227 b_max += a - c;
1228 prev_overlap = cur->end_overlap;
1229 } else {
1230 i = with_extenders;
1231 while (i > 0) {
1232 c = cur->start_overlap;
1233 if (min_overlap < c)
1234 c = min_overlap;
1235 if (prev_overlap < c)
1236 c = prev_overlap;
1237 a = cur->advance;
1238 if (a == 0) {
1239 /* for tfm fonts */
1240 if (boxtype == vlist_node)
1241 a = height_plus_depth(f, cur->glyph);
1242 else
1243 a = char_width(f, cur->glyph);
1244 assert(a >= 0);
1246 b_max += a - c;
1247 prev_overlap = cur->end_overlap;
1248 i--;
1255 assemble box using |with_extenders| copies of each extender, with
1256 appropriate glue wherever an overlap occurs
1258 prev_overlap = 0;
1259 b_max = 0;
1260 s_max = 0;
1261 for (cur = ext; cur != NULL; cur = cur->next) {
1262 if (cur->extender == 0) {
1263 c = cur->start_overlap;
1264 if (prev_overlap < c)
1265 c = prev_overlap;
1266 d = c;
1267 if (min_overlap < c)
1268 c = min_overlap;
1269 if (d > 0) {
1270 stack_glue_into_box(b, -d, -c);
1271 s_max += (-c) - (-d);
1272 b_max -= d;
1274 b_max += stack_into_box(b, f, cur->glyph);
1275 prev_overlap = cur->end_overlap;
1276 i--;
1277 } else {
1278 i = with_extenders;
1279 while (i > 0) {
1280 c = cur->start_overlap;
1281 if (prev_overlap < c)
1282 c = prev_overlap;
1283 d = c;
1284 if (min_overlap < c)
1285 c = min_overlap;
1286 if (d > 0) {
1287 stack_glue_into_box(b, -d, -c);
1288 s_max += (-c) - (-d);
1289 b_max -= d;
1291 b_max += stack_into_box(b, f, cur->glyph);
1292 prev_overlap = cur->end_overlap;
1293 i--;
1298 /* set glue so as to stretch the connections if needed */
1300 d = 0;
1301 if (v > b_max && s_max > 0) {
1302 d = v-b_max;
1303 /* don't stretch more than |s_max| */
1304 if (d > s_max)
1305 d = s_max;
1306 glue_order(b) = normal;
1307 glue_sign(b) = stretching;
1308 glue_set(b) = unfloat(d/(float) s_max);
1309 b_max += d;
1312 if (boxtype == vlist_node) {
1313 height(b) = b_max;
1314 } else {
1315 width(b) = b_max;
1318 return b;
1321 @ The |var_delimiter| function, which finds or constructs a sufficiently
1322 large delimiter, is the most interesting of the auxiliary functions that
1323 currently concern us. Given a pointer |d| to a delimiter field in some noad,
1324 together with a size code |s| and a vertical distance |v|, this function
1325 returns a pointer to a box that contains the smallest variant of |d| whose
1326 height plus depth is |v| or more. (And if no variant is large enough, it
1327 returns the largest available variant.) In particular, this routine will
1328 construct arbitrarily large delimiters from extensible components, if
1329 |d| leads to such characters.
1331 The value returned is a box whose |shift_amount| has been set so that
1332 the box is vertically centered with respect to the axis in the given size.
1333 If a built-up symbol is returned, the height of the box before shifting
1334 will be the height of its topmost component.
1337 static void endless_loop_error(internal_font_number g, int y)
1339 char s[256];
1340 const char *hlp[] = {
1341 "You managed to create a seemingly endless charlist chain in the current",
1342 "font. I have counted until 10000 already and still have not escaped, so"
1343 "I will jump out of the loop all by myself now. Fix your font!",
1344 NULL
1346 snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
1347 (int) y, font_name(g));
1348 tex_error(s, hlp);
1351 static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta)
1353 pointer b; /* the box that will be constructed */
1354 internal_font_number f, g; /* best-so-far and tentative font codes */
1355 int c, i, x, y; /* best-so-far and tentative character codes */
1356 scaled u; /* height-plus-depth of a tentative character */
1357 scaled w; /* largest height-plus-depth so far */
1358 int z; /* runs through font family members */
1359 boolean large_attempt; /* are we trying the ``large'' variant? */
1360 pointer att; /* to save the current attribute list */
1361 boolean do_parts;
1362 extinfo *ext;
1363 att = null;
1364 f = null_font;
1365 c = 0;
1366 w = 0;
1367 do_parts = false;
1368 large_attempt = false;
1369 if (d == null)
1370 goto FOUND;
1371 z = small_fam(d);
1372 x = small_char(d);
1373 i = 0;
1374 while (true) {
1376 The search process is complicated slightly by the facts that some of the
1377 characters might not be present in some of the fonts, and they might not
1378 be probed in increasing order of height.
1380 if ((z != 0) || (x != 0)) {
1381 g = fam_fnt(z, s);
1382 if (g != null_font) {
1383 y = x;
1384 CONTINUE:
1385 i++;
1386 if (char_exists(g, y)) {
1387 if (flat)
1388 u = char_width(g, y);
1389 else
1390 u = height_plus_depth(g, y);
1391 if (u > w) {
1392 f = g;
1393 c = y;
1394 w = u;
1395 if (u >= v)
1396 goto FOUND;
1398 if (char_tag(g, y) == ext_tag) {
1399 f = g;
1400 c = y;
1401 do_parts = true;
1402 goto FOUND;
1404 if (i > 10000) {
1405 /* endless loop */
1406 endless_loop_error(g, y);
1407 goto FOUND;
1409 if (char_tag(g, y) == list_tag) {
1410 y = char_remainder(g, y);
1411 goto CONTINUE;
1416 if (large_attempt)
1417 goto FOUND; /* there were none large enough */
1418 large_attempt = true;
1419 z = large_fam(d);
1420 x = large_char(d);
1422 FOUND:
1423 if (d != null) {
1424 att = node_attr(d);
1425 node_attr(d) = null;
1426 flush_node(d);
1428 if (f != null_font) {
1430 When the following code is executed, |do_parts| will be true
1431 if a built-up symbol is supposed to be returned.
1433 ext = NULL;
1434 if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
1435 || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
1436 if (flat) {
1437 b = get_delim_box(d, ext, f, v, att, cur_style, hlist_node);
1438 } else {
1439 b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node);
1441 if (delta != NULL) {
1442 if (do_new_math(f)) {
1443 *delta = char_vert_italic(f,x);
1444 } else {
1445 *delta = char_italic(f,x);
1448 if (stack != NULL)
1449 *stack = true ;
1450 } else {
1451 b = char_box(f, c, att);
1452 if (!do_new_math(f)) {
1453 /* italic gets added to width */
1454 width(b) += char_italic(f, c);
1456 if (delta != NULL) {
1457 *delta = char_italic(f, c); /* was historically (f, x) */
1459 if (stack != NULL)
1460 *stack = false ;
1462 } else {
1463 b = new_null_box();
1464 reset_attributes(b, att);
1465 if (flat) {
1466 width(b) = 0;
1467 } else {
1468 /* use this width if no delimiter was found */
1469 width(b) = null_delimiter_space_par;
1471 if (delta != NULL) {
1472 *delta = 0;
1474 if (stack != NULL)
1475 *stack = false ;
1477 if (!flat) {
1478 /* vertical variant */
1479 shift_amount(b) = half(height(b) - depth(b));
1480 if (shift) {
1481 shift_amount(b) -= math_axis_size(s);
1484 delete_attribute_ref(att);
1485 return b;
1488 @ The next subroutine is much simpler; it is used for numerators and
1489 denominators of fractions as well as for displayed operators and
1490 their limits above and below. It takes a given box~|b| and
1491 changes it so that the new box is centered in a box of width~|w|.
1492 The centering is done by putting \.{\\hss} glue at the left and right
1493 of the list inside |b|, then packaging the new box; thus, the
1494 actual box might not really be centered, if it already contains
1495 infinite glue.
1497 The given box might contain a single character whose italic correction
1498 has been added to the width of the box; in this case a compensating
1499 kern is inserted.
1502 static pointer rebox(pointer b, scaled w)
1504 pointer p, q, r, att; /* temporary registers for list manipulation */
1505 internal_font_number f; /* font in a one-character box */
1506 scaled v; /* width of a character without italic correction */
1508 if ((width(b) != w) && (list_ptr(b) != null)) {
1509 if (type(b) == vlist_node) {
1510 p = hpack(b, 0, additional, -1);
1511 reset_attributes(p, node_attr(b));
1512 b = p;
1514 p = list_ptr(b);
1515 att = node_attr(b);
1516 add_node_attr_ref(att);
1517 if ((is_char_node(p)) && (vlink(p) == null)) {
1518 f = font(p);
1519 v = char_width(f, character(p));
1520 if (v != width(b)) {
1521 q = new_kern(width(b) - v);
1522 reset_attributes(q, att);
1523 couple_nodes(p,q);
1526 list_ptr(b) = null;
1527 flush_node(b);
1528 b = new_glue(ss_glue);
1529 reset_attributes(b, att);
1530 couple_nodes(b,p);
1531 while (vlink(p) != null)
1532 p = vlink(p);
1533 q = new_glue(ss_glue);
1534 reset_attributes(q, att);
1535 couple_nodes(p,q);
1536 r = hpack(b, w, exactly, -1);
1537 reset_attributes(r, att);
1538 delete_attribute_ref(att);
1539 return r;
1540 } else {
1541 width(b) = w;
1542 return b;
1546 @ Here is a subroutine that creates a new glue specification from another
1547 one that is expressed in `\.{mu}', given the value of the math unit.
1550 #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
1552 static pointer math_glue(pointer g, scaled m)
1554 int n = x_over_n(m, unity); /* integer part of |m| */
1555 scaled f = tex_remainder; /* fraction part of |m| */
1556 pointer p; /* the new glue specification */
1557 if (f < 0) {
1558 decr(n);
1559 f = f + unity;
1561 p = new_node(glue_node, 0);
1562 width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
1563 stretch_order(p) = stretch_order(g);
1564 if (stretch_order(p) == normal)
1565 stretch(p) = mu_mult(stretch(g));
1566 else
1567 stretch(p) = stretch(g);
1568 shrink_order(p) = shrink_order(g);
1569 if (shrink_order(p) == normal)
1570 shrink(p) = mu_mult(shrink(g));
1571 else
1572 shrink(p) = shrink(g);
1573 return p;
1576 static void math_glue_to_glue(pointer p, scaled m)
1578 int n = x_over_n(m, unity); /* integer part of |m| */
1579 scaled f = tex_remainder; /* fraction part of |m| */
1580 if (f < 0) {
1581 decr(n);
1582 f = f + unity;
1584 width(p) = mu_mult(width(p)); /* convert \.{mu} to \.{pt} */
1585 if (stretch_order(p) == normal)
1586 stretch(p) = mu_mult(stretch(p));
1587 if (shrink_order(p) == normal)
1588 shrink(p) = mu_mult(shrink(p));
1589 subtype(p) = normal;
1592 @ The |math_kern| subroutine removes |mu_glue| from a kern node, given
1593 the value of the math unit.
1596 static void math_kern(pointer p, scaled m)
1598 int n; /* integer part of |m| */
1599 scaled f; /* fraction part of |m| */
1600 if (subtype(p) == mu_glue) {
1601 n = x_over_n(m, unity);
1602 f = tex_remainder;
1603 if (f < 0) {
1604 decr(n);
1605 f = f + unity;
1607 width(p) = mu_mult(width(p));
1608 subtype(p) = italic_kern;
1612 @ @c
1613 void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
1615 int callback_id;
1616 int a, sfix;
1617 if (p == null) {
1618 vlink(temp_head) = null;
1619 return;
1621 finalize_math_parameters();
1622 callback_id = callback_defined(mlist_to_hlist_callback);
1623 if (callback_id > 0) {
1624 sfix = lua_gettop(Luas);
1625 if (!get_callback(Luas, callback_id)) {
1626 lua_settop(Luas, sfix);
1627 return;
1629 alink(p) = null ;
1630 nodelist_to_lua(Luas, p);
1631 lua_push_math_style_name(Luas, mstyle);
1632 lua_pushboolean(Luas, penalties);
1633 if (lua_pcall(Luas, 3, 1, 0) != 0) { /* 3 args, 1 result */
1634 char errmsg[256]; /* temp hack ... we will have a formatted error */
1635 snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
1636 errmsg[255]='\0';
1637 lua_settop(Luas, sfix);
1638 normal_error("mlist to hlist",errmsg); /* to be done */
1639 return;
1641 a = nodelist_from_lua(Luas);
1642 /* alink(vlink(a)) = null; */
1643 lua_settop(Luas, sfix);
1644 vlink(temp_head) = a;
1645 } else if (callback_id == 0) {
1646 mlist_to_hlist(p, penalties, mstyle);
1647 } else {
1648 vlink(temp_head) = null;
1652 @ The recursion in |mlist_to_hlist| is due primarily to a subroutine
1653 called |clean_box| that puts a given noad field into a box using a given
1654 math style; |mlist_to_hlist| can call |clean_box|, which can call
1655 |mlist_to_hlist|.
1656 @^recursion@>
1658 The box returned by |clean_box| is ``clean'' in the
1659 sense that its |shift_amount| is zero.
1662 static pointer clean_box(pointer p, int s, int cur_style)
1664 pointer q; /* beginning of a list to be boxed */
1665 pointer x; /* box to be returned */
1666 pointer r; /* temporary pointer */
1667 pointer mlist = null; /* beginning of mlist to be translated */
1668 switch (type(p)) {
1669 case math_char_node:
1670 mlist = new_noad();
1671 r = math_clone(p);
1672 nucleus(mlist) = r;
1673 break;
1674 case sub_box_node:
1675 q = math_list(p);
1676 goto FOUND;
1677 break;
1678 case sub_mlist_node:
1679 mlist = math_list(p);
1680 break;
1681 default:
1682 q = new_null_box();
1683 goto FOUND;
1685 mlist_to_hlist(mlist, false, s);
1686 q = vlink(temp_head); /* recursive call */
1687 setup_cur_size(cur_style);
1688 FOUND:
1689 if (is_char_node(q) || (q == null))
1690 x = hpack(q, 0, additional, -1);
1691 else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
1692 x = q; /* it's already clean */
1693 else
1694 x = hpack(q, 0, additional, -1);
1695 if (x != q && q != null)
1696 reset_attributes(x, node_attr(q));
1697 /* Here we save memory space in a common case. */
1698 q = list_ptr(x);
1699 if (is_char_node(q)) {
1700 r = vlink(q);
1701 if (r != null) {
1702 if (vlink(r) == null) {
1703 if (!is_char_node(r)) {
1704 if (type(r) == kern_node) {
1705 /* unneeded italic correction */
1706 flush_node(r);
1707 vlink(q) = null;
1713 return x;
1716 @ It is convenient to have a procedure that converts a |math_char|
1717 field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
1718 to the font code and character code of a given noad field.
1719 It also takes care of issuing error messages for
1720 nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
1721 after |fetch| has acted, and the field will also have been reset to |null|.
1723 The outputs of |fetch| are placed in global variables.
1726 internal_font_number cur_f; /* the |font| field of a |math_char| */
1727 int cur_c; /* the |character| field of a |math_char| */
1729 @ Here we unpack the |math_char| field |a|.
1731 @c static void fetch(pointer a)
1733 cur_c = math_character(a);
1734 cur_f = fam_fnt(math_fam(a), cur_size);
1735 if (cur_f == null_font) {
1736 char *msg;
1737 const char *hlp[] = {
1738 "Somewhere in the math formula just ended, you used the",
1739 "stated character from an undefined font family. For example,",
1740 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
1741 "and I'll try to forget that I needed that character.",
1742 NULL
1744 msg = xmalloc(256);
1745 snprintf(msg, 255, "\\%s%d is undefined (character %d)",
1746 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
1747 tex_error(msg, hlp);
1748 free(msg);
1749 } else if (!(char_exists(cur_f, cur_c))) {
1750 char_warning(cur_f, cur_c);
1754 @ We need to do a lot of different things, so |mlist_to_hlist| makes two
1755 passes over the given mlist.
1757 The first pass does most of the processing: It removes ``mu'' spacing from
1758 glue, it recursively evaluates all subsidiary mlists so that only the
1759 top-level mlist remains to be handled, it puts fractions and square roots
1760 and such things into boxes, it attaches subscripts and superscripts, and
1761 it computes the overall height and depth of the top-level mlist so that
1762 the size of delimiters for a |fence_noad| will be known.
1763 The hlist resulting from each noad is recorded in that noad's |new_hlist|
1764 field, an integer field that replaces the |nucleus| or |thickness|.
1765 @^recursion@>
1767 The second pass eliminates all noads and inserts the correct glue and
1768 penalties between nodes.
1771 static void assign_new_hlist(pointer q, pointer r)
1773 switch (type(q)) {
1774 case fraction_noad:
1775 math_list(numerator(q)) = null;
1776 flush_node(numerator(q));
1777 numerator(q) = null;
1778 math_list(denominator(q)) = null;
1779 flush_node(denominator(q));
1780 denominator(q) = null;
1781 break;
1782 case radical_noad:
1783 case simple_noad:
1784 case accent_noad:
1785 if (nucleus(q) != null) {
1786 math_list(nucleus(q)) = null;
1787 flush_node(nucleus(q));
1788 nucleus(q) = null;
1790 break;
1792 new_hlist(q) = r;
1795 @ @c
1796 #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
1798 @ Most of the actual construction work of |mlist_to_hlist| is done
1799 by procedures with names like |make_fraction|, |make_radical|, etc. To
1800 illustrate the general setup of such procedures, let's begin with a
1801 couple of simple ones.
1804 static void make_over(pointer q, int cur_style, int cur_size, int cur_fam)
1806 pointer p;
1807 p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
1808 overbar_vgap(cur_style), overbar_rule(cur_style),
1809 overbar_kern(cur_style), node_attr(nucleus(q)), math_over_rule, cur_size, cur_fam);
1810 math_list(nucleus(q)) = p;
1811 type(nucleus(q)) = sub_box_node;
1814 static void make_under(pointer q, int cur_style, int cur_size, int cur_fam)
1816 pointer p, x, y, r; /* temporary registers for box construction */
1817 scaled delta; /* overall height plus depth */
1818 x = clean_box(nucleus(q), cur_style, cur_style);
1819 p = new_kern(underbar_vgap(cur_style));
1820 reset_attributes(p, node_attr(q));
1821 couple_nodes(x,p);
1822 r = do_fraction_rule(underbar_rule(cur_style), node_attr(q), math_under_rule, cur_size, cur_fam);
1823 couple_nodes(p,r);
1824 y = vpackage(x, 0, additional, max_dimen, math_direction_par);
1825 reset_attributes(y, node_attr(q));
1826 delta = height(y) + depth(y) + underbar_kern(cur_style);
1827 height(y) = height(x);
1828 depth(y) = delta - height(y);
1829 math_list(nucleus(q)) = y;
1830 type(nucleus(q)) = sub_box_node;
1833 static void make_vcenter(pointer q)
1835 pointer v; /* the box that should be centered vertically */
1836 scaled delta; /* its height plus depth */
1837 v = math_list(nucleus(q));
1838 if (type(v) != vlist_node)
1839 confusion("vcenter");
1840 delta = height(v) + depth(v);
1841 height(v) = math_axis_size(cur_size) + half(delta);
1842 depth(v) = delta - height(v);
1845 @ According to the rules in the \.{DVI} file specifications, we ensure alignment
1846 @^square roots@>
1847 between a square root sign and the rule above its nucleus by assuming that the
1848 baseline of the square-root symbol is the same as the bottom of the rule. The
1849 height of the square-root symbol will be the thickness of the rule, and the
1850 depth of the square-root symbol should exceed or equal the height-plus-depth
1851 of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
1852 placed so that the actual clearance is |psi| plus half the excess.
1855 static void make_hextension(pointer q, int cur_style)
1857 pointer e, p;
1858 halfword w;
1859 boolean stack = false;
1860 e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL);
1861 w = width(e);
1862 if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
1863 if (radicalmiddle(q)) {
1864 p = new_kern(half(radicalwidth(q)-w));
1865 reset_attributes(p, node_attr(q));
1866 couple_nodes(p,e);
1867 e = p;
1868 w = radicalwidth(q);
1869 } else if (radicalexact(q)) {
1870 w = radicalwidth(q);
1873 e = hpack(e, 0, additional, -1);
1874 width(e) = w ;
1875 reset_attributes(e, node_attr(q));
1876 math_list(nucleus(q)) = e;
1877 left_delimiter(q) = null;
1880 static void make_radical(pointer q, int cur_style)
1882 pointer x, y, p, l1, l2; /* temporary registers for box construction */
1883 scaled delta, clr, theta, h; /* dimensions involved in the calculation */
1884 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
1885 clr = radical_vgap(cur_style);
1886 theta = radical_rule_par(cur_style);
1887 if (theta == undefined_math_parameter) {
1888 /* a real radical */
1889 theta = fraction_rule(cur_style);
1890 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1892 If |y| is a composite then set |theta| to the height of its top
1893 character, else set it to the height of |y|.
1895 l1 = list_ptr(y);
1896 if ((l1 != null) && (type(l1) == hlist_node)) {
1897 /* possible composite */
1898 l2 = list_ptr(l1);
1899 if ((l2 != null) && (type(l2) == glyph_node)) {
1900 /* top character */
1901 theta = char_height(font(l2), character(l2));
1902 } else {
1903 theta = height(y);
1905 } else {
1906 theta = height(y);
1908 } else {
1909 /* not really a radical but we use its node, historical sharing (like in mathml) */
1910 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1912 left_delimiter(q) = null;
1913 delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
1914 if (delta > 0) {
1915 /* increase the actual clearance */
1916 clr = clr + half(delta);
1918 shift_amount(y) = (height(y) - theta) - (height(x) + clr);
1919 h = depth(y) + height(y);
1920 p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, small_fam(left_delimiter(q)));
1921 couple_nodes(y,p);
1922 if (degree(q) != null) {
1923 scaled wr, br, ar;
1924 pointer r = clean_box(degree(q), script_script_style, cur_style);
1925 reset_attributes(r, node_attr(degree(q)));
1926 wr = width(r);
1927 if (wr == 0) {
1928 flush_node(r);
1929 } else {
1930 br = radical_degree_before(cur_style);
1931 ar = radical_degree_after(cur_style);
1932 if (-ar > (wr + br))
1933 ar = -(wr + br);
1934 x = new_kern(ar);
1935 reset_attributes(x, node_attr(degree(q)));
1936 couple_nodes(x,y);
1937 shift_amount(r) =
1938 -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
1939 depth(y) - shift_amount(y));
1940 couple_nodes(r,x);
1941 x = new_kern(br);
1942 reset_attributes(x, node_attr(degree(q)));
1943 couple_nodes(x,r);
1944 y = x;
1946 /* for \.{\\Uroot ..{<list>}{}} : */
1947 math_list(degree(q)) = null;
1948 flush_node(degree(q));
1950 p = hpack(y, 0, additional, -1);
1951 reset_attributes(p, node_attr(q));
1952 math_list(nucleus(q)) = p;
1953 type(nucleus(q)) = sub_box_node;
1956 @ Construct a vlist box
1959 static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
1961 pointer p; /* temporary register for box construction */
1962 pointer v = new_null_box();
1963 type(v) = vlist_node;
1964 height(v) = shift_up + height(x);
1965 depth(v) = depth(y) + shift_down;
1966 reset_attributes(v, node_attr(q));
1967 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
1968 reset_attributes(p, node_attr(q));
1969 couple_nodes(p,y);
1970 couple_nodes(x,p);
1971 list_ptr(v) = x;
1972 return v;
1975 /* when exact use radicalwidth (y is delimiter) */
1977 @ @c
1979 #define fixup_widths(q,x,y) do { \
1980 if (width(y) >= width(x)) { \
1981 if (radicalwidth(q) != 0) { \
1982 shift_amount(x) += half(width(y)-width(x)) ; \
1984 width(x) = width(y); \
1985 } else { \
1986 if (radicalwidth(q) != 0) { \
1987 shift_amount(y) += half(width(x)-width(y)) ; \
1989 width(y) = width(x); \
1991 } while (0)
1994 #define check_radical(q,stack,r,t) do { \
1995 if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != 0) && (radicalwidth(q) != width(r))) { \
1996 if (radicalleft(q)) { \
1997 halfword p = new_kern(radicalwidth(q)-width(r)); \
1998 reset_attributes(p, node_attr(q)); \
1999 couple_nodes(p,r); \
2000 r = hpack(p, 0, additional, -1); \
2001 width(r) = radicalwidth(q); \
2002 reset_attributes(r, node_attr(q)); \
2003 } else if (radicalmiddle(q)) { \
2004 halfword p = new_kern(half(radicalwidth(q)-width(r))); \
2005 reset_attributes(p, node_attr(q)); \
2006 couple_nodes(p,r); \
2007 r = hpack(p, 0, additional, -1); \
2008 width(r) = radicalwidth(q); \
2009 reset_attributes(r, node_attr(q)); \
2010 } else if (radicalright(q)) { \
2011 /* also kind of exact compared to vertical */ \
2012 r = hpack(r, 0, additional, -1); \
2013 width(r) = radicalwidth(q); \
2014 reset_attributes(r, node_attr(q)); \
2017 } while (0)
2019 #define check_widths(q,p) do { \
2020 if (radicalwidth(q) != 0) { \
2021 wd = radicalwidth(q); \
2022 } else { \
2023 wd = width(p); \
2025 } while (0)
2027 @ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
2030 static void make_over_delimiter(pointer q, int cur_style)
2032 pointer x, y, v; /* temporary registers for box construction */
2033 scaled shift_up, shift_down, clr, delta, wd;
2034 boolean stack;
2035 x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
2036 check_widths(q,x);
2037 y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2038 left_delimiter(q) = null;
2039 check_radical(q,stack,y,x);
2040 fixup_widths(q, x, y);
2041 shift_up = over_delimiter_bgap(cur_style);
2042 shift_down = 0;
2043 clr = over_delimiter_vgap(cur_style);
2044 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2045 if (delta > 0) {
2046 shift_up = shift_up + delta;
2048 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2049 width(v) = width(x); /* this also equals |width(y)| */
2050 math_list(nucleus(q)) = v;
2051 type(nucleus(q)) = sub_box_node;
2054 @ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
2057 static void make_under_delimiter(pointer q, int cur_style)
2059 pointer x, y, v; /* temporary registers for box construction */
2060 scaled shift_up, shift_down, clr, delta, wd;
2061 boolean stack;
2062 y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
2063 check_widths(q,y);
2064 x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2065 left_delimiter(q) = null;
2066 check_radical(q,stack,x,y);
2067 fixup_widths(q, x, y);
2068 shift_up = 0;
2069 shift_down = under_delimiter_bgap(cur_style);
2070 clr = under_delimiter_vgap(cur_style);
2071 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2072 if (delta > 0) {
2073 shift_down = shift_down + delta;
2075 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2076 width(v) = width(y); /* this also equals |width(y)| */
2077 math_list(nucleus(q)) = v;
2078 type(nucleus(q)) = sub_box_node;
2081 @ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
2084 static void make_delimiter_over(pointer q, int cur_style)
2086 pointer x, y, v; /* temporary registers for box construction */
2087 scaled shift_up, shift_down, clr, actual, wd;
2088 boolean stack;
2089 y = clean_box(nucleus(q), cur_style, cur_style);
2090 check_widths(q,y);
2091 x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2092 left_delimiter(q) = null;
2093 check_radical(q,stack,x,y);
2094 fixup_widths(q, x, y);
2095 shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
2096 shift_down = 0;
2097 clr = over_delimiter_vgap(cur_style);
2098 actual = shift_up - height(y);
2099 if (actual < clr) {
2100 shift_up = shift_up + (clr-actual);
2102 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2103 width(v) = width(x); /* this also equals |width(y)| */
2104 math_list(nucleus(q)) = v;
2105 type(nucleus(q)) = sub_box_node;
2108 @ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
2111 static void make_delimiter_under(pointer q, int cur_style)
2113 pointer x, y, v; /* temporary registers for box construction */
2114 scaled shift_up, shift_down, clr, actual, wd;
2115 boolean stack;
2116 x = clean_box(nucleus(q), cur_style, cur_style);
2117 check_widths(q,x);
2118 y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2119 left_delimiter(q) = null;
2120 check_radical(q,stack,y,x);
2121 fixup_widths(q, x, y);
2122 shift_up = 0;
2123 shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
2124 clr = under_delimiter_vgap(cur_style);
2125 actual = shift_down - depth(x);
2126 if (actual<clr) {
2127 shift_down += (clr-actual);
2129 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2130 width(v) = width(y); /* this also equals |width(y)| */
2131 math_list(nucleus(q)) = v;
2132 type(nucleus(q)) = sub_box_node;
2135 @ Slants are not considered when placing accents in math mode. The accenter is
2136 centered over the accentee, and the accent width is treated as zero with
2137 respect to the size of the final box.
2140 #define TOP_CODE 1
2141 #define BOT_CODE 2
2142 #define OVERLAY_CODE 4
2143 #define STRETCH_ACCENT_CODE 8
2145 static boolean compute_accent_skew(pointer q, int flags, scaled *s)
2147 pointer p; /* temporary register for box construction */
2148 boolean s_is_absolute = false; /* will be true if a top-accent is placed in |s| */
2149 if (type(nucleus(q)) == math_char_node) {
2150 fetch(nucleus(q));
2151 if (do_new_math(cur_f)) {
2153 there is no bot_accent so let's assume similarity
2155 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2156 *s = char_top_accent(cur_f, cur_c);
2157 if (*s != INT_MIN) {
2158 s_is_absolute = true;
2160 } else {
2161 *s = char_bot_accent(cur_f, cur_c);
2162 if (*s != INT_MIN) {
2163 s_is_absolute = true;
2167 *s = char_top_accent(cur_f, cur_c);
2168 if (*s != INT_MIN) {
2169 s_is_absolute = true;
2171 } else {
2172 if (flags & TOP_CODE) {
2173 *s = get_kern(cur_f, cur_c, skew_char(cur_f));
2174 } else {
2175 *s = 0;
2178 } else if (type(nucleus(q)) == sub_mlist_node) {
2180 if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
2182 * use the positioning of the nucleus of that noad, recursing until
2183 * the inner most |accent_noad|. This way multiple stacked accents are
2184 * aligned to the inner most one.
2186 p = math_list(nucleus(q));
2187 if (type(p) == accent_noad) {
2188 s_is_absolute = compute_accent_skew(p, flags, s);
2192 return s_is_absolute;
2195 static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
2197 pointer p, r, x, y; /* temporary registers for box construction */
2198 scaled s; /* amount to skew the accent to the right */
2199 scaled h; /* height of character being accented */
2200 scaled delta; /* space to remove between accent and accentee */
2201 scaled w; /* width of the accentee, not including sub/superscripts */
2202 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
2203 scaled fraction ;
2204 scaled ic = 0;
2205 scaled target ;
2206 extinfo *ext;
2207 pointer attr_p;
2208 attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
2209 fraction = accentfraction(q);
2210 c = cur_c;
2211 f = cur_f;
2212 s = 1;
2213 if (fraction == 0) {
2214 fraction = 1000;
2216 /* Compute the amount of skew, or set |s| to an alignment point */
2217 s_is_absolute = compute_accent_skew(q, flags, &s);
2218 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
2219 w = width(x);
2220 h = height(x);
2221 if (do_new_math(cur_f) && !s_is_absolute) {
2222 s = half(w);
2223 s_is_absolute = true;
2225 /* Switch to a larger accent if available and appropriate */
2226 y = null;
2227 ext = NULL;
2228 if (flags & OVERLAY_CODE) {
2229 if (fraction > 0) {
2230 target = xn_over_d(h,fraction,1000);
2231 } else {
2232 target = h;
2234 } else {
2235 if (fraction > 0) {
2236 target = xn_over_d(w,fraction,1000);
2237 } else {
2238 target = w;
2241 if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
2242 while (1) {
2243 if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
2244 /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
2245 y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node);
2246 break;
2247 } else if (char_tag(f, c) != list_tag) {
2248 break;
2249 } else {
2250 int yy = char_remainder(f, c);
2251 if (!char_exists(f, yy)) {
2252 break;
2253 } else if (flags & OVERLAY_CODE) {
2254 if (char_height(f, yy) > target) {
2255 break;
2257 } else {
2258 if (char_width(f, yy) > target)
2259 break;
2261 c = yy;
2265 if (y == null) {
2266 y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */
2268 if (flags & TOP_CODE) {
2269 if (h < accent_base_height(f)) {
2270 delta = h;
2271 } else {
2272 delta = accent_base_height(f);
2274 } else if (flags & OVERLAY_CODE) {
2275 delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */
2276 } else {
2277 delta = 0; /* hm */
2279 if ((supscr(q) != null) || (subscr(q) != null)) {
2280 if (type(nucleus(q)) == math_char_node) {
2281 /* swap the subscript and superscript into box |x| */
2282 flush_node_list(x);
2283 x = new_noad();
2284 r = math_clone(nucleus(q));
2285 nucleus(x) = r;
2286 supscr(x) = supscr(q);
2287 supscr(q) = null;
2288 subscr(x) = subscr(q);
2289 subscr(q) = null;
2290 type(nucleus(q)) = sub_mlist_node;
2291 math_list(nucleus(q)) = x;
2292 x = clean_box(nucleus(q), cur_style, cur_style);
2293 delta = delta + height(x) - h;
2294 h = height(x);
2296 } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) {
2297 /* only pure math char nodes */
2298 internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size);
2299 if (do_new_math(f)) {
2300 ic = char_italic(f,math_character(nucleus(q)));
2303 /* the top accents of both characters are aligned */
2304 if (s_is_absolute) {
2305 scaled sa;
2306 if (ext != NULL) {
2307 /* if the accent is extensible just take the center */
2308 sa = half(width(y));
2309 } else {
2311 there is no bot_accent so let's assume similarity
2313 if (flags & BOT_CODE) {
2314 sa = char_bot_accent(f, c);
2315 } else {
2316 sa = char_top_accent(f, c);
2319 sa = char_top_accent(f, c);
2321 if (sa == INT_MIN) {
2322 /* just take the center */
2323 sa = half(width(y));
2325 if (math_direction_par == dir_TRT) {
2326 shift_amount(y) = s + sa - width(y);
2327 } else {
2328 shift_amount(y) = s - sa;
2330 } else {
2331 if (width(y)== 0) {
2332 shift_amount(y) = s + w;
2333 } else if (math_direction_par == dir_TRT) {
2334 shift_amount(y) = s + width(y); /* ok? */
2335 } else {
2336 shift_amount(y) = s + half(w - width(y));
2339 width(y) = 0;
2340 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2341 p = new_kern(-delta);
2342 reset_attributes(p, node_attr(q));
2343 couple_nodes(p,x);
2344 couple_nodes(y,p);
2345 } else {
2346 couple_nodes(x,y);
2347 y = x;
2349 r = vpackage(y, 0, additional, max_dimen, math_direction_par);
2350 reset_attributes(r, node_attr(q));
2351 width(r) = width(x);
2352 y = r;
2353 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2354 if (height(y) < h) {
2355 /* make the height of box |y| equal to |h| */
2356 p = new_kern(h - height(y));
2357 reset_attributes(p, node_attr(q));
2358 try_couple_nodes(p,list_ptr(y));
2359 list_ptr(y) = p;
2360 height(y) = h;
2362 } else {
2363 shift_amount(y) = -(h - height(y));
2365 if (ic != 0) {
2366 /* old font codepath has ic built in, new font code doesn't */
2367 width(r) += ic ;
2369 math_list(nucleus(q)) = y;
2370 type(nucleus(q)) = sub_box_node;
2373 static void make_math_accent(pointer q, int cur_style)
2375 int topstretch = !(subtype(q) % 2);
2376 int botstretch = !(subtype(q) / 2);
2378 if (top_accent_chr(q) != null) {
2379 fetch(top_accent_chr(q));
2380 if (char_exists(cur_f, cur_c)) {
2381 do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2383 flush_node(top_accent_chr(q));
2384 top_accent_chr(q) = null;
2386 if (bot_accent_chr(q) != null) {
2387 fetch(bot_accent_chr(q));
2388 if (char_exists(cur_f, cur_c)) {
2389 do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2391 flush_node(bot_accent_chr(q));
2392 bot_accent_chr(q) = null;
2394 if (overlay_accent_chr(q) != null) {
2395 fetch(overlay_accent_chr(q));
2396 if (char_exists(cur_f, cur_c)) {
2397 do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
2399 flush_node(overlay_accent_chr(q));
2400 overlay_accent_chr(q) = null;
2404 @ The |make_fraction| procedure is a bit different because it sets
2405 |new_hlist(q)| directly rather than making a sub-box.
2408 static void make_fraction(pointer q, int cur_style)
2410 pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */
2411 scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2;
2412 /* dimensions for box calculations */
2413 if (thickness(q) == default_code)
2414 thickness(q) = fraction_rule(cur_style);
2416 Create equal-width boxes |x| and |z| for the numerator and denominator,
2417 and compute the default amounts |shift_up| and |shift_down| by which they
2418 are displaced from the baseline
2421 x = clean_box(numerator(q), num_style(cur_style), cur_style);
2422 z = clean_box(denominator(q), denom_style(cur_style), cur_style);
2424 if (middle_delimiter(q) != null) {
2425 delta = 0;
2426 m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2427 middle_delimiter(q) = null;
2428 } else {
2429 m = null ;
2430 if (width(x) < width(z)) {
2431 x = rebox(x, width(z));
2432 } else {
2433 z = rebox(z, width(x));
2437 if (m != null) {
2438 shift_up = 0;
2439 shift_down = 0;
2440 } else if (thickness(q) == 0) {
2441 shift_up = stack_num_up(cur_style);
2442 shift_down = stack_denom_down(cur_style);
2444 the numerator and denominator must be separated by a certain minimum
2445 clearance, called |clr| in the following program. The difference between
2446 |clr| and the actual clearance is |2delta|.
2448 clr1 = stack_vgap(cur_style);
2449 delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
2450 if (delta > 0) {
2451 shift_up = shift_up + delta;
2452 shift_down = shift_down + delta;
2454 } else {
2455 shift_up = fraction_num_up(cur_style);
2456 shift_down = fraction_denom_down(cur_style);
2458 in the case of a fraction line, the minimum clearance depends on the actual
2459 thickness of the line.
2461 clr1 = fraction_num_vgap(cur_style);
2462 clr2 = fraction_denom_vgap(cur_style);
2463 delta = half(thickness(q));
2464 if (fractionexact(q)) {
2465 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta));
2466 delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
2467 } else {
2468 clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
2469 clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
2470 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta));
2471 delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
2473 if (delta1 > 0) {
2474 shift_up = shift_up + delta1;
2476 if (delta2 > 0) {
2477 shift_down = shift_down + delta2;
2480 if (m != null) {
2482 construct a hlist box for the fraction, according to |hgap| and |vgap|
2484 shift_up = skewed_fraction_vgap(cur_style);
2486 if (!fractionnoaxis(q)) {
2487 shift_up += half(math_axis_size(cur_size));
2490 shift_down = shift_up;
2491 v = new_null_box();
2492 reset_attributes(v, node_attr(q));
2493 type(v) = hlist_node;
2494 list_ptr(v) = x;
2495 width(v) = width(x);
2496 height(v) = height(x) + shift_up;
2497 depth(v) = depth(x);
2498 shift_amount(v) = - shift_up;
2499 x = v;
2501 v = new_null_box();
2502 reset_attributes(v, node_attr(q));
2503 type(v) = hlist_node;
2504 list_ptr(v) = z;
2505 width(v) = width(z);
2506 height(v) = height(z);
2507 depth(v) = depth(z) + shift_down;
2508 shift_amount(v) = shift_down;
2509 z = v;
2511 v = new_null_box();
2512 reset_attributes(v, node_attr(q));
2513 type(v) = hlist_node;
2514 if (height(x) > height(z)) {
2515 height(v) = height(x);
2516 } else {
2517 height(v) = height(z);
2519 if (depth(x) > depth(z)) {
2520 depth(v) = depth(x);
2521 } else {
2522 depth(v) = depth(z);
2524 if (height(m) > height(v)) {
2525 height(v) = height(m);
2527 if (depth(m) > depth(v)) {
2528 depth(v) = depth(m);
2531 if (fractionexact(q)) {
2532 delta1 = -half(skewed_fraction_hgap(cur_style));
2533 delta2 = delta1;
2534 width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
2535 } else {
2536 delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
2537 delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
2538 width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
2539 width(m) = 0;
2542 p1 = new_kern(delta1);
2543 reset_attributes(p1, node_attr(q));
2544 p2 = new_kern(delta2);
2545 reset_attributes(p2, node_attr(q));
2547 couple_nodes(x,p1);
2548 couple_nodes(p1,m);
2549 couple_nodes(m,p2);
2550 couple_nodes(p2,z);
2552 list_ptr(v) = x;
2553 } else {
2555 construct a vlist box for the fraction, according to |shift_up| and |shift_down|
2557 v = new_null_box();
2558 type(v) = vlist_node;
2559 height(v) = shift_up + height(x);
2560 depth(v) = depth(z) + shift_down;
2561 width(v) = width(x); /* this also equals |width(z)| */
2562 reset_attributes(v, node_attr(q));
2563 if (thickness(q) == 0) {
2564 p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
2565 couple_nodes(p,z);
2566 } else {
2567 y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, math_rules_fam_par);
2568 p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down));
2569 reset_attributes(p, node_attr(q));
2570 couple_nodes(y,p);
2571 couple_nodes(p,z);
2572 p = new_kern((shift_up - depth(x)) - (math_axis_size(cur_size) + delta));
2573 couple_nodes(p,y);
2575 reset_attributes(p, node_attr(q));
2576 couple_nodes(x,p);
2577 list_ptr(v) = x;
2580 put the fraction into a box with its delimiters, and make |new_hlist(q)|
2581 point to it
2583 if (do_new_math(cur_f)) {
2584 if (math_use_old_fraction_scaling_par) {
2585 delta = fraction_del_size_old(cur_style);
2586 } else {
2587 delta = fraction_del_size_new(cur_style);
2589 if (delta == undefined_math_parameter) {
2590 delta = get_delimiter_height(depth(v), height(v), true);
2592 } else {
2593 delta = fraction_del_size_old(cur_style);
2595 l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2596 left_delimiter(q) = null;
2597 r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2598 right_delimiter(q) = null;
2599 couple_nodes(l,v);
2600 couple_nodes(v,r);
2601 y = hpack(l, 0, additional, -1);
2602 reset_attributes(y, node_attr(q));
2603 assign_new_hlist(q, y);
2606 @ If the nucleus of an |op_noad| is a single character, it is to be
2607 centered vertically with respect to the axis, after first being enlarged
2608 (via a character list in the font) if we are in display style. The normal
2609 convention for placing displayed limits is to put them above and below the
2610 operator in display style.
2612 The italic correction is removed from the character if there is a subscript
2613 and the limits are not being displayed. The |make_op| routine returns the
2614 value that should be used as an offset between subscript and superscript.
2616 After |make_op| has acted, |subtype(q)| will be |limits| if and only if
2617 the limits have been set above and below the operator. In that case,
2618 |new_hlist(q)| will already contain the desired final box.
2621 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
2622 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style);
2624 static scaled make_op(pointer q, int cur_style)
2626 scaled delta = 0; /* offset between subscript and superscript */
2627 scaled dummy = 0;
2628 pointer p, v, x, y, z, n; /* temporary registers for box construction */
2629 int c; /* register for character examination */
2630 scaled shift_up, shift_down; /* dimensions for box calculation */
2631 boolean axis_shift = false;
2632 scaled ok_size;
2633 if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
2634 subtype(q) = op_noad_type_limits;
2636 if (type(nucleus(q)) == math_char_node) {
2637 fetch(nucleus(q));
2638 if (cur_style < text_style) {
2639 /* try to make it larger */
2640 ok_size = minimum_operator_size(cur_style);
2641 if (ok_size != undefined_math_parameter) {
2642 /* creating a temporary delimiter is the cleanest way */
2643 y = new_node(delim_node, 0);
2644 reset_attributes(y, node_attr(q));
2645 small_fam(y) = math_fam(nucleus(q));
2646 small_char(y) = math_character(nucleus(q));
2647 x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta);
2648 if (do_new_math(cur_f)) {
2649 /* we never added italic correction */
2650 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2651 /* remove italic correction */
2652 width(x) -= delta;
2654 } else {
2655 ok_size = height_plus_depth(cur_f, cur_c) + 1;
2656 while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
2657 c = char_remainder(cur_f, cur_c);
2658 if (!char_exists(cur_f, c))
2659 break;
2660 cur_c = c;
2661 math_character(nucleus(q)) = c;
2663 delta = char_italic(cur_f, cur_c);
2664 x = clean_box(nucleus(q), cur_style, cur_style);
2665 if (delta != 0) {
2666 if (do_new_math(cur_f)) {
2667 /* we never added italic correction */
2668 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2669 /* remove italic correction */
2670 width(x) = width(x) - delta;
2673 axis_shift = true;
2675 } else {
2676 /* normal size */
2677 delta = char_italic(cur_f, cur_c);
2678 x = clean_box(nucleus(q), cur_style, cur_style);
2679 if (delta != 0) {
2680 if (do_new_math(cur_f)) {
2681 /* we never added italic correction */
2682 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2683 /* remove italic correction */
2684 width(x) = width(x) - delta;
2687 axis_shift = true;
2689 if (axis_shift) {
2690 /* center vertically */
2691 shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size);
2693 type(nucleus(q)) = sub_box_node;
2694 math_list(nucleus(q)) = x;
2697 /* we now handle op_nod_type_no_limits here too */
2699 if (subtype(q) == op_noad_type_no_limits) {
2700 if (do_new_math(cur_f)) {
2702 if (delta != 0) {
2703 delta = half(delta) ;
2706 p = check_nucleus_complexity(q, &dummy, cur_style);
2707 if ((subscr(q) == null) && (supscr(q) == null)) {
2708 assign_new_hlist(q, p);
2709 } else {
2711 make_scripts(q, p, 0, cur_style, delta, -delta);
2713 int mode = nolimits_mode_par; /* wins */
2715 for easy configuration ... fonts are somewhat inconsistent and the
2716 values for italic correction run from 30 to 60% of the width
2718 switch (mode) {
2719 case 0 :
2720 /* full bottom correction */
2721 make_scripts(q, p, 0, cur_style, 0, -delta);
2722 break;
2723 case 1 :
2724 /* MathConstants driven */
2725 make_scripts(q, p, 0, cur_style,
2726 round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000),
2727 -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000));
2728 case 2 :
2729 /* no correction */
2730 make_scripts(q, p, 0, cur_style, 0, 0);
2731 break ;
2732 case 3 :
2733 /* half bottom correction */
2734 make_scripts(q, p, 0, cur_style, 0, -half(delta));
2735 break;
2736 case 4 :
2737 /* half bottom and top correction */
2738 make_scripts(q, p, 0, cur_style, half(delta), -half(delta));
2739 break;
2740 default :
2741 if (mode > 15) {
2742 /* for quickly testing values */
2743 make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000));
2744 } else {
2745 make_scripts(q, p, 0, cur_style, 0, 0);
2747 break;
2750 delta = 0;
2751 } else {
2752 /* similar code then the caller (before CHECK_DIMENSIONS) */
2753 p = check_nucleus_complexity(q, &delta, cur_style);
2754 if ((subscr(q) == null) && (supscr(q) == null)) {
2755 assign_new_hlist(q, p);
2756 } else {
2757 make_scripts(q, p, delta, cur_style, 0, 0);
2760 } else if (subtype(q) == op_noad_type_limits) {
2761 /* The following program builds a vlist box |v| for displayed limits. The
2762 width of the box is not affected by the fact that the limits may be skewed. */
2763 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2764 y = clean_box(nucleus(q), cur_style, cur_style);
2765 z = clean_box(subscr(q), sub_style(cur_style), cur_style);
2766 v = new_null_box();
2767 reset_attributes(v, node_attr(q));
2768 type(v) = vlist_node;
2769 if (do_new_math(cur_f)) {
2770 n = null;
2771 if (! math_no_italic_compensation_par) {
2772 n = nucleus(q);
2773 if (n != null) {
2774 if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
2775 n = math_list(n);
2776 if (n != null) {
2777 if (type(n) == hlist_node) {
2778 n = list_ptr(n); /* just a not scaled char */
2779 while (n != null) {
2780 if (type(n) == glyph_node) {
2781 delta = char_italic(font(n),character(n));
2783 n = vlink(n);
2785 } else {
2786 while (n != null) {
2787 if (type(n) == fence_noad) {
2788 if (delimiteritalic(n) > delta) {
2789 /* we can have dummies, the period ones */
2790 delta = delimiteritalic(n);
2793 n = vlink(n);
2797 } else {
2798 n = nucleus(q);
2799 if (type(n) == math_char_node) {
2800 delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
2806 width(v) = width(y);
2807 if (width(x) > width(v))
2808 width(v) = width(x);
2809 if (width(z) > width(v))
2810 width(v) = width(z);
2811 x = rebox(x, width(v));
2812 y = rebox(y, width(v));
2813 z = rebox(z, width(v));
2814 shift_amount(x) = half(delta);
2815 shift_amount(z) = -shift_amount(x);
2816 /* v is the still empty target */
2817 height(v) = height(y);
2818 depth(v) = depth(y);
2820 attach the limits to |y| and adjust |height(v)|, |depth(v)| to
2821 account for their presence
2823 we use |shift_up| and |shift_down| in the following program for the
2824 amount of glue between the displayed operator |y| and its limits |x| and
2827 the vlist inside box |v| will consist of |x| followed by |y| followed
2828 by |z|, with kern nodes for the spaces between and around them
2830 b: baseline v: minumum gap
2833 if (supscr(q) == null) {
2834 list_ptr(x) = null;
2835 flush_node(x);
2836 list_ptr(v) = y;
2837 } else {
2838 shift_up = limit_above_bgap(cur_style) - depth(x);
2839 if (shift_up < limit_above_vgap(cur_style))
2840 shift_up = limit_above_vgap(cur_style);
2841 p = new_kern(shift_up);
2842 reset_attributes(p, node_attr(q));
2843 couple_nodes(p,y);
2844 couple_nodes(x,p);
2845 p = new_kern(limit_above_kern(cur_style));
2846 reset_attributes(p, node_attr(q));
2847 couple_nodes(p,x);
2848 list_ptr(v) = p;
2849 height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
2851 if (subscr(q) == null) {
2852 list_ptr(z) = null;
2853 flush_node(z);
2854 } else {
2855 shift_down = limit_below_bgap(cur_style) - height(z);
2856 if (shift_down < limit_below_vgap(cur_style))
2857 shift_down = limit_below_vgap(cur_style);
2858 if (shift_down > 0) {
2859 p = new_kern(shift_down);
2860 reset_attributes(p, node_attr(q));
2861 couple_nodes(y,p);
2862 couple_nodes(p,z);
2864 p = new_kern(limit_below_kern(cur_style));
2865 reset_attributes(p, node_attr(q));
2866 couple_nodes(z,p);
2867 depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
2869 if (subscr(q) != null) {
2870 math_list(subscr(q)) = null;
2871 flush_node(subscr(q));
2872 subscr(q) = null;
2874 if (supscr(q) != null) {
2875 math_list(supscr(q)) = null;
2876 flush_node(supscr(q));
2877 supscr(q) = null;
2879 assign_new_hlist(q, v);
2880 if (do_new_math(cur_f)) {
2881 delta = 0;
2884 return delta;
2887 @ A ligature found in a math formula does not create a ligature, because
2888 there is no question of hyphenation afterwards; the ligature will simply be
2889 stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
2891 The |type| is converted to |math_text_char| here if we would not want to
2892 apply an italic correction to the current character unless it belongs
2893 to a math font (i.e., a font with |space=0|).
2895 No boundary characters enter into these ligatures.
2898 #define simple_char_noad(p) (\
2899 (p != null) && \
2900 (type(p) == simple_noad) && \
2901 (subtype(p) <= punct_noad_type) && \
2902 (type(nucleus(p)) == math_char_node) \
2905 #define same_nucleus_fam(p,q) \
2906 (math_fam(nucleus(p)) == math_fam(nucleus(q)))
2908 static void make_ord(pointer q)
2910 int a; /* the left-side character for lig/kern testing */
2911 pointer p, r, s; /* temporary registers for list manipulation */
2912 scaled k; /* a kern */
2913 liginfo lig; /* a ligature */
2914 RESTART:
2915 if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
2916 p = vlink(q);
2917 if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
2918 type(nucleus(q)) = math_text_char_node;
2919 fetch(nucleus(q));
2920 a = cur_c;
2921 /* add italic correction */
2922 if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
2923 p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
2924 reset_attributes(p, node_attr(q));
2925 couple_nodes(p,vlink(q));
2926 couple_nodes(q,p);
2927 return;
2929 /* construct ligatures, quite unlikely in new math fonts */
2930 if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
2931 cur_c = math_character(nucleus(p));
2933 if character |a| has a kern with |cur_c|, attach the kern after~|q|; or if
2934 it has a ligature with |cur_c|, combine noads |q| and~|p| appropriately;
2935 then |return| if the cursor has moved past a noad, or |goto restart|
2937 note that a ligature between an |ord_noad| and another kind of noad
2938 is replaced by an |ord_noad|, when the two noads collapse into one
2940 we could make a parenthesis (say) change shape when it follows
2941 certain letters. Presumably a font designer will define such
2942 ligatures only when this convention makes sense
2945 if (disable_lig_par == 0 && has_lig(cur_f, a)) {
2946 lig = get_ligature(cur_f, a, cur_c);
2947 if (is_valid_ligature(lig)) {
2948 check_interrupt(); /* allow a way out of infinite ligature loop */
2949 switch (lig_type(lig)) {
2950 case 1:
2951 /* \.{=:\char`\|} */
2952 case 5:
2953 /* \.{=:\char`\|>} */
2954 math_character(nucleus(q)) = lig_replacement(lig);
2955 break;
2956 case 2:
2957 /* \.{\char`\|=:} */
2958 case 6:
2959 /* \.{\char`\|=:>} */
2960 math_character(nucleus(p)) = lig_replacement(lig);
2961 break;
2962 case 3:
2963 /* \.{\char`\|=:\char`\|} */
2964 case 7:
2965 /* \.{\char`\|=:\char`\|>} */
2966 case 11:
2967 /* \.{\char`\|=:\char`\|>>} */
2968 r = new_noad();
2969 reset_attributes(r, node_attr(q));
2970 s = new_node(math_char_node, 0);
2971 reset_attributes(s, node_attr(q));
2972 nucleus(r) = s;
2973 math_character(nucleus(r)) = lig_replacement(lig);
2974 math_fam(nucleus(r)) = math_fam(nucleus(q));
2975 couple_nodes(q,r);
2976 couple_nodes(r,p);
2977 if (lig_type(lig) < 11)
2978 type(nucleus(r)) = math_char_node;
2979 else
2980 /* prevent combination */
2981 type(nucleus(r)) = math_text_char_node;
2982 break;
2983 default:
2984 try_couple_nodes(q,vlink(p));
2985 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
2986 s = math_clone(subscr(p));
2987 subscr(q) = s;
2988 s = math_clone(supscr(p));
2989 supscr(q) = s;
2990 math_reset(subscr(p)); /* just in case */
2991 math_reset(supscr(p));
2992 flush_node(p);
2993 break;
2995 if (lig_type(lig) > 3)
2996 return;
2997 type(nucleus(q)) = math_char_node;
2998 goto RESTART;
3001 if (disable_kern_par == 0 && has_kern(cur_f, a)) {
3002 /* todo: should this use mathkerns? */
3003 k = get_kern(cur_f, a, cur_c);
3004 if (k != 0) {
3005 p = new_kern(k);
3006 reset_attributes(p, node_attr(q));
3007 couple_nodes(p,vlink(q));
3008 couple_nodes(q,p);
3009 return;
3017 @ If the fonts for the left and right bits of a mathkern are not
3018 both new-style fonts, then return a sentinel value meaning:
3019 please use old-style italic correction placement
3022 #define MATH_KERN_NOT_FOUND 0x7FFFFFFF
3024 @ This function tries to find the kern needed for proper cut-ins.
3025 The left side doesn't move, but the right side does, so the first
3026 order of business is to create a staggered fence line on the
3027 left side of the right character.
3029 The microsoft spec says that there are four quadrants, but the
3030 actual images say
3033 static scaled math_kern_at(internal_font_number f, int c, int side, int v)
3035 int h, k, numkerns;
3036 scaled *kerns_heights;
3037 scaled kern = 0;
3038 charinfo *co = char_info(f, c); /* known to exist */
3039 numkerns = get_charinfo_math_kerns(co, side);
3040 #ifdef DEBUG
3041 fprintf(stderr, " entries = %d, height = %d\n", numkerns, v);
3042 #endif
3043 if (numkerns == 0)
3044 return kern;
3045 if (side == top_left_kern) {
3046 kerns_heights = co->top_left_math_kern_array;
3047 } else if (side == bottom_left_kern) {
3048 kerns_heights = co->bottom_left_math_kern_array;
3049 } else if (side == top_right_kern) {
3050 kerns_heights = co->top_right_math_kern_array;
3051 } else if (side == bottom_right_kern) {
3052 kerns_heights = co->bottom_right_math_kern_array;
3053 } else {
3054 confusion("math_kern_at");
3055 kerns_heights = NULL; /* not reached */
3057 #ifdef DEBUG
3058 fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
3059 #endif
3060 if (v < kerns_heights[0])
3061 return kerns_heights[1];
3062 for (k = 0; k < numkerns; k++) {
3063 h = kerns_heights[(k * 2)];
3064 kern = kerns_heights[(k * 2) + 1];
3065 #ifdef DEBUG
3066 if (k > 0)
3067 fprintf(stderr, " entry %d: %d,%d\n", k, h, kern);
3068 #endif
3069 if (h > v) {
3070 return kern;
3073 return kern;
3076 @ @c
3077 static scaled find_math_kern(internal_font_number l_f, int l_c,
3078 internal_font_number r_f, int r_c,
3079 int cmd, scaled shift)
3081 scaled corr_height_top = 0, corr_height_bot = 0;
3082 scaled krn_l = 0, krn_r = 0, krn = 0;
3083 if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
3084 return MATH_KERN_NOT_FOUND;
3086 if (cmd == sup_mark_cmd) {
3087 corr_height_top = char_height(l_f, l_c);
3088 corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
3089 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
3090 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
3091 #ifdef DEBUG
3092 fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift);
3093 #endif
3094 krn = (krn_l + krn_r);
3095 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
3096 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
3097 #ifdef DEBUG
3098 fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
3099 #endif
3100 if ((krn_l + krn_r) < krn)
3101 krn = (krn_l + krn_r);
3102 return (krn);
3104 } else if (cmd == sub_mark_cmd) {
3105 corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
3106 corr_height_bot = -char_depth(l_f, l_c);
3107 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
3108 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
3109 #ifdef DEBUG
3110 fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
3111 #endif
3112 krn = (krn_l + krn_r);
3113 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
3114 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
3115 #ifdef DEBUG
3116 fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
3117 #endif
3118 if ((krn_l + krn_r) < krn)
3119 krn = (krn_l + krn_r);
3120 return (krn);
3122 } else {
3123 confusion("find_math_kern");
3125 return 0; /* not reached */
3128 @ just a small helper
3130 static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
3132 pointer y;
3133 pointer z = new_kern(delta2);
3134 reset_attributes(z, node_attr(q));
3135 if (new_hlist(q) == null) {
3136 /* this is somewhat weird */
3137 new_hlist(q) = z;
3138 } else {
3139 y = new_hlist(q);
3140 while (vlink(y) != null)
3141 y = vlink(y);
3142 couple_nodes(y,z);
3144 return new_hlist(q);
3149 #ifdef DEBUG
3150 void dump_simple_field(pointer q)
3152 pointer p;
3153 printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q));
3154 switch (type(q)) {
3155 case math_char_node:
3156 printf("mathchar ");
3157 break;
3158 case math_text_char_node:
3159 printf("texchar ");
3160 break;
3161 case sub_box_node:
3162 printf("box ");
3163 break;
3164 case sub_mlist_node:
3165 printf("mlist ");
3166 p = math_list(q);
3167 while (p != null) {
3168 dump_simple_field(p);
3169 p = vlink(p);
3171 break;
3175 void dump_simple_node(pointer q)
3177 printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
3178 printf("nucleus: ");
3179 dump_simple_field(nucleus(q));
3180 printf("\n");
3181 printf("sub: ");
3182 dump_simple_field(subscr(q));
3183 printf("\n");
3184 printf("sup: ");
3185 dump_simple_field(supscr(q));
3186 printf("\n\n");
3188 #endif
3190 @ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
3191 superscript of noad |q| to the list that starts at |new_hlist(q)|,
3192 given that subscript and superscript aren't both empty. The superscript
3193 will be horizontally shifted over |delta1|, the subscript over |delta2|.
3195 We set |shift_down| and |shift_up| to the minimum amounts to shift the
3196 baseline of subscripts and superscripts based on the given nucleus.
3198 Note: We need to look at a character but also at the first one in a sub list
3199 and there we ignore leading kerns and glue. Elsewhere is code that removes
3200 kerns assuming that is italic correction. The heuristics are unreliable for
3201 the new fonts so eventualy there will be an option to ignore such corrections.
3203 @ @c
3204 #define analyze_script(init,su_n,su_f,su_c) do { \
3205 su_n = init; \
3206 if (su_n != null) { \
3207 if (type(su_n) == sub_mlist_node && math_list(su_n)) { \
3208 su_n = math_list(su_n); \
3209 if (su_n != null) { \
3210 while (su_n) { \
3211 if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\
3212 su_n = vlink(su_n); \
3213 } else if (type(su_n) == simple_noad) { \
3214 su_n = nucleus(su_n); \
3215 if (type(su_n) != math_char_node) { \
3216 su_n = null; \
3218 break; \
3219 } else { \
3220 su_n = null; \
3221 break; \
3226 if ((su_n != null) && (type(su_n) == math_char_node)) { \
3227 fetch(su_n); \
3228 if (char_exists(cur_f, cur_c)) { \
3229 su_f = cur_f; \
3230 su_c = cur_c; \
3231 } else { \
3232 su_n = null; \
3236 } while (0)
3238 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
3240 pointer x, y, z; /* temporary registers for box construction */
3241 scaled shift_up, shift_down, clr; /* dimensions in the calculation */
3242 scaled delta1, delta2;
3243 halfword sub_n, sup_n;
3244 internal_font_number sub_f, sup_f;
3245 int sub_c, sup_c;
3246 sub_n = null;
3247 sup_n = null;
3248 sub_f = 0;
3249 sup_f = 0;
3250 sub_c = 0;
3251 sup_c = 0;
3252 delta1 = it;
3253 delta2 = 0;
3255 #ifdef DEBUG
3256 printf("it: %d\n", it);
3257 dump_simple_node(q);
3258 printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
3259 #endif
3260 switch (type(nucleus(q))) {
3261 case math_char_node:
3262 case math_text_char_node:
3263 if ((subscr(q) == null) && (delta1 != 0)) {
3264 /* todo: selective */
3265 x = new_kern(delta1); /* italic correction */
3266 reset_attributes(x, node_attr(nucleus(q)));
3267 couple_nodes(p,x);
3268 delta1 = 0;
3271 assign_new_hlist(q, p);
3272 if (is_char_node(p)) {
3273 shift_up = 0;
3274 shift_down = 0;
3275 } else {
3276 z = hpack(p, 0, additional, -1);
3277 shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */
3278 shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
3279 list_ptr(z) = null;
3280 flush_node(z);
3283 if (is_char_node(p)) {
3284 /* we look at the subscript character (_i) or first character in a list (_{ij}) */
3285 analyze_script(subscr(q),sub_n,sub_f,sub_c);
3286 /* we look at the superscript character (^i) or first character in a list (^{ij}) */
3287 analyze_script(supscr(q),sup_n,sup_f,sup_c);
3290 if (supscr(q) == null) {
3292 construct a subscript box |x| when there is no superscript
3294 when there is a subscript without a superscript, the top of the subscript
3295 should not exceed the baseline plus four-fifths of the x-height.
3297 x = clean_box(subscr(q), sub_style(cur_style), cur_style);
3298 width(x) = width(x) + space_after_script(cur_style);
3299 switch (scripts_mode_par) {
3300 case 1:
3301 shift_down = sub_shift_down(cur_style) ;
3302 break;
3303 case 2:
3304 shift_down = sub_sup_shift_down(cur_style) ;
3305 break;
3306 case 3:
3307 shift_down = sub_sup_shift_down(cur_style) ;
3308 break;
3309 case 4:
3310 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3311 break;
3312 case 5:
3313 shift_down = sub_shift_down(cur_style) ;
3314 break;
3315 default:
3316 if (shift_down < sub_shift_down(cur_style))
3317 shift_down = sub_shift_down(cur_style);
3318 clr = height(x) - sub_top_max(cur_style);
3319 if (shift_down < clr)
3320 shift_down = clr;
3321 break;
3323 shift_amount(x) = shift_down;
3325 /* now find and correct for horizontal shift */
3326 if (sub_n != null) {
3327 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3328 if (delta2 == MATH_KERN_NOT_FOUND) {
3329 delta2 = subshift ;
3330 } else {
3331 delta2 = delta2 + subshift ;
3333 } else {
3334 delta2 = subshift ;
3336 if (delta2 != 0) {
3337 p = attach_hkern_to_new_hlist(q, delta2);
3340 } else {
3342 construct a superscript box |x|
3344 the bottom of a superscript should never descend below the baseline plus
3345 one-fourth of the x-height.
3347 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
3348 width(x) = width(x) + space_after_script(cur_style);
3349 switch (scripts_mode_par) {
3350 case 1:
3351 shift_up = sup_shift_up(cur_style);
3352 break;
3353 case 2:
3354 shift_up = sup_shift_up(cur_style) ;
3355 break;
3356 case 3:
3357 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
3358 break;
3359 case 4:
3360 shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3361 break;
3362 case 5:
3363 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
3364 break;
3365 default:
3366 clr = sup_shift_up(cur_style);
3367 if (shift_up < clr)
3368 shift_up = clr;
3369 clr = depth(x) + sup_bottom_min(cur_style);
3370 if (shift_up < clr)
3371 shift_up = clr;
3372 break;
3374 if (subscr(q) == null) {
3375 shift_amount(x) = -shift_up;
3376 /* now find and correct for horizontal shift */
3377 if (sup_n != null) {
3378 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3379 if (clr == MATH_KERN_NOT_FOUND) {
3380 clr = supshift ;
3381 } else {
3382 clr = clr + supshift ;
3384 } else {
3385 clr = supshift;
3387 if (clr != 0) {
3388 p = attach_hkern_to_new_hlist(q, clr);
3390 } else {
3392 construct a sub/superscript combination box |x|, with the superscript offset
3393 by |delta|
3395 when both subscript and superscript are present, the subscript must be
3396 separated from the superscript by at least four times |default_rule_thickness|
3398 if this condition would be violated, the subscript moves down, after which
3399 both subscript and superscript move up so that the bottom of the superscript
3400 is at least as high as the baseline plus four-fifths of the x-height
3402 y = clean_box(subscr(q), sub_style(cur_style), cur_style);
3403 width(y) = width(y) + space_after_script(cur_style);
3404 switch (scripts_mode_par) {
3405 case 1:
3406 shift_down = sub_shift_down(cur_style) ;
3407 break;
3408 case 2:
3409 shift_down = sub_sup_shift_down(cur_style) ;
3410 break;
3411 case 3:
3412 shift_down = sub_sup_shift_down(cur_style) ;
3413 break;
3414 case 4:
3415 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3416 break;
3417 case 5:
3418 shift_down = sub_shift_down(cur_style) ;
3419 break;
3420 default:
3421 if (shift_down < sub_sup_shift_down(cur_style))
3422 shift_down = sub_sup_shift_down(cur_style);
3423 clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
3424 if (clr > 0) {
3425 shift_down = shift_down + clr;
3426 clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
3427 if (clr > 0) {
3428 shift_up = shift_up + clr;
3429 shift_down = shift_down - clr;
3432 break;
3434 /* now find and correct for horizontal shift */
3435 if (sub_n != null) {
3436 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3437 if (delta2 == MATH_KERN_NOT_FOUND) {
3438 delta2 = subshift ;
3439 } else {
3440 delta2 = delta2 + subshift ;
3442 } else {
3443 delta2 = subshift ;
3445 if (delta2 != 0) {
3446 p = attach_hkern_to_new_hlist(q, delta2);
3449 now the horizontal shift for the superscript; the superscript is also to be shifted
3450 by |delta1| (the italic correction)
3452 clr = MATH_KERN_NOT_FOUND;
3453 if (sup_n != null) {
3454 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3457 /* delta can already have been applied and now be 0 */
3458 if (delta2 == MATH_KERN_NOT_FOUND)
3459 delta2 = - supshift ;
3460 else
3461 delta2 = delta2 - supshift ;
3462 if (clr != MATH_KERN_NOT_FOUND) {
3463 shift_amount(x) = clr + delta1 - delta2;
3464 } else {
3465 shift_amount(x) = delta1 - delta2;
3467 /* todo: only if kern != 0 */
3468 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
3469 reset_attributes(p, node_attr(q));
3470 couple_nodes(x,p);
3471 couple_nodes(p,y);
3472 /* we end up with funny dimensions */
3473 x = vpackage(x, 0, additional, max_dimen, math_direction_par);
3474 reset_attributes(x, node_attr(q));
3475 shift_amount(x) = shift_down;
3479 if (new_hlist(q) == null) {
3480 new_hlist(q) = x;
3481 } else {
3482 p = new_hlist(q);
3483 while (vlink(p) != null)
3484 p = vlink(p);
3485 couple_nodes(p,x);
3487 if (subscr(q) != null) {
3488 math_list(subscr(q)) = null;
3489 flush_node(subscr(q));
3490 subscr(q) = null;
3492 if (supscr(q) != null) {
3493 math_list(supscr(q)) = null;
3494 flush_node(supscr(q));
3495 supscr(q) = null;
3499 @ The |make_left_right| function constructs a left or right delimiter of
3500 the required size and returns the value |open_noad| or |close_noad|. The
3501 |left_noad_side| and |right_noad_side| will both be based on the original |style|,
3502 so they will have consistent sizes.
3505 static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
3507 scaled delta;
3508 pointer tmp, lst;
3509 scaled hd_asked = 0;
3510 scaled ic = 0;
3511 boolean stack = false;
3512 boolean axis = false;
3514 scaled hd_done = 0;
3515 boolean fitting = true;
3516 boolean fence = false;
3517 int chr = 0;
3518 int cls = 0;
3520 setup_cur_size(style);
3522 if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
3524 hd_asked = delimiterheight(q) + delimiterdepth(q);
3525 tmp = do_delimiter(q, delimiter(q), cur_size, hd_asked, false, style, false, &stack, &ic);
3526 delimiteritalic(q) = ic;
3528 /* beware, a stacked delimiter has a shift but no corrected height/depth (yet) */
3530 if (stack) {
3531 shift_amount(tmp) = delimiterdepth(q);
3535 hd_done = height(tmp) + depth(tmp);
3536 fitting = stack || ((hd_done-hd_asked) == 0);
3538 if (type(delimiter(q)) == delim_node && (small_char(delimiter(q)) != 0)) {
3539 chr = small_char(delimiter(q));
3540 cls = get_math_code(chr).class_value ;
3541 fence = (cls == 4) || (cls == 5) ;
3544 printf("delimiter stack %i fence %i fitting %i\n",stack,fence,fitting);
3547 if (delimiterexact(q)) {
3548 delimiterheight(q) = height(tmp) - shift_amount(tmp);
3549 delimiterdepth(q) = depth(tmp) + shift_amount(tmp);
3551 if (delimiteraxis(q)) {
3552 delimiterheight(q) += math_axis_size(cur_size);
3553 delimiterdepth(q) -= math_axis_size(cur_size);
3554 shift_amount(tmp) -= math_axis_size(cur_size);
3556 lst = new_node(hlist_node,0);
3557 reset_attributes(lst, node_attr(q));
3558 box_dir(lst) = dir_TLT ;
3559 height(lst) = delimiterheight(q);
3560 depth(lst) = delimiterdepth(q);
3561 width(lst) = width(tmp);
3562 list_ptr(lst) = tmp;
3563 tmp = lst ;
3564 } else {
3565 axis = ! delimiternoaxis(q);
3566 delta = get_delimiter_height(max_d,max_h,axis);
3567 tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic);
3568 delimiteritalic(q) = ic;
3570 delimiter(q) = null;
3571 assign_new_hlist(q, tmp);
3572 if (delimiterclass(q) >= ord_noad_type) {
3573 if (delimiterclass(q) <= inner_noad_type) {
3574 return delimiterclass(q);
3575 } else {
3576 return ord_noad_type;
3578 } else if (subtype(q) == no_noad_side) {
3579 return open_noad_type;
3580 } else if (subtype(q) == left_noad_side) {
3581 return open_noad_type;
3582 } else {
3583 return close_noad_type;
3587 @ @c
3588 #define TEXT_STYLES(A,B) do { \
3589 def_math_param(A,display_style,(B),level_one); \
3590 def_math_param(A,cramped_display_style,(B),level_one); \
3591 def_math_param(A,text_style,(B),level_one); \
3592 def_math_param(A,cramped_text_style,(B),level_one); \
3593 } while (0)
3595 #define SCRIPT_STYLES(A,B) do { \
3596 def_math_param(A,script_style,(B),level_one); \
3597 def_math_param(A,cramped_script_style,(B),level_one); \
3598 def_math_param(A,script_script_style,(B),level_one); \
3599 def_math_param(A,cramped_script_script_style,(B),level_one); \
3600 } while (0)
3602 #define ALL_STYLES(A,B) do { \
3603 TEXT_STYLES(A,(B)); \
3604 SCRIPT_STYLES(A,(B)); \
3605 } while (0)
3607 #define SPLIT_STYLES(A,B,C) do { \
3608 TEXT_STYLES(A,(B)); \
3609 SCRIPT_STYLES(A,(C)); \
3610 } while (0)
3613 void initialize_math_spacing(void)
3615 /* *INDENT-OFF* */
3616 ALL_STYLES (math_param_ord_ord_spacing, 0);
3617 ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code);
3618 SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0);
3619 SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0);
3620 ALL_STYLES (math_param_ord_open_spacing, 0);
3621 ALL_STYLES (math_param_ord_close_spacing, 0);
3622 ALL_STYLES (math_param_ord_punct_spacing, 0);
3623 SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0);
3625 ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code);
3626 ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code);
3627 ALL_STYLES (math_param_op_bin_spacing, 0);
3628 SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0);
3629 ALL_STYLES (math_param_op_open_spacing, 0);
3630 ALL_STYLES (math_param_op_close_spacing, 0);
3631 ALL_STYLES (math_param_op_punct_spacing, 0);
3632 SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0);
3634 SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0);
3635 SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0);
3636 ALL_STYLES (math_param_bin_bin_spacing, 0);
3637 ALL_STYLES (math_param_bin_rel_spacing, 0);
3638 SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0);
3639 ALL_STYLES (math_param_bin_close_spacing, 0);
3640 ALL_STYLES (math_param_bin_punct_spacing, 0);
3641 SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0);
3643 SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0);
3644 SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0);
3645 ALL_STYLES (math_param_rel_bin_spacing, 0);
3646 ALL_STYLES (math_param_rel_rel_spacing, 0);
3647 SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0);
3648 ALL_STYLES (math_param_rel_close_spacing, 0);
3649 ALL_STYLES (math_param_rel_punct_spacing, 0);
3650 SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0);
3652 ALL_STYLES (math_param_open_ord_spacing, 0);
3653 ALL_STYLES (math_param_open_op_spacing, 0);
3654 ALL_STYLES (math_param_open_bin_spacing, 0);
3655 ALL_STYLES (math_param_open_rel_spacing, 0);
3656 ALL_STYLES (math_param_open_open_spacing, 0);
3657 ALL_STYLES (math_param_open_close_spacing, 0);
3658 ALL_STYLES (math_param_open_punct_spacing, 0);
3659 ALL_STYLES (math_param_open_inner_spacing, 0);
3661 ALL_STYLES (math_param_close_ord_spacing, 0);
3662 ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code);
3663 SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0);
3664 SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0);
3665 ALL_STYLES (math_param_close_open_spacing, 0);
3666 ALL_STYLES (math_param_close_close_spacing, 0);
3667 ALL_STYLES (math_param_close_punct_spacing, 0);
3668 SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
3670 SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0);
3671 SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0);
3672 ALL_STYLES (math_param_punct_bin_spacing, 0);
3673 SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0);
3674 SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0);
3675 SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
3676 SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
3677 SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
3679 SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0);
3680 ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code);
3681 SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0);
3682 SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0);
3683 SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0);
3684 ALL_STYLES (math_param_inner_close_spacing, 0);
3685 SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
3686 SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
3687 /* *INDENT-ON* */
3690 @ @c
3691 #define both_types(A,B) ((A)*16+(B))
3693 static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
3695 int x = -1;
3696 pointer z = null;
3697 if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
3698 l_type = op_noad_type_normal;
3699 if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
3700 r_type = op_noad_type_normal;
3701 switch (both_types(l_type, r_type)) {
3702 /* *INDENT-OFF* */
3703 case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
3704 case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
3705 case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
3706 case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
3707 case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
3708 case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
3709 case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
3710 case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
3711 case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
3712 case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
3713 #if 0
3714 case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
3715 #endif
3716 case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
3717 case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
3718 case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
3719 case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
3720 case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
3721 case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
3722 case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
3723 #if 0
3724 case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
3725 case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
3726 #endif
3727 case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
3728 #if 0
3729 case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
3730 case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
3731 #endif
3732 case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
3733 case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
3734 case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
3735 #if 0
3736 case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
3737 #endif
3738 case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
3739 case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
3740 case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
3741 case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
3742 case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
3743 case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
3744 case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
3745 #if 0
3746 case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
3747 #endif
3748 case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
3749 case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
3750 case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
3751 case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
3752 case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
3753 case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
3754 case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
3755 case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
3756 case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
3757 case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
3758 case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
3759 case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
3760 case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
3761 case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
3762 case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
3763 #if 0
3764 case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
3765 #endif
3766 case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
3767 case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
3768 case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
3769 case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
3770 case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
3771 case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
3772 case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
3773 case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
3774 case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
3775 case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
3776 case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
3777 case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
3778 case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
3779 /* *INDENT-ON* */
3781 if (x < 0) {
3782 confusion("mathspacing");
3784 if (x != 0) {
3785 if (x <= thick_mu_skip_code) {
3786 /* trap thin/med/thick settings cf. old TeX */
3787 z = math_glue(glue_par(x), mmu); /* allocates a glue */
3788 /* store a symbolic subtype */
3789 subtype(z) = (quarterword) (x + 1);
3790 } else {
3791 z = math_glue(x, mmu); /* allocates a glue */
3794 return z;
3797 @ @c
3798 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style)
3800 pointer p = null;
3801 switch (type(nucleus(q))) {
3802 case math_char_node:
3803 case math_text_char_node:
3804 fetch(nucleus(q));
3805 if (char_exists(cur_f, cur_c)) {
3806 /* we could look at neighbours */
3807 if (do_new_math(cur_f)) {
3808 *delta = 0 ; /* cf spec only the last one */
3809 } else {
3810 *delta = char_italic(cur_f, cur_c);
3812 p = new_glyph(cur_f, cur_c);
3813 reset_attributes(p, node_attr(nucleus(q)));
3814 if (do_new_math(cur_f)) {
3815 if (! math_no_char_italic_par) {
3816 /* keep italic, but bad with two successive letters */
3817 } else if (get_char_cat_code(cur_c) == 11) {
3818 /* no italic correction in mid-word of text font */
3819 *delta = 0;
3821 } else {
3822 /* no italic correction in mid-word of text font */
3823 if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
3824 *delta = 0;
3827 /* so we only add italic correction when we have no scripts */
3828 if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
3829 pointer x = new_kern(*delta);
3830 reset_attributes(x, node_attr(nucleus(q)));
3831 couple_nodes(p,x);
3832 *delta = 0;
3834 if (do_new_math(cur_f)) {
3835 *delta = char_italic(cur_f, cur_c); /* must be more selective */
3838 break;
3839 case sub_box_node:
3840 p = math_list(nucleus(q));
3841 break;
3842 case sub_mlist_node:
3843 mlist_to_hlist(math_list(nucleus(q)), false, cur_style); /* recursive call */
3844 setup_cur_size(cur_style);
3845 p = hpack(vlink(temp_head), 0, additional, -1);
3846 reset_attributes(p, node_attr(nucleus(q)));
3847 break;
3848 default:
3849 confusion("mlist2"); /* this can't happen mlist2 */
3851 return p;
3854 @ Here is the overall plan of |mlist_to_hlist|, and the list of its
3855 local variables.
3858 void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
3860 pointer q = mlist; /* runs through the mlist */
3861 pointer r = null; /* the most recent noad preceding |q| */
3862 int style = cur_style; /* tuck global parameter away as local variable */
3863 int r_type = simple_noad; /* the |type| of noad |r|, or |op_noad| if |r=null| */
3864 int r_subtype = op_noad_type_normal; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
3865 int t; /* the effective |type| of noad |q| during the second pass */
3866 int t_subtype; /* the effective |subtype| of noad |q| during the second pass */
3867 pointer p = null;
3868 pointer z = null;
3869 int pen; /* a penalty to be inserted */
3870 scaled max_hl = 0; /* maximum height of the list translated so far */
3871 scaled max_d = 0; /* maximum depth of the list translated so far */
3872 scaled delta; /* italic correction offset for subscript and superscript */
3873 scaled cur_mu; /* the math unit width corresponding to |cur_size| */
3874 r_subtype = op_noad_type_normal;
3875 setup_cur_size(cur_style);
3876 cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
3877 while (q != null) {
3879 we use the fact that no character nodes appear in an mlist, hence
3880 the field |type(q)| is always present.
3882 one of the things we must do on the first pass is change a |bin_noad| to
3883 an |ord_noad| if the |bin_noad| is not in the context of a binary operator
3885 the values of |r| and |r_type| make this fairly easy
3887 RESWITCH:
3888 delta = 0;
3889 switch (type(q)) {
3890 case simple_noad:
3891 switch (subtype(q)) {
3892 case bin_noad_type:
3893 switch (r_type) {
3894 case simple_noad:
3895 switch (r_subtype) {
3896 case bin_noad_type:
3897 case op_noad_type_normal:
3898 case op_noad_type_limits:
3899 case op_noad_type_no_limits:
3900 case rel_noad_type:
3901 case open_noad_type:
3902 case punct_noad_type:
3903 subtype(q) = ord_noad_type;
3904 goto RESWITCH;
3905 break;
3907 break;
3908 case fence_noad:
3909 if (r_subtype == left_noad_side) {
3910 subtype(q) = ord_noad_type; /* so these can best be the same size */
3911 goto RESWITCH;
3913 break;
3915 break;
3916 case over_noad_type:
3917 make_over(q, cur_style, cur_size, math_rules_fam_par);
3918 break;
3919 case under_noad_type:
3920 make_under(q, cur_style, cur_size, math_rules_fam_par);
3921 break;
3922 case vcenter_noad_type:
3923 make_vcenter(q);
3924 break;
3925 case rel_noad_type:
3926 case close_noad_type:
3927 case punct_noad_type:
3928 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3929 type(r) = simple_noad; /* assumes the same size .. can't this go */
3930 subtype(r) = ord_noad_type;
3932 break;
3933 case op_noad_type_normal:
3934 case op_noad_type_limits:
3935 case op_noad_type_no_limits:
3936 delta = make_op(q, cur_style);
3937 if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
3938 goto CHECK_DIMENSIONS;
3939 break;
3940 case ord_noad_type:
3941 make_ord(q);
3942 break;
3943 case open_noad_type:
3944 case inner_noad_type:
3945 break;
3947 break;
3948 case fence_noad:
3949 if (subtype(q) != left_noad_side)
3950 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3951 type(r) = simple_noad; /* assumes the same size */
3952 subtype(r) = ord_noad_type;
3954 goto DONE_WITH_NOAD;
3955 break;
3956 case fraction_noad:
3957 make_fraction(q, cur_style);
3958 goto CHECK_DIMENSIONS;
3959 break;
3960 case radical_noad:
3961 if (subtype(q) == 7)
3962 make_hextension(q, cur_style);
3963 else if (subtype(q) == 6)
3964 make_delimiter_over(q, cur_style);
3965 else if (subtype(q) == 5)
3966 make_delimiter_under(q, cur_style);
3967 else if (subtype(q) == 4)
3968 make_over_delimiter(q, cur_style);
3969 else if (subtype(q) == 3)
3970 make_under_delimiter(q, cur_style);
3971 else
3972 make_radical(q, cur_style);
3973 break;
3974 case accent_noad:
3975 make_math_accent(q, cur_style);
3976 break;
3977 case style_node:
3978 cur_style = subtype(q);
3979 setup_cur_size(cur_style);
3980 cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
3981 goto DONE_WITH_NODE;
3982 break;
3983 case choice_node:
3984 switch (cur_style / 2) {
3985 case 0: /* |display_style=0| */
3986 choose_mlist(display_mlist);
3987 break;
3988 case 1: /* |text_style=2| */
3989 choose_mlist(text_mlist);
3990 break;
3991 case 2: /* |script_style=4| */
3992 choose_mlist(script_mlist);
3993 break;
3994 case 3: /* |script_script_style=6| */
3995 choose_mlist(script_script_mlist);
3996 break;
3998 flush_node_list(display_mlist(q));
3999 flush_node_list(text_mlist(q));
4000 flush_node_list(script_mlist(q));
4001 flush_node_list(script_script_mlist(q));
4002 type(q) = style_node;
4003 subtype(q) = (quarterword) cur_style;
4004 if (p != null) {
4005 z = vlink(q);
4006 couple_nodes(q,p);
4007 while (vlink(p) != null)
4008 p = vlink(p);
4009 try_couple_nodes(p,z);
4011 goto DONE_WITH_NODE;
4012 break;
4013 case ins_node:
4014 case mark_node:
4015 case adjust_node:
4016 case boundary_node:
4017 case whatsit_node:
4018 case penalty_node:
4019 case disc_node:
4020 goto DONE_WITH_NODE;
4021 break;
4022 case rule_node:
4023 if (height(q) > max_hl)
4024 max_hl = height(q);
4025 if (depth(q) > max_d)
4026 max_d = depth(q);
4027 goto DONE_WITH_NODE;
4028 break;
4029 case glue_node:
4031 conditional math glue (`\.{\\nonscript}') results in a |glue_node|
4032 pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
4033 the node following will be eliminated if it is a glue or kern node and if the
4034 current size is different from |text_size|
4036 unconditional math glue (`\.{\\muskip}') is converted to normal glue by
4037 multiplying the dimensions by |cur_mu|
4040 if (subtype(q) == mu_glue) {
4041 math_glue_to_glue(q, cur_mu);
4042 } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
4043 p = vlink(q);
4044 if (p != null)
4045 if ((type(p) == glue_node) || (type(p) == kern_node)) {
4046 if (vlink(p) != null) {
4047 couple_nodes(q,vlink(p));
4048 vlink(p) = null;
4049 } else {
4050 vlink(q) = null;
4052 flush_node_list(p);
4055 goto DONE_WITH_NODE;
4056 break;
4057 case kern_node:
4058 math_kern(q, cur_mu);
4059 goto DONE_WITH_NODE;
4060 break;
4061 default:
4062 confusion("mlist1");
4065 When we get to the following part of the program, we have ``fallen through''
4066 from cases that did not lead to |check_dimensions| or |done_with_noad| or
4067 |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
4068 converted to an hlist, and whose subscripts and superscripts need to be
4069 appended if they are present.
4071 If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
4072 by which a superscript should be moved right with respect to a subscript
4073 when both are present.
4076 p = check_nucleus_complexity(q, &delta, cur_style);
4078 if ((subscr(q) == null) && (supscr(q) == null)) {
4079 assign_new_hlist(q, p);
4080 } else {
4081 /* top, bottom */
4082 make_scripts(q, p, delta, cur_style, 0, 0);
4084 CHECK_DIMENSIONS:
4085 z = hpack(new_hlist(q), 0, additional, -1);
4086 if (height(z) > max_hl)
4087 max_hl = height(z);
4088 if (depth(z) > max_d)
4089 max_d = depth(z);
4090 list_ptr(z) = null;
4091 /* only drop the \.{\\hbox} */
4092 flush_node(z);
4093 DONE_WITH_NOAD:
4094 r = q;
4095 r_type = type(r);
4096 r_subtype = subtype(r);
4097 if (r_type == fence_noad) {
4098 r_subtype = left_noad_side;
4099 cur_style = style;
4100 setup_cur_size(cur_style);
4101 cur_mu = x_over_n(get_math_quad_size(cur_size), 18); /* style */
4103 DONE_WITH_NODE:
4104 q = vlink(q);
4106 if (r_type == simple_noad && r_subtype == bin_noad_type) {
4107 type(r) = simple_noad;
4108 subtype(r) = ord_noad_type;
4111 Make a second pass over the mlist, removing all noads and inserting the
4112 proper spacing and penalties.
4114 We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
4115 The second pass simply goes through and hooks everything together with the
4116 proper glue and penalties. It also handles the |fence_noad|s that
4117 might be present, since |max_hl| and |max_d| are now known. Variable |p| points
4118 to a node at the current end of the final hlist.
4120 p = temp_head;
4121 vlink(p) = null;
4122 q = mlist;
4123 r_type = 0;
4124 r_subtype = 0;
4125 cur_style = style;
4126 setup_cur_size(cur_style);
4127 cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
4128 NEXT_NODE:
4129 while (q != null) {
4131 If node |q| is a style node, change the style and |goto delete_q|;
4132 otherwise if it is not a noad, put it into the hlist,
4133 advance |q|, and |goto done|; otherwise set |s| to the size
4134 of noad |q|, set |t| to the associated type (|ord_noad..
4135 inner_noad|), and set |pen| to the associated penalty
4137 Just before doing the big |case| switch in the second pass, the program
4138 sets up default values so that most of the branches are short.
4140 t = simple_noad;
4141 t_subtype = ord_noad_type;
4142 pen = inf_penalty;
4143 switch (type(q)) {
4144 case simple_noad:
4145 t_subtype = subtype(q);
4146 switch (t_subtype) {
4147 case bin_noad_type:
4148 pen = bin_op_penalty_par;
4149 break;
4150 case rel_noad_type:
4151 pen = rel_penalty_par;
4152 break;
4153 case vcenter_noad_type:
4154 case over_noad_type:
4155 case under_noad_type:
4156 t_subtype = ord_noad_type;
4157 break;
4159 case radical_noad:
4160 break;
4161 case accent_noad:
4162 break;
4163 case fraction_noad:
4164 t = simple_noad;
4165 t_subtype = inner_noad_type;
4166 break;
4167 case fence_noad:
4168 t_subtype = make_left_right(q, style, max_d, max_hl);
4169 break;
4170 case style_node:
4171 /* Change the current style and |goto delete_q| */
4172 cur_style = subtype(q);
4173 setup_cur_size(cur_style);
4174 cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
4175 goto DELETE_Q;
4176 break;
4177 case whatsit_node:
4178 case penalty_node:
4179 case rule_node:
4180 case disc_node:
4181 case adjust_node:
4182 case ins_node:
4183 case mark_node:
4184 case glue_node:
4185 case kern_node:
4186 couple_nodes(p,q);
4187 p = q;
4188 q = vlink(q);
4189 vlink(p) = null;
4190 goto NEXT_NODE;
4191 break;
4192 default:
4193 confusion("mlist3");
4195 /* Append inter-element spacing based on |r_type| and |t| */
4196 if (r_type > 0) {
4197 /* not the first noad */
4198 z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
4199 if (z != null) {
4200 reset_attributes(z, node_attr(p));
4201 couple_nodes(p,z);
4202 p = z;
4206 Append any |new_hlist| entries for |q|, and any appropriate penalties
4208 We insert a penalty node after the hlist entries of noad |q| if |pen|
4209 is not an ``infinite'' penalty, and if the node immediately following |q|
4210 is not a penalty node or a |rel_noad| or absent entirely.
4212 if (new_hlist(q) != null) {
4213 couple_nodes(p,new_hlist(q));
4214 do {
4215 p = vlink(p);
4216 } while (vlink(p) != null);
4218 if (penalties && vlink(q) != null && pen < inf_penalty) {
4219 r_type = type(vlink(q));
4220 r_subtype = subtype(vlink(q));
4221 if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) {
4222 z = new_penalty(pen);
4223 reset_attributes(z, node_attr(q));
4224 couple_nodes(p,z);
4225 p = z;
4228 if (type(q) == fence_noad && subtype(q) == right_noad_side) {
4229 t = simple_noad;
4230 t_subtype = open_noad_type;
4232 r_type = t;
4233 r_subtype = t_subtype;
4234 DELETE_Q:
4235 r = q;
4236 q = vlink(q);
4238 The m-to-hlist conversion takes place in-place, so the various dependant
4239 fields may not be freed (as would happen if |flush_node| was called).
4241 A low-level |free_node| is easier than attempting to nullify such dependant
4242 fields for all possible node and noad types.
4244 if (nodetype_has_attributes(type(r))) {
4245 delete_attribute_ref(node_attr(r));
4247 reset_node_properties(r);
4248 free_node(r, get_node_size(type(r), subtype(r)));