use the _new macro instead of _old also in trunk (HH)
[luatex.git] / source / texk / web2c / luatexdir / tex / mlist.w
blob3499a6714f3ad4f7ea41583e7957a8824c905e11
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 math_old mathoption_int_par(c_mathoption_old_code)
55 #define math_no_italic_compensation mathoption_int_par(c_mathoption_no_italic_compensation_code)
56 #define math_no_char_italic mathoption_int_par(c_mathoption_no_char_italic_code)
57 #define math_use_old_fraction_scaling mathoption_int_par(c_mathoption_use_old_fraction_scaling_code)
59 #define is_new_mathfont(A) ((font_math_params(A) >0) && (math_old == 0))
60 #define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B)))
63 \def\LuaTeX{Lua\TeX}
65 @ @c
67 #include "ptexlib.h"
68 #include "lua/luatex-api.h"
70 @ @c
71 #define delimiter_factor int_par(delimiter_factor_code)
72 #define delimiter_shortfall dimen_par(delimiter_shortfall_code)
73 #define bin_op_penalty int_par(bin_op_penalty_code)
74 #define rel_penalty int_par(rel_penalty_code)
75 #define null_delimiter_space dimen_par(null_delimiter_space_code)
76 #define script_space dimen_par(script_space_code)
77 #define disable_lig int_par(disable_lig_code)
78 #define disable_kern int_par(disable_kern_code)
79 #define disable_space int_par(disable_space_code)
80 #define scripts_mode int_par(math_scripts_mode_code)
82 #define nDEBUG
84 #define reset_attributes(p,newatt) do { \
85 delete_attribute_ref(node_attr(p)); \
86 node_attr(p) = newatt; \
87 if (newatt!=null) { \
88 assert(type(newatt)==attribute_list_node); \
89 add_node_attr_ref(node_attr(p)); \
90 } \
91 } while (0)
93 #define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \
94 if (B==text_size) { \
95 def_math_param(A, text_style, (C),D); \
96 def_math_param(A, cramped_text_style, (C),D); \
97 } else if (B==script_size) { \
98 def_math_param(A, script_style, (C),D); \
99 def_math_param(A, cramped_script_style, (C),D); \
100 } else if (B==script_script_size) { \
101 def_math_param(A, script_script_style, (C),D); \
102 def_math_param(A, cramped_script_script_style, (C),D); \
104 } while (0)
106 #define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \
107 if (B==text_size) { \
108 def_math_param(A, display_style,(C),D); \
109 def_math_param(A, cramped_display_style,(C),D); \
111 } while (0)
113 #define font_MATH_par(a,b) \
114 (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
116 @ here are the math parameters that are font-dependant
118 @ Before an mlist is converted to an hlist, \TeX\ makes sure that
119 the fonts in family~2 have enough parameters to be math-symbol
120 fonts, and that the fonts in family~3 have enough parameters to be
121 math-extension fonts. The math-symbol parameters are referred to by using the
122 following macros, which take a size code as their parameter; for example,
123 |num1(cur_size)| gives the value of the |num1| parameter for the current size.
124 @^parameters for symbols@>
125 @^font parameters@>
128 #define total_mathsy_params 22
129 #define total_mathex_params 13
131 #define mathsy(A,B) font_param(fam_fnt(2,A),B)
133 #define math_x_height(A) mathsy(A,5) /* height of `\.x' */
134 #define math_quad(A) mathsy(A,6) /* \.{18mu} */
135 #define num1(A) mathsy(A,8) /* numerator shift-up in display styles */
136 #define num2(A) mathsy(A,9) /* numerator shift-up in non-display, non-\.{\\atop} */
137 #define num3(A) mathsy(A,10) /* numerator shift-up in non-display \.{\\atop} */
138 #define denom1(A) mathsy(A,11) /* denominator shift-down in display styles */
139 #define denom2(A) mathsy(A,12) /* denominator shift-down in non-display styles */
140 #define sup1(A) mathsy(A,13) /* superscript shift-up in uncramped display style */
141 #define sup2(A) mathsy(A,14) /* superscript shift-up in uncramped non-display */
142 #define sup3(A) mathsy(A,15) /* superscript shift-up in cramped styles */
143 #define sub1(A) mathsy(A,16) /* subscript shift-down if superscript is absent */
144 #define sub2(A) mathsy(A,17) /* subscript shift-down if superscript is present */
145 #define sup_drop(A) mathsy(A,18) /* superscript baseline below top of large box */
146 #define sub_drop(A) mathsy(A,19) /* subscript baseline below bottom of large box */
147 #define delim1(A) mathsy(A,20) /* size of \.{\\atopwithdelims} delimiters in display styles */
148 #define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */
149 #define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */
151 @ The math-extension parameters have similar macros, but the size code is
152 omitted (since it is always |cur_size| when we refer to such parameters).
153 @^parameters for symbols@>
154 @^font parameters@>
157 #define mathex(A,B) font_param(fam_fnt(3,A),B)
158 #define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */
159 #define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */
160 #define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
161 #define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
162 #define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
163 #define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
165 @ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
166 the MathConstants values have no matching usage in \LuaTeX\ right now.
168 ScriptPercentScaleDown,
169 ScriptScriptPercentScaleDown:
170 These should be handled by the macro package, on the engine
171 side there are three separate fonts
173 DelimitedSubFormulaMinHeight:
174 This is perhaps related to word's natural math input? I have
175 no idea what to do about it
177 MathLeading:
178 LuaTeX does not currently handle multi-line displays, and
179 the parameter does not seem to make much sense elsewhere
181 FlattenedAccentBaseHeight:
182 This is based on the 'flac' GSUB feature. It would not be hard
183 to support that, but proper math accent placements cf. MATH
184 needs support for MathTopAccentAttachment table to be
185 implemented first
187 Also still TODO for OpenType Math:
188 * prescripts
190 @ this is not really a math parameter at all
193 static void math_param_error(const char *param, int style)
195 char s[256];
196 const char *hlp[] = {
197 "Sorry, but I can't typeset math unless various parameters have",
198 "been set. This is normally done by loading special math fonts",
199 "into the math family slots. Your font set is lacking at least",
200 "the parameter mentioned earlier.",
201 NULL
203 snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
204 param, math_style_names[style]);
205 tex_error(s, hlp);
206 #if 0
207 flush_math();
208 #endif
209 return;
212 @ @c
213 static scaled accent_base_height(int f)
215 scaled a;
216 if (is_new_mathfont(f)) {
217 a = font_MATH_par(f, AccentBaseHeight);
218 if (a == undefined_math_parameter)
219 a = x_height(f);
220 } else {
221 a = x_height(f);
223 return a;
226 @ the non-staticness of this function is for the benefit of |texmath.w|
229 scaled get_math_quad(int var)
231 scaled a = get_math_param(math_param_quad, var);
232 if (a == undefined_math_parameter) {
233 math_param_error("quad", var);
234 a = 0;
236 return a;
239 @ this parameter is different because it is called with a size
240 specifier instead of a style specifier.
243 static scaled math_axis(int b)
245 scaled a;
246 int var;
247 if (b == script_size)
248 var = script_style;
249 else if (b == script_script_size)
250 var = script_script_style;
251 else
252 var = text_style;
253 a = get_math_param(math_param_axis, var);
254 if (a == undefined_math_parameter) {
255 math_param_error("axis", var);
256 a = 0;
258 return a;
261 @ @c
262 static scaled get_math_quad_size(int b)
264 int var;
265 if (b == script_size)
266 var = script_style;
267 else if (b == script_script_size)
268 var = script_script_style;
269 else
270 var = text_style;
271 return get_math_param(math_param_quad, var);
274 @ @c
275 static scaled minimum_operator_size(int var)
277 scaled a = get_math_param(math_param_operator_size, var);
278 return a;
281 @ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select
282 the backward compatibility code, and it means that we can't raise an error here.
285 static scaled radical_rule(int var)
287 scaled a = get_math_param(math_param_radical_rule, var);
288 return a;
291 @ now follow all the trivial math parameters
294 #define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
296 static scaled do_get_math_param_or_error(int var, int param, const char *name)
298 scaled a = get_math_param(param, var);
299 if (a == undefined_math_parameter) {
300 math_param_error(name, var);
301 a = 0;
303 return a;
306 @ A variant on a suggestion on the list based on analysis by UV.
309 static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
310 scaled delta, delta1, delta2;
311 if (axis) {
312 delta2 = max_d + math_axis(cur_size);
313 } else {
314 delta2 = max_d;
316 delta1 = max_h + max_d - delta2;
317 if (delta2 > delta1) {
318 /* |delta1| is max distance from axis */
319 delta1 = delta2;
321 delta = (delta1 / 500) * delimiter_factor;
322 delta2 = delta1 + delta1 - delimiter_shortfall;
323 if (delta < delta2) {
324 return delta2;
325 } else {
326 return delta;
330 @ @c
331 #define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
332 #define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after)
333 #define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise)
335 #define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
337 #define overbar_rule(a) get_math_param_or_error(a, overbar_rule)
338 #define overbar_kern(a) get_math_param_or_error(a, overbar_kern)
339 #define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap)
341 #define underbar_rule(a) get_math_param_or_error(a, underbar_rule)
342 #define underbar_kern(a) get_math_param_or_error(a, underbar_kern)
343 #define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap)
345 #define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap)
346 #define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap)
348 #define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap)
349 #define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap)
351 #define radical_vgap(a) get_math_param_or_error(a, radical_vgap)
352 #define radical_kern(a) get_math_param_or_error(a, radical_kern)
354 #define stack_vgap(a) get_math_param_or_error(a, stack_vgap)
355 #define stack_num_up(a) get_math_param_or_error(a, stack_num_up)
356 #define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down)
358 #define fraction_rule(a) get_math_param_or_error(a, fraction_rule)
359 #define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap)
360 #define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap)
361 #define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up)
362 #define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down)
363 #define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
364 #define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
366 #define fraction_del_size_old(a) get_math_param(a, math_param_fraction_del_size)
368 #define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size)
370 #define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap)
371 #define skewed_fraction_vgap(a) get_math_param_or_error(a, skewed_fraction_vgap)
373 #define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap)
374 #define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap)
375 #define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern)
377 #define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap)
378 #define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap)
379 #define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern)
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)
396 if (is_new_mathfont(f)) { /* fix all known parameters */
398 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
399 font_size(f), lvl);
400 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
401 font_size(f), lvl);
402 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
403 font_MATH_par(f, AxisHeight), lvl);
404 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
405 font_MATH_par(f, AxisHeight), lvl);
406 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
407 font_MATH_par(f, OverbarExtraAscender), lvl);
408 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
409 font_MATH_par(f, OverbarExtraAscender), lvl);
410 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
411 font_MATH_par(f, OverbarRuleThickness), lvl);
412 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
413 font_MATH_par(f, OverbarRuleThickness), lvl);
414 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
415 font_MATH_par(f, OverbarVerticalGap), lvl);
416 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
417 font_MATH_par(f, OverbarVerticalGap), lvl);
418 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
419 font_MATH_par(f, UnderbarExtraDescender), lvl);
420 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
421 font_MATH_par(f, UnderbarExtraDescender), lvl);
422 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
423 font_MATH_par(f, UnderbarRuleThickness), lvl);
424 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
425 font_MATH_par(f, UnderbarRuleThickness), lvl);
426 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
427 font_MATH_par(f, UnderbarVerticalGap), lvl);
428 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
429 font_MATH_par(f, UnderbarVerticalGap), lvl);
431 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
432 font_MATH_par(f, StretchStackGapAboveMin), lvl);
433 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
434 font_MATH_par(f, StretchStackGapAboveMin), lvl);
435 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
436 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
437 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
438 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
440 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
441 font_MATH_par(f, StretchStackGapBelowMin), lvl);
442 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
443 font_MATH_par(f, StretchStackGapBelowMin), lvl);
444 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
445 font_MATH_par(f, StretchStackTopShiftUp), lvl);
446 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
447 font_MATH_par(f, StretchStackTopShiftUp), lvl);
449 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
450 font_MATH_par(f, StackTopShiftUp), lvl);
451 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
452 font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
453 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
454 font_MATH_par(f, StackBottomShiftDown), lvl);
455 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
456 font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
457 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
458 font_MATH_par(f, StackGapMin), lvl);
459 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
460 font_MATH_par(f, StackDisplayStyleGapMin), lvl);
462 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
463 font_MATH_par(f, RadicalExtraAscender), lvl);
464 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
465 font_MATH_par(f, RadicalExtraAscender), lvl);
467 DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
468 font_MATH_par(f, DisplayOperatorMinHeight), lvl);
470 DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
471 font_MATH_par(f, RadicalRuleThickness), lvl);
472 DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
473 font_MATH_par(f, RadicalRuleThickness), lvl);
474 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
475 font_MATH_par(f, RadicalVerticalGap), lvl);
476 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
477 font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
478 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
479 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
480 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
481 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
482 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
483 font_MATH_par(f, RadicalKernAfterDegree), lvl);
484 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
485 font_MATH_par(f, RadicalKernAfterDegree), lvl);
486 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
487 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
488 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
489 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
491 if (size_id == text_size) {
492 def_math_param(math_param_sup_shift_up, display_style,
493 font_MATH_par(f, SuperscriptShiftUp), lvl);
494 def_math_param(math_param_sup_shift_up, cramped_display_style,
495 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
496 def_math_param(math_param_sup_shift_up, text_style,
497 font_MATH_par(f, SuperscriptShiftUp), lvl);
498 def_math_param(math_param_sup_shift_up, cramped_text_style,
499 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
500 } else if (size_id == script_size) {
501 def_math_param(math_param_sup_shift_up, script_style,
502 font_MATH_par(f, SuperscriptShiftUp), lvl);
503 def_math_param(math_param_sup_shift_up, cramped_script_style,
504 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
505 } else if (size_id == script_script_size) {
506 def_math_param(math_param_sup_shift_up, script_script_style,
507 font_MATH_par(f, SuperscriptShiftUp), lvl);
508 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
509 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
512 DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
513 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
514 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
515 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
516 DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
517 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
518 DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
519 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
520 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
521 font_MATH_par(f, SubscriptShiftDown), lvl);
522 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
523 font_MATH_par(f, SubscriptShiftDown), lvl);
525 if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
526 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
527 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
528 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
529 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
530 } else {
531 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
532 font_MATH_par(f, SubscriptShiftDown), lvl);
533 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
534 font_MATH_par(f, SubscriptShiftDown), lvl);
537 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
538 font_MATH_par(f, SubscriptTopMax), lvl);
539 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
540 font_MATH_par(f, SubscriptTopMax), lvl);
541 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
542 font_MATH_par(f, SuperscriptBottomMin), lvl);
543 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
544 font_MATH_par(f, SuperscriptBottomMin), lvl);
545 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
546 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
547 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
548 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
549 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
550 font_MATH_par(f, SubSuperscriptGapMin), lvl);
551 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
552 font_MATH_par(f, SubSuperscriptGapMin), lvl);
554 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
555 font_MATH_par(f, UpperLimitGapMin), lvl);
556 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
557 font_MATH_par(f, UpperLimitGapMin), lvl);
558 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
559 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
560 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
561 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
562 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
563 0, lvl);
564 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
565 0, lvl);
566 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
567 font_MATH_par(f, LowerLimitGapMin), lvl);
568 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
569 font_MATH_par(f, LowerLimitGapMin), lvl);
570 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
571 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
572 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
573 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
574 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
575 0, lvl);
576 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
577 0, lvl);
579 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
580 font_MATH_par(f, FractionRuleThickness), lvl);
581 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
582 font_MATH_par(f, FractionRuleThickness), lvl);
583 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
584 font_MATH_par(f, FractionNumeratorGapMin), lvl);
585 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
586 font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
587 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
588 font_MATH_par(f, FractionNumeratorShiftUp), lvl);
589 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
590 font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
591 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
592 font_MATH_par(f, FractionDenominatorGapMin), lvl);
593 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
594 font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
595 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
596 font_MATH_par(f, FractionDenominatorShiftDown), lvl);
597 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
598 font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
600 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
601 font_MATH_par(f, FractionDelimiterSize), lvl);
602 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
603 font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
605 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
606 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
607 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
608 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
609 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
610 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
611 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
612 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
614 DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
615 font_MATH_par(f, SpaceAfterScript), lvl);
616 DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
617 font_MATH_par(f, SpaceAfterScript), lvl);
619 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
620 font_MATH_par(f, MinConnectorOverlap), lvl);
621 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
622 font_MATH_par(f, MinConnectorOverlap), lvl);
624 } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
626 /* fix old-style |sy| parameters */
628 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
629 math_quad(size_id), lvl);
630 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
631 math_quad(size_id), lvl);
632 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
633 axis_height(size_id), lvl);
634 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
635 axis_height(size_id), lvl);
636 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
637 num3(size_id), lvl);
638 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
639 num1(size_id), lvl);
640 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
641 denom2(size_id), lvl);
642 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
643 denom1(size_id), lvl);
644 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
645 num2(size_id), lvl);
646 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
647 num1(size_id), lvl);
648 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
649 denom2(size_id), lvl);
650 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
651 denom1(size_id), lvl);
652 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
653 delim2(size_id), lvl);
654 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
655 delim1(size_id), lvl);
657 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
658 0, lvl);
659 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
660 0, lvl);
661 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
662 0, lvl);
663 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
664 0, lvl);
666 if (size_id == text_size) {
667 def_math_param(math_param_sup_shift_up, display_style,
668 sup1(size_id), lvl);
669 def_math_param(math_param_sup_shift_up, cramped_display_style,
670 sup3(size_id), lvl);
671 def_math_param(math_param_sup_shift_up, text_style,
672 sup2(size_id), lvl);
673 def_math_param(math_param_sup_shift_up, cramped_text_style,
674 sup3(size_id), lvl);
675 } else if (size_id == script_size) {
676 def_math_param(math_param_sub_shift_drop, display_style,
677 sub_drop(size_id), lvl);
678 def_math_param(math_param_sub_shift_drop, cramped_display_style,
679 sub_drop(size_id), lvl);
680 def_math_param(math_param_sub_shift_drop, text_style,
681 sub_drop(size_id), lvl);
682 def_math_param(math_param_sub_shift_drop, cramped_text_style,
683 sub_drop(size_id), lvl);
684 def_math_param(math_param_sup_shift_drop, display_style,
685 sup_drop(size_id), lvl);
686 def_math_param(math_param_sup_shift_drop, cramped_display_style,
687 sup_drop(size_id), lvl);
688 def_math_param(math_param_sup_shift_drop, text_style,
689 sup_drop(size_id), lvl);
690 def_math_param(math_param_sup_shift_drop, cramped_text_style,
691 sup_drop(size_id), lvl);
692 def_math_param(math_param_sup_shift_up, script_style,
693 sup2(size_id), lvl);
694 def_math_param(math_param_sup_shift_up, cramped_script_style,
695 sup3(size_id), lvl);
696 } else if (size_id == script_script_size) {
697 def_math_param(math_param_sub_shift_drop, script_style,
698 sub_drop(size_id), lvl);
699 def_math_param(math_param_sub_shift_drop, cramped_script_style,
700 sub_drop(size_id), lvl);
701 def_math_param(math_param_sub_shift_drop, script_script_style,
702 sub_drop(size_id), lvl);
703 def_math_param(math_param_sub_shift_drop,
704 cramped_script_script_style, sub_drop(size_id), lvl);
705 def_math_param(math_param_sup_shift_drop, script_style,
706 sup_drop(size_id), lvl);
707 def_math_param(math_param_sup_shift_drop, cramped_script_style,
708 sup_drop(size_id), lvl);
709 def_math_param(math_param_sup_shift_drop, script_script_style,
710 sup_drop(size_id), lvl);
711 def_math_param(math_param_sup_shift_drop,
712 cramped_script_script_style, sup_drop(size_id), lvl);
713 def_math_param(math_param_sup_shift_up, script_script_style,
714 sup2(size_id), lvl);
715 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
716 sup3(size_id), lvl);
719 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
720 sub1(size_id), lvl);
721 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
722 sub1(size_id), lvl);
723 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
724 sub2(size_id), lvl);
725 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
726 sub2(size_id), lvl);
727 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
728 (abs(math_x_height(size_id) * 4) / 5), lvl);
729 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
730 (abs(math_x_height(size_id) * 4) / 5), lvl);
731 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
732 (abs(math_x_height(size_id)) / 4), lvl);
733 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
734 (abs(math_x_height(size_id)) / 4), lvl);
735 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
736 (abs(math_x_height(size_id) * 4) / 5), lvl);
737 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
738 (abs(math_x_height(size_id) * 4) / 5), lvl);
741 The display-size |radical_vgap| is done twice because it needs
742 values from both the sy and the ex font.
745 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
746 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
747 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
748 60, lvl);
749 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
750 60, lvl);
751 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
752 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
753 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
754 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
755 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
756 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
757 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
758 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
760 } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
762 /* fix old-style |ex| parameters */
764 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
765 default_rule_thickness(size_id), lvl);
766 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
767 default_rule_thickness(size_id), lvl);
768 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
769 3 * default_rule_thickness(size_id), lvl);
770 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
771 default_rule_thickness(size_id), lvl);
772 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
773 default_rule_thickness(size_id), lvl);
774 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
775 3 * default_rule_thickness(size_id), lvl);
776 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
777 default_rule_thickness(size_id), lvl);
778 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
779 default_rule_thickness(size_id), lvl);
780 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
781 3 * default_rule_thickness(size_id), lvl);
782 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
783 default_rule_thickness(size_id), lvl);
784 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
785 default_rule_thickness(size_id), lvl);
786 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
787 3 * default_rule_thickness(size_id), lvl);
788 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
789 default_rule_thickness(size_id), lvl);
790 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
791 default_rule_thickness(size_id), lvl);
792 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
793 (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
794 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
795 3 * default_rule_thickness(size_id), lvl);
796 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
797 7 * default_rule_thickness(size_id), lvl);
798 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
799 default_rule_thickness(size_id), lvl);
800 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
801 default_rule_thickness(size_id), lvl);
802 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
803 default_rule_thickness(size_id), lvl);
804 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
805 3 * default_rule_thickness(size_id), lvl);
806 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
807 default_rule_thickness(size_id), lvl);
808 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
809 3 * default_rule_thickness(size_id), lvl);
810 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
811 big_op_spacing1(size_id), lvl);
812 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
813 big_op_spacing1(size_id), lvl);
814 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
815 big_op_spacing3(size_id), lvl);
816 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
817 big_op_spacing3(size_id), lvl);
818 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
819 big_op_spacing5(size_id), lvl);
820 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
821 big_op_spacing5(size_id), lvl);
822 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
823 big_op_spacing2(size_id), lvl);
824 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
825 big_op_spacing2(size_id), lvl);
826 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
827 big_op_spacing4(size_id), lvl);
828 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
829 big_op_spacing4(size_id), lvl);
830 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
831 big_op_spacing5(size_id), lvl);
832 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
833 big_op_spacing5(size_id), lvl);
834 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
835 4 * default_rule_thickness(size_id), lvl);
836 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
837 4 * default_rule_thickness(size_id), lvl);
840 All of the |space_after_script|s are done in |finalize_math_parameters|
841 because the \.{\\scriptspace} may have been altered by the user
844 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
845 0, lvl);
846 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
847 0, lvl);
849 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
850 big_op_spacing2(size_id), lvl);
851 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
852 big_op_spacing2(size_id), lvl);
853 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
854 big_op_spacing4(size_id), lvl);
855 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
856 big_op_spacing4(size_id), lvl);
857 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
858 big_op_spacing1(size_id), lvl);
859 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
860 big_op_spacing1(size_id), lvl);
861 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
862 big_op_spacing3(size_id), lvl);
863 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
864 big_op_spacing3(size_id), lvl);
867 The display-size |radical_vgap| is done twice because it needs
868 values from both the sy and the ex font.
871 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
872 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
877 @ This needs to be called just at the start of |mlist_to_hlist|, for
878 backward compatibility with \.{\\scriptspace}.
881 static void finalize_math_parameters(void)
883 int saved_trace = int_par(tracing_assigns_code);
884 int_par(tracing_assigns_code) = 0;
885 if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
886 def_math_param(math_param_space_after_script, display_style,
887 script_space, level_one);
888 def_math_param(math_param_space_after_script, text_style,
889 script_space, level_one);
890 def_math_param(math_param_space_after_script, script_style,
891 script_space, level_one);
892 def_math_param(math_param_space_after_script, script_script_style,
893 script_space, level_one);
894 def_math_param(math_param_space_after_script, cramped_display_style,
895 script_space, level_one);
896 def_math_param(math_param_space_after_script, cramped_text_style,
897 script_space, level_one);
898 def_math_param(math_param_space_after_script, cramped_script_style,
899 script_space, level_one);
900 def_math_param(math_param_space_after_script, cramped_script_script_style,
901 script_space, level_one);
903 int_par(tracing_assigns_code) = saved_trace;
906 @ In order to convert mlists to hlists, i.e., noads to nodes, we need several
907 subroutines that are conveniently dealt with now.
909 Let us first introduce the macros that make it easy to get at the parameters and
910 other font information. A size code, which is a multiple of 256, is added to a
911 family number to get an index into the table of internal font numbers
912 for each combination of family and size. (Be alert: Size codes get
913 larger as the type gets smaller.)
916 static const char *math_size_string(int s)
918 if (s == text_size)
919 return "textfont";
920 else if (s == script_size)
921 return "scriptfont";
922 else
923 return "scriptscriptfont";
926 @ When the style changes, the following piece of program computes associated
927 information:
930 #define setup_cur_size(a) do { \
931 if (a==script_style || \
932 a==cramped_script_style) \
933 cur_size=script_size; \
934 else if (a==script_script_style || \
935 a==cramped_script_script_style) \
936 cur_size=script_script_size; \
937 else cur_size=text_size; \
938 } while (0)
941 @ a simple routine that creates a flat copy of a nucleus
943 static pointer math_clone(pointer q)
945 pointer x;
946 if (q == null)
947 return null;
948 x = new_node(type(q), 0);
949 reset_attributes(x, node_attr(q));
950 if (type(q) == math_char_node) {
951 math_fam(x) = math_fam(q);
952 math_character(x) = math_character(q);
953 } else {
954 math_list(x) = math_list(q);
956 return x;
959 @ Here is a function that returns a pointer to a rule node having a given
960 thickness |t|. The rule will extend horizontally to the boundary of the vlist
961 that eventually contains it.
964 static pointer do_fraction_rule(scaled t, pointer att)
966 pointer p; /* the new node */
967 p = new_rule(normal_rule);
968 rule_dir(p) = math_direction;
969 height(p) = t;
970 depth(p) = 0;
971 reset_attributes(p, att);
972 return p;
975 @ The |overbar| function returns a pointer to a vlist box that consists of
976 a given box |b|, above which has been placed a kern of height |k| under a
977 fraction rule of thickness |t| under additional space of height |ht|.
980 static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att)
982 pointer p, q; /* nodes being constructed */
983 p = new_kern(k);
984 reset_attributes(p, att);
985 couple_nodes(p,b);
986 q = do_fraction_rule(t, att);
987 couple_nodes(q,p);
988 p = new_kern(ht);
989 reset_attributes(p, att);
990 couple_nodes(p,q);
991 q = vpackage(p, 0, additional, max_dimen, math_direction);
992 reset_attributes(q, att);
993 return q;
996 @ Here is a subroutine that creates a new box, whose list contains a
997 single character, and whose width includes the italic correction for
998 that character. The height or depth of the box will be negative, if
999 the height or depth of the character is negative; thus, this routine
1000 may deliver a slightly different result than |hpack| would produce.
1003 static pointer char_box(internal_font_number f, int c, pointer bb)
1005 pointer b, p; /* the new box and its character node */
1006 b = new_null_box();
1007 if (is_new_mathfont(f))
1008 width(b) = char_width(f, c);
1009 else
1010 width(b) = char_width(f, c) + char_italic(f, c);
1011 height(b) = char_height(f, c);
1012 depth(b) = char_depth(f, c);
1013 reset_attributes(b, bb);
1014 p = new_char(f, c);
1015 reset_attributes(p, bb);
1016 list_ptr(b) = p;
1017 return b;
1020 @ Another handy subroutine computes the height plus depth of
1021 a given character:
1024 static scaled height_plus_depth(internal_font_number f, int c)
1026 return (char_height(f, c) + char_depth(f, c));
1029 @ When we build an extensible character, it's handy to have the
1030 following subroutine, which puts a given character on top
1031 of the characters already in box |b|:
1034 static scaled stack_into_box(pointer b, internal_font_number f, int c)
1036 pointer p, q; /* new node placed into |b| */
1037 p = char_box(f, c, node_attr(b)); /* italic gets added to width */
1038 if (type(b) == vlist_node) {
1039 try_couple_nodes(p,list_ptr(b));
1040 list_ptr(b) = p;
1041 height(b) = height(p);
1042 if (width(b) < width(p))
1043 width(b) = width(p);
1044 return height_plus_depth(f, c);
1045 } else {
1046 q = list_ptr(b);
1047 if (q == null) {
1048 list_ptr(b) = p;
1049 } else {
1050 while (vlink(q) != null)
1051 q = vlink(q);
1052 couple_nodes(q,p);
1054 if (height(b) < height(p))
1055 height(b) = height(p);
1056 if (depth(b) < depth(p))
1057 depth(b) = depth(p);
1058 return char_width(f, c);
1062 static void stack_glue_into_box(pointer b, scaled min, scaled max) {
1063 pointer p, q; /* new node placed into |b| */
1064 q = new_spec(zero_glue);
1065 width(q) = min;
1066 stretch(q) = max-min;
1067 p = new_glue(q);
1068 reset_attributes(p, node_attr(b));
1069 if (type(b) == vlist_node) {
1070 try_couple_nodes(p,list_ptr(b));
1071 list_ptr(b) = p;
1072 } else {
1073 q = list_ptr(b);
1074 if (q == null) {
1075 list_ptr(b) = p;
1076 } else {
1077 while (vlink(q) != null)
1078 q = vlink(q);
1079 couple_nodes(q,p);
1084 @ \TeX's most important routine for dealing with formulas is called
1085 |mlist_to_hlist|. After a formula has been scanned and represented
1086 as an mlist, this routine converts it to an hlist that can be placed
1087 into a box or incorporated into the text of a paragraph. The
1088 explicit parameter |cur_mlist| points to the first node or noad in
1089 the given mlist (and it might be |null|); the parameter |penalties|
1090 is |true| if penalty nodes for potential line breaks are to be
1091 inserted into the resulting hlist, the parameter |cur_style| is a
1092 style code. After |mlist_to_hlist| has acted, |vlink(temp_head)|
1093 points to the translated hlist.
1095 Since mlists can be inside mlists, the procedure is recursive. And since this
1096 is not part of \TeX's inner loop, the program has been written in a manner
1097 that stresses compactness over efficiency.
1098 @^recursion@>
1101 int cur_size; /* size code corresponding to |cur_style| */
1103 @ @c
1104 static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v,
1105 pointer att, int cur_style, int boxtype)
1107 pointer b; /* new box */
1108 scaled b_max; /* natural (maximum) size of the stack */
1109 scaled s_max; /* amount of possible shrink in the stack */
1110 extinfo *cur;
1111 scaled min_overlap, prev_overlap;
1112 int i; /* a temporary counter number of extensible pieces */
1113 int with_extenders; /* number of times to repeat each repeatable item in |ext| */
1114 int num_extenders, num_normal;
1115 scaled a, c, d;
1117 assert(ext != NULL);
1118 b = new_null_box();
1119 type(b) = (quarterword) boxtype;
1120 reset_attributes(b, att);
1121 min_overlap = connector_overlap_min(cur_style);
1122 assert(min_overlap >= 0);
1123 with_extenders = -1;
1124 num_extenders = 0;
1125 num_normal = 0;
1127 cur = ext;
1128 while (cur != NULL) {
1129 if (!char_exists(f, cur->glyph)) {
1130 const char *hlp[] = {
1131 "Each glyph part in an extensible item should exist in the font.",
1132 "I will give up trying to find a suitable size for now. Fix your font!",
1133 NULL
1135 tex_error("Variant part doesn't exist.", hlp);
1136 width(b) = null_delimiter_space;
1137 return b;
1139 if (cur->extender > 0)
1140 num_extenders++;
1141 else
1142 num_normal++;
1143 /* no negative overlaps or advances are allowed */
1144 if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
1145 const char *hlp[] = {
1146 "All measurements in extensible items should be positive.",
1147 "To get around this problem, I have changed the font metrics.",
1148 "Fix your font!",
1149 NULL
1151 tex_error("Extensible recipe has negative fields.", hlp);
1152 if (cur->start_overlap < 0)
1153 cur->start_overlap = 0;
1154 if (cur->end_overlap < 0)
1155 cur->end_overlap = 0;
1156 if (cur->advance < 0)
1157 cur->advance = 0;
1159 cur = cur->next;
1161 if (num_normal == 0) {
1162 const char *hlp[] = {
1163 "Each extensible recipe should have at least one non-repeatable part.",
1164 "To get around this problem, I have changed the first part to be",
1165 "non-repeatable. Fix your font!",
1166 NULL
1168 tex_error("Extensible recipe has no fixed parts.", hlp);
1169 ext->extender = 0;
1170 num_normal = 1;
1171 num_extenders--;
1174 |ext| holds a linked list of numerous items that may or may not be
1175 repeatable. For the total height, we have to figure out how many items
1176 are needed to create a stack of at least |v|.
1178 The next |while| loop does that. It has two goals: it finds out
1179 the natural height |b_max| of the all the parts needed to reach
1180 at least |v|, and it sets |with_extenders| to the number of times
1181 each of the repeatable items in |ext| has to be repeated to reach
1182 that height.
1185 cur = ext;
1186 b_max = 0;
1187 while (b_max < v && num_extenders > 0) {
1188 b_max = 0;
1189 prev_overlap = 0;
1190 with_extenders++;
1191 for (cur = ext; cur != NULL; cur = cur->next) {
1192 if (cur->extender == 0) {
1193 c = cur->start_overlap;
1194 if (min_overlap < c)
1195 c = min_overlap;
1196 if (prev_overlap < c)
1197 c = prev_overlap;
1198 a = cur->advance;
1199 if (a == 0) {
1200 /* for tfm fonts */
1201 if (boxtype == vlist_node)
1202 a = height_plus_depth(f, cur->glyph);
1203 else
1204 a = char_width(f, cur->glyph);
1205 assert(a >= 0);
1207 b_max += a - c;
1208 prev_overlap = cur->end_overlap;
1209 } else {
1210 i = with_extenders;
1211 while (i > 0) {
1212 c = cur->start_overlap;
1213 if (min_overlap < c)
1214 c = min_overlap;
1215 if (prev_overlap < c)
1216 c = prev_overlap;
1217 a = cur->advance;
1218 if (a == 0) {
1219 /* for tfm fonts */
1220 if (boxtype == vlist_node)
1221 a = height_plus_depth(f, cur->glyph);
1222 else
1223 a = char_width(f, cur->glyph);
1224 assert(a >= 0);
1226 b_max += a - c;
1227 prev_overlap = cur->end_overlap;
1228 i--;
1235 assemble box using |with_extenders| copies of each extender, with
1236 appropriate glue wherever an overlap occurs
1238 prev_overlap = 0;
1239 b_max = 0;
1240 s_max = 0;
1241 for (cur = ext; cur != NULL; cur = cur->next) {
1242 if (cur->extender == 0) {
1243 c = cur->start_overlap;
1244 if (prev_overlap < c)
1245 c = prev_overlap;
1246 d = c;
1247 if (min_overlap < c)
1248 c = min_overlap;
1249 if (d > 0) {
1250 stack_glue_into_box(b, -d, -c);
1251 s_max += (-c) - (-d);
1252 b_max -= d;
1254 b_max += stack_into_box(b, f, cur->glyph);
1255 prev_overlap = cur->end_overlap;
1256 i--;
1257 } else {
1258 i = with_extenders;
1259 while (i > 0) {
1260 c = cur->start_overlap;
1261 if (prev_overlap < c)
1262 c = prev_overlap;
1263 d = c;
1264 if (min_overlap < c)
1265 c = min_overlap;
1266 if (d > 0) {
1267 stack_glue_into_box(b, -d, -c);
1268 s_max += (-c) - (-d);
1269 b_max -= d;
1271 b_max += stack_into_box(b, f, cur->glyph);
1272 prev_overlap = cur->end_overlap;
1273 i--;
1278 /* set glue so as to stretch the connections if needed */
1280 d = 0;
1281 if (v > b_max && s_max > 0) {
1282 d = v-b_max;
1283 /* don't stretch more than |s_max| */
1284 if (d > s_max)
1285 d = s_max;
1286 glue_order(b) = normal;
1287 glue_sign(b) = stretching;
1288 glue_set(b) = unfloat(d/(float) s_max);
1289 b_max += d;
1292 if (boxtype == vlist_node) {
1293 height(b) = b_max;
1294 } else {
1295 width(b) = b_max;
1298 return b;
1301 @ The |var_delimiter| function, which finds or constructs a sufficiently
1302 large delimiter, is the most interesting of the auxiliary functions that
1303 currently concern us. Given a pointer |d| to a delimiter field in some noad,
1304 together with a size code |s| and a vertical distance |v|, this function
1305 returns a pointer to a box that contains the smallest variant of |d| whose
1306 height plus depth is |v| or more. (And if no variant is large enough, it
1307 returns the largest available variant.) In particular, this routine will
1308 construct arbitrarily large delimiters from extensible components, if
1309 |d| leads to such characters.
1311 The value returned is a box whose |shift_amount| has been set so that
1312 the box is vertically centered with respect to the axis in the given size.
1313 If a built-up symbol is returned, the height of the box before shifting
1314 will be the height of its topmost component.
1317 static void endless_loop_error(internal_font_number g, int y)
1319 char s[256];
1320 const char *hlp[] = {
1321 "You managed to create a seemingly endless charlist chain in the current",
1322 "font. I have counted until 10000 already and still have not escaped, so"
1323 "I will jump out of the loop all by myself now. Fix your font!",
1324 NULL
1326 snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
1327 (int) y, font_name(g));
1328 tex_error(s, hlp);
1331 static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta)
1333 pointer b; /* the box that will be constructed */
1334 internal_font_number f, g; /* best-so-far and tentative font codes */
1335 int c, i, x, y; /* best-so-far and tentative character codes */
1336 scaled u; /* height-plus-depth of a tentative character */
1337 scaled w; /* largest height-plus-depth so far */
1338 int z; /* runs through font family members */
1339 boolean large_attempt; /* are we trying the ``large'' variant? */
1340 pointer att; /* to save the current attribute list */
1341 boolean do_parts;
1342 extinfo *ext;
1343 att = null;
1344 f = null_font;
1345 c = 0;
1346 w = 0;
1347 do_parts = false;
1348 large_attempt = false;
1349 if (d == null)
1350 goto FOUND;
1351 z = small_fam(d);
1352 x = small_char(d);
1353 i = 0;
1354 while (true) {
1356 The search process is complicated slightly by the facts that some of the
1357 characters might not be present in some of the fonts, and they might not
1358 be probed in increasing order of height.
1360 if ((z != 0) || (x != 0)) {
1361 g = fam_fnt(z, s);
1362 if (g != null_font) {
1363 y = x;
1364 CONTINUE:
1365 i++;
1366 if (char_exists(g, y)) {
1367 if (flat)
1368 u = char_width(g, y);
1369 else
1370 u = height_plus_depth(g, y);
1371 if (u > w) {
1372 f = g;
1373 c = y;
1374 w = u;
1375 if (u >= v)
1376 goto FOUND;
1378 if (char_tag(g, y) == ext_tag) {
1379 f = g;
1380 c = y;
1381 do_parts = true;
1382 goto FOUND;
1384 if (i > 10000) {
1385 /* endless loop */
1386 endless_loop_error(g, y);
1387 goto FOUND;
1389 if (char_tag(g, y) == list_tag) {
1390 y = char_remainder(g, y);
1391 goto CONTINUE;
1396 if (large_attempt)
1397 goto FOUND; /* there were none large enough */
1398 large_attempt = true;
1399 z = large_fam(d);
1400 x = large_char(d);
1402 FOUND:
1403 if (d != null) {
1404 att = node_attr(d);
1405 node_attr(d) = null;
1406 flush_node(d);
1408 if (f != null_font) {
1410 When the following code is executed, |do_parts| will be true
1411 if a built-up symbol is supposed to be returned.
1413 ext = NULL;
1414 if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
1415 || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
1416 if (flat) {
1417 b = get_delim_box(d, ext, f, v, att, cur_style, hlist_node);
1418 } else {
1419 b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node);
1421 if (delta != NULL) {
1422 if (is_new_mathfont(f)) {
1423 *delta = char_vert_italic(f,x);
1424 } else {
1425 *delta = char_italic(f,x);
1428 if (stack != NULL)
1429 *stack = true ;
1430 } else {
1431 b = char_box(f, c, att);
1432 if (!is_new_mathfont(f)) {
1433 /* italic gets added to width */
1434 width(b) += char_italic(f, c);
1436 if (delta != NULL) {
1437 *delta = char_italic(f,x);
1439 if (stack != NULL)
1440 *stack = false ;
1442 } else {
1443 b = new_null_box();
1444 reset_attributes(b, att);
1445 if (flat) {
1446 width(b) = 0;
1447 } else {
1448 /* use this width if no delimiter was found */
1449 width(b) = null_delimiter_space;
1451 if (delta != NULL) {
1452 *delta = 0;
1454 if (stack != NULL)
1455 *stack = false ;
1457 if (!flat) {
1458 /* vertical variant */
1459 shift_amount(b) = half(height(b) - depth(b));
1460 if (shift) {
1461 shift_amount(b) -= math_axis(s);
1464 delete_attribute_ref(att);
1465 return b;
1468 @ The next subroutine is much simpler; it is used for numerators and
1469 denominators of fractions as well as for displayed operators and
1470 their limits above and below. It takes a given box~|b| and
1471 changes it so that the new box is centered in a box of width~|w|.
1472 The centering is done by putting \.{\\hss} glue at the left and right
1473 of the list inside |b|, then packaging the new box; thus, the
1474 actual box might not really be centered, if it already contains
1475 infinite glue.
1477 The given box might contain a single character whose italic correction
1478 has been added to the width of the box; in this case a compensating
1479 kern is inserted.
1482 static pointer rebox(pointer b, scaled w)
1484 pointer p, q, r, att; /* temporary registers for list manipulation */
1485 internal_font_number f; /* font in a one-character box */
1486 scaled v; /* width of a character without italic correction */
1488 if ((width(b) != w) && (list_ptr(b) != null)) {
1489 if (type(b) == vlist_node) {
1490 p = hpack(b, 0, additional, -1);
1491 reset_attributes(p, node_attr(b));
1492 b = p;
1494 p = list_ptr(b);
1495 att = node_attr(b);
1496 add_node_attr_ref(att);
1497 if ((is_char_node(p)) && (vlink(p) == null)) {
1498 f = font(p);
1499 v = char_width(f, character(p));
1500 if (v != width(b)) {
1501 q = new_kern(width(b) - v);
1502 reset_attributes(q, att);
1503 couple_nodes(p,q);
1506 list_ptr(b) = null;
1507 flush_node(b);
1508 b = new_glue(ss_glue);
1509 reset_attributes(b, att);
1510 couple_nodes(b,p);
1511 while (vlink(p) != null)
1512 p = vlink(p);
1513 q = new_glue(ss_glue);
1514 reset_attributes(q, att);
1515 couple_nodes(p,q);
1516 r = hpack(b, w, exactly, -1);
1517 reset_attributes(r, att);
1518 delete_attribute_ref(att);
1519 return r;
1520 } else {
1521 width(b) = w;
1522 return b;
1526 @ Here is a subroutine that creates a new glue specification from another
1527 one that is expressed in `\.{mu}', given the value of the math unit.
1530 #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
1532 static pointer math_glue(pointer g, scaled m)
1534 pointer p; /* the new glue specification */
1535 int n; /* integer part of |m| */
1536 scaled f; /* fraction part of |m| */
1537 n = x_over_n(m, unity);
1538 f = tex_remainder;
1539 if (f < 0) {
1540 decr(n);
1541 f = f + unity;
1543 p = new_node(glue_spec_node, 0);
1544 width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
1545 stretch_order(p) = stretch_order(g);
1546 if (stretch_order(p) == normal)
1547 stretch(p) = mu_mult(stretch(g));
1548 else
1549 stretch(p) = stretch(g);
1550 shrink_order(p) = shrink_order(g);
1551 if (shrink_order(p) == normal)
1552 shrink(p) = mu_mult(shrink(g));
1553 else
1554 shrink(p) = shrink(g);
1555 return p;
1558 @ The |math_kern| subroutine removes |mu_glue| from a kern node, given
1559 the value of the math unit.
1562 static void math_kern(pointer p, scaled m)
1564 int n; /* integer part of |m| */
1565 scaled f; /* fraction part of |m| */
1566 if (subtype(p) == mu_glue) {
1567 n = x_over_n(m, unity);
1568 f = tex_remainder;
1569 if (f < 0) {
1570 decr(n);
1571 f = f + unity;
1573 width(p) = mu_mult(width(p));
1574 subtype(p) = italic_kern;
1578 @ @c
1579 void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
1581 int callback_id;
1582 int a, sfix;
1583 lua_State *L = Luas;
1584 if (p == null) {
1585 vlink(temp_head) = null;
1586 return;
1588 finalize_math_parameters();
1589 callback_id = callback_defined(mlist_to_hlist_callback);
1590 if (callback_id > 0) {
1591 sfix = lua_gettop(L);
1592 if (!get_callback(L, callback_id)) {
1593 lua_settop(L, sfix);
1594 return;
1596 alink(p) = null ;
1597 nodelist_to_lua(L, p);
1599 lua_pushstring(L, math_style_names[mstyle]);
1601 lua_push_math_style_name(L,mstyle);
1602 lua_pushboolean(L, penalties);
1603 if (lua_pcall(L, 3, 1, 0) != 0) { /* 3 args, 1 result */
1604 char errmsg[256]; /* temp hack ... we will have a formatted error */
1605 snprintf(errmsg, 255, "error: %s\n", lua_tostring(L, -1));
1606 errmsg[255]='\0';
1607 lua_settop(L, sfix);
1608 normal_error("mlist to hlist",errmsg); /* to be done */
1609 return;
1611 a = nodelist_from_lua(L);
1612 /* alink(vlink(a)) = null; */
1613 lua_settop(L, sfix);
1614 vlink(temp_head) = a;
1615 } else if (callback_id == 0) {
1616 mlist_to_hlist(p, penalties, mstyle);
1617 } else {
1618 vlink(temp_head) = null;
1622 @ The recursion in |mlist_to_hlist| is due primarily to a subroutine
1623 called |clean_box| that puts a given noad field into a box using a given
1624 math style; |mlist_to_hlist| can call |clean_box|, which can call
1625 |mlist_to_hlist|.
1626 @^recursion@>
1628 The box returned by |clean_box| is ``clean'' in the
1629 sense that its |shift_amount| is zero.
1632 static pointer clean_box(pointer p, int s, int cur_style)
1634 pointer q; /* beginning of a list to be boxed */
1635 pointer x; /* box to be returned */
1636 pointer r; /* temporary pointer */
1637 pointer mlist = null; /* beginning of mlist to be translated */
1638 switch (type(p)) {
1639 case math_char_node:
1640 mlist = new_noad();
1641 r = math_clone(p);
1642 nucleus(mlist) = r;
1643 break;
1644 case sub_box_node:
1645 q = math_list(p);
1646 goto FOUND;
1647 break;
1648 case sub_mlist_node:
1649 mlist = math_list(p);
1650 break;
1651 default:
1652 q = new_null_box();
1653 goto FOUND;
1655 mlist_to_hlist(mlist, false, s);
1656 q = vlink(temp_head); /* recursive call */
1657 setup_cur_size(cur_style);
1658 FOUND:
1659 if (is_char_node(q) || (q == null))
1660 x = hpack(q, 0, additional, -1);
1661 else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
1662 x = q; /* it's already clean */
1663 else
1664 x = hpack(q, 0, additional, -1);
1665 if (x != q && q != null)
1666 reset_attributes(x, node_attr(q));
1667 /* Here we save memory space in a common case. */
1668 q = list_ptr(x);
1669 if (is_char_node(q)) {
1670 r = vlink(q);
1671 if (r != null) {
1672 if (vlink(r) == null) {
1673 if (!is_char_node(r)) {
1674 if (type(r) == kern_node) {
1675 /* unneeded italic correction */
1676 flush_node(r);
1677 vlink(q) = null;
1683 return x;
1686 @ It is convenient to have a procedure that converts a |math_char|
1687 field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
1688 to the font code and character code of a given noad field.
1689 It also takes care of issuing error messages for
1690 nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
1691 after |fetch| has acted, and the field will also have been reset to |null|.
1693 The outputs of |fetch| are placed in global variables.
1696 internal_font_number cur_f; /* the |font| field of a |math_char| */
1697 int cur_c; /* the |character| field of a |math_char| */
1699 @ Here we unpack the |math_char| field |a|.
1701 @c static void fetch(pointer a)
1703 cur_c = math_character(a);
1704 cur_f = fam_fnt(math_fam(a), cur_size);
1705 if (cur_f == null_font) {
1706 char *msg;
1707 const char *hlp[] = {
1708 "Somewhere in the math formula just ended, you used the",
1709 "stated character from an undefined font family. For example,",
1710 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
1711 "and I'll try to forget that I needed that character.",
1712 NULL
1714 msg = xmalloc(256);
1715 snprintf(msg, 255, "\\%s%d is undefined (character %d)",
1716 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
1717 tex_error(msg, hlp);
1718 free(msg);
1719 } else if (!(char_exists(cur_f, cur_c))) {
1720 char_warning(cur_f, cur_c);
1724 @ We need to do a lot of different things, so |mlist_to_hlist| makes two
1725 passes over the given mlist.
1727 The first pass does most of the processing: It removes ``mu'' spacing from
1728 glue, it recursively evaluates all subsidiary mlists so that only the
1729 top-level mlist remains to be handled, it puts fractions and square roots
1730 and such things into boxes, it attaches subscripts and superscripts, and
1731 it computes the overall height and depth of the top-level mlist so that
1732 the size of delimiters for a |fence_noad| will be known.
1733 The hlist resulting from each noad is recorded in that noad's |new_hlist|
1734 field, an integer field that replaces the |nucleus| or |thickness|.
1735 @^recursion@>
1737 The second pass eliminates all noads and inserts the correct glue and
1738 penalties between nodes.
1741 static void assign_new_hlist(pointer q, pointer r)
1743 switch (type(q)) {
1744 case fraction_noad:
1745 math_list(numerator(q)) = null;
1746 flush_node(numerator(q));
1747 numerator(q) = null;
1748 math_list(denominator(q)) = null;
1749 flush_node(denominator(q));
1750 denominator(q) = null;
1751 break;
1752 case radical_noad:
1753 case simple_noad:
1754 case accent_noad:
1755 if (nucleus(q) != null) {
1756 math_list(nucleus(q)) = null;
1757 flush_node(nucleus(q));
1758 nucleus(q) = null;
1760 break;
1762 new_hlist(q) = r;
1765 @ @c
1766 #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
1768 @ Most of the actual construction work of |mlist_to_hlist| is done
1769 by procedures with names like |make_fraction|, |make_radical|, etc. To
1770 illustrate the general setup of such procedures, let's begin with a
1771 couple of simple ones.
1774 static void make_over(pointer q, int cur_style)
1776 pointer p;
1777 p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
1778 overbar_vgap(cur_style), overbar_rule(cur_style),
1779 overbar_kern(cur_style), node_attr(nucleus(q)));
1780 math_list(nucleus(q)) = p;
1781 type(nucleus(q)) = sub_box_node;
1784 static void make_under(pointer q, int cur_style)
1786 pointer p, x, y, r; /* temporary registers for box construction */
1787 scaled delta; /* overall height plus depth */
1788 x = clean_box(nucleus(q), cur_style, cur_style);
1789 p = new_kern(underbar_vgap(cur_style));
1790 reset_attributes(p, node_attr(q));
1791 couple_nodes(x,p);
1792 r = do_fraction_rule(underbar_rule(cur_style), node_attr(q));
1793 couple_nodes(p,r);
1794 y = vpackage(x, 0, additional, max_dimen, math_direction);
1795 reset_attributes(y, node_attr(q));
1796 delta = height(y) + depth(y) + underbar_kern(cur_style);
1797 height(y) = height(x);
1798 depth(y) = delta - height(y);
1799 math_list(nucleus(q)) = y;
1800 type(nucleus(q)) = sub_box_node;
1803 static void make_vcenter(pointer q)
1805 pointer v; /* the box that should be centered vertically */
1806 scaled delta; /* its height plus depth */
1807 v = math_list(nucleus(q));
1808 if (type(v) != vlist_node)
1809 confusion("vcenter");
1810 delta = height(v) + depth(v);
1811 height(v) = math_axis(cur_size) + half(delta);
1812 depth(v) = delta - height(v);
1815 @ According to the rules in the \.{DVI} file specifications, we ensure alignment
1816 @^square roots@>
1817 between a square root sign and the rule above its nucleus by assuming that the
1818 baseline of the square-root symbol is the same as the bottom of the rule. The
1819 height of the square-root symbol will be the thickness of the rule, and the
1820 depth of the square-root symbol should exceed or equal the height-plus-depth
1821 of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
1822 placed so that the actual clearance is |psi| plus half the excess.
1825 static void make_hextension(pointer q, int cur_style)
1827 pointer e, p;
1828 halfword w;
1829 boolean stack = false;
1830 e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL);
1831 w = width(e);
1832 if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
1833 if (radicalmiddle(q)) {
1834 p = new_kern(half(radicalwidth(q)-w));
1835 reset_attributes(p, node_attr(q));
1836 couple_nodes(p,e);
1837 e = p;
1838 w = radicalwidth(q);
1839 } else if (radicalexact(q)) {
1840 w = radicalwidth(q);
1843 e = hpack(e, 0, additional, -1);
1844 width(e) = w ;
1845 reset_attributes(e, node_attr(q));
1846 math_list(nucleus(q)) = e;
1847 left_delimiter(q) = null;
1850 static void make_radical(pointer q, int cur_style)
1852 pointer x, y, p, l1, l2; /* temporary registers for box construction */
1853 scaled delta, clr, theta, h; /* dimensions involved in the calculation */
1854 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
1855 clr = radical_vgap(cur_style);
1856 theta = radical_rule(cur_style);
1857 if (theta == undefined_math_parameter) {
1858 /* a real radical */
1859 theta = fraction_rule(cur_style);
1860 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1862 If |y| is a composite then set |theta| to the height of its top
1863 character, else set it to the height of |y|.
1865 l1 = list_ptr(y);
1866 if ((l1 != null) && (type(l1) == hlist_node)) {
1867 /* possible composite */
1868 l2 = list_ptr(l1);
1869 if ((l2 != null) && (type(l2) == glyph_node)) {
1870 /* top character */
1871 theta = char_height(font(l2), character(l2));
1872 } else {
1873 theta = height(y);
1875 } else {
1876 theta = height(y);
1878 } else {
1879 /* not really a radical but we use its node, historical sharing (like in mathml) */
1880 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1882 left_delimiter(q) = null;
1883 delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
1884 if (delta > 0) {
1885 /* increase the actual clearance */
1886 clr = clr + half(delta);
1888 shift_amount(y) = (height(y) - theta) - (height(x) + clr);
1889 h = depth(y) + height(y);
1890 p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y));
1891 couple_nodes(y,p);
1892 if (degree(q) != null) {
1893 scaled wr, br, ar;
1894 pointer r = clean_box(degree(q), script_script_style, cur_style);
1895 reset_attributes(r, node_attr(degree(q)));
1896 wr = width(r);
1897 if (wr == 0) {
1898 flush_node(r);
1899 } else {
1900 br = radical_degree_before(cur_style);
1901 ar = radical_degree_after(cur_style);
1902 if (-ar > (wr + br))
1903 ar = -(wr + br);
1904 x = new_kern(ar);
1905 reset_attributes(x, node_attr(degree(q)));
1906 couple_nodes(x,y);
1907 shift_amount(r) =
1908 -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
1909 depth(y) - shift_amount(y));
1910 couple_nodes(r,x);
1911 x = new_kern(br);
1912 reset_attributes(x, node_attr(degree(q)));
1913 couple_nodes(x,r);
1914 y = x;
1916 /* for \.{\\Uroot ..{<list>}{}} : */
1917 math_list(degree(q)) = null;
1918 flush_node(degree(q));
1920 p = hpack(y, 0, additional, -1);
1921 reset_attributes(p, node_attr(q));
1922 math_list(nucleus(q)) = p;
1923 type(nucleus(q)) = sub_box_node;
1926 @ Construct a vlist box
1929 static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
1931 pointer p; /* temporary register for box construction */
1932 pointer v = new_null_box();
1933 type(v) = vlist_node;
1934 height(v) = shift_up + height(x);
1935 depth(v) = depth(y) + shift_down;
1936 reset_attributes(v, node_attr(q));
1937 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
1938 reset_attributes(p, node_attr(q));
1939 couple_nodes(p,y);
1940 couple_nodes(x,p);
1941 list_ptr(v) = x;
1942 return v;
1945 /* when exact use radicalwidth (y is delimiter) */
1947 @ @c
1949 #define fixup_widths(q,x,y) do { \
1950 if (width(y) >= width(x)) { \
1951 if (radicalwidth(q) != zero_glue) { \
1952 shift_amount(x) += half(width(y)-width(x)) ; \
1954 width(x) = width(y); \
1955 } else { \
1956 if (radicalwidth(q) != zero_glue) { \
1957 shift_amount(y) += half(width(x)-width(y)) ; \
1959 width(y) = width(x); \
1961 } while (0)
1964 #define check_radical(q,stack,r,t) do { \
1965 if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != zero_glue) && (radicalwidth(q) != width(r))) { \
1966 if (radicalleft(q)) { \
1967 halfword p = new_kern(radicalwidth(q)-width(r)); \
1968 reset_attributes(p, node_attr(q)); \
1969 couple_nodes(p,r); \
1970 r = hpack(p, 0, additional, -1); \
1971 width(r) = radicalwidth(q); \
1972 reset_attributes(r, node_attr(q)); \
1973 } else if (radicalmiddle(q)) { \
1974 halfword p = new_kern(half(radicalwidth(q)-width(r))); \
1975 reset_attributes(p, node_attr(q)); \
1976 couple_nodes(p,r); \
1977 r = hpack(p, 0, additional, -1); \
1978 width(r) = radicalwidth(q); \
1979 reset_attributes(r, node_attr(q)); \
1980 } else if (radicalright(q)) { \
1981 /* also kind of exact compared to vertical */ \
1982 r = hpack(r, 0, additional, -1); \
1983 width(r) = radicalwidth(q); \
1984 reset_attributes(r, node_attr(q)); \
1987 } while (0)
1989 #define check_widths(q,p) do { \
1990 if (radicalwidth(q) != zero_glue) { \
1991 wd = radicalwidth(q); \
1992 } else { \
1993 wd = width(p); \
1995 } while (0)
1997 @ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
2000 static void make_over_delimiter(pointer q, int cur_style)
2002 pointer x, y, v; /* temporary registers for box construction */
2003 scaled shift_up, shift_down, clr, delta, wd;
2004 boolean stack;
2005 x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
2006 check_widths(q,x);
2007 y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2008 left_delimiter(q) = null;
2009 check_radical(q,stack,y,x);
2010 fixup_widths(q, x, y);
2011 shift_up = over_delimiter_bgap(cur_style);
2012 shift_down = 0;
2013 clr = over_delimiter_vgap(cur_style);
2014 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2015 if (delta > 0) {
2016 shift_up = shift_up + delta;
2018 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2019 width(v) = width(x); /* this also equals |width(y)| */
2020 math_list(nucleus(q)) = v;
2021 type(nucleus(q)) = sub_box_node;
2024 @ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
2027 static void make_under_delimiter(pointer q, int cur_style)
2029 pointer x, y, v; /* temporary registers for box construction */
2030 scaled shift_up, shift_down, clr, delta, wd;
2031 boolean stack;
2032 y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
2033 check_widths(q,y);
2034 x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2035 left_delimiter(q) = null;
2036 check_radical(q,stack,x,y);
2037 fixup_widths(q, x, y);
2038 shift_up = 0;
2039 shift_down = under_delimiter_bgap(cur_style);
2040 clr = under_delimiter_vgap(cur_style);
2041 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2042 if (delta > 0) {
2043 shift_down = shift_down + delta;
2045 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2046 width(v) = width(y); /* this also equals |width(y)| */
2047 math_list(nucleus(q)) = v;
2048 type(nucleus(q)) = sub_box_node;
2051 @ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
2054 static void make_delimiter_over(pointer q, int cur_style)
2056 pointer x, y, v; /* temporary registers for box construction */
2057 scaled shift_up, shift_down, clr, actual, wd;
2058 boolean stack;
2059 y = clean_box(nucleus(q), cur_style, cur_style);
2060 check_widths(q,y);
2061 x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2062 left_delimiter(q) = null;
2063 check_radical(q,stack,x,y);
2064 fixup_widths(q, x, y);
2065 shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
2066 shift_down = 0;
2067 clr = over_delimiter_vgap(cur_style);
2068 actual = shift_up - height(y);
2069 if (actual < clr) {
2070 shift_up = shift_up + (clr-actual);
2072 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2073 width(v) = width(x); /* this also equals |width(y)| */
2074 math_list(nucleus(q)) = v;
2075 type(nucleus(q)) = sub_box_node;
2078 @ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
2081 static void make_delimiter_under(pointer q, int cur_style)
2083 pointer x, y, v; /* temporary registers for box construction */
2084 scaled shift_up, shift_down, clr, actual, wd;
2085 boolean stack;
2086 x = clean_box(nucleus(q), cur_style, cur_style);
2087 check_widths(q,x);
2088 y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2089 left_delimiter(q) = null;
2090 check_radical(q,stack,y,x);
2091 fixup_widths(q, x, y);
2092 shift_up = 0;
2093 shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
2094 clr = under_delimiter_vgap(cur_style);
2095 actual = shift_down - depth(x);
2096 if (actual<clr) {
2097 shift_down += (clr-actual);
2099 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2100 width(v) = width(y); /* this also equals |width(y)| */
2101 math_list(nucleus(q)) = v;
2102 type(nucleus(q)) = sub_box_node;
2105 @ Slants are not considered when placing accents in math mode. The accenter is
2106 centered over the accentee, and the accent width is treated as zero with
2107 respect to the size of the final box.
2110 #define TOP_CODE 1
2111 #define BOT_CODE 2
2112 #define OVERLAY_CODE 4
2113 #define STRETCH_ACCENT_CODE 8
2115 static boolean compute_accent_skew(pointer q, int flags, scaled *s)
2117 pointer p; /* temporary register for box construction */
2118 boolean s_is_absolute = false; /* will be true if a top-accent is placed in |s| */
2119 if (type(nucleus(q)) == math_char_node) {
2120 fetch(nucleus(q));
2121 if (is_new_mathfont(cur_f)) {
2123 there is no bot_accent so let's assume similarity
2125 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2126 *s = char_top_accent(cur_f, cur_c);
2127 if (*s != INT_MIN) {
2128 s_is_absolute = true;
2130 } else {
2131 *s = char_bot_accent(cur_f, cur_c);
2132 if (*s != INT_MIN) {
2133 s_is_absolute = true;
2137 *s = char_top_accent(cur_f, cur_c);
2138 if (*s != INT_MIN) {
2139 s_is_absolute = true;
2141 } else {
2142 if (flags & TOP_CODE) {
2143 *s = get_kern(cur_f, cur_c, skew_char(cur_f));
2144 } else {
2145 *s = 0;
2148 } else if (type(nucleus(q)) == sub_mlist_node) {
2150 if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
2152 * use the positioning of the nucleus of that noad, recursing until
2153 * the inner most |accent_noad|. This way multiple stacked accents are
2154 * aligned to the inner most one.
2156 p = math_list(nucleus(q));
2157 if (type(p) == accent_noad) {
2158 s_is_absolute = compute_accent_skew(p, flags, s);
2162 return s_is_absolute;
2165 static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
2167 pointer p, r, x, y; /* temporary registers for box construction */
2168 scaled s; /* amount to skew the accent to the right */
2169 scaled h; /* height of character being accented */
2170 scaled delta; /* space to remove between accent and accentee */
2171 scaled w; /* width of the accentee, not including sub/superscripts */
2172 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
2173 scaled fraction ;
2174 scaled target ;
2175 extinfo *ext;
2176 pointer attr_p;
2177 attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
2178 fraction = accentfraction(q);
2179 c = cur_c;
2180 f = cur_f;
2181 s = 1;
2182 if (fraction == 0) {
2183 fraction = 1000;
2185 /* Compute the amount of skew, or set |s| to an alignment point */
2186 s_is_absolute = compute_accent_skew(q, flags, &s);
2187 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
2188 w = width(x);
2189 h = height(x);
2190 if (is_new_mathfont(cur_f) && !s_is_absolute) {
2191 s = half(w);
2192 s_is_absolute = true;
2194 /* Switch to a larger accent if available and appropriate */
2195 y = null;
2196 ext = NULL;
2197 if (flags & OVERLAY_CODE) {
2198 if (fraction > 0) {
2199 target = xn_over_d(h,fraction,1000);
2200 } else {
2201 target = h;
2203 } else {
2204 if (fraction > 0) {
2205 target = xn_over_d(w,fraction,1000);
2206 } else {
2207 target = w;
2210 if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
2211 while (1) {
2212 if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
2213 /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
2214 y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node);
2215 break;
2216 } else if (char_tag(f, c) != list_tag) {
2217 break;
2218 } else {
2219 int yy = char_remainder(f, c);
2220 if (!char_exists(f, yy)) {
2221 break;
2222 } else if (flags & OVERLAY_CODE) {
2223 if (char_height(f, yy) > target) {
2224 break;
2226 } else {
2227 if (char_width(f, yy) > target)
2228 break;
2230 c = yy;
2234 if (y == null) {
2235 y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */
2237 if (flags & TOP_CODE) {
2238 if (h < accent_base_height(f)) {
2239 delta = h;
2240 } else {
2241 delta = accent_base_height(f);
2243 } else if (flags & OVERLAY_CODE) {
2244 delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */
2245 } else {
2246 delta = 0; /* hm */
2248 if ((supscr(q) != null) || (subscr(q) != null)) {
2249 if (type(nucleus(q)) == math_char_node) {
2250 /* swap the subscript and superscript into box |x| */
2251 flush_node_list(x);
2252 x = new_noad();
2253 r = math_clone(nucleus(q));
2254 nucleus(x) = r;
2255 supscr(x) = supscr(q);
2256 supscr(q) = null;
2257 subscr(x) = subscr(q);
2258 subscr(q) = null;
2259 type(nucleus(q)) = sub_mlist_node;
2260 math_list(nucleus(q)) = x;
2261 x = clean_box(nucleus(q), cur_style, cur_style);
2262 delta = delta + height(x) - h;
2263 h = height(x);
2266 /* the top accents of both characters are aligned */
2267 if (s_is_absolute) {
2268 scaled sa;
2269 if (ext != NULL) {
2270 /* if the accent is extensible just take the center */
2271 sa = half(width(y));
2272 } else {
2274 there is no bot_accent so let's assume similarity
2276 if (flags & BOT_CODE) {
2277 sa = char_bot_accent(f, c);
2278 } else {
2279 sa = char_top_accent(f, c);
2282 sa = char_top_accent(f, c);
2284 if (sa == INT_MIN) {
2285 /* just take the center */
2286 sa = half(width(y));
2288 if (math_direction == dir_TRT) {
2289 shift_amount(y) = s + sa - width(y);
2290 } else {
2291 shift_amount(y) = s - sa;
2293 } else {
2294 if (width(y)== 0) {
2295 shift_amount(y) = s + w;
2296 } else if (math_direction == dir_TRT) {
2297 shift_amount(y) = s + width(y); /* ok? */
2298 } else {
2299 shift_amount(y) = s + half(w - width(y));
2302 width(y) = 0;
2303 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2304 p = new_kern(-delta);
2305 reset_attributes(p, node_attr(q));
2306 couple_nodes(p,x);
2307 couple_nodes(y,p);
2308 } else {
2309 couple_nodes(x,y);
2310 y = x;
2312 r = vpackage(y, 0, additional, max_dimen, math_direction);
2313 reset_attributes(r, node_attr(q));
2314 width(r) = width(x);
2315 y = r;
2316 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2317 if (height(y) < h) {
2318 /* make the height of box |y| equal to |h| */
2319 p = new_kern(h - height(y));
2320 reset_attributes(p, node_attr(q));
2321 try_couple_nodes(p,list_ptr(y));
2322 list_ptr(y) = p;
2323 height(y) = h;
2325 } else {
2326 shift_amount(y) = -(h - height(y));
2328 math_list(nucleus(q)) = y;
2329 type(nucleus(q)) = sub_box_node;
2332 static void make_math_accent(pointer q, int cur_style)
2334 int topstretch = !(subtype(q) % 2);
2335 int botstretch = !(subtype(q) / 2);
2337 if (top_accent_chr(q) != null) {
2338 fetch(top_accent_chr(q));
2339 if (char_exists(cur_f, cur_c)) {
2340 do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2342 flush_node(top_accent_chr(q));
2343 top_accent_chr(q) = null;
2345 if (bot_accent_chr(q) != null) {
2346 fetch(bot_accent_chr(q));
2347 if (char_exists(cur_f, cur_c)) {
2348 do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2350 flush_node(bot_accent_chr(q));
2351 bot_accent_chr(q) = null;
2353 if (overlay_accent_chr(q) != null) {
2354 fetch(overlay_accent_chr(q));
2355 if (char_exists(cur_f, cur_c)) {
2356 do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
2358 flush_node(overlay_accent_chr(q));
2359 overlay_accent_chr(q) = null;
2363 @ The |make_fraction| procedure is a bit different because it sets
2364 |new_hlist(q)| directly rather than making a sub-box.
2367 static void make_fraction(pointer q, int cur_style)
2369 pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */
2370 scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2;
2371 /* dimensions for box calculations */
2372 if (thickness(q) == default_code)
2373 thickness(q) = fraction_rule(cur_style);
2375 Create equal-width boxes |x| and |z| for the numerator and denominator,
2376 and compute the default amounts |shift_up| and |shift_down| by which they
2377 are displaced from the baseline
2380 x = clean_box(numerator(q), num_style(cur_style), cur_style);
2381 z = clean_box(denominator(q), denom_style(cur_style), cur_style);
2383 if (middle_delimiter(q) != null) {
2384 delta = 0;
2385 m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2386 middle_delimiter(q) = null;
2387 } else {
2388 m = null ;
2389 if (width(x) < width(z)) {
2390 x = rebox(x, width(z));
2391 } else {
2392 z = rebox(z, width(x));
2396 if (m != null) {
2397 shift_up = 0;
2398 shift_down = 0;
2399 } else if (thickness(q) == 0) {
2400 shift_up = stack_num_up(cur_style);
2401 shift_down = stack_denom_down(cur_style);
2403 the numerator and denominator must be separated by a certain minimum
2404 clearance, called |clr| in the following program. The difference between
2405 |clr| and the actual clearance is |2delta|.
2407 clr1 = stack_vgap(cur_style);
2408 delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
2409 if (delta > 0) {
2410 shift_up = shift_up + delta;
2411 shift_down = shift_down + delta;
2413 } else {
2414 shift_up = fraction_num_up(cur_style);
2415 shift_down = fraction_denom_down(cur_style);
2417 in the case of a fraction line, the minimum clearance depends on the actual
2418 thickness of the line.
2420 clr1 = fraction_num_vgap(cur_style);
2421 clr2 = fraction_denom_vgap(cur_style);
2422 delta = half(thickness(q));
2423 if (fractionexact(q)) {
2424 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis(cur_size) + delta));
2425 delta2 = clr2 - ((shift_down - height(z)) + (math_axis(cur_size) - delta));
2426 } else {
2427 delta = half(thickness(q));
2428 clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
2429 clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
2430 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis(cur_size) + delta));
2431 delta2 = clr2 - ((shift_down - height(z)) + (math_axis(cur_size) - delta));
2433 if (delta1 > 0) {
2434 shift_up = shift_up + delta1;
2436 if (delta2 > 0) {
2437 shift_down = shift_down + delta2;
2440 if (m != null) {
2442 construct a hlist box for the fraction, according to |hgap| and |vgap|
2444 shift_up = skewed_fraction_vgap(cur_style);
2446 if (!fractionnoaxis(q)) {
2447 shift_up += half(math_axis(cur_size));
2450 shift_down = shift_up;
2451 v = new_null_box();
2452 reset_attributes(v, node_attr(q));
2453 type(v) = hlist_node;
2454 list_ptr(v) = x;
2455 width(v) = width(x);
2456 height(v) = height(x) + shift_up;
2457 depth(v) = depth(x);
2458 shift_amount(v) = - shift_up;
2459 x = v;
2461 v = new_null_box();
2462 reset_attributes(v, node_attr(q));
2463 type(v) = hlist_node;
2464 list_ptr(v) = z;
2465 width(v) = width(z);
2466 height(v) = height(z);
2467 depth(v) = depth(z) + shift_down;
2468 shift_amount(v) = shift_down;
2469 z = v;
2471 v = new_null_box();
2472 reset_attributes(v, node_attr(q));
2473 type(v) = hlist_node;
2474 if (height(x) > height(z)) {
2475 height(v) = height(x);
2476 } else {
2477 height(v) = height(z);
2479 if (depth(x) > depth(z)) {
2480 depth(v) = depth(x);
2481 } else {
2482 depth(v) = depth(z);
2484 if (height(m) > height(v)) {
2485 height(v) = height(m);
2487 if (depth(m) > depth(v)) {
2488 depth(v) = depth(m);
2491 if (fractionexact(q)) {
2492 delta1 = -half(skewed_fraction_hgap(cur_style));
2493 delta2 = delta1;
2494 width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
2495 } else {
2496 delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
2497 delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
2498 width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
2499 width(m) = 0;
2502 p1 = new_kern(delta1);
2503 reset_attributes(p1, node_attr(q));
2504 p2 = new_kern(delta2);
2505 reset_attributes(p2, node_attr(q));
2507 couple_nodes(x,p1);
2508 couple_nodes(p1,m);
2509 couple_nodes(m,p2);
2510 couple_nodes(p2,z);
2512 list_ptr(v) = x;
2513 } else {
2515 construct a vlist box for the fraction, according to |shift_up| and |shift_down|
2517 v = new_null_box();
2518 type(v) = vlist_node;
2519 height(v) = shift_up + height(x);
2520 depth(v) = depth(z) + shift_down;
2521 width(v) = width(x); /* this also equals |width(z)| */
2522 reset_attributes(v, node_attr(q));
2523 if (thickness(q) == 0) {
2524 p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
2525 couple_nodes(p,z);
2526 } else {
2527 y = do_fraction_rule(thickness(q), node_attr(q));
2528 p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down));
2529 reset_attributes(p, node_attr(q));
2530 couple_nodes(y,p);
2531 couple_nodes(p,z);
2532 p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2533 couple_nodes(p,y);
2535 reset_attributes(p, node_attr(q));
2536 couple_nodes(x,p);
2537 list_ptr(v) = x;
2540 put the fraction into a box with its delimiters, and make |new_hlist(q)|
2541 point to it
2543 if (is_new_mathfont(cur_f)) {
2544 if (math_use_old_fraction_scaling) {
2545 delta = fraction_del_size_old(cur_style);
2546 } else {
2547 delta = fraction_del_size_new(cur_style);
2549 if (delta == undefined_math_parameter) {
2550 delta = get_delimiter_height(depth(v), height(v), true);
2552 } else {
2553 delta = fraction_del_size_old(cur_style);
2556 l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2557 left_delimiter(q) = null;
2558 r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2559 right_delimiter(q) = null;
2560 couple_nodes(l,v);
2561 couple_nodes(v,r);
2562 y = hpack(l, 0, additional, -1);
2563 reset_attributes(y, node_attr(q));
2564 assign_new_hlist(q, y);
2567 @ If the nucleus of an |op_noad| is a single character, it is to be
2568 centered vertically with respect to the axis, after first being enlarged
2569 (via a character list in the font) if we are in display style. The normal
2570 convention for placing displayed limits is to put them above and below the
2571 operator in display style.
2573 The italic correction is removed from the character if there is a subscript
2574 and the limits are not being displayed. The |make_op| routine returns the
2575 value that should be used as an offset between subscript and superscript.
2577 After |make_op| has acted, |subtype(q)| will be |limits| if and only if
2578 the limits have been set above and below the operator. In that case,
2579 |new_hlist(q)| will already contain the desired final box.
2582 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
2583 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style);
2585 static scaled make_op(pointer q, int cur_style)
2587 scaled delta = 0; /* offset between subscript and superscript */
2588 scaled dummy = 0;
2589 pointer p, v, x, y, z, n; /* temporary registers for box construction */
2590 int c; /* register for character examination */
2591 scaled shift_up, shift_down; /* dimensions for box calculation */
2592 boolean axis_shift = false;
2593 scaled ok_size;
2594 if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
2595 subtype(q) = op_noad_type_limits;
2597 if (type(nucleus(q)) == math_char_node) {
2598 fetch(nucleus(q));
2599 if (cur_style < text_style) {
2600 /* try to make it larger */
2601 ok_size = minimum_operator_size(cur_style);
2602 if (ok_size != undefined_math_parameter) {
2603 /* creating a temporary delimiter is the cleanest way */
2604 y = new_node(delim_node, 0);
2605 reset_attributes(y, node_attr(q));
2606 small_fam(y) = math_fam(nucleus(q));
2607 small_char(y) = math_character(nucleus(q));
2608 x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta);
2609 if (is_new_mathfont(cur_f)) {
2610 /* we never added italic correction */
2611 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2612 /* remove italic correction */
2613 width(x) -= delta;
2615 } else {
2616 ok_size = height_plus_depth(cur_f, cur_c) + 1;
2617 while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
2618 c = char_remainder(cur_f, cur_c);
2619 if (!char_exists(cur_f, c))
2620 break;
2621 cur_c = c;
2622 math_character(nucleus(q)) = c;
2624 delta = char_italic(cur_f, cur_c);
2625 x = clean_box(nucleus(q), cur_style, cur_style);
2626 if (delta != null) {
2627 if (is_new_mathfont(cur_f)) {
2628 /* we never added italic correction */
2629 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2630 /* remove italic correction */
2631 width(x) = width(x) - delta;
2634 axis_shift = true;
2636 } else {
2637 /* normal size */
2638 delta = char_italic(cur_f, cur_c);
2639 x = clean_box(nucleus(q), cur_style, cur_style);
2640 if (delta != 0) {
2641 if (is_new_mathfont(cur_f)) {
2642 /* we never added italic correction */
2643 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2644 /* remove italic correction */
2645 width(x) = width(x) - delta;
2648 axis_shift = true;
2650 if (axis_shift) {
2651 /* center vertically */
2652 shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size);
2654 type(nucleus(q)) = sub_box_node;
2655 math_list(nucleus(q)) = x;
2658 /* we now handle op_nod_type_no_limits here too */
2660 if (subtype(q) == op_noad_type_no_limits) {
2661 if (is_new_mathfont(cur_f)) {
2662 if (delta != 0) {
2663 delta = half(delta) ;
2665 p = check_nucleus_complexity(q, &dummy, cur_style);
2666 if ((subscr(q) == null) && (supscr(q) == null)) {
2667 assign_new_hlist(q, p);
2668 } else {
2669 make_scripts(q, p, 0, cur_style, delta, -delta);
2671 delta = 0;
2672 } else {
2673 /* similar code then the caller (before CHECK_DIMENSIONS) */
2674 p = check_nucleus_complexity(q, &delta, cur_style);
2675 if ((subscr(q) == null) && (supscr(q) == null)) {
2676 assign_new_hlist(q, p);
2677 } else {
2678 make_scripts(q, p, delta, cur_style, 0, 0);
2681 } else if (subtype(q) == op_noad_type_limits) {
2682 /* The following program builds a vlist box |v| for displayed limits. The
2683 width of the box is not affected by the fact that the limits may be skewed. */
2684 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2685 y = clean_box(nucleus(q), cur_style, cur_style);
2686 z = clean_box(subscr(q), sub_style(cur_style), cur_style);
2687 v = new_null_box();
2688 reset_attributes(v, node_attr(q));
2689 type(v) = vlist_node;
2690 if (is_new_mathfont(cur_f)) {
2691 n = null;
2692 if (! math_no_italic_compensation) {
2693 n = nucleus(q);
2694 if (n != null) {
2695 if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
2696 n = math_list(n);
2697 if (n != null) {
2698 if (type(n) == hlist_node) {
2699 n = list_ptr(n); /* just a not scaled char */
2700 while (n != null) {
2701 if (type(n) == glyph_node) {
2702 delta = char_italic(font(n),character(n));
2704 n = vlink(n);
2706 } else {
2707 while (n != null) {
2708 if (type(n) == fence_noad) {
2709 if (delimiteritalic(n) > delta) {
2710 /* we can have dummies, the period ones */
2711 delta = delimiteritalic(n);
2714 n = vlink(n);
2718 } else {
2719 n = nucleus(q);
2720 if (type(n) == math_char_node) {
2721 delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
2727 width(v) = width(y);
2728 if (width(x) > width(v))
2729 width(v) = width(x);
2730 if (width(z) > width(v))
2731 width(v) = width(z);
2732 x = rebox(x, width(v));
2733 y = rebox(y, width(v));
2734 z = rebox(z, width(v));
2735 shift_amount(x) = half(delta);
2736 shift_amount(z) = -shift_amount(x);
2737 /* v is the still empty target */
2738 height(v) = height(y);
2739 depth(v) = depth(y);
2741 attach the limits to |y| and adjust |height(v)|, |depth(v)| to
2742 account for their presence
2744 we use |shift_up| and |shift_down| in the following program for the
2745 amount of glue between the displayed operator |y| and its limits |x| and
2748 the vlist inside box |v| will consist of |x| followed by |y| followed
2749 by |z|, with kern nodes for the spaces between and around them
2751 b: baseline v: minumum gap
2754 if (supscr(q) == null) {
2755 list_ptr(x) = null;
2756 flush_node(x);
2757 list_ptr(v) = y;
2758 } else {
2759 shift_up = limit_above_bgap(cur_style) - depth(x);
2760 if (shift_up < limit_above_vgap(cur_style))
2761 shift_up = limit_above_vgap(cur_style);
2762 p = new_kern(shift_up);
2763 reset_attributes(p, node_attr(q));
2764 couple_nodes(p,y);
2765 couple_nodes(x,p);
2766 p = new_kern(limit_above_kern(cur_style));
2767 reset_attributes(p, node_attr(q));
2768 couple_nodes(p,x);
2769 list_ptr(v) = p;
2770 height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
2772 if (subscr(q) == null) {
2773 list_ptr(z) = null;
2774 flush_node(z);
2775 } else {
2776 shift_down = limit_below_bgap(cur_style) - height(z);
2777 if (shift_down < limit_below_vgap(cur_style))
2778 shift_down = limit_below_vgap(cur_style);
2779 if (shift_down > 0) {
2780 p = new_kern(shift_down);
2781 reset_attributes(p, node_attr(q));
2782 couple_nodes(y,p);
2783 couple_nodes(p,z);
2785 p = new_kern(limit_below_kern(cur_style));
2786 reset_attributes(p, node_attr(q));
2787 couple_nodes(z,p);
2788 depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
2790 if (subscr(q) != null) {
2791 math_list(subscr(q)) = null;
2792 flush_node(subscr(q));
2793 subscr(q) = null;
2795 if (supscr(q) != null) {
2796 math_list(supscr(q)) = null;
2797 flush_node(supscr(q));
2798 supscr(q) = null;
2800 assign_new_hlist(q, v);
2801 if (is_new_mathfont(cur_f)) {
2802 delta = 0;
2805 return delta;
2808 @ A ligature found in a math formula does not create a ligature, because
2809 there is no question of hyphenation afterwards; the ligature will simply be
2810 stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
2812 The |type| is converted to |math_text_char| here if we would not want to
2813 apply an italic correction to the current character unless it belongs
2814 to a math font (i.e., a font with |space=0|).
2816 No boundary characters enter into these ligatures.
2819 #define simple_char_noad(p) (\
2820 (p != null) && \
2821 (type(p) == simple_noad) && \
2822 (subtype(p) <= punct_noad_type) && \
2823 (type(nucleus(p)) == math_char_node) \
2826 #define same_nucleus_fam(p,q) \
2827 (math_fam(nucleus(p)) == math_fam(nucleus(q)))
2829 static void make_ord(pointer q)
2831 int a; /* the left-side character for lig/kern testing */
2832 pointer p, r, s; /* temporary registers for list manipulation */
2833 scaled k; /* a kern */
2834 liginfo lig; /* a ligature */
2835 RESTART:
2836 if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
2837 p = vlink(q);
2838 if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
2839 type(nucleus(q)) = math_text_char_node;
2840 fetch(nucleus(q));
2841 a = cur_c;
2842 /* add italic correction */
2843 if (is_new_mathfont(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
2844 p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
2845 reset_attributes(p, node_attr(q));
2846 couple_nodes(p,vlink(q));
2847 couple_nodes(q,p);
2848 return;
2850 /* construct ligatures, quite unlikely in new math fonts */
2851 if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
2852 cur_c = math_character(nucleus(p));
2854 if character |a| has a kern with |cur_c|, attach the kern after~|q|; or if
2855 it has a ligature with |cur_c|, combine noads |q| and~|p| appropriately;
2856 then |return| if the cursor has moved past a noad, or |goto restart|
2858 note that a ligature between an |ord_noad| and another kind of noad
2859 is replaced by an |ord_noad|, when the two noads collapse into one
2861 we could make a parenthesis (say) change shape when it follows
2862 certain letters. Presumably a font designer will define such
2863 ligatures only when this convention makes sense
2866 if (disable_lig == 0 && has_lig(cur_f, a)) {
2867 lig = get_ligature(cur_f, a, cur_c);
2868 if (is_valid_ligature(lig)) {
2869 check_interrupt(); /* allow a way out of infinite ligature loop */
2870 switch (lig_type(lig)) {
2871 case 1:
2872 /* \.{=:\char`\|} */
2873 case 5:
2874 /* \.{=:\char`\|>} */
2875 math_character(nucleus(q)) = lig_replacement(lig);
2876 break;
2877 case 2:
2878 /* \.{\char`\|=:} */
2879 case 6:
2880 /* \.{\char`\|=:>} */
2881 math_character(nucleus(p)) = lig_replacement(lig);
2882 break;
2883 case 3:
2884 /* \.{\char`\|=:\char`\|} */
2885 case 7:
2886 /* \.{\char`\|=:\char`\|>} */
2887 case 11:
2888 /* \.{\char`\|=:\char`\|>>} */
2889 r = new_noad();
2890 reset_attributes(r, node_attr(q));
2891 s = new_node(math_char_node, 0);
2892 reset_attributes(s, node_attr(q));
2893 nucleus(r) = s;
2894 math_character(nucleus(r)) = lig_replacement(lig);
2895 math_fam(nucleus(r)) = math_fam(nucleus(q));
2896 couple_nodes(q,r);
2897 couple_nodes(r,p);
2898 if (lig_type(lig) < 11)
2899 type(nucleus(r)) = math_char_node;
2900 else
2901 /* prevent combination */
2902 type(nucleus(r)) = math_text_char_node;
2903 break;
2904 default:
2905 try_couple_nodes(q,vlink(p));
2906 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
2907 s = math_clone(subscr(p));
2908 subscr(q) = s;
2909 s = math_clone(supscr(p));
2910 supscr(q) = s;
2911 math_reset(subscr(p)); /* just in case */
2912 math_reset(supscr(p));
2913 flush_node(p);
2914 break;
2916 if (lig_type(lig) > 3)
2917 return;
2918 type(nucleus(q)) = math_char_node;
2919 goto RESTART;
2922 if (disable_kern == 0 && has_kern(cur_f, a)) {
2923 /* todo: should this use mathkerns? */
2924 k = get_kern(cur_f, a, cur_c);
2925 if (k != 0) {
2926 p = new_kern(k);
2927 reset_attributes(p, node_attr(q));
2928 couple_nodes(p,vlink(q));
2929 couple_nodes(q,p);
2930 return;
2938 @ If the fonts for the left and right bits of a mathkern are not
2939 both new-style fonts, then return a sentinel value meaning:
2940 please use old-style italic correction placement
2943 #define MATH_KERN_NOT_FOUND 0x7FFFFFFF
2945 @ This function tries to find the kern needed for proper cut-ins.
2946 The left side doesn't move, but the right side does, so the first
2947 order of business is to create a staggered fence line on the
2948 left side of the right character.
2950 The microsoft spec says that there are four quadrants, but the
2951 actual images say
2954 static scaled math_kern_at(internal_font_number f, int c, int side, int v)
2956 int h, k, numkerns;
2957 scaled *kerns_heights;
2958 scaled kern = 0;
2959 charinfo *co = char_info(f, c); /* known to exist */
2960 numkerns = get_charinfo_math_kerns(co, side);
2961 #ifdef DEBUG
2962 fprintf(stderr, " entries = %d, height = %d\n", numkerns, v);
2963 #endif
2964 if (numkerns == 0)
2965 return kern;
2966 if (side == top_left_kern) {
2967 kerns_heights = co->top_left_math_kern_array;
2968 } else if (side == bottom_left_kern) {
2969 kerns_heights = co->bottom_left_math_kern_array;
2970 } else if (side == top_right_kern) {
2971 kerns_heights = co->top_right_math_kern_array;
2972 } else if (side == bottom_right_kern) {
2973 kerns_heights = co->bottom_right_math_kern_array;
2974 } else {
2975 confusion("math_kern_at");
2976 kerns_heights = NULL; /* not reached */
2978 #ifdef DEBUG
2979 fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
2980 #endif
2981 if (v < kerns_heights[0])
2982 return kerns_heights[1];
2983 for (k = 0; k < numkerns; k++) {
2984 h = kerns_heights[(k * 2)];
2985 kern = kerns_heights[(k * 2) + 1];
2986 #ifdef DEBUG
2987 if (k > 0)
2988 fprintf(stderr, " entry %d: %d,%d\n", k, h, kern);
2989 #endif
2990 if (h > v) {
2991 return kern;
2994 return kern;
2997 @ @c
2998 static scaled find_math_kern(internal_font_number l_f, int l_c,
2999 internal_font_number r_f, int r_c,
3000 int cmd, scaled shift)
3002 scaled corr_height_top = 0, corr_height_bot = 0;
3003 scaled krn_l = 0, krn_r = 0, krn = 0;
3004 if ((!is_new_mathfont(l_f)) || (!is_new_mathfont(r_f)) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
3005 return MATH_KERN_NOT_FOUND;
3007 if (cmd == sup_mark_cmd) {
3008 corr_height_top = char_height(l_f, l_c);
3009 corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
3010 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
3011 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
3012 #ifdef DEBUG
3013 fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift);
3014 #endif
3015 krn = (krn_l + krn_r);
3016 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
3017 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
3018 #ifdef DEBUG
3019 fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
3020 #endif
3021 if ((krn_l + krn_r) < krn)
3022 krn = (krn_l + krn_r);
3023 return (krn);
3025 } else if (cmd == sub_mark_cmd) {
3026 corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
3027 corr_height_bot = -char_depth(l_f, l_c);
3028 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
3029 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
3030 #ifdef DEBUG
3031 fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
3032 #endif
3033 krn = (krn_l + krn_r);
3034 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
3035 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
3036 #ifdef DEBUG
3037 fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
3038 #endif
3039 if ((krn_l + krn_r) < krn)
3040 krn = (krn_l + krn_r);
3041 return (krn);
3043 } else {
3044 confusion("find_math_kern");
3046 return 0; /* not reached */
3049 @ just a small helper
3051 static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
3053 pointer y;
3054 pointer z = new_kern(delta2);
3055 reset_attributes(z, node_attr(q));
3056 if (new_hlist(q) == null) {
3057 /* this is somewhat weird */
3058 new_hlist(q) = z;
3059 } else {
3060 y = new_hlist(q);
3061 while (vlink(y) != null)
3062 y = vlink(y);
3063 couple_nodes(y,z);
3065 return new_hlist(q);
3070 #ifdef DEBUG
3071 void dump_simple_field(pointer q)
3073 pointer p;
3074 printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q));
3075 switch (type(q)) {
3076 case math_char_node:
3077 printf("mathchar ");
3078 break;
3079 case math_text_char_node:
3080 printf("texchar ");
3081 break;
3082 case sub_box_node:
3083 printf("box ");
3084 break;
3085 case sub_mlist_node:
3086 printf("mlist ");
3087 p = math_list(q);
3088 while (p != null) {
3089 dump_simple_field(p);
3090 p = vlink(p);
3092 break;
3096 void dump_simple_node(pointer q)
3098 printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
3099 printf("nucleus: ");
3100 dump_simple_field(nucleus(q));
3101 printf("\n");
3102 printf("sub: ");
3103 dump_simple_field(subscr(q));
3104 printf("\n");
3105 printf("sup: ");
3106 dump_simple_field(supscr(q));
3107 printf("\n\n");
3109 #endif
3111 @ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
3112 superscript of noad |q| to the list that starts at |new_hlist(q)|,
3113 given that subscript and superscript aren't both empty. The superscript
3114 will be horizontally shifted over |delta1|, the subscript over |delta2|.
3116 We set |shift_down| and |shift_up| to the minimum amounts to shift the
3117 baseline of subscripts and superscripts based on the given nucleus.
3119 Note: We need to look at a character but also at the first one in a sub list
3120 and there we ignore leading kerns and glue. Elsewhere is code that removes
3121 kerns assuming that is italic correction. The heuristics are unreliable for
3122 the new fonts so eventualy there will be an option to ignore such corrections.
3124 @ @c
3125 #define analyze_script(init,su_n,su_f,su_c) do { \
3126 su_n = init; \
3127 if (su_n != null) { \
3128 if (type(su_n) == sub_mlist_node && math_list(su_n)) { \
3129 su_n = math_list(su_n); \
3130 if (su_n != null) { \
3131 while (su_n) { \
3132 if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\
3133 su_n = vlink(su_n); \
3134 } else if (type(su_n) == simple_noad) { \
3135 su_n = nucleus(su_n); \
3136 if (type(su_n) != math_char_node) { \
3137 su_n = null; \
3139 break; \
3140 } else { \
3141 su_n = null; \
3142 break; \
3147 if ((su_n != null) && (type(su_n) == math_char_node)) { \
3148 fetch(su_n); \
3149 if (char_exists(cur_f, cur_c)) { \
3150 su_f = cur_f; \
3151 su_c = cur_c; \
3152 } else { \
3153 su_n = null; \
3157 } while (0)
3159 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
3161 pointer x, y, z; /* temporary registers for box construction */
3162 scaled shift_up, shift_down, clr; /* dimensions in the calculation */
3163 scaled delta1, delta2;
3164 halfword sub_n, sup_n;
3165 internal_font_number sub_f, sup_f;
3166 int sub_c, sup_c;
3167 sub_n = null;
3168 sup_n = null;
3169 sub_f = 0;
3170 sup_f = 0;
3171 sub_c = 0;
3172 sup_c = 0;
3173 delta1 = it;
3174 delta2 = 0;
3176 #ifdef DEBUG
3177 printf("it: %d\n", it);
3178 dump_simple_node(q);
3179 printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
3180 #endif
3181 switch (type(nucleus(q))) {
3182 case math_char_node:
3183 case math_text_char_node:
3184 if ((subscr(q) == null) && (delta1 != 0)) {
3185 /* todo: selective */
3186 x = new_kern(delta1); /* italic correction */
3187 reset_attributes(x, node_attr(nucleus(q)));
3188 couple_nodes(p,x);
3189 delta1 = 0;
3192 assign_new_hlist(q, p);
3193 if (is_char_node(p)) {
3194 shift_up = 0;
3195 shift_down = 0;
3196 } else {
3197 z = hpack(p, 0, additional, -1);
3198 shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */
3199 shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
3200 list_ptr(z) = null;
3201 flush_node(z);
3204 if (is_char_node(p)) {
3205 /* we look at the subscript character (_i) or first character in a list (_{ij}) */
3206 analyze_script(subscr(q),sub_n,sub_f,sub_c);
3207 /* we look at the superscript character (^i) or first character in a list (^{ij}) */
3208 analyze_script(supscr(q),sup_n,sup_f,sup_c);
3211 if (supscr(q) == null) {
3213 construct a subscript box |x| when there is no superscript
3215 when there is a subscript without a superscript, the top of the subscript
3216 should not exceed the baseline plus four-fifths of the x-height.
3218 x = clean_box(subscr(q), sub_style(cur_style), cur_style);
3219 width(x) = width(x) + space_after_script(cur_style);
3220 switch (scripts_mode) {
3221 case 1:
3222 shift_down = sub_shift_down(cur_style) ;
3223 break;
3224 case 2:
3225 shift_down = sub_sup_shift_down(cur_style) ;
3226 break;
3227 case 3:
3228 shift_down = sub_sup_shift_down(cur_style) ;
3229 break;
3230 case 4:
3231 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3232 break;
3233 case 5:
3234 shift_down = sub_shift_down(cur_style) ;
3235 break;
3236 default:
3237 if (shift_down < sub_shift_down(cur_style))
3238 shift_down = sub_shift_down(cur_style);
3239 clr = height(x) - sub_top_max(cur_style);
3240 if (shift_down < clr)
3241 shift_down = clr;
3242 break;
3244 shift_amount(x) = shift_down;
3246 /* now find and correct for horizontal shift */
3247 if (sub_n != null) {
3248 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3249 if (delta2 == MATH_KERN_NOT_FOUND) {
3250 delta2 = subshift ;
3251 } else {
3252 delta2 = delta2 + subshift ;
3254 } else {
3255 delta2 = subshift ;
3257 if (delta2 != 0) {
3258 p = attach_hkern_to_new_hlist(q, delta2);
3261 } else {
3263 construct a superscript box |x|
3265 the bottom of a superscript should never descend below the baseline plus
3266 one-fourth of the x-height.
3268 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
3269 width(x) = width(x) + space_after_script(cur_style);
3270 switch (scripts_mode) {
3271 case 1:
3272 shift_up = sup_shift_up(cur_style);
3273 break;
3274 case 2:
3275 shift_up = sup_shift_up(cur_style) ;
3276 break;
3277 case 3:
3278 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
3279 break;
3280 case 4:
3281 shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3282 break;
3283 case 5:
3284 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
3285 break;
3286 default:
3287 clr = sup_shift_up(cur_style);
3288 if (shift_up < clr)
3289 shift_up = clr;
3290 clr = depth(x) + sup_bottom_min(cur_style);
3291 if (shift_up < clr)
3292 shift_up = clr;
3293 break;
3295 if (subscr(q) == null) {
3296 shift_amount(x) = -shift_up;
3297 /* now find and correct for horizontal shift */
3298 if (sup_n != null) {
3299 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3300 if (clr == MATH_KERN_NOT_FOUND) {
3301 clr = supshift ;
3302 } else {
3303 clr = clr + supshift ;
3305 } else {
3306 clr = supshift;
3308 if (clr != 0) {
3309 p = attach_hkern_to_new_hlist(q, clr);
3311 } else {
3313 construct a sub/superscript combination box |x|, with the superscript offset
3314 by |delta|
3316 when both subscript and superscript are present, the subscript must be
3317 separated from the superscript by at least four times |default_rule_thickness|
3319 if this condition would be violated, the subscript moves down, after which
3320 both subscript and superscript move up so that the bottom of the superscript
3321 is at least as high as the baseline plus four-fifths of the x-height
3323 y = clean_box(subscr(q), sub_style(cur_style), cur_style);
3324 width(y) = width(y) + space_after_script(cur_style);
3325 switch (scripts_mode) {
3326 case 1:
3327 shift_down = sub_shift_down(cur_style) ;
3328 break;
3329 case 2:
3330 shift_down = sub_sup_shift_down(cur_style) ;
3331 break;
3332 case 3:
3333 shift_down = sub_sup_shift_down(cur_style) ;
3334 break;
3335 case 4:
3336 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3337 break;
3338 case 5:
3339 shift_down = sub_shift_down(cur_style) ;
3340 break;
3341 default:
3342 if (shift_down < sub_sup_shift_down(cur_style))
3343 shift_down = sub_sup_shift_down(cur_style);
3344 clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
3345 if (clr > 0) {
3346 shift_down = shift_down + clr;
3347 clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
3348 if (clr > 0) {
3349 shift_up = shift_up + clr;
3350 shift_down = shift_down - clr;
3353 break;
3355 /* now find and correct for horizontal shift */
3356 if (sub_n != null) {
3357 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3358 if (delta2 == MATH_KERN_NOT_FOUND) {
3359 delta2 = subshift ;
3360 } else {
3361 delta2 = delta2 + subshift ;
3363 } else {
3364 delta2 = subshift ;
3366 if (delta2 != 0) {
3367 p = attach_hkern_to_new_hlist(q, delta2);
3370 now the horizontal shift for the superscript; the superscript is also to be shifted
3371 by |delta1| (the italic correction)
3373 clr = MATH_KERN_NOT_FOUND;
3374 if (sup_n != null) {
3375 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3378 /* delta can already have been applied and now be 0 */
3379 if (delta2 == MATH_KERN_NOT_FOUND)
3380 delta2 = - supshift ;
3381 else
3382 delta2 = delta2 - supshift ;
3383 if (clr != MATH_KERN_NOT_FOUND) {
3384 shift_amount(x) = clr + delta1 - delta2;
3385 } else {
3386 shift_amount(x) = delta1 - delta2;
3388 /* todo: only if kern != 0 */
3389 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
3390 reset_attributes(p, node_attr(q));
3391 couple_nodes(x,p);
3392 couple_nodes(p,y);
3393 /* we end up with funny dimensions */
3394 x = vpackage(x, 0, additional, max_dimen, math_direction);
3395 reset_attributes(x, node_attr(q));
3396 shift_amount(x) = shift_down;
3400 if (new_hlist(q) == null) {
3401 new_hlist(q) = x;
3402 } else {
3403 p = new_hlist(q);
3404 while (vlink(p) != null)
3405 p = vlink(p);
3406 couple_nodes(p,x);
3408 if (subscr(q) != null) {
3409 math_list(subscr(q)) = null;
3410 flush_node(subscr(q));
3411 subscr(q) = null;
3413 if (supscr(q) != null) {
3414 math_list(supscr(q)) = null;
3415 flush_node(supscr(q));
3416 supscr(q) = null;
3420 @ The |make_left_right| function constructs a left or right delimiter of
3421 the required size and returns the value |open_noad| or |close_noad|. The
3422 |left_noad_side| and |right_noad_side| will both be based on the original |style|,
3423 so they will have consistent sizes.
3426 static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
3428 scaled delta;
3429 pointer tmp, lst;
3430 scaled hd_asked = 0;
3431 scaled ic = 0;
3432 boolean stack = false;
3433 boolean axis = false;
3435 scaled hd_done = 0;
3436 boolean fitting = true;
3437 boolean fence = false;
3438 int chr = 0;
3439 int cls = 0;
3441 setup_cur_size(style);
3443 if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
3445 hd_asked = delimiterheight(q) + delimiterdepth(q);
3446 tmp = do_delimiter(q, delimiter(q), cur_size, hd_asked, false, style, false, &stack, &ic);
3447 delimiteritalic(q) = ic;
3449 /* beware, a stacked delimiter has a shift but no corrected height/depth (yet) */
3451 if (stack) {
3452 shift_amount(tmp) = delimiterdepth(q);
3456 hd_done = height(tmp) + depth(tmp);
3457 fitting = stack || ((hd_done-hd_asked) == 0);
3459 if (type(delimiter(q)) == delim_node && (small_char(delimiter(q)) != 0)) {
3460 chr = small_char(delimiter(q));
3461 cls = get_math_code(chr).class_value ;
3462 fence = (cls == 4) || (cls == 5) ;
3465 printf("delimiter stack %i fence %i fitting %i\n",stack,fence,fitting);
3468 if (delimiterexact(q)) {
3469 delimiterheight(q) = height(tmp) - shift_amount(tmp);
3470 delimiterdepth(q) = depth(tmp) + shift_amount(tmp);
3472 if (delimiteraxis(q)) {
3473 delimiterheight(q) += math_axis(cur_size);
3474 delimiterdepth(q) -= math_axis(cur_size);
3475 shift_amount(tmp) -= math_axis(cur_size);
3477 lst = new_node(hlist_node,0);
3478 reset_attributes(lst, node_attr(q));
3479 box_dir(lst) = dir_TLT ;
3480 height(lst) = delimiterheight(q);
3481 depth(lst) = delimiterdepth(q);
3482 width(lst) = width(tmp);
3483 list_ptr(lst) = tmp;
3484 tmp = lst ;
3485 } else {
3486 axis = ! delimiternoaxis(q);
3487 delta = get_delimiter_height(max_d,max_h,axis);
3488 tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic);
3489 delimiteritalic(q) = ic;
3491 delimiter(q) = null;
3492 assign_new_hlist(q, tmp);
3493 if (delimiterclass(q) >= ord_noad_type) {
3494 if (delimiterclass(q) <= inner_noad_type) {
3495 return delimiterclass(q);
3496 } else {
3497 return ord_noad_type;
3499 } else if (subtype(q) == no_noad_side) {
3500 return open_noad_type;
3501 } else if (subtype(q) == left_noad_side) {
3502 return open_noad_type;
3503 } else {
3504 return close_noad_type;
3508 @ @c
3509 #define TEXT_STYLES(A,B) do { \
3510 def_math_param(A,display_style,(B),level_one); \
3511 def_math_param(A,cramped_display_style,(B),level_one); \
3512 def_math_param(A,text_style,(B),level_one); \
3513 def_math_param(A,cramped_text_style,(B),level_one); \
3514 } while (0)
3516 #define SCRIPT_STYLES(A,B) do { \
3517 def_math_param(A,script_style,(B),level_one); \
3518 def_math_param(A,cramped_script_style,(B),level_one); \
3519 def_math_param(A,script_script_style,(B),level_one); \
3520 def_math_param(A,cramped_script_script_style,(B),level_one); \
3521 } while (0)
3523 #define ALL_STYLES(A,B) do { \
3524 TEXT_STYLES(A,(B)); \
3525 SCRIPT_STYLES(A,(B)); \
3526 } while (0)
3528 #define SPLIT_STYLES(A,B,C) do { \
3529 TEXT_STYLES(A,(B)); \
3530 SCRIPT_STYLES(A,(C)); \
3531 } while (0)
3534 void initialize_math_spacing(void)
3536 /* *INDENT-OFF* */
3537 ALL_STYLES (math_param_ord_ord_spacing, 0);
3538 ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code);
3539 SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0);
3540 SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0);
3541 ALL_STYLES (math_param_ord_open_spacing, 0);
3542 ALL_STYLES (math_param_ord_close_spacing, 0);
3543 ALL_STYLES (math_param_ord_punct_spacing, 0);
3544 SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0);
3546 ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code);
3547 ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code);
3548 ALL_STYLES (math_param_op_bin_spacing, 0);
3549 SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0);
3550 ALL_STYLES (math_param_op_open_spacing, 0);
3551 ALL_STYLES (math_param_op_close_spacing, 0);
3552 ALL_STYLES (math_param_op_punct_spacing, 0);
3553 SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0);
3555 SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0);
3556 SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0);
3557 ALL_STYLES (math_param_bin_bin_spacing, 0);
3558 ALL_STYLES (math_param_bin_rel_spacing, 0);
3559 SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0);
3560 ALL_STYLES (math_param_bin_close_spacing, 0);
3561 ALL_STYLES (math_param_bin_punct_spacing, 0);
3562 SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0);
3564 SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0);
3565 SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0);
3566 ALL_STYLES (math_param_rel_bin_spacing, 0);
3567 ALL_STYLES (math_param_rel_rel_spacing, 0);
3568 SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0);
3569 ALL_STYLES (math_param_rel_close_spacing, 0);
3570 ALL_STYLES (math_param_rel_punct_spacing, 0);
3571 SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0);
3573 ALL_STYLES (math_param_open_ord_spacing, 0);
3574 ALL_STYLES (math_param_open_op_spacing, 0);
3575 ALL_STYLES (math_param_open_bin_spacing, 0);
3576 ALL_STYLES (math_param_open_rel_spacing, 0);
3577 ALL_STYLES (math_param_open_open_spacing, 0);
3578 ALL_STYLES (math_param_open_close_spacing, 0);
3579 ALL_STYLES (math_param_open_punct_spacing, 0);
3580 ALL_STYLES (math_param_open_inner_spacing, 0);
3582 ALL_STYLES (math_param_close_ord_spacing, 0);
3583 ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code);
3584 SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0);
3585 SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0);
3586 ALL_STYLES (math_param_close_open_spacing, 0);
3587 ALL_STYLES (math_param_close_close_spacing, 0);
3588 ALL_STYLES (math_param_close_punct_spacing, 0);
3589 SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
3591 SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0);
3592 SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0);
3593 ALL_STYLES (math_param_punct_bin_spacing, 0);
3594 SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0);
3595 SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0);
3596 SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
3597 SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
3598 SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
3600 SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0);
3601 ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code);
3602 SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0);
3603 SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0);
3604 SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0);
3605 ALL_STYLES (math_param_inner_close_spacing, 0);
3606 SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
3607 SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
3608 /* *INDENT-ON* */
3611 @ @c
3612 #define both_types(A,B) ((A)*16+(B))
3614 static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
3616 int x = -1;
3617 pointer z = null;
3618 if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
3619 l_type = op_noad_type_normal;
3620 if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
3621 r_type = op_noad_type_normal;
3622 switch (both_types(l_type, r_type)) {
3623 /* *INDENT-OFF* */
3624 case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
3625 case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
3626 case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
3627 case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
3628 case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
3629 case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
3630 case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
3631 case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
3632 case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
3633 case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
3634 #if 0
3635 case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
3636 #endif
3637 case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
3638 case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
3639 case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
3640 case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
3641 case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
3642 case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
3643 case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
3644 #if 0
3645 case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
3646 case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
3647 #endif
3648 case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
3649 #if 0
3650 case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
3651 case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
3652 #endif
3653 case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
3654 case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
3655 case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
3656 #if 0
3657 case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
3658 #endif
3659 case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
3660 case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
3661 case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
3662 case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
3663 case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
3664 case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
3665 case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
3666 #if 0
3667 case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
3668 #endif
3669 case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
3670 case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
3671 case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
3672 case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
3673 case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
3674 case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
3675 case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
3676 case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
3677 case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
3678 case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
3679 case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
3680 case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
3681 case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
3682 case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
3683 case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
3684 #if 0
3685 case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
3686 #endif
3687 case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
3688 case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
3689 case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
3690 case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
3691 case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
3692 case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
3693 case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
3694 case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
3695 case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
3696 case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
3697 case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
3698 case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
3699 case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
3700 /* *INDENT-ON* */
3702 if (x < 0) {
3703 confusion("mathspacing");
3705 if (x != 0) {
3706 pointer y;
3707 if (x <= thick_mu_skip_code) {
3708 /* trap thin/med/thick settings cf. old TeX */
3709 y = math_glue(glue_par(x), mmu);
3710 z = new_glue(y);
3711 glue_ref_count(y) = null;
3712 /* store a symbolic subtype */
3713 subtype(z) = (quarterword) (x + 1);
3714 } else {
3715 y = math_glue(x, mmu);
3716 z = new_glue(y);
3717 glue_ref_count(y) = null;
3720 return z;
3723 @ @c
3724 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style)
3726 pointer p = null;
3727 switch (type(nucleus(q))) {
3728 case math_char_node:
3729 case math_text_char_node:
3730 fetch(nucleus(q));
3731 if (char_exists(cur_f, cur_c)) {
3732 /* we could look at neighbours */
3733 if (is_new_mathfont(cur_f)) {
3734 *delta = 0 ; /* cf spec only the last one */
3735 } else {
3736 *delta = char_italic(cur_f, cur_c);
3738 p = new_glyph(cur_f, cur_c);
3739 reset_attributes(p, node_attr(nucleus(q)));
3740 if (is_new_mathfont(cur_f)) {
3741 if (! math_no_char_italic) {
3742 /* keep italic, but bad with two successive letters */
3743 } else if (get_char_cat_code(cur_c) == 11) {
3744 /* no italic correction in mid-word of text font */
3745 *delta = 0;
3747 } else {
3748 /* no italic correction in mid-word of text font */
3749 if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
3750 *delta = 0;
3753 /* so we only add italic correction when we have no scripts */
3754 if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
3755 pointer x = new_kern(*delta);
3756 reset_attributes(x, node_attr(nucleus(q)));
3757 couple_nodes(p,x);
3758 *delta = 0;
3760 if (is_new_mathfont(cur_f)) {
3761 *delta = char_italic(cur_f, cur_c); /* must be more selective */
3764 break;
3765 case sub_box_node:
3766 p = math_list(nucleus(q));
3767 break;
3768 case sub_mlist_node:
3769 mlist_to_hlist(math_list(nucleus(q)), false, cur_style); /* recursive call */
3770 setup_cur_size(cur_style);
3771 p = hpack(vlink(temp_head), 0, additional, -1);
3772 reset_attributes(p, node_attr(nucleus(q)));
3773 break;
3774 default:
3775 confusion("mlist2"); /* this can't happen mlist2 */
3777 return p;
3780 @ Here is the overall plan of |mlist_to_hlist|, and the list of its
3781 local variables.
3784 void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
3786 pointer q = mlist; /* runs through the mlist */
3787 pointer r = null; /* the most recent noad preceding |q| */
3788 int style = cur_style; /* tuck global parameter away as local variable */
3789 int r_type = simple_noad; /* the |type| of noad |r|, or |op_noad| if |r=null| */
3790 int r_subtype = op_noad_type_normal; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
3791 int t; /* the effective |type| of noad |q| during the second pass */
3792 int t_subtype; /* the effective |subtype| of noad |q| during the second pass */
3793 pointer p = null;
3794 pointer x = null;
3795 pointer y = null;
3796 pointer z = null;
3797 int pen; /* a penalty to be inserted */
3798 scaled max_hl = 0; /* maximum height of the list translated so far */
3799 scaled max_d = 0; /* maximum depth of the list translated so far */
3800 scaled delta; /* italic correction offset for subscript and superscript */
3801 scaled cur_mu; /* the math unit width corresponding to |cur_size| */
3802 r_subtype = op_noad_type_normal;
3803 setup_cur_size(cur_style);
3804 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3805 while (q != null) {
3807 we use the fact that no character nodes appear in an mlist, hence
3808 the field |type(q)| is always present.
3810 one of the things we must do on the first pass is change a |bin_noad| to
3811 an |ord_noad| if the |bin_noad| is not in the context of a binary operator
3813 the values of |r| and |r_type| make this fairly easy
3815 RESWITCH:
3816 delta = 0;
3817 switch (type(q)) {
3818 case simple_noad:
3819 switch (subtype(q)) {
3820 case bin_noad_type:
3821 switch (r_type) {
3822 case simple_noad:
3823 switch (r_subtype) {
3824 case bin_noad_type:
3825 case op_noad_type_normal:
3826 case op_noad_type_limits:
3827 case op_noad_type_no_limits:
3828 case rel_noad_type:
3829 case open_noad_type:
3830 case punct_noad_type:
3831 subtype(q) = ord_noad_type;
3832 goto RESWITCH;
3833 break;
3835 break;
3836 case fence_noad:
3837 if (r_subtype == left_noad_side) {
3838 subtype(q) = ord_noad_type; /* so these can best be the same size */
3839 goto RESWITCH;
3841 break;
3843 break;
3844 case over_noad_type:
3845 make_over(q, cur_style);
3846 break;
3847 case under_noad_type:
3848 make_under(q, cur_style);
3849 break;
3850 case vcenter_noad_type:
3851 make_vcenter(q);
3852 break;
3853 case rel_noad_type:
3854 case close_noad_type:
3855 case punct_noad_type:
3856 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3857 type(r) = simple_noad; /* assumes the same size .. can't this go */
3858 subtype(r) = ord_noad_type;
3860 break;
3861 case op_noad_type_normal:
3862 case op_noad_type_limits:
3863 case op_noad_type_no_limits:
3864 delta = make_op(q, cur_style);
3865 if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
3866 goto CHECK_DIMENSIONS;
3867 break;
3868 case ord_noad_type:
3869 make_ord(q);
3870 break;
3871 case open_noad_type:
3872 case inner_noad_type:
3873 break;
3875 break;
3876 case fence_noad:
3877 if (subtype(q) != left_noad_side)
3878 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3879 type(r) = simple_noad; /* assumes the same size */
3880 subtype(r) = ord_noad_type;
3882 goto DONE_WITH_NOAD;
3883 break;
3884 case fraction_noad:
3885 make_fraction(q, cur_style);
3886 goto CHECK_DIMENSIONS;
3887 break;
3888 case radical_noad:
3889 if (subtype(q) == 7)
3890 make_hextension(q, cur_style);
3891 else if (subtype(q) == 6)
3892 make_delimiter_over(q, cur_style);
3893 else if (subtype(q) == 5)
3894 make_delimiter_under(q, cur_style);
3895 else if (subtype(q) == 4)
3896 make_over_delimiter(q, cur_style);
3897 else if (subtype(q) == 3)
3898 make_under_delimiter(q, cur_style);
3899 else
3900 make_radical(q, cur_style);
3901 break;
3902 case accent_noad:
3903 make_math_accent(q, cur_style);
3904 break;
3905 case style_node:
3906 cur_style = subtype(q);
3907 setup_cur_size(cur_style);
3908 cur_mu = x_over_n(get_math_quad(cur_style), 18);
3909 goto DONE_WITH_NODE;
3910 break;
3911 case choice_node:
3912 switch (cur_style / 2) {
3913 case 0: /* |display_style=0| */
3914 choose_mlist(display_mlist);
3915 break;
3916 case 1: /* |text_style=2| */
3917 choose_mlist(text_mlist);
3918 break;
3919 case 2: /* |script_style=4| */
3920 choose_mlist(script_mlist);
3921 break;
3922 case 3: /* |script_script_style=6| */
3923 choose_mlist(script_script_mlist);
3924 break;
3926 flush_node_list(display_mlist(q));
3927 flush_node_list(text_mlist(q));
3928 flush_node_list(script_mlist(q));
3929 flush_node_list(script_script_mlist(q));
3930 type(q) = style_node;
3931 subtype(q) = (quarterword) cur_style;
3932 if (p != null) {
3933 z = vlink(q);
3934 couple_nodes(q,p);
3935 while (vlink(p) != null)
3936 p = vlink(p);
3937 try_couple_nodes(p,z);
3939 goto DONE_WITH_NODE;
3940 break;
3941 case ins_node:
3942 case mark_node:
3943 case adjust_node:
3944 case boundary_node:
3945 case whatsit_node:
3946 case penalty_node:
3947 case disc_node:
3948 goto DONE_WITH_NODE;
3949 break;
3950 case rule_node:
3951 if (height(q) > max_hl)
3952 max_hl = height(q);
3953 if (depth(q) > max_d)
3954 max_d = depth(q);
3955 goto DONE_WITH_NODE;
3956 break;
3957 case glue_node:
3959 conditional math glue (`\.{\\nonscript}') results in a |glue_node|
3960 pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
3961 the node following will be eliminated if it is a glue or kern node and if the
3962 current size is different from |text_size|
3964 unconditional math glue (`\.{\\muskip}') is converted to normal glue by
3965 multiplying the dimensions by |cur_mu|
3968 if (subtype(q) == mu_glue) {
3969 x = glue_ptr(q);
3970 y = math_glue(x, cur_mu);
3971 delete_glue_ref(x);
3972 glue_ptr(q) = y;
3973 subtype(q) = normal;
3974 } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
3975 p = vlink(q);
3976 if (p != null)
3977 if ((type(p) == glue_node) || (type(p) == kern_node)) {
3978 couple_nodes(q,vlink(p));
3979 vlink(p) = null;
3980 flush_node_list(p);
3983 goto DONE_WITH_NODE;
3984 break;
3985 case kern_node:
3986 math_kern(q, cur_mu);
3987 goto DONE_WITH_NODE;
3988 break;
3989 default:
3990 confusion("mlist1");
3993 When we get to the following part of the program, we have ``fallen through''
3994 from cases that did not lead to |check_dimensions| or |done_with_noad| or
3995 |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
3996 converted to an hlist, and whose subscripts and superscripts need to be
3997 appended if they are present.
3999 If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
4000 by which a superscript should be moved right with respect to a subscript
4001 when both are present.
4004 p = check_nucleus_complexity(q, &delta, cur_style);
4006 if ((subscr(q) == null) && (supscr(q) == null)) {
4007 assign_new_hlist(q, p);
4008 } else {
4009 /* top, bottom */
4010 make_scripts(q, p, delta, cur_style, 0, 0);
4012 CHECK_DIMENSIONS:
4013 z = hpack(new_hlist(q), 0, additional, -1);
4014 if (height(z) > max_hl)
4015 max_hl = height(z);
4016 if (depth(z) > max_d)
4017 max_d = depth(z);
4018 list_ptr(z) = null;
4019 /* only drop the \.{\\hbox} */
4020 flush_node(z);
4021 DONE_WITH_NOAD:
4022 r = q;
4023 r_type = type(r);
4024 r_subtype = subtype(r);
4025 if (r_type == fence_noad) {
4026 r_subtype = left_noad_side;
4027 cur_style = style;
4028 setup_cur_size(cur_style);
4029 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4031 DONE_WITH_NODE:
4032 q = vlink(q);
4034 if (r_type == simple_noad && r_subtype == bin_noad_type) {
4035 type(r) = simple_noad;
4036 subtype(r) = ord_noad_type;
4039 Make a second pass over the mlist, removing all noads and inserting the
4040 proper spacing and penalties.
4042 We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
4043 The second pass simply goes through and hooks everything together with the
4044 proper glue and penalties. It also handles the |fence_noad|s that
4045 might be present, since |max_hl| and |max_d| are now known. Variable |p| points
4046 to a node at the current end of the final hlist.
4048 p = temp_head;
4049 vlink(p) = null;
4050 q = mlist;
4051 r_type = 0;
4052 r_subtype = 0;
4053 cur_style = style;
4054 setup_cur_size(cur_style);
4055 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4056 NEXT_NODE:
4057 while (q != null) {
4059 If node |q| is a style node, change the style and |goto delete_q|;
4060 otherwise if it is not a noad, put it into the hlist,
4061 advance |q|, and |goto done|; otherwise set |s| to the size
4062 of noad |q|, set |t| to the associated type (|ord_noad..
4063 inner_noad|), and set |pen| to the associated penalty
4065 Just before doing the big |case| switch in the second pass, the program
4066 sets up default values so that most of the branches are short.
4068 t = simple_noad;
4069 t_subtype = ord_noad_type;
4070 pen = inf_penalty;
4071 switch (type(q)) {
4072 case simple_noad:
4073 t_subtype = subtype(q);
4074 switch (t_subtype) {
4075 case bin_noad_type:
4076 pen = bin_op_penalty;
4077 break;
4078 case rel_noad_type:
4079 pen = rel_penalty;
4080 break;
4081 case vcenter_noad_type:
4082 case over_noad_type:
4083 case under_noad_type:
4084 t_subtype = ord_noad_type;
4085 break;
4087 case radical_noad:
4088 break;
4089 case accent_noad:
4090 break;
4091 case fraction_noad:
4092 t = simple_noad;
4093 t_subtype = inner_noad_type;
4094 break;
4095 case fence_noad:
4096 t_subtype = make_left_right(q, style, max_d, max_hl);
4097 break;
4098 case style_node:
4099 /* Change the current style and |goto delete_q| */
4100 cur_style = subtype(q);
4101 setup_cur_size(cur_style);
4102 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4103 goto DELETE_Q;
4104 break;
4105 case whatsit_node:
4106 case penalty_node:
4107 case rule_node:
4108 case disc_node:
4109 case adjust_node:
4110 case ins_node:
4111 case mark_node:
4112 case glue_node:
4113 case kern_node:
4114 couple_nodes(p,q);
4115 p = q;
4116 q = vlink(q);
4117 vlink(p) = null;
4118 goto NEXT_NODE;
4119 break;
4120 default:
4121 confusion("mlist3");
4123 /* Append inter-element spacing based on |r_type| and |t| */
4124 if (r_type > 0) {
4125 /* not the first noad */
4126 z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
4127 if (z != null) {
4128 reset_attributes(z, node_attr(p));
4129 couple_nodes(p,z);
4130 p = z;
4134 Append any |new_hlist| entries for |q|, and any appropriate penalties
4136 We insert a penalty node after the hlist entries of noad |q| if |pen|
4137 is not an ``infinite'' penalty, and if the node immediately following |q|
4138 is not a penalty node or a |rel_noad| or absent entirely.
4140 if (new_hlist(q) != null) {
4141 couple_nodes(p,new_hlist(q));
4142 do {
4143 p = vlink(p);
4144 } while (vlink(p) != null);
4146 if (penalties && vlink(q) != null && pen < inf_penalty) {
4147 r_type = type(vlink(q));
4148 r_subtype = subtype(vlink(q));
4149 if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) {
4150 z = new_penalty(pen);
4151 reset_attributes(z, node_attr(q));
4152 couple_nodes(p,z);
4153 p = z;
4156 if (type(q) == fence_noad && subtype(q) == right_noad_side) {
4157 t = simple_noad;
4158 t_subtype = open_noad_type;
4160 r_type = t;
4161 r_subtype = t_subtype;
4162 DELETE_Q:
4163 r = q;
4164 q = vlink(q);
4166 The m-to-hlist conversion takes place in-place, so the various dependant
4167 fields may not be freed (as would happen if |flush_node| was called).
4169 A low-level |free_node| is easier than attempting to nullify such dependant
4170 fields for all possible node and noad types.
4172 if (nodetype_has_attributes(type(r)))
4173 delete_attribute_ref(node_attr(r));
4174 reset_node_properties(r);
4175 free_node(r, get_node_size(type(r), subtype(r)));