beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / tex / mlist.w
blobfbded284414aacc4ab148a9cfd2876a8c1df063f
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 int_par(math_old_code)
55 #define math_no_italic_compensation int_par(math_no_italic_compensation_code)
56 #define math_no_char_italic int_par(math_no_char_italic_code)
57 #define math_use_old_fraction_scaling int_par(math_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_old(a) get_math_param(a, math_param_fraction_del_size)
366 #define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap)
367 #define skewed_fraction_vgap(a) get_math_param_or_error(a, skewed_fraction_vgap)
369 #define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap)
370 #define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap)
371 #define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern)
373 #define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap)
374 #define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap)
375 #define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern)
377 #define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop)
378 #define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop)
379 #define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down)
380 #define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down)
381 #define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up)
382 #define sub_top_max(a) get_math_param_or_error(a, sub_top_max)
383 #define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min)
384 #define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max)
385 #define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap)
387 #define space_after_script(a) get_math_param_or_error(a, space_after_script)
389 @ @c
390 void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
392 if (is_new_mathfont(f)) { /* fix all known parameters */
394 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
395 font_size(f), lvl);
396 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
397 font_size(f), lvl);
398 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
399 font_MATH_par(f, AxisHeight), lvl);
400 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
401 font_MATH_par(f, AxisHeight), lvl);
402 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
403 font_MATH_par(f, OverbarExtraAscender), lvl);
404 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
405 font_MATH_par(f, OverbarExtraAscender), lvl);
406 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
407 font_MATH_par(f, OverbarRuleThickness), lvl);
408 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
409 font_MATH_par(f, OverbarRuleThickness), lvl);
410 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
411 font_MATH_par(f, OverbarVerticalGap), lvl);
412 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
413 font_MATH_par(f, OverbarVerticalGap), lvl);
414 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
415 font_MATH_par(f, UnderbarExtraDescender), lvl);
416 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
417 font_MATH_par(f, UnderbarExtraDescender), lvl);
418 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
419 font_MATH_par(f, UnderbarRuleThickness), lvl);
420 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
421 font_MATH_par(f, UnderbarRuleThickness), lvl);
422 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
423 font_MATH_par(f, UnderbarVerticalGap), lvl);
424 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
425 font_MATH_par(f, UnderbarVerticalGap), lvl);
427 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
428 font_MATH_par(f, StretchStackGapAboveMin), lvl);
429 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
430 font_MATH_par(f, StretchStackGapAboveMin), lvl);
431 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
432 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
433 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
434 font_MATH_par(f, StretchStackBottomShiftDown), lvl);
436 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
437 font_MATH_par(f, StretchStackGapBelowMin), lvl);
438 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
439 font_MATH_par(f, StretchStackGapBelowMin), lvl);
440 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
441 font_MATH_par(f, StretchStackTopShiftUp), lvl);
442 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
443 font_MATH_par(f, StretchStackTopShiftUp), lvl);
445 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
446 font_MATH_par(f, StackTopShiftUp), lvl);
447 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
448 font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
449 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
450 font_MATH_par(f, StackBottomShiftDown), lvl);
451 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
452 font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
453 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
454 font_MATH_par(f, StackGapMin), lvl);
455 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
456 font_MATH_par(f, StackDisplayStyleGapMin), lvl);
458 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
459 font_MATH_par(f, RadicalExtraAscender), lvl);
460 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
461 font_MATH_par(f, RadicalExtraAscender), lvl);
463 DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
464 font_MATH_par(f, DisplayOperatorMinHeight), lvl);
466 DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
467 font_MATH_par(f, RadicalRuleThickness), lvl);
468 DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
469 font_MATH_par(f, RadicalRuleThickness), lvl);
470 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
471 font_MATH_par(f, RadicalVerticalGap), lvl);
472 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
473 font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
474 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
475 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
476 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
477 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
478 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
479 font_MATH_par(f, RadicalKernAfterDegree), lvl);
480 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
481 font_MATH_par(f, RadicalKernAfterDegree), lvl);
482 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
483 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
484 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
485 font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
487 if (size_id == text_size) {
488 def_math_param(math_param_sup_shift_up, display_style,
489 font_MATH_par(f, SuperscriptShiftUp), lvl);
490 def_math_param(math_param_sup_shift_up, cramped_display_style,
491 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
492 def_math_param(math_param_sup_shift_up, text_style,
493 font_MATH_par(f, SuperscriptShiftUp), lvl);
494 def_math_param(math_param_sup_shift_up, cramped_text_style,
495 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
496 } else if (size_id == script_size) {
497 def_math_param(math_param_sup_shift_up, script_style,
498 font_MATH_par(f, SuperscriptShiftUp), lvl);
499 def_math_param(math_param_sup_shift_up, cramped_script_style,
500 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
501 } else if (size_id == script_script_size) {
502 def_math_param(math_param_sup_shift_up, script_script_style,
503 font_MATH_par(f, SuperscriptShiftUp), lvl);
504 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
505 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
508 DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
509 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
510 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
511 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
512 DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
513 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
514 DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
515 font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
516 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
517 font_MATH_par(f, SubscriptShiftDown), lvl);
518 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
519 font_MATH_par(f, SubscriptShiftDown), lvl);
521 if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
522 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
523 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
524 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
525 font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
526 } else {
527 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
528 font_MATH_par(f, SubscriptShiftDown), lvl);
529 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
530 font_MATH_par(f, SubscriptShiftDown), lvl);
533 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
534 font_MATH_par(f, SubscriptTopMax), lvl);
535 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
536 font_MATH_par(f, SubscriptTopMax), lvl);
537 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
538 font_MATH_par(f, SuperscriptBottomMin), lvl);
539 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
540 font_MATH_par(f, SuperscriptBottomMin), lvl);
541 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
542 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
543 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
544 font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
545 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
546 font_MATH_par(f, SubSuperscriptGapMin), lvl);
547 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
548 font_MATH_par(f, SubSuperscriptGapMin), lvl);
550 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
551 font_MATH_par(f, UpperLimitGapMin), lvl);
552 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
553 font_MATH_par(f, UpperLimitGapMin), lvl);
554 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
555 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
556 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
557 font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
558 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
559 0, lvl);
560 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
561 0, lvl);
562 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
563 font_MATH_par(f, LowerLimitGapMin), lvl);
564 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
565 font_MATH_par(f, LowerLimitGapMin), lvl);
566 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
567 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
568 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
569 font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
570 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
571 0, lvl);
572 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
573 0, lvl);
575 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
576 font_MATH_par(f, FractionRuleThickness), lvl);
577 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
578 font_MATH_par(f, FractionRuleThickness), lvl);
579 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
580 font_MATH_par(f, FractionNumeratorGapMin), lvl);
581 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
582 font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
583 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
584 font_MATH_par(f, FractionNumeratorShiftUp), lvl);
585 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
586 font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
587 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
588 font_MATH_par(f, FractionDenominatorGapMin), lvl);
589 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
590 font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
591 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
592 font_MATH_par(f, FractionDenominatorShiftDown), lvl);
593 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
594 font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
596 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
597 font_MATH_par(f, FractionDelimiterSize), lvl);
598 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
599 font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
601 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
602 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
603 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
604 font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
605 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
606 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
607 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
608 font_MATH_par(f, SkewedFractionVerticalGap), lvl);
610 DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
611 font_MATH_par(f, SpaceAfterScript), lvl);
612 DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
613 font_MATH_par(f, SpaceAfterScript), lvl);
615 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
616 font_MATH_par(f, MinConnectorOverlap), lvl);
617 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
618 font_MATH_par(f, MinConnectorOverlap), lvl);
620 } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
622 /* fix old-style |sy| parameters */
624 DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
625 math_quad(size_id), lvl);
626 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
627 math_quad(size_id), lvl);
628 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
629 axis_height(size_id), lvl);
630 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
631 axis_height(size_id), lvl);
632 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
633 num3(size_id), lvl);
634 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
635 num1(size_id), lvl);
636 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
637 denom2(size_id), lvl);
638 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
639 denom1(size_id), lvl);
640 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
641 num2(size_id), lvl);
642 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
643 num1(size_id), lvl);
644 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
645 denom2(size_id), lvl);
646 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
647 denom1(size_id), lvl);
648 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
649 delim2(size_id), lvl);
650 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
651 delim1(size_id), lvl);
653 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
654 0, lvl);
655 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
656 0, lvl);
657 DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
658 0, lvl);
659 DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
660 0, lvl);
662 if (size_id == text_size) {
663 def_math_param(math_param_sup_shift_up, display_style,
664 sup1(size_id), lvl);
665 def_math_param(math_param_sup_shift_up, cramped_display_style,
666 sup3(size_id), lvl);
667 def_math_param(math_param_sup_shift_up, text_style,
668 sup2(size_id), lvl);
669 def_math_param(math_param_sup_shift_up, cramped_text_style,
670 sup3(size_id), lvl);
671 } else if (size_id == script_size) {
672 def_math_param(math_param_sub_shift_drop, display_style,
673 sub_drop(size_id), lvl);
674 def_math_param(math_param_sub_shift_drop, cramped_display_style,
675 sub_drop(size_id), lvl);
676 def_math_param(math_param_sub_shift_drop, text_style,
677 sub_drop(size_id), lvl);
678 def_math_param(math_param_sub_shift_drop, cramped_text_style,
679 sub_drop(size_id), lvl);
680 def_math_param(math_param_sup_shift_drop, display_style,
681 sup_drop(size_id), lvl);
682 def_math_param(math_param_sup_shift_drop, cramped_display_style,
683 sup_drop(size_id), lvl);
684 def_math_param(math_param_sup_shift_drop, text_style,
685 sup_drop(size_id), lvl);
686 def_math_param(math_param_sup_shift_drop, cramped_text_style,
687 sup_drop(size_id), lvl);
688 def_math_param(math_param_sup_shift_up, script_style,
689 sup2(size_id), lvl);
690 def_math_param(math_param_sup_shift_up, cramped_script_style,
691 sup3(size_id), lvl);
692 } else if (size_id == script_script_size) {
693 def_math_param(math_param_sub_shift_drop, script_style,
694 sub_drop(size_id), lvl);
695 def_math_param(math_param_sub_shift_drop, cramped_script_style,
696 sub_drop(size_id), lvl);
697 def_math_param(math_param_sub_shift_drop, script_script_style,
698 sub_drop(size_id), lvl);
699 def_math_param(math_param_sub_shift_drop,
700 cramped_script_script_style, sub_drop(size_id), lvl);
701 def_math_param(math_param_sup_shift_drop, script_style,
702 sup_drop(size_id), lvl);
703 def_math_param(math_param_sup_shift_drop, cramped_script_style,
704 sup_drop(size_id), lvl);
705 def_math_param(math_param_sup_shift_drop, script_script_style,
706 sup_drop(size_id), lvl);
707 def_math_param(math_param_sup_shift_drop,
708 cramped_script_script_style, sup_drop(size_id), lvl);
709 def_math_param(math_param_sup_shift_up, script_script_style,
710 sup2(size_id), lvl);
711 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
712 sup3(size_id), lvl);
715 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
716 sub1(size_id), lvl);
717 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
718 sub1(size_id), lvl);
719 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
720 sub2(size_id), lvl);
721 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
722 sub2(size_id), lvl);
723 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
724 (abs(math_x_height(size_id) * 4) / 5), lvl);
725 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
726 (abs(math_x_height(size_id) * 4) / 5), lvl);
727 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
728 (abs(math_x_height(size_id)) / 4), lvl);
729 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
730 (abs(math_x_height(size_id)) / 4), lvl);
731 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
732 (abs(math_x_height(size_id) * 4) / 5), lvl);
733 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
734 (abs(math_x_height(size_id) * 4) / 5), lvl);
737 The display-size |radical_vgap| is done twice because it needs
738 values from both the sy and the ex font.
741 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
742 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
743 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
744 60, lvl);
745 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
746 60, lvl);
747 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
748 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
749 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
750 xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
751 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
752 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
753 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
754 (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
756 } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
758 /* fix old-style |ex| parameters */
760 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
761 default_rule_thickness(size_id), lvl);
762 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
763 default_rule_thickness(size_id), lvl);
764 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
765 3 * default_rule_thickness(size_id), lvl);
766 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
767 default_rule_thickness(size_id), lvl);
768 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
769 default_rule_thickness(size_id), lvl);
770 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
771 3 * default_rule_thickness(size_id), lvl);
772 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
773 default_rule_thickness(size_id), lvl);
774 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
775 default_rule_thickness(size_id), lvl);
776 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
777 3 * default_rule_thickness(size_id), lvl);
778 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
779 default_rule_thickness(size_id), lvl);
780 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
781 default_rule_thickness(size_id), lvl);
782 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
783 3 * default_rule_thickness(size_id), lvl);
784 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
785 default_rule_thickness(size_id), lvl);
786 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
787 default_rule_thickness(size_id), lvl);
788 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
789 (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
790 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
791 3 * default_rule_thickness(size_id), lvl);
792 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
793 7 * default_rule_thickness(size_id), lvl);
794 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
795 default_rule_thickness(size_id), lvl);
796 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
797 default_rule_thickness(size_id), lvl);
798 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
799 default_rule_thickness(size_id), lvl);
800 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
801 3 * default_rule_thickness(size_id), lvl);
802 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
803 default_rule_thickness(size_id), lvl);
804 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
805 3 * default_rule_thickness(size_id), lvl);
806 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
807 big_op_spacing1(size_id), lvl);
808 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
809 big_op_spacing1(size_id), lvl);
810 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
811 big_op_spacing3(size_id), lvl);
812 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
813 big_op_spacing3(size_id), lvl);
814 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
815 big_op_spacing5(size_id), lvl);
816 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
817 big_op_spacing5(size_id), lvl);
818 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
819 big_op_spacing2(size_id), lvl);
820 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
821 big_op_spacing2(size_id), lvl);
822 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
823 big_op_spacing4(size_id), lvl);
824 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
825 big_op_spacing4(size_id), lvl);
826 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
827 big_op_spacing5(size_id), lvl);
828 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
829 big_op_spacing5(size_id), lvl);
830 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
831 4 * default_rule_thickness(size_id), lvl);
832 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
833 4 * default_rule_thickness(size_id), lvl);
836 All of the |space_after_script|s are done in |finalize_math_parameters|
837 because the \.{\\scriptspace} may have been altered by the user
840 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
841 0, lvl);
842 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
843 0, lvl);
845 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
846 big_op_spacing2(size_id), lvl);
847 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
848 big_op_spacing2(size_id), lvl);
849 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
850 big_op_spacing4(size_id), lvl);
851 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
852 big_op_spacing4(size_id), lvl);
853 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
854 big_op_spacing1(size_id), lvl);
855 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
856 big_op_spacing1(size_id), lvl);
857 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
858 big_op_spacing3(size_id), lvl);
859 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
860 big_op_spacing3(size_id), lvl);
863 The display-size |radical_vgap| is done twice because it needs
864 values from both the sy and the ex font.
867 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
868 (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
873 @ This needs to be called just at the start of |mlist_to_hlist|, for
874 backward compatibility with \.{\\scriptspace}.
877 static void finalize_math_parameters(void)
879 int saved_trace = int_par(tracing_assigns_code);
880 int_par(tracing_assigns_code) = 0;
881 if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
882 def_math_param(math_param_space_after_script, display_style,
883 script_space, level_one);
884 def_math_param(math_param_space_after_script, text_style,
885 script_space, level_one);
886 def_math_param(math_param_space_after_script, script_style,
887 script_space, level_one);
888 def_math_param(math_param_space_after_script, script_script_style,
889 script_space, level_one);
890 def_math_param(math_param_space_after_script, cramped_display_style,
891 script_space, level_one);
892 def_math_param(math_param_space_after_script, cramped_text_style,
893 script_space, level_one);
894 def_math_param(math_param_space_after_script, cramped_script_style,
895 script_space, level_one);
896 def_math_param(math_param_space_after_script, cramped_script_script_style,
897 script_space, level_one);
899 int_par(tracing_assigns_code) = saved_trace;
902 @ In order to convert mlists to hlists, i.e., noads to nodes, we need several
903 subroutines that are conveniently dealt with now.
905 Let us first introduce the macros that make it easy to get at the parameters and
906 other font information. A size code, which is a multiple of 256, is added to a
907 family number to get an index into the table of internal font numbers
908 for each combination of family and size. (Be alert: Size codes get
909 larger as the type gets smaller.)
912 static const char *math_size_string(int s)
914 if (s == text_size)
915 return "textfont";
916 else if (s == script_size)
917 return "scriptfont";
918 else
919 return "scriptscriptfont";
922 @ When the style changes, the following piece of program computes associated
923 information:
926 #define setup_cur_size(a) do { \
927 if (a==script_style || \
928 a==cramped_script_style) \
929 cur_size=script_size; \
930 else if (a==script_script_style || \
931 a==cramped_script_script_style) \
932 cur_size=script_script_size; \
933 else cur_size=text_size; \
934 } while (0)
937 @ a simple routine that creates a flat copy of a nucleus
939 static pointer math_clone(pointer q)
941 pointer x;
942 if (q == null)
943 return null;
944 x = new_node(type(q), 0);
945 reset_attributes(x, node_attr(q));
946 if (type(q) == math_char_node) {
947 math_fam(x) = math_fam(q);
948 math_character(x) = math_character(q);
949 } else {
950 math_list(x) = math_list(q);
952 return x;
955 @ Here is a function that returns a pointer to a rule node having a given
956 thickness |t|. The rule will extend horizontally to the boundary of the vlist
957 that eventually contains it.
960 static pointer do_fraction_rule(scaled t, pointer att)
962 pointer p; /* the new node */
963 p = new_rule(normal_rule);
964 rule_dir(p) = math_direction;
965 height(p) = t;
966 depth(p) = 0;
967 reset_attributes(p, att);
968 return p;
971 @ The |overbar| function returns a pointer to a vlist box that consists of
972 a given box |b|, above which has been placed a kern of height |k| under a
973 fraction rule of thickness |t| under additional space of height |ht|.
976 static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att)
978 pointer p, q; /* nodes being constructed */
979 p = new_kern(k);
980 reset_attributes(p, att);
981 couple_nodes(p,b);
982 q = do_fraction_rule(t, att);
983 couple_nodes(q,p);
984 p = new_kern(ht);
985 reset_attributes(p, att);
986 couple_nodes(p,q);
987 q = vpackage(p, 0, additional, max_dimen, math_direction);
988 reset_attributes(q, att);
989 return q;
992 @ Here is a subroutine that creates a new box, whose list contains a
993 single character, and whose width includes the italic correction for
994 that character. The height or depth of the box will be negative, if
995 the height or depth of the character is negative; thus, this routine
996 may deliver a slightly different result than |hpack| would produce.
999 static pointer char_box(internal_font_number f, int c, pointer bb)
1001 pointer b, p; /* the new box and its character node */
1002 b = new_null_box();
1003 if (is_new_mathfont(f))
1004 width(b) = char_width(f, c);
1005 else
1006 width(b) = char_width(f, c) + char_italic(f, c);
1007 height(b) = char_height(f, c);
1008 depth(b) = char_depth(f, c);
1009 reset_attributes(b, bb);
1010 p = new_char(f, c);
1011 reset_attributes(p, bb);
1012 list_ptr(b) = p;
1013 return b;
1016 @ Another handy subroutine computes the height plus depth of
1017 a given character:
1020 static scaled height_plus_depth(internal_font_number f, int c)
1022 return (char_height(f, c) + char_depth(f, c));
1025 @ When we build an extensible character, it's handy to have the
1026 following subroutine, which puts a given character on top
1027 of the characters already in box |b|:
1030 static scaled stack_into_box(pointer b, internal_font_number f, int c)
1032 pointer p, q; /* new node placed into |b| */
1033 p = char_box(f, c, node_attr(b)); /* italic gets added to width */
1034 if (type(b) == vlist_node) {
1035 try_couple_nodes(p,list_ptr(b));
1036 list_ptr(b) = p;
1037 height(b) = height(p);
1038 if (width(b) < width(p))
1039 width(b) = width(p);
1040 return height_plus_depth(f, c);
1041 } else {
1042 q = list_ptr(b);
1043 if (q == null) {
1044 list_ptr(b) = p;
1045 } else {
1046 while (vlink(q) != null)
1047 q = vlink(q);
1048 couple_nodes(q,p);
1050 if (height(b) < height(p))
1051 height(b) = height(p);
1052 if (depth(b) < depth(p))
1053 depth(b) = depth(p);
1054 return char_width(f, c);
1058 static void stack_glue_into_box(pointer b, scaled min, scaled max) {
1059 pointer p, q; /* new node placed into |b| */
1060 q = new_spec(zero_glue);
1061 width(q) = min;
1062 stretch(q) = max-min;
1063 p = new_glue(q);
1064 reset_attributes(p, node_attr(b));
1065 if (type(b) == vlist_node) {
1066 try_couple_nodes(p,list_ptr(b));
1067 list_ptr(b) = p;
1068 } else {
1069 q = list_ptr(b);
1070 if (q == null) {
1071 list_ptr(b) = p;
1072 } else {
1073 while (vlink(q) != null)
1074 q = vlink(q);
1075 couple_nodes(q,p);
1080 @ \TeX's most important routine for dealing with formulas is called
1081 |mlist_to_hlist|. After a formula has been scanned and represented
1082 as an mlist, this routine converts it to an hlist that can be placed
1083 into a box or incorporated into the text of a paragraph. The
1084 explicit parameter |cur_mlist| points to the first node or noad in
1085 the given mlist (and it might be |null|); the parameter |penalties|
1086 is |true| if penalty nodes for potential line breaks are to be
1087 inserted into the resulting hlist, the parameter |cur_style| is a
1088 style code. After |mlist_to_hlist| has acted, |vlink(temp_head)|
1089 points to the translated hlist.
1091 Since mlists can be inside mlists, the procedure is recursive. And since this
1092 is not part of \TeX's inner loop, the program has been written in a manner
1093 that stresses compactness over efficiency.
1094 @^recursion@>
1097 int cur_size; /* size code corresponding to |cur_style| */
1099 @ @c
1100 static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v,
1101 pointer att, int cur_style, int boxtype)
1103 pointer b; /* new box */
1104 scaled b_max; /* natural (maximum) size of the stack */
1105 scaled s_max; /* amount of possible shrink in the stack */
1106 extinfo *cur;
1107 scaled min_overlap, prev_overlap;
1108 int i; /* a temporary counter number of extensible pieces */
1109 int with_extenders; /* number of times to repeat each repeatable item in |ext| */
1110 int num_extenders, num_normal;
1111 scaled a, c, d;
1113 assert(ext != NULL);
1114 b = new_null_box();
1115 type(b) = (quarterword) boxtype;
1116 reset_attributes(b, att);
1117 min_overlap = connector_overlap_min(cur_style);
1118 assert(min_overlap >= 0);
1119 with_extenders = -1;
1120 num_extenders = 0;
1121 num_normal = 0;
1123 cur = ext;
1124 while (cur != NULL) {
1125 if (!char_exists(f, cur->glyph)) {
1126 const char *hlp[] = {
1127 "Each glyph part in an extensible item should exist in the font.",
1128 "I will give up trying to find a suitable size for now. Fix your font!",
1129 NULL
1131 tex_error("Variant part doesn't exist.", hlp);
1132 width(b) = null_delimiter_space;
1133 return b;
1135 if (cur->extender > 0)
1136 num_extenders++;
1137 else
1138 num_normal++;
1139 /* no negative overlaps or advances are allowed */
1140 if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
1141 const char *hlp[] = {
1142 "All measurements in extensible items should be positive.",
1143 "To get around this problem, I have changed the font metrics.",
1144 "Fix your font!",
1145 NULL
1147 tex_error("Extensible recipe has negative fields.", hlp);
1148 if (cur->start_overlap < 0)
1149 cur->start_overlap = 0;
1150 if (cur->end_overlap < 0)
1151 cur->end_overlap = 0;
1152 if (cur->advance < 0)
1153 cur->advance = 0;
1155 cur = cur->next;
1157 if (num_normal == 0) {
1158 const char *hlp[] = {
1159 "Each extensible recipe should have at least one non-repeatable part.",
1160 "To get around this problem, I have changed the first part to be",
1161 "non-repeatable. Fix your font!",
1162 NULL
1164 tex_error("Extensible recipe has no fixed parts.", hlp);
1165 ext->extender = 0;
1166 num_normal = 1;
1167 num_extenders--;
1170 |ext| holds a linked list of numerous items that may or may not be
1171 repeatable. For the total height, we have to figure out how many items
1172 are needed to create a stack of at least |v|.
1174 The next |while| loop does that. It has two goals: it finds out
1175 the natural height |b_max| of the all the parts needed to reach
1176 at least |v|, and it sets |with_extenders| to the number of times
1177 each of the repeatable items in |ext| has to be repeated to reach
1178 that height.
1181 cur = ext;
1182 b_max = 0;
1183 while (b_max < v && num_extenders > 0) {
1184 b_max = 0;
1185 prev_overlap = 0;
1186 with_extenders++;
1187 for (cur = ext; cur != NULL; cur = cur->next) {
1188 if (cur->extender == 0) {
1189 c = cur->start_overlap;
1190 if (min_overlap < c)
1191 c = min_overlap;
1192 if (prev_overlap < c)
1193 c = prev_overlap;
1194 a = cur->advance;
1195 if (a == 0) {
1196 /* for tfm fonts */
1197 if (boxtype == vlist_node)
1198 a = height_plus_depth(f, cur->glyph);
1199 else
1200 a = char_width(f, cur->glyph);
1201 assert(a >= 0);
1203 b_max += a - c;
1204 prev_overlap = cur->end_overlap;
1205 } else {
1206 i = with_extenders;
1207 while (i > 0) {
1208 c = cur->start_overlap;
1209 if (min_overlap < c)
1210 c = min_overlap;
1211 if (prev_overlap < c)
1212 c = prev_overlap;
1213 a = cur->advance;
1214 if (a == 0) {
1215 /* for tfm fonts */
1216 if (boxtype == vlist_node)
1217 a = height_plus_depth(f, cur->glyph);
1218 else
1219 a = char_width(f, cur->glyph);
1220 assert(a >= 0);
1222 b_max += a - c;
1223 prev_overlap = cur->end_overlap;
1224 i--;
1231 assemble box using |with_extenders| copies of each extender, with
1232 appropriate glue wherever an overlap occurs
1234 prev_overlap = 0;
1235 b_max = 0;
1236 s_max = 0;
1237 for (cur = ext; cur != NULL; cur = cur->next) {
1238 if (cur->extender == 0) {
1239 c = cur->start_overlap;
1240 if (prev_overlap < c)
1241 c = prev_overlap;
1242 d = c;
1243 if (min_overlap < c)
1244 c = min_overlap;
1245 if (d > 0) {
1246 stack_glue_into_box(b, -d, -c);
1247 s_max += (-c) - (-d);
1248 b_max -= d;
1250 b_max += stack_into_box(b, f, cur->glyph);
1251 prev_overlap = cur->end_overlap;
1252 i--;
1253 } else {
1254 i = with_extenders;
1255 while (i > 0) {
1256 c = cur->start_overlap;
1257 if (prev_overlap < c)
1258 c = prev_overlap;
1259 d = c;
1260 if (min_overlap < c)
1261 c = min_overlap;
1262 if (d > 0) {
1263 stack_glue_into_box(b, -d, -c);
1264 s_max += (-c) - (-d);
1265 b_max -= d;
1267 b_max += stack_into_box(b, f, cur->glyph);
1268 prev_overlap = cur->end_overlap;
1269 i--;
1274 /* set glue so as to stretch the connections if needed */
1276 d = 0;
1277 if (v > b_max && s_max > 0) {
1278 d = v-b_max;
1279 /* don't stretch more than |s_max| */
1280 if (d > s_max)
1281 d = s_max;
1282 glue_order(b) = normal;
1283 glue_sign(b) = stretching;
1284 glue_set(b) = unfloat(d/(float) s_max);
1285 b_max += d;
1288 if (boxtype == vlist_node) {
1289 height(b) = b_max;
1290 } else {
1291 width(b) = b_max;
1294 return b;
1297 @ The |var_delimiter| function, which finds or constructs a sufficiently
1298 large delimiter, is the most interesting of the auxiliary functions that
1299 currently concern us. Given a pointer |d| to a delimiter field in some noad,
1300 together with a size code |s| and a vertical distance |v|, this function
1301 returns a pointer to a box that contains the smallest variant of |d| whose
1302 height plus depth is |v| or more. (And if no variant is large enough, it
1303 returns the largest available variant.) In particular, this routine will
1304 construct arbitrarily large delimiters from extensible components, if
1305 |d| leads to such characters.
1307 The value returned is a box whose |shift_amount| has been set so that
1308 the box is vertically centered with respect to the axis in the given size.
1309 If a built-up symbol is returned, the height of the box before shifting
1310 will be the height of its topmost component.
1313 static void endless_loop_error(internal_font_number g, int y)
1315 char s[256];
1316 const char *hlp[] = {
1317 "You managed to create a seemingly endless charlist chain in the current",
1318 "font. I have counted until 10000 already and still have not escaped, so"
1319 "I will jump out of the loop all by myself now. Fix your font!",
1320 NULL
1322 snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
1323 (int) y, font_name(g));
1324 tex_error(s, hlp);
1327 static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta)
1329 pointer b; /* the box that will be constructed */
1330 internal_font_number f, g; /* best-so-far and tentative font codes */
1331 int c, i, x, y; /* best-so-far and tentative character codes */
1332 scaled u; /* height-plus-depth of a tentative character */
1333 scaled w; /* largest height-plus-depth so far */
1334 int z; /* runs through font family members */
1335 boolean large_attempt; /* are we trying the ``large'' variant? */
1336 pointer att; /* to save the current attribute list */
1337 boolean do_parts;
1338 extinfo *ext;
1339 att = null;
1340 f = null_font;
1341 c = 0;
1342 w = 0;
1343 do_parts = false;
1344 large_attempt = false;
1345 if (d == null)
1346 goto FOUND;
1347 z = small_fam(d);
1348 x = small_char(d);
1349 i = 0;
1350 while (true) {
1352 The search process is complicated slightly by the facts that some of the
1353 characters might not be present in some of the fonts, and they might not
1354 be probed in increasing order of height.
1356 if ((z != 0) || (x != 0)) {
1357 g = fam_fnt(z, s);
1358 if (g != null_font) {
1359 y = x;
1360 CONTINUE:
1361 i++;
1362 if (char_exists(g, y)) {
1363 if (flat)
1364 u = char_width(g, y);
1365 else
1366 u = height_plus_depth(g, y);
1367 if (u > w) {
1368 f = g;
1369 c = y;
1370 w = u;
1371 if (u >= v)
1372 goto FOUND;
1374 if (char_tag(g, y) == ext_tag) {
1375 f = g;
1376 c = y;
1377 do_parts = true;
1378 goto FOUND;
1380 if (i > 10000) {
1381 /* endless loop */
1382 endless_loop_error(g, y);
1383 goto FOUND;
1385 if (char_tag(g, y) == list_tag) {
1386 y = char_remainder(g, y);
1387 goto CONTINUE;
1392 if (large_attempt)
1393 goto FOUND; /* there were none large enough */
1394 large_attempt = true;
1395 z = large_fam(d);
1396 x = large_char(d);
1398 FOUND:
1399 if (d != null) {
1400 att = node_attr(d);
1401 node_attr(d) = null;
1402 flush_node(d);
1404 if (f != null_font) {
1406 When the following code is executed, |do_parts| will be true
1407 if a built-up symbol is supposed to be returned.
1409 ext = NULL;
1410 if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
1411 || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
1412 if (flat) {
1413 b = get_delim_box(d, ext, f, v, att, cur_style, hlist_node);
1414 } else {
1415 b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node);
1417 if (delta != NULL) {
1418 if (is_new_mathfont(f)) {
1419 *delta = char_vert_italic(f,x);
1420 } else {
1421 *delta = char_italic(f,x);
1424 if (stack != NULL)
1425 *stack = true ;
1426 } else {
1427 b = char_box(f, c, att);
1428 if (!is_new_mathfont(f)) {
1429 /* italic gets added to width */
1430 width(b) += char_italic(f, c);
1432 if (delta != NULL) {
1433 *delta = char_italic(f,x);
1435 if (stack != NULL)
1436 *stack = false ;
1438 } else {
1439 b = new_null_box();
1440 reset_attributes(b, att);
1441 if (flat) {
1442 width(b) = 0;
1443 } else {
1444 /* use this width if no delimiter was found */
1445 width(b) = null_delimiter_space;
1447 if (delta != NULL) {
1448 *delta = 0;
1450 if (stack != NULL)
1451 *stack = false ;
1453 if (!flat) {
1454 /* vertical variant */
1455 shift_amount(b) = half(height(b) - depth(b));
1456 if (shift) {
1457 shift_amount(b) -= math_axis(s);
1460 delete_attribute_ref(att);
1461 return b;
1464 @ The next subroutine is much simpler; it is used for numerators and
1465 denominators of fractions as well as for displayed operators and
1466 their limits above and below. It takes a given box~|b| and
1467 changes it so that the new box is centered in a box of width~|w|.
1468 The centering is done by putting \.{\\hss} glue at the left and right
1469 of the list inside |b|, then packaging the new box; thus, the
1470 actual box might not really be centered, if it already contains
1471 infinite glue.
1473 The given box might contain a single character whose italic correction
1474 has been added to the width of the box; in this case a compensating
1475 kern is inserted.
1478 static pointer rebox(pointer b, scaled w)
1480 pointer p, q, r, att; /* temporary registers for list manipulation */
1481 internal_font_number f; /* font in a one-character box */
1482 scaled v; /* width of a character without italic correction */
1484 if ((width(b) != w) && (list_ptr(b) != null)) {
1485 if (type(b) == vlist_node) {
1486 p = hpack(b, 0, additional, -1);
1487 reset_attributes(p, node_attr(b));
1488 b = p;
1490 p = list_ptr(b);
1491 att = node_attr(b);
1492 add_node_attr_ref(att);
1493 if ((is_char_node(p)) && (vlink(p) == null)) {
1494 f = font(p);
1495 v = char_width(f, character(p));
1496 if (v != width(b)) {
1497 q = new_kern(width(b) - v);
1498 reset_attributes(q, att);
1499 couple_nodes(p,q);
1502 list_ptr(b) = null;
1503 flush_node(b);
1504 b = new_glue(ss_glue);
1505 reset_attributes(b, att);
1506 couple_nodes(b,p);
1507 while (vlink(p) != null)
1508 p = vlink(p);
1509 q = new_glue(ss_glue);
1510 reset_attributes(q, att);
1511 couple_nodes(p,q);
1512 r = hpack(b, w, exactly, -1);
1513 reset_attributes(r, att);
1514 delete_attribute_ref(att);
1515 return r;
1516 } else {
1517 width(b) = w;
1518 return b;
1522 @ Here is a subroutine that creates a new glue specification from another
1523 one that is expressed in `\.{mu}', given the value of the math unit.
1526 #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
1528 static pointer math_glue(pointer g, scaled m)
1530 pointer p; /* the new glue specification */
1531 int n; /* integer part of |m| */
1532 scaled f; /* fraction part of |m| */
1533 n = x_over_n(m, unity);
1534 f = tex_remainder;
1535 if (f < 0) {
1536 decr(n);
1537 f = f + unity;
1539 p = new_node(glue_spec_node, 0);
1540 width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
1541 stretch_order(p) = stretch_order(g);
1542 if (stretch_order(p) == normal)
1543 stretch(p) = mu_mult(stretch(g));
1544 else
1545 stretch(p) = stretch(g);
1546 shrink_order(p) = shrink_order(g);
1547 if (shrink_order(p) == normal)
1548 shrink(p) = mu_mult(shrink(g));
1549 else
1550 shrink(p) = shrink(g);
1551 return p;
1554 @ The |math_kern| subroutine removes |mu_glue| from a kern node, given
1555 the value of the math unit.
1558 static void math_kern(pointer p, scaled m)
1560 int n; /* integer part of |m| */
1561 scaled f; /* fraction part of |m| */
1562 if (subtype(p) == mu_glue) {
1563 n = x_over_n(m, unity);
1564 f = tex_remainder;
1565 if (f < 0) {
1566 decr(n);
1567 f = f + unity;
1569 width(p) = mu_mult(width(p));
1570 subtype(p) = italic_kern;
1574 @ @c
1575 void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
1577 int callback_id;
1578 int a, sfix;
1579 lua_State *L = Luas;
1580 if (p == null) {
1581 vlink(temp_head) = null;
1582 return;
1584 finalize_math_parameters();
1585 callback_id = callback_defined(mlist_to_hlist_callback);
1586 if (callback_id > 0) {
1587 sfix = lua_gettop(L);
1588 if (!get_callback(L, callback_id)) {
1589 lua_settop(L, sfix);
1590 return;
1592 alink(p) = null ;
1593 nodelist_to_lua(L, p); /* arg 1 */
1594 lua_pushstring(L, math_style_names[mstyle]); /* arg 2 */
1595 lua_pushboolean(L, penalties); /* arg 3 */
1596 if (lua_pcall(L, 3, 1, 0) != 0) { /* 3 args, 1 result */
1597 char errmsg[256]; /* temp hack ... we will have a formatted error */
1598 snprintf(errmsg, 255, "error: %s\n", lua_tostring(L, -1));
1599 errmsg[255]='\0';
1600 lua_settop(L, sfix);
1601 normal_error("mlist to hlist",errmsg); /* to be done */
1602 return;
1604 a = nodelist_from_lua(L);
1605 /* alink(vlink(a)) = null; */
1606 lua_settop(L, sfix);
1607 vlink(temp_head) = a;
1608 } else if (callback_id == 0) {
1609 mlist_to_hlist(p, penalties, mstyle);
1610 } else {
1611 vlink(temp_head) = null;
1615 @ The recursion in |mlist_to_hlist| is due primarily to a subroutine
1616 called |clean_box| that puts a given noad field into a box using a given
1617 math style; |mlist_to_hlist| can call |clean_box|, which can call
1618 |mlist_to_hlist|.
1619 @^recursion@>
1621 The box returned by |clean_box| is ``clean'' in the
1622 sense that its |shift_amount| is zero.
1625 static pointer clean_box(pointer p, int s, int cur_style)
1627 pointer q; /* beginning of a list to be boxed */
1628 pointer x; /* box to be returned */
1629 pointer r; /* temporary pointer */
1630 pointer mlist = null; /* beginning of mlist to be translated */
1631 switch (type(p)) {
1632 case math_char_node:
1633 mlist = new_noad();
1634 r = math_clone(p);
1635 nucleus(mlist) = r;
1636 break;
1637 case sub_box_node:
1638 q = math_list(p);
1639 goto FOUND;
1640 break;
1641 case sub_mlist_node:
1642 mlist = math_list(p);
1643 break;
1644 default:
1645 q = new_null_box();
1646 goto FOUND;
1648 mlist_to_hlist(mlist, false, s);
1649 q = vlink(temp_head); /* recursive call */
1650 setup_cur_size(cur_style);
1651 FOUND:
1652 if (is_char_node(q) || (q == null))
1653 x = hpack(q, 0, additional, -1);
1654 else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
1655 x = q; /* it's already clean */
1656 else
1657 x = hpack(q, 0, additional, -1);
1658 if (x != q && q != null)
1659 reset_attributes(x, node_attr(q));
1660 /* Here we save memory space in a common case. */
1661 q = list_ptr(x);
1662 if (is_char_node(q)) {
1663 r = vlink(q);
1664 if (r != null) {
1665 if (vlink(r) == null) {
1666 if (!is_char_node(r)) {
1667 if (type(r) == kern_node) {
1668 /* unneeded italic correction */
1669 flush_node(r);
1670 vlink(q) = null;
1676 return x;
1679 @ It is convenient to have a procedure that converts a |math_char|
1680 field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
1681 to the font code and character code of a given noad field.
1682 It also takes care of issuing error messages for
1683 nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
1684 after |fetch| has acted, and the field will also have been reset to |null|.
1686 The outputs of |fetch| are placed in global variables.
1689 internal_font_number cur_f; /* the |font| field of a |math_char| */
1690 int cur_c; /* the |character| field of a |math_char| */
1692 @ Here we unpack the |math_char| field |a|.
1694 @c static void fetch(pointer a)
1696 cur_c = math_character(a);
1697 cur_f = fam_fnt(math_fam(a), cur_size);
1698 if (cur_f == null_font) {
1699 char *msg;
1700 const char *hlp[] = {
1701 "Somewhere in the math formula just ended, you used the",
1702 "stated character from an undefined font family. For example,",
1703 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
1704 "and I'll try to forget that I needed that character.",
1705 NULL
1707 msg = xmalloc(256);
1708 snprintf(msg, 255, "\\%s%d is undefined (character %d)",
1709 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
1710 tex_error(msg, hlp);
1711 free(msg);
1712 } else if (!(char_exists(cur_f, cur_c))) {
1713 char_warning(cur_f, cur_c);
1717 @ We need to do a lot of different things, so |mlist_to_hlist| makes two
1718 passes over the given mlist.
1720 The first pass does most of the processing: It removes ``mu'' spacing from
1721 glue, it recursively evaluates all subsidiary mlists so that only the
1722 top-level mlist remains to be handled, it puts fractions and square roots
1723 and such things into boxes, it attaches subscripts and superscripts, and
1724 it computes the overall height and depth of the top-level mlist so that
1725 the size of delimiters for a |fence_noad| will be known.
1726 The hlist resulting from each noad is recorded in that noad's |new_hlist|
1727 field, an integer field that replaces the |nucleus| or |thickness|.
1728 @^recursion@>
1730 The second pass eliminates all noads and inserts the correct glue and
1731 penalties between nodes.
1734 static void assign_new_hlist(pointer q, pointer r)
1736 switch (type(q)) {
1737 case fraction_noad:
1738 math_list(numerator(q)) = null;
1739 flush_node(numerator(q));
1740 numerator(q) = null;
1741 math_list(denominator(q)) = null;
1742 flush_node(denominator(q));
1743 denominator(q) = null;
1744 break;
1745 case radical_noad:
1746 case simple_noad:
1747 case accent_noad:
1748 if (nucleus(q) != null) {
1749 math_list(nucleus(q)) = null;
1750 flush_node(nucleus(q));
1751 nucleus(q) = null;
1753 break;
1755 new_hlist(q) = r;
1758 @ @c
1759 #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
1761 @ Most of the actual construction work of |mlist_to_hlist| is done
1762 by procedures with names like |make_fraction|, |make_radical|, etc. To
1763 illustrate the general setup of such procedures, let's begin with a
1764 couple of simple ones.
1767 static void make_over(pointer q, int cur_style)
1769 pointer p;
1770 p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
1771 overbar_vgap(cur_style), overbar_rule(cur_style),
1772 overbar_kern(cur_style), node_attr(nucleus(q)));
1773 math_list(nucleus(q)) = p;
1774 type(nucleus(q)) = sub_box_node;
1777 static void make_under(pointer q, int cur_style)
1779 pointer p, x, y, r; /* temporary registers for box construction */
1780 scaled delta; /* overall height plus depth */
1781 x = clean_box(nucleus(q), cur_style, cur_style);
1782 p = new_kern(underbar_vgap(cur_style));
1783 reset_attributes(p, node_attr(q));
1784 couple_nodes(x,p);
1785 r = do_fraction_rule(underbar_rule(cur_style), node_attr(q));
1786 couple_nodes(p,r);
1787 y = vpackage(x, 0, additional, max_dimen, math_direction);
1788 reset_attributes(y, node_attr(q));
1789 delta = height(y) + depth(y) + underbar_kern(cur_style);
1790 height(y) = height(x);
1791 depth(y) = delta - height(y);
1792 math_list(nucleus(q)) = y;
1793 type(nucleus(q)) = sub_box_node;
1796 static void make_vcenter(pointer q)
1798 pointer v; /* the box that should be centered vertically */
1799 scaled delta; /* its height plus depth */
1800 v = math_list(nucleus(q));
1801 if (type(v) != vlist_node)
1802 confusion("vcenter");
1803 delta = height(v) + depth(v);
1804 height(v) = math_axis(cur_size) + half(delta);
1805 depth(v) = delta - height(v);
1808 @ According to the rules in the \.{DVI} file specifications, we ensure alignment
1809 @^square roots@>
1810 between a square root sign and the rule above its nucleus by assuming that the
1811 baseline of the square-root symbol is the same as the bottom of the rule. The
1812 height of the square-root symbol will be the thickness of the rule, and the
1813 depth of the square-root symbol should exceed or equal the height-plus-depth
1814 of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
1815 placed so that the actual clearance is |psi| plus half the excess.
1818 static void make_hextension(pointer q, int cur_style)
1820 pointer e, p;
1821 halfword w;
1822 boolean stack = false;
1823 e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL);
1824 w = width(e);
1825 if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
1826 if (radicalmiddle(q)) {
1827 p = new_kern(half(radicalwidth(q)-w));
1828 reset_attributes(p, node_attr(q));
1829 couple_nodes(p,e);
1830 e = p;
1831 w = radicalwidth(q);
1832 } else if (radicalexact(q)) {
1833 w = radicalwidth(q);
1836 e = hpack(e, 0, additional, -1);
1837 width(e) = w ;
1838 reset_attributes(e, node_attr(q));
1839 math_list(nucleus(q)) = e;
1840 left_delimiter(q) = null;
1843 static void make_radical(pointer q, int cur_style)
1845 pointer x, y, p, l1, l2; /* temporary registers for box construction */
1846 scaled delta, clr, theta, h; /* dimensions involved in the calculation */
1847 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
1848 clr = radical_vgap(cur_style);
1849 theta = radical_rule(cur_style);
1850 if (theta == undefined_math_parameter) {
1851 /* a real radical */
1852 theta = fraction_rule(cur_style);
1853 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1855 If |y| is a composite then set |theta| to the height of its top
1856 character, else set it to the height of |y|.
1858 l1 = list_ptr(y);
1859 if ((l1 != null) && (type(l1) == hlist_node)) {
1860 /* possible composite */
1861 l2 = list_ptr(l1);
1862 if ((l2 != null) && (type(l2) == glyph_node)) {
1863 /* top character */
1864 theta = char_height(font(l2), character(l2));
1865 } else {
1866 theta = height(y);
1868 } else {
1869 theta = height(y);
1871 } else {
1872 /* not really a radical but we use its node, historical sharing (like in mathml) */
1873 y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL);
1875 left_delimiter(q) = null;
1876 delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
1877 if (delta > 0) {
1878 /* increase the actual clearance */
1879 clr = clr + half(delta);
1881 shift_amount(y) = (height(y) - theta) - (height(x) + clr);
1882 h = depth(y) + height(y);
1883 p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y));
1884 couple_nodes(y,p);
1885 if (degree(q) != null) {
1886 scaled wr, br, ar;
1887 pointer r = clean_box(degree(q), script_script_style, cur_style);
1888 reset_attributes(r, node_attr(degree(q)));
1889 wr = width(r);
1890 if (wr == 0) {
1891 flush_node(r);
1892 } else {
1893 br = radical_degree_before(cur_style);
1894 ar = radical_degree_after(cur_style);
1895 if (-ar > (wr + br))
1896 ar = -(wr + br);
1897 x = new_kern(ar);
1898 reset_attributes(x, node_attr(degree(q)));
1899 couple_nodes(x,y);
1900 shift_amount(r) =
1901 -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
1902 depth(y) - shift_amount(y));
1903 couple_nodes(r,x);
1904 x = new_kern(br);
1905 reset_attributes(x, node_attr(degree(q)));
1906 couple_nodes(x,r);
1907 y = x;
1909 /* for \.{\\Uroot ..{<list>}{}} : */
1910 math_list(degree(q)) = null;
1911 flush_node(degree(q));
1913 p = hpack(y, 0, additional, -1);
1914 reset_attributes(p, node_attr(q));
1915 math_list(nucleus(q)) = p;
1916 type(nucleus(q)) = sub_box_node;
1919 @ Construct a vlist box
1922 static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
1924 pointer p; /* temporary register for box construction */
1925 pointer v = new_null_box();
1926 type(v) = vlist_node;
1927 height(v) = shift_up + height(x);
1928 depth(v) = depth(y) + shift_down;
1929 reset_attributes(v, node_attr(q));
1930 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
1931 reset_attributes(p, node_attr(q));
1932 couple_nodes(p,y);
1933 couple_nodes(x,p);
1934 list_ptr(v) = x;
1935 return v;
1938 /* when exact use radicalwidth (y is delimiter) */
1940 @ @c
1942 #define fixup_widths(q,x,y) do { \
1943 if (width(y) >= width(x)) { \
1944 if (radicalwidth(q) != zero_glue) { \
1945 shift_amount(x) += half(width(y)-width(x)) ; \
1947 width(x) = width(y); \
1948 } else { \
1949 if (radicalwidth(q) != zero_glue) { \
1950 shift_amount(y) += half(width(x)-width(y)) ; \
1952 width(y) = width(x); \
1954 } while (0)
1957 #define check_radical(q,stack,r,t) do { \
1958 if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != zero_glue) && (radicalwidth(q) != width(r))) { \
1959 if (radicalleft(q)) { \
1960 halfword p = new_kern(radicalwidth(q)-width(r)); \
1961 reset_attributes(p, node_attr(q)); \
1962 couple_nodes(p,r); \
1963 r = hpack(p, 0, additional, -1); \
1964 width(r) = radicalwidth(q); \
1965 reset_attributes(r, node_attr(q)); \
1966 } else if (radicalmiddle(q)) { \
1967 halfword p = new_kern(half(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 (radicalright(q)) { \
1974 /* also kind of exact compared to vertical */ \
1975 r = hpack(r, 0, additional, -1); \
1976 width(r) = radicalwidth(q); \
1977 reset_attributes(r, node_attr(q)); \
1980 } while (0)
1982 #define check_widths(q,p) do { \
1983 if (radicalwidth(q) != zero_glue) { \
1984 wd = radicalwidth(q); \
1985 } else { \
1986 wd = width(p); \
1988 } while (0)
1990 @ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
1993 static void make_over_delimiter(pointer q, int cur_style)
1995 pointer x, y, v; /* temporary registers for box construction */
1996 scaled shift_up, shift_down, clr, delta, wd;
1997 boolean stack;
1998 x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
1999 check_widths(q,x);
2000 y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2001 left_delimiter(q) = null;
2002 check_radical(q,stack,y,x);
2003 fixup_widths(q, x, y);
2004 shift_up = over_delimiter_bgap(cur_style);
2005 shift_down = 0;
2006 clr = over_delimiter_vgap(cur_style);
2007 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2008 if (delta > 0) {
2009 shift_up = shift_up + delta;
2011 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2012 width(v) = width(x); /* this also equals |width(y)| */
2013 math_list(nucleus(q)) = v;
2014 type(nucleus(q)) = sub_box_node;
2017 @ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
2020 static void make_under_delimiter(pointer q, int cur_style)
2022 pointer x, y, v; /* temporary registers for box construction */
2023 scaled shift_up, shift_down, clr, delta, wd;
2024 boolean stack;
2025 y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
2026 check_widths(q,y);
2027 x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL);
2028 left_delimiter(q) = null;
2029 check_radical(q,stack,x,y);
2030 fixup_widths(q, x, y);
2031 shift_up = 0;
2032 shift_down = under_delimiter_bgap(cur_style);
2033 clr = under_delimiter_vgap(cur_style);
2034 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
2035 if (delta > 0) {
2036 shift_down = shift_down + delta;
2038 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2039 width(v) = width(y); /* this also equals |width(y)| */
2040 math_list(nucleus(q)) = v;
2041 type(nucleus(q)) = sub_box_node;
2044 @ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
2047 static void make_delimiter_over(pointer q, int cur_style)
2049 pointer x, y, v; /* temporary registers for box construction */
2050 scaled shift_up, shift_down, clr, actual, wd;
2051 boolean stack;
2052 y = clean_box(nucleus(q), cur_style, cur_style);
2053 check_widths(q,y);
2054 x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2055 left_delimiter(q) = null;
2056 check_radical(q,stack,x,y);
2057 fixup_widths(q, x, y);
2058 shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
2059 shift_down = 0;
2060 clr = over_delimiter_vgap(cur_style);
2061 actual = shift_up - height(y);
2062 if (actual < clr) {
2063 shift_up = shift_up + (clr-actual);
2065 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2066 width(v) = width(x); /* this also equals |width(y)| */
2067 math_list(nucleus(q)) = v;
2068 type(nucleus(q)) = sub_box_node;
2071 @ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
2074 static void make_delimiter_under(pointer q, int cur_style)
2076 pointer x, y, v; /* temporary registers for box construction */
2077 scaled shift_up, shift_down, clr, actual, wd;
2078 boolean stack;
2079 x = clean_box(nucleus(q), cur_style, cur_style);
2080 check_widths(q,x);
2081 y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL);
2082 left_delimiter(q) = null;
2083 check_radical(q,stack,y,x);
2084 fixup_widths(q, x, y);
2085 shift_up = 0;
2086 shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
2087 clr = under_delimiter_vgap(cur_style);
2088 actual = shift_down - depth(x);
2089 if (actual<clr) {
2090 shift_down += (clr-actual);
2092 v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
2093 width(v) = width(y); /* this also equals |width(y)| */
2094 math_list(nucleus(q)) = v;
2095 type(nucleus(q)) = sub_box_node;
2098 @ Slants are not considered when placing accents in math mode. The accenter is
2099 centered over the accentee, and the accent width is treated as zero with
2100 respect to the size of the final box.
2103 #define TOP_CODE 1
2104 #define BOT_CODE 2
2105 #define OVERLAY_CODE 4
2106 #define STRETCH_ACCENT_CODE 8
2108 static boolean compute_accent_skew(pointer q, int flags, scaled *s)
2110 pointer p; /* temporary register for box construction */
2111 boolean s_is_absolute = false; /* will be true if a top-accent is placed in |s| */
2112 if (type(nucleus(q)) == math_char_node) {
2113 fetch(nucleus(q));
2114 if (is_new_mathfont(cur_f)) {
2116 there is no bot_accent so let's assume similarity
2118 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2119 *s = char_top_accent(cur_f, cur_c);
2120 if (*s != INT_MIN) {
2121 s_is_absolute = true;
2123 } else {
2124 *s = char_bot_accent(cur_f, cur_c);
2125 if (*s != INT_MIN) {
2126 s_is_absolute = true;
2130 *s = char_top_accent(cur_f, cur_c);
2131 if (*s != INT_MIN) {
2132 s_is_absolute = true;
2134 } else {
2135 if (flags & TOP_CODE) {
2136 *s = get_kern(cur_f, cur_c, skew_char(cur_f));
2137 } else {
2138 *s = 0;
2141 } else if (type(nucleus(q)) == sub_mlist_node) {
2143 if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
2145 * use the positioning of the nucleus of that noad, recursing until
2146 * the inner most |accent_noad|. This way multiple stacked accents are
2147 * aligned to the inner most one.
2149 p = math_list(nucleus(q));
2150 if (type(p) == accent_noad) {
2151 s_is_absolute = compute_accent_skew(p, flags, s);
2155 return s_is_absolute;
2158 static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
2160 pointer p, r, x, y; /* temporary registers for box construction */
2161 scaled s; /* amount to skew the accent to the right */
2162 scaled h; /* height of character being accented */
2163 scaled delta; /* space to remove between accent and accentee */
2164 scaled w; /* width of the accentee, not including sub/superscripts */
2165 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
2166 scaled fraction ;
2167 scaled target ;
2168 extinfo *ext;
2169 pointer attr_p;
2170 attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
2171 fraction = accentfraction(q);
2172 c = cur_c;
2173 f = cur_f;
2174 s = 1;
2175 if (fraction == 0) {
2176 fraction = 1000;
2178 /* Compute the amount of skew, or set |s| to an alignment point */
2179 s_is_absolute = compute_accent_skew(q, flags, &s);
2180 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
2181 w = width(x);
2182 h = height(x);
2183 if (is_new_mathfont(cur_f) && !s_is_absolute) {
2184 s = half(w);
2185 s_is_absolute = true;
2187 /* Switch to a larger accent if available and appropriate */
2188 y = null;
2189 ext = NULL;
2190 if (flags & OVERLAY_CODE) {
2191 if (fraction > 0) {
2192 target = xn_over_d(h,fraction,1000);
2193 } else {
2194 target = h;
2196 } else {
2197 if (fraction > 0) {
2198 target = xn_over_d(w,fraction,1000);
2199 } else {
2200 target = w;
2203 if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
2204 while (1) {
2205 if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
2206 /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
2207 y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node);
2208 break;
2209 } else if (char_tag(f, c) != list_tag) {
2210 break;
2211 } else {
2212 int yy = char_remainder(f, c);
2213 if (!char_exists(f, yy)) {
2214 break;
2215 } else if (flags & OVERLAY_CODE) {
2216 if (char_height(f, yy) > target) {
2217 break;
2219 } else {
2220 if (char_width(f, yy) > target)
2221 break;
2223 c = yy;
2227 if (y == null) {
2228 y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */
2230 if (flags & TOP_CODE) {
2231 if (h < accent_base_height(f)) {
2232 delta = h;
2233 } else {
2234 delta = accent_base_height(f);
2236 } else if (flags & OVERLAY_CODE) {
2237 delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */
2238 } else {
2239 delta = 0; /* hm */
2241 if ((supscr(q) != null) || (subscr(q) != null)) {
2242 if (type(nucleus(q)) == math_char_node) {
2243 /* swap the subscript and superscript into box |x| */
2244 flush_node_list(x);
2245 x = new_noad();
2246 r = math_clone(nucleus(q));
2247 nucleus(x) = r;
2248 supscr(x) = supscr(q);
2249 supscr(q) = null;
2250 subscr(x) = subscr(q);
2251 subscr(q) = null;
2252 type(nucleus(q)) = sub_mlist_node;
2253 math_list(nucleus(q)) = x;
2254 x = clean_box(nucleus(q), cur_style, cur_style);
2255 delta = delta + height(x) - h;
2256 h = height(x);
2259 /* the top accents of both characters are aligned */
2260 if (s_is_absolute) {
2261 scaled sa;
2262 if (ext != NULL) {
2263 /* if the accent is extensible just take the center */
2264 sa = half(width(y));
2265 } else {
2267 there is no bot_accent so let's assume similarity
2269 if (flags & BOT_CODE) {
2270 sa = char_bot_accent(f, c);
2271 } else {
2272 sa = char_top_accent(f, c);
2275 sa = char_top_accent(f, c);
2277 if (sa == INT_MIN) {
2278 /* just take the center */
2279 sa = half(width(y));
2281 if (math_direction == dir_TRT) {
2282 shift_amount(y) = s + sa - width(y);
2283 } else {
2284 shift_amount(y) = s - sa;
2286 } else {
2287 if (width(y)== 0) {
2288 shift_amount(y) = s + w;
2289 } else if (math_direction == dir_TRT) {
2290 shift_amount(y) = s + width(y); /* ok? */
2291 } else {
2292 shift_amount(y) = s + half(w - width(y));
2295 width(y) = 0;
2296 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2297 p = new_kern(-delta);
2298 reset_attributes(p, node_attr(q));
2299 couple_nodes(p,x);
2300 couple_nodes(y,p);
2301 } else {
2302 couple_nodes(x,y);
2303 y = x;
2305 r = vpackage(y, 0, additional, max_dimen, math_direction);
2306 reset_attributes(r, node_attr(q));
2307 width(r) = width(x);
2308 y = r;
2309 if (flags & (TOP_CODE | OVERLAY_CODE)) {
2310 if (height(y) < h) {
2311 /* make the height of box |y| equal to |h| */
2312 p = new_kern(h - height(y));
2313 reset_attributes(p, node_attr(q));
2314 try_couple_nodes(p,list_ptr(y));
2315 list_ptr(y) = p;
2316 height(y) = h;
2318 } else {
2319 shift_amount(y) = -(h - height(y));
2321 math_list(nucleus(q)) = y;
2322 type(nucleus(q)) = sub_box_node;
2325 static void make_math_accent(pointer q, int cur_style)
2327 int topstretch = !(subtype(q) % 2);
2328 int botstretch = !(subtype(q) / 2);
2330 if (top_accent_chr(q) != null) {
2331 fetch(top_accent_chr(q));
2332 if (char_exists(cur_f, cur_c)) {
2333 do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2335 flush_node(top_accent_chr(q));
2336 top_accent_chr(q) = null;
2338 if (bot_accent_chr(q) != null) {
2339 fetch(bot_accent_chr(q));
2340 if (char_exists(cur_f, cur_c)) {
2341 do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2343 flush_node(bot_accent_chr(q));
2344 bot_accent_chr(q) = null;
2346 if (overlay_accent_chr(q) != null) {
2347 fetch(overlay_accent_chr(q));
2348 if (char_exists(cur_f, cur_c)) {
2349 do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
2351 flush_node(overlay_accent_chr(q));
2352 overlay_accent_chr(q) = null;
2356 @ The |make_fraction| procedure is a bit different because it sets
2357 |new_hlist(q)| directly rather than making a sub-box.
2360 static void make_fraction(pointer q, int cur_style)
2362 pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */
2363 scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2;
2364 /* dimensions for box calculations */
2365 if (thickness(q) == default_code)
2366 thickness(q) = fraction_rule(cur_style);
2368 Create equal-width boxes |x| and |z| for the numerator and denominator,
2369 and compute the default amounts |shift_up| and |shift_down| by which they
2370 are displaced from the baseline
2373 x = clean_box(numerator(q), num_style(cur_style), cur_style);
2374 z = clean_box(denominator(q), denom_style(cur_style), cur_style);
2376 if (middle_delimiter(q) != null) {
2377 delta = 0;
2378 m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2379 middle_delimiter(q) = null;
2380 } else {
2381 m = null ;
2382 if (width(x) < width(z)) {
2383 x = rebox(x, width(z));
2384 } else {
2385 z = rebox(z, width(x));
2389 if (m != null) {
2390 shift_up = 0;
2391 shift_down = 0;
2392 } else if (thickness(q) == 0) {
2393 shift_up = stack_num_up(cur_style);
2394 shift_down = stack_denom_down(cur_style);
2396 the numerator and denominator must be separated by a certain minimum
2397 clearance, called |clr| in the following program. The difference between
2398 |clr| and the actual clearance is |2delta|.
2400 clr1 = stack_vgap(cur_style);
2401 delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
2402 if (delta > 0) {
2403 shift_up = shift_up + delta;
2404 shift_down = shift_down + delta;
2406 } else {
2407 shift_up = fraction_num_up(cur_style);
2408 shift_down = fraction_denom_down(cur_style);
2410 in the case of a fraction line, the minimum clearance depends on the actual
2411 thickness of the line.
2413 clr1 = fraction_num_vgap(cur_style);
2414 clr2 = fraction_denom_vgap(cur_style);
2415 delta = half(thickness(q));
2416 if (fractionexact(q)) {
2417 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis(cur_size) + delta));
2418 delta2 = clr2 - ((shift_down - height(z)) + (math_axis(cur_size) - delta));
2419 } else {
2420 delta = half(thickness(q));
2421 clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
2422 clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
2423 delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis(cur_size) + delta));
2424 delta2 = clr2 - ((shift_down - height(z)) + (math_axis(cur_size) - delta));
2426 if (delta1 > 0) {
2427 shift_up = shift_up + delta1;
2429 if (delta2 > 0) {
2430 shift_down = shift_down + delta2;
2433 if (m != null) {
2435 construct a hlist box for the fraction, according to |hgap| and |vgap|
2437 shift_up = skewed_fraction_vgap(cur_style);
2439 if (!fractionnoaxis(q)) {
2440 shift_up += half(math_axis(cur_size));
2443 shift_down = shift_up;
2444 v = new_null_box();
2445 reset_attributes(v, node_attr(q));
2446 type(v) = hlist_node;
2447 list_ptr(v) = x;
2448 width(v) = width(x);
2449 height(v) = height(x) + shift_up;
2450 depth(v) = depth(x);
2451 shift_amount(v) = - shift_up;
2452 x = v;
2454 v = new_null_box();
2455 reset_attributes(v, node_attr(q));
2456 type(v) = hlist_node;
2457 list_ptr(v) = z;
2458 width(v) = width(z);
2459 height(v) = height(z);
2460 depth(v) = depth(z) + shift_down;
2461 shift_amount(v) = shift_down;
2462 z = v;
2464 v = new_null_box();
2465 reset_attributes(v, node_attr(q));
2466 type(v) = hlist_node;
2467 if (height(x) > height(z)) {
2468 height(v) = height(x);
2469 } else {
2470 height(v) = height(z);
2472 if (depth(x) > depth(z)) {
2473 depth(v) = depth(x);
2474 } else {
2475 depth(v) = depth(z);
2477 if (height(m) > height(v)) {
2478 height(v) = height(m);
2480 if (depth(m) > depth(v)) {
2481 depth(v) = depth(m);
2484 if (fractionexact(q)) {
2485 delta1 = -half(skewed_fraction_hgap(cur_style));
2486 delta2 = delta1;
2487 width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
2488 } else {
2489 delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
2490 delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
2491 width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
2492 width(m) = 0;
2495 p1 = new_kern(delta1);
2496 reset_attributes(p1, node_attr(q));
2497 p2 = new_kern(delta2);
2498 reset_attributes(p2, node_attr(q));
2500 couple_nodes(x,p1);
2501 couple_nodes(p1,m);
2502 couple_nodes(m,p2);
2503 couple_nodes(p2,z);
2505 list_ptr(v) = x;
2506 } else {
2508 construct a vlist box for the fraction, according to |shift_up| and |shift_down|
2510 v = new_null_box();
2511 type(v) = vlist_node;
2512 height(v) = shift_up + height(x);
2513 depth(v) = depth(z) + shift_down;
2514 width(v) = width(x); /* this also equals |width(z)| */
2515 reset_attributes(v, node_attr(q));
2516 if (thickness(q) == 0) {
2517 p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
2518 couple_nodes(p,z);
2519 } else {
2520 y = do_fraction_rule(thickness(q), node_attr(q));
2521 p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down));
2522 reset_attributes(p, node_attr(q));
2523 couple_nodes(y,p);
2524 couple_nodes(p,z);
2525 p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2526 couple_nodes(p,y);
2528 reset_attributes(p, node_attr(q));
2529 couple_nodes(x,p);
2530 list_ptr(v) = x;
2533 put the fraction into a box with its delimiters, and make |new_hlist(q)|
2534 point to it
2536 if (is_new_mathfont(cur_f)) {
2537 if (math_use_old_fraction_scaling) {
2538 delta = fraction_del_size_old(cur_style);
2539 } else {
2540 delta = fraction_del_size_new(cur_style);
2542 if (delta == undefined_math_parameter) {
2543 delta = get_delimiter_height(depth(v), height(v), true);
2545 } else {
2546 delta = fraction_del_size_old(cur_style);
2549 l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2550 left_delimiter(q) = null;
2551 r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL);
2552 right_delimiter(q) = null;
2553 couple_nodes(l,v);
2554 couple_nodes(v,r);
2555 y = hpack(l, 0, additional, -1);
2556 reset_attributes(y, node_attr(q));
2557 assign_new_hlist(q, y);
2560 @ If the nucleus of an |op_noad| is a single character, it is to be
2561 centered vertically with respect to the axis, after first being enlarged
2562 (via a character list in the font) if we are in display style. The normal
2563 convention for placing displayed limits is to put them above and below the
2564 operator in display style.
2566 The italic correction is removed from the character if there is a subscript
2567 and the limits are not being displayed. The |make_op| routine returns the
2568 value that should be used as an offset between subscript and superscript.
2570 After |make_op| has acted, |subtype(q)| will be |limits| if and only if
2571 the limits have been set above and below the operator. In that case,
2572 |new_hlist(q)| will already contain the desired final box.
2575 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
2576 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style);
2578 static scaled make_op(pointer q, int cur_style)
2580 scaled delta = 0; /* offset between subscript and superscript */
2581 scaled dummy = 0;
2582 pointer p, v, x, y, z, n; /* temporary registers for box construction */
2583 int c; /* register for character examination */
2584 scaled shift_up, shift_down; /* dimensions for box calculation */
2585 boolean axis_shift = false;
2586 scaled ok_size;
2587 if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
2588 subtype(q) = op_noad_type_limits;
2590 if (type(nucleus(q)) == math_char_node) {
2591 fetch(nucleus(q));
2592 if (cur_style < text_style) {
2593 /* try to make it larger */
2594 ok_size = minimum_operator_size(cur_style);
2595 if (ok_size != undefined_math_parameter) {
2596 /* creating a temporary delimiter is the cleanest way */
2597 y = new_node(delim_node, 0);
2598 reset_attributes(y, node_attr(q));
2599 small_fam(y) = math_fam(nucleus(q));
2600 small_char(y) = math_character(nucleus(q));
2601 x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta);
2602 if (is_new_mathfont(cur_f)) {
2603 /* we never added italic correction */
2604 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2605 /* remove italic correction */
2606 width(x) -= delta;
2608 } else {
2609 ok_size = height_plus_depth(cur_f, cur_c) + 1;
2610 while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
2611 c = char_remainder(cur_f, cur_c);
2612 if (!char_exists(cur_f, c))
2613 break;
2614 cur_c = c;
2615 math_character(nucleus(q)) = c;
2617 delta = char_italic(cur_f, cur_c);
2618 x = clean_box(nucleus(q), cur_style, cur_style);
2619 if (delta != null) {
2620 if (is_new_mathfont(cur_f)) {
2621 /* we never added italic correction */
2622 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2623 /* remove italic correction */
2624 width(x) = width(x) - delta;
2627 axis_shift = true;
2629 } else {
2630 /* normal size */
2631 delta = char_italic(cur_f, cur_c);
2632 x = clean_box(nucleus(q), cur_style, cur_style);
2633 if (delta != 0) {
2634 if (is_new_mathfont(cur_f)) {
2635 /* we never added italic correction */
2636 } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2637 /* remove italic correction */
2638 width(x) = width(x) - delta;
2641 axis_shift = true;
2643 if (axis_shift) {
2644 /* center vertically */
2645 shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size);
2647 type(nucleus(q)) = sub_box_node;
2648 math_list(nucleus(q)) = x;
2651 /* we now handle op_nod_type_no_limits here too */
2653 if (subtype(q) == op_noad_type_no_limits) {
2654 if (is_new_mathfont(cur_f)) {
2655 if (delta != 0) {
2656 delta = half(delta) ;
2658 p = check_nucleus_complexity(q, &dummy, cur_style);
2659 if ((subscr(q) == null) && (supscr(q) == null)) {
2660 assign_new_hlist(q, p);
2661 } else {
2662 make_scripts(q, p, 0, cur_style, delta, -delta);
2664 delta = 0;
2665 } else {
2666 /* similar code then the caller (before CHECK_DIMENSIONS) */
2667 p = check_nucleus_complexity(q, &delta, cur_style);
2668 if ((subscr(q) == null) && (supscr(q) == null)) {
2669 assign_new_hlist(q, p);
2670 } else {
2671 make_scripts(q, p, delta, cur_style, 0, 0);
2674 } else if (subtype(q) == op_noad_type_limits) {
2675 /* The following program builds a vlist box |v| for displayed limits. The
2676 width of the box is not affected by the fact that the limits may be skewed. */
2677 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2678 y = clean_box(nucleus(q), cur_style, cur_style);
2679 z = clean_box(subscr(q), sub_style(cur_style), cur_style);
2680 v = new_null_box();
2681 reset_attributes(v, node_attr(q));
2682 type(v) = vlist_node;
2683 if (is_new_mathfont(cur_f)) {
2684 n = null;
2685 if (! math_no_italic_compensation) {
2686 n = nucleus(q);
2687 if (n != null) {
2688 if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
2689 n = math_list(n);
2690 if (n != null) {
2691 if (type(n) == hlist_node) {
2692 n = list_ptr(n); /* just a not scaled char */
2693 while (n != null) {
2694 if (type(n) == glyph_node) {
2695 delta = char_italic(font(n),character(n));
2697 n = vlink(n);
2699 } else {
2700 while (n != null) {
2701 if (type(n) == fence_noad) {
2702 if (delimiteritalic(n) > delta) {
2703 /* we can have dummies, the period ones */
2704 delta = delimiteritalic(n);
2707 n = vlink(n);
2711 } else {
2712 n = nucleus(q);
2713 if (type(n) == math_char_node) {
2714 delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
2720 width(v) = width(y);
2721 if (width(x) > width(v))
2722 width(v) = width(x);
2723 if (width(z) > width(v))
2724 width(v) = width(z);
2725 x = rebox(x, width(v));
2726 y = rebox(y, width(v));
2727 z = rebox(z, width(v));
2728 shift_amount(x) = half(delta);
2729 shift_amount(z) = -shift_amount(x);
2730 /* v is the still empty target */
2731 height(v) = height(y);
2732 depth(v) = depth(y);
2734 attach the limits to |y| and adjust |height(v)|, |depth(v)| to
2735 account for their presence
2737 we use |shift_up| and |shift_down| in the following program for the
2738 amount of glue between the displayed operator |y| and its limits |x| and
2741 the vlist inside box |v| will consist of |x| followed by |y| followed
2742 by |z|, with kern nodes for the spaces between and around them
2744 b: baseline v: minumum gap
2747 if (supscr(q) == null) {
2748 list_ptr(x) = null;
2749 flush_node(x);
2750 list_ptr(v) = y;
2751 } else {
2752 shift_up = limit_above_bgap(cur_style) - depth(x);
2753 if (shift_up < limit_above_vgap(cur_style))
2754 shift_up = limit_above_vgap(cur_style);
2755 p = new_kern(shift_up);
2756 reset_attributes(p, node_attr(q));
2757 couple_nodes(p,y);
2758 couple_nodes(x,p);
2759 p = new_kern(limit_above_kern(cur_style));
2760 reset_attributes(p, node_attr(q));
2761 couple_nodes(p,x);
2762 list_ptr(v) = p;
2763 height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
2765 if (subscr(q) == null) {
2766 list_ptr(z) = null;
2767 flush_node(z);
2768 } else {
2769 shift_down = limit_below_bgap(cur_style) - height(z);
2770 if (shift_down < limit_below_vgap(cur_style))
2771 shift_down = limit_below_vgap(cur_style);
2772 if (shift_down > 0) {
2773 p = new_kern(shift_down);
2774 reset_attributes(p, node_attr(q));
2775 couple_nodes(y,p);
2776 couple_nodes(p,z);
2778 p = new_kern(limit_below_kern(cur_style));
2779 reset_attributes(p, node_attr(q));
2780 couple_nodes(z,p);
2781 depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
2783 if (subscr(q) != null) {
2784 math_list(subscr(q)) = null;
2785 flush_node(subscr(q));
2786 subscr(q) = null;
2788 if (supscr(q) != null) {
2789 math_list(supscr(q)) = null;
2790 flush_node(supscr(q));
2791 supscr(q) = null;
2793 assign_new_hlist(q, v);
2794 if (is_new_mathfont(cur_f)) {
2795 delta = 0;
2798 return delta;
2801 @ A ligature found in a math formula does not create a ligature, because
2802 there is no question of hyphenation afterwards; the ligature will simply be
2803 stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
2805 The |type| is converted to |math_text_char| here if we would not want to
2806 apply an italic correction to the current character unless it belongs
2807 to a math font (i.e., a font with |space=0|).
2809 No boundary characters enter into these ligatures.
2812 #define simple_char_noad(p) (\
2813 (p != null) && \
2814 (type(p) == simple_noad) && \
2815 (subtype(p) <= punct_noad_type) && \
2816 (type(nucleus(p)) == math_char_node) \
2819 #define same_nucleus_fam(p,q) \
2820 (math_fam(nucleus(p)) == math_fam(nucleus(q)))
2822 static void make_ord(pointer q)
2824 int a; /* the left-side character for lig/kern testing */
2825 pointer p, r, s; /* temporary registers for list manipulation */
2826 scaled k; /* a kern */
2827 liginfo lig; /* a ligature */
2828 RESTART:
2829 if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
2830 p = vlink(q);
2831 if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
2832 type(nucleus(q)) = math_text_char_node;
2833 fetch(nucleus(q));
2834 a = cur_c;
2835 /* add italic correction */
2836 if (is_new_mathfont(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
2837 p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
2838 reset_attributes(p, node_attr(q));
2839 couple_nodes(p,vlink(q));
2840 couple_nodes(q,p);
2841 return;
2843 /* construct ligatures, quite unlikely in new math fonts */
2844 if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
2845 cur_c = math_character(nucleus(p));
2847 if character |a| has a kern with |cur_c|, attach the kern after~|q|; or if
2848 it has a ligature with |cur_c|, combine noads |q| and~|p| appropriately;
2849 then |return| if the cursor has moved past a noad, or |goto restart|
2851 note that a ligature between an |ord_noad| and another kind of noad
2852 is replaced by an |ord_noad|, when the two noads collapse into one
2854 we could make a parenthesis (say) change shape when it follows
2855 certain letters. Presumably a font designer will define such
2856 ligatures only when this convention makes sense
2859 if (disable_lig == 0 && has_lig(cur_f, a)) {
2860 lig = get_ligature(cur_f, a, cur_c);
2861 if (is_valid_ligature(lig)) {
2862 check_interrupt(); /* allow a way out of infinite ligature loop */
2863 switch (lig_type(lig)) {
2864 case 1:
2865 /* \.{=:\char`\|} */
2866 case 5:
2867 /* \.{=:\char`\|>} */
2868 math_character(nucleus(q)) = lig_replacement(lig);
2869 break;
2870 case 2:
2871 /* \.{\char`\|=:} */
2872 case 6:
2873 /* \.{\char`\|=:>} */
2874 math_character(nucleus(p)) = lig_replacement(lig);
2875 break;
2876 case 3:
2877 /* \.{\char`\|=:\char`\|} */
2878 case 7:
2879 /* \.{\char`\|=:\char`\|>} */
2880 case 11:
2881 /* \.{\char`\|=:\char`\|>>} */
2882 r = new_noad();
2883 reset_attributes(r, node_attr(q));
2884 s = new_node(math_char_node, 0);
2885 reset_attributes(s, node_attr(q));
2886 nucleus(r) = s;
2887 math_character(nucleus(r)) = lig_replacement(lig);
2888 math_fam(nucleus(r)) = math_fam(nucleus(q));
2889 couple_nodes(q,r);
2890 couple_nodes(r,p);
2891 if (lig_type(lig) < 11)
2892 type(nucleus(r)) = math_char_node;
2893 else
2894 /* prevent combination */
2895 type(nucleus(r)) = math_text_char_node;
2896 break;
2897 default:
2898 try_couple_nodes(q,vlink(p));
2899 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
2900 s = math_clone(subscr(p));
2901 subscr(q) = s;
2902 s = math_clone(supscr(p));
2903 supscr(q) = s;
2904 math_reset(subscr(p)); /* just in case */
2905 math_reset(supscr(p));
2906 flush_node(p);
2907 break;
2909 if (lig_type(lig) > 3)
2910 return;
2911 type(nucleus(q)) = math_char_node;
2912 goto RESTART;
2915 if (disable_kern == 0 && has_kern(cur_f, a)) {
2916 /* todo: should this use mathkerns? */
2917 k = get_kern(cur_f, a, cur_c);
2918 if (k != 0) {
2919 p = new_kern(k);
2920 reset_attributes(p, node_attr(q));
2921 couple_nodes(p,vlink(q));
2922 couple_nodes(q,p);
2923 return;
2931 @ If the fonts for the left and right bits of a mathkern are not
2932 both new-style fonts, then return a sentinel value meaning:
2933 please use old-style italic correction placement
2936 #define MATH_KERN_NOT_FOUND 0x7FFFFFFF
2938 @ This function tries to find the kern needed for proper cut-ins.
2939 The left side doesn't move, but the right side does, so the first
2940 order of business is to create a staggered fence line on the
2941 left side of the right character.
2943 The microsoft spec says that there are four quadrants, but the
2944 actual images say
2947 static scaled math_kern_at(internal_font_number f, int c, int side, int v)
2949 int h, k, numkerns;
2950 scaled *kerns_heights;
2951 scaled kern = 0;
2952 charinfo *co = char_info(f, c); /* known to exist */
2953 numkerns = get_charinfo_math_kerns(co, side);
2954 #ifdef DEBUG
2955 fprintf(stderr, " entries = %d, height = %d\n", numkerns, v);
2956 #endif
2957 if (numkerns == 0)
2958 return kern;
2959 if (side == top_left_kern) {
2960 kerns_heights = co->top_left_math_kern_array;
2961 } else if (side == bottom_left_kern) {
2962 kerns_heights = co->bottom_left_math_kern_array;
2963 } else if (side == top_right_kern) {
2964 kerns_heights = co->top_right_math_kern_array;
2965 } else if (side == bottom_right_kern) {
2966 kerns_heights = co->bottom_right_math_kern_array;
2967 } else {
2968 confusion("math_kern_at");
2969 kerns_heights = NULL; /* not reached */
2971 #ifdef DEBUG
2972 fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
2973 #endif
2974 if (v < kerns_heights[0])
2975 return kerns_heights[1];
2976 for (k = 0; k < numkerns; k++) {
2977 h = kerns_heights[(k * 2)];
2978 kern = kerns_heights[(k * 2) + 1];
2979 #ifdef DEBUG
2980 if (k > 0)
2981 fprintf(stderr, " entry %d: %d,%d\n", k, h, kern);
2982 #endif
2983 if (h > v) {
2984 return kern;
2987 return kern;
2990 @ @c
2991 static scaled find_math_kern(internal_font_number l_f, int l_c,
2992 internal_font_number r_f, int r_c,
2993 int cmd, scaled shift)
2995 scaled corr_height_top = 0, corr_height_bot = 0;
2996 scaled krn_l = 0, krn_r = 0, krn = 0;
2997 if ((!is_new_mathfont(l_f)) || (!is_new_mathfont(r_f)) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
2998 return MATH_KERN_NOT_FOUND;
3000 if (cmd == sup_mark_cmd) {
3001 corr_height_top = char_height(l_f, l_c);
3002 corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
3003 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
3004 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
3005 #ifdef DEBUG
3006 fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift);
3007 #endif
3008 krn = (krn_l + krn_r);
3009 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
3010 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
3011 #ifdef DEBUG
3012 fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
3013 #endif
3014 if ((krn_l + krn_r) < krn)
3015 krn = (krn_l + krn_r);
3016 return (krn);
3018 } else if (cmd == sub_mark_cmd) {
3019 corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
3020 corr_height_bot = -char_depth(l_f, l_c);
3021 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
3022 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
3023 #ifdef DEBUG
3024 fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
3025 #endif
3026 krn = (krn_l + krn_r);
3027 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
3028 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
3029 #ifdef DEBUG
3030 fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
3031 #endif
3032 if ((krn_l + krn_r) < krn)
3033 krn = (krn_l + krn_r);
3034 return (krn);
3036 } else {
3037 confusion("find_math_kern");
3039 return 0; /* not reached */
3042 @ just a small helper
3044 static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
3046 pointer y;
3047 pointer z = new_kern(delta2);
3048 reset_attributes(z, node_attr(q));
3049 if (new_hlist(q) == null) {
3050 /* this is somewhat weird */
3051 new_hlist(q) = z;
3052 } else {
3053 y = new_hlist(q);
3054 while (vlink(y) != null)
3055 y = vlink(y);
3056 couple_nodes(y,z);
3058 return new_hlist(q);
3063 #ifdef DEBUG
3064 void dump_simple_field(pointer q)
3066 pointer p;
3067 printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q));
3068 switch (type(q)) {
3069 case math_char_node:
3070 printf("mathchar ");
3071 break;
3072 case math_text_char_node:
3073 printf("texchar ");
3074 break;
3075 case sub_box_node:
3076 printf("box ");
3077 break;
3078 case sub_mlist_node:
3079 printf("mlist ");
3080 p = math_list(q);
3081 while (p != null) {
3082 dump_simple_field(p);
3083 p = vlink(p);
3085 break;
3089 void dump_simple_node(pointer q)
3091 printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
3092 printf("nucleus: ");
3093 dump_simple_field(nucleus(q));
3094 printf("\n");
3095 printf("sub: ");
3096 dump_simple_field(subscr(q));
3097 printf("\n");
3098 printf("sup: ");
3099 dump_simple_field(supscr(q));
3100 printf("\n\n");
3102 #endif
3104 @ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
3105 superscript of noad |q| to the list that starts at |new_hlist(q)|,
3106 given that subscript and superscript aren't both empty. The superscript
3107 will be horizontally shifted over |delta1|, the subscript over |delta2|.
3109 We set |shift_down| and |shift_up| to the minimum amounts to shift the
3110 baseline of subscripts and superscripts based on the given nucleus.
3112 Note: We need to look at a character but also at the first one in a sub list
3113 and there we ignore leading kerns and glue. Elsewhere is code that removes
3114 kerns assuming that is italic correction. The heuristics are unreliable for
3115 the new fonts so eventualy there will be an option to ignore such corrections.
3117 @ @c
3118 #define analyze_script(init,su_n,su_f,su_c) do { \
3119 su_n = init; \
3120 if (su_n != null) { \
3121 if (type(su_n) == sub_mlist_node && math_list(su_n)) { \
3122 su_n = math_list(su_n); \
3123 if (su_n != null) { \
3124 while (su_n) { \
3125 if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\
3126 su_n = vlink(su_n); \
3127 } else if (type(su_n) == simple_noad) { \
3128 su_n = nucleus(su_n); \
3129 if (type(su_n) != math_char_node) { \
3130 su_n = null; \
3132 break; \
3133 } else { \
3134 su_n = null; \
3135 break; \
3140 if ((su_n != null) && (type(su_n) == math_char_node)) { \
3141 fetch(su_n); \
3142 if (char_exists(cur_f, cur_c)) { \
3143 su_f = cur_f; \
3144 su_c = cur_c; \
3145 } else { \
3146 su_n = null; \
3150 } while (0)
3152 static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
3154 pointer x, y, z; /* temporary registers for box construction */
3155 scaled shift_up, shift_down, clr; /* dimensions in the calculation */
3156 scaled delta1, delta2;
3157 halfword sub_n, sup_n;
3158 internal_font_number sub_f, sup_f;
3159 int sub_c, sup_c;
3160 sub_n = null;
3161 sup_n = null;
3162 sub_f = 0;
3163 sup_f = 0;
3164 sub_c = 0;
3165 sup_c = 0;
3166 delta1 = it;
3167 delta2 = 0;
3169 #ifdef DEBUG
3170 printf("it: %d\n", it);
3171 dump_simple_node(q);
3172 printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
3173 #endif
3174 switch (type(nucleus(q))) {
3175 case math_char_node:
3176 case math_text_char_node:
3177 if ((subscr(q) == null) && (delta1 != 0)) {
3178 /* todo: selective */
3179 x = new_kern(delta1); /* italic correction */
3180 reset_attributes(x, node_attr(nucleus(q)));
3181 couple_nodes(p,x);
3182 delta1 = 0;
3185 assign_new_hlist(q, p);
3186 if (is_char_node(p)) {
3187 shift_up = 0;
3188 shift_down = 0;
3189 } else {
3190 z = hpack(p, 0, additional, -1);
3191 shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */
3192 shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
3193 list_ptr(z) = null;
3194 flush_node(z);
3197 if (is_char_node(p)) {
3198 /* we look at the subscript character (_i) or first character in a list (_{ij}) */
3199 analyze_script(subscr(q),sub_n,sub_f,sub_c);
3200 /* we look at the superscript character (^i) or first character in a list (^{ij}) */
3201 analyze_script(supscr(q),sup_n,sup_f,sup_c);
3204 if (supscr(q) == null) {
3206 construct a subscript box |x| when there is no superscript
3208 when there is a subscript without a superscript, the top of the subscript
3209 should not exceed the baseline plus four-fifths of the x-height.
3211 x = clean_box(subscr(q), sub_style(cur_style), cur_style);
3212 width(x) = width(x) + space_after_script(cur_style);
3213 switch (scripts_mode) {
3214 case 1:
3215 shift_down = sub_shift_down(cur_style) ;
3216 break;
3217 case 2:
3218 shift_down = sub_sup_shift_down(cur_style) ;
3219 break;
3220 case 3:
3221 shift_down = sub_sup_shift_down(cur_style) ;
3222 break;
3223 case 4:
3224 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3225 break;
3226 case 5:
3227 shift_down = sub_shift_down(cur_style) ;
3228 break;
3229 default:
3230 if (shift_down < sub_shift_down(cur_style))
3231 shift_down = sub_shift_down(cur_style);
3232 clr = height(x) - sub_top_max(cur_style);
3233 if (shift_down < clr)
3234 shift_down = clr;
3235 break;
3237 shift_amount(x) = shift_down;
3239 /* now find and correct for horizontal shift */
3240 if (sub_n != null) {
3241 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3242 if (delta2 == MATH_KERN_NOT_FOUND) {
3243 delta2 = subshift ;
3244 } else {
3245 delta2 = delta2 + subshift ;
3247 } else {
3248 delta2 = subshift ;
3250 if (delta2 != 0) {
3251 p = attach_hkern_to_new_hlist(q, delta2);
3254 } else {
3256 construct a superscript box |x|
3258 the bottom of a superscript should never descend below the baseline plus
3259 one-fourth of the x-height.
3261 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
3262 width(x) = width(x) + space_after_script(cur_style);
3263 switch (scripts_mode) {
3264 case 1:
3265 shift_up = sup_shift_up(cur_style);
3266 break;
3267 case 2:
3268 shift_up = sup_shift_up(cur_style) ;
3269 break;
3270 case 3:
3271 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
3272 break;
3273 case 4:
3274 shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3275 break;
3276 case 5:
3277 shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
3278 break;
3279 default:
3280 clr = sup_shift_up(cur_style);
3281 if (shift_up < clr)
3282 shift_up = clr;
3283 clr = depth(x) + sup_bottom_min(cur_style);
3284 if (shift_up < clr)
3285 shift_up = clr;
3286 break;
3288 if (subscr(q) == null) {
3289 shift_amount(x) = -shift_up;
3290 /* now find and correct for horizontal shift */
3291 if (sup_n != null) {
3292 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3293 if (clr == MATH_KERN_NOT_FOUND) {
3294 clr = supshift ;
3295 } else {
3296 clr = clr + supshift ;
3298 } else {
3299 clr = supshift;
3301 if (clr != 0) {
3302 p = attach_hkern_to_new_hlist(q, clr);
3304 } else {
3306 construct a sub/superscript combination box |x|, with the superscript offset
3307 by |delta|
3309 when both subscript and superscript are present, the subscript must be
3310 separated from the superscript by at least four times |default_rule_thickness|
3312 if this condition would be violated, the subscript moves down, after which
3313 both subscript and superscript move up so that the bottom of the superscript
3314 is at least as high as the baseline plus four-fifths of the x-height
3316 y = clean_box(subscr(q), sub_style(cur_style), cur_style);
3317 width(y) = width(y) + space_after_script(cur_style);
3318 switch (scripts_mode) {
3319 case 1:
3320 shift_down = sub_shift_down(cur_style) ;
3321 break;
3322 case 2:
3323 shift_down = sub_sup_shift_down(cur_style) ;
3324 break;
3325 case 3:
3326 shift_down = sub_sup_shift_down(cur_style) ;
3327 break;
3328 case 4:
3329 shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
3330 break;
3331 case 5:
3332 shift_down = sub_shift_down(cur_style) ;
3333 break;
3334 default:
3335 if (shift_down < sub_sup_shift_down(cur_style))
3336 shift_down = sub_sup_shift_down(cur_style);
3337 clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
3338 if (clr > 0) {
3339 shift_down = shift_down + clr;
3340 clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
3341 if (clr > 0) {
3342 shift_up = shift_up + clr;
3343 shift_down = shift_down - clr;
3346 break;
3348 /* now find and correct for horizontal shift */
3349 if (sub_n != null) {
3350 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
3351 if (delta2 == MATH_KERN_NOT_FOUND) {
3352 delta2 = subshift ;
3353 } else {
3354 delta2 = delta2 + subshift ;
3356 } else {
3357 delta2 = subshift ;
3359 if (delta2 != 0) {
3360 p = attach_hkern_to_new_hlist(q, delta2);
3363 now the horizontal shift for the superscript; the superscript is also to be shifted
3364 by |delta1| (the italic correction)
3366 clr = MATH_KERN_NOT_FOUND;
3367 if (sup_n != null) {
3368 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
3371 /* delta can already have been applied and now be 0 */
3372 if (delta2 == MATH_KERN_NOT_FOUND)
3373 delta2 = - supshift ;
3374 else
3375 delta2 = delta2 - supshift ;
3376 if (clr != MATH_KERN_NOT_FOUND) {
3377 shift_amount(x) = clr + delta1 - delta2;
3378 } else {
3379 shift_amount(x) = delta1 - delta2;
3381 /* todo: only if kern != 0 */
3382 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
3383 reset_attributes(p, node_attr(q));
3384 couple_nodes(x,p);
3385 couple_nodes(p,y);
3386 /* we end up with funny dimensions */
3387 x = vpackage(x, 0, additional, max_dimen, math_direction);
3388 reset_attributes(x, node_attr(q));
3389 shift_amount(x) = shift_down;
3393 if (new_hlist(q) == null) {
3394 new_hlist(q) = x;
3395 } else {
3396 p = new_hlist(q);
3397 while (vlink(p) != null)
3398 p = vlink(p);
3399 couple_nodes(p,x);
3401 if (subscr(q) != null) {
3402 math_list(subscr(q)) = null;
3403 flush_node(subscr(q));
3404 subscr(q) = null;
3406 if (supscr(q) != null) {
3407 math_list(supscr(q)) = null;
3408 flush_node(supscr(q));
3409 supscr(q) = null;
3413 @ The |make_left_right| function constructs a left or right delimiter of
3414 the required size and returns the value |open_noad| or |close_noad|. The
3415 |left_noad_side| and |right_noad_side| will both be based on the original |style|,
3416 so they will have consistent sizes.
3419 static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
3421 scaled delta;
3422 pointer tmp, lst;
3423 scaled hd_asked = 0;
3424 scaled ic = 0;
3425 boolean stack = false;
3426 boolean axis = false;
3428 scaled hd_done = 0;
3429 boolean fitting = true;
3430 boolean fence = false;
3431 int chr = 0;
3432 int cls = 0;
3434 setup_cur_size(style);
3436 if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
3438 hd_asked = delimiterheight(q) + delimiterdepth(q);
3439 tmp = do_delimiter(q, delimiter(q), cur_size, hd_asked, false, style, false, &stack, &ic);
3440 delimiteritalic(q) = ic;
3442 /* beware, a stacked delimiter has a shift but no corrected height/depth (yet) */
3444 if (stack) {
3445 shift_amount(tmp) = delimiterdepth(q);
3449 hd_done = height(tmp) + depth(tmp);
3450 fitting = stack || ((hd_done-hd_asked) == 0);
3452 if (type(delimiter(q)) == delim_node && (small_char(delimiter(q)) != 0)) {
3453 chr = small_char(delimiter(q));
3454 cls = get_math_code(chr).class_value ;
3455 fence = (cls == 4) || (cls == 5) ;
3458 printf("delimiter stack %i fence %i fitting %i\n",stack,fence,fitting);
3461 if (delimiterexact(q)) {
3462 delimiterheight(q) = height(tmp) - shift_amount(tmp);
3463 delimiterdepth(q) = depth(tmp) + shift_amount(tmp);
3465 if (delimiteraxis(q)) {
3466 delimiterheight(q) += math_axis(cur_size);
3467 delimiterdepth(q) -= math_axis(cur_size);
3468 shift_amount(tmp) -= math_axis(cur_size);
3470 lst = new_node(hlist_node,0);
3471 reset_attributes(lst, node_attr(q));
3472 box_dir(lst) = dir_TLT ;
3473 height(lst) = delimiterheight(q);
3474 depth(lst) = delimiterdepth(q);
3475 width(lst) = width(tmp);
3476 list_ptr(lst) = tmp;
3477 tmp = lst ;
3478 } else {
3479 axis = ! delimiternoaxis(q);
3480 delta = get_delimiter_height(max_d,max_h,axis);
3481 tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic);
3482 delimiteritalic(q) = ic;
3484 delimiter(q) = null;
3485 assign_new_hlist(q, tmp);
3486 if (delimiterclass(q) >= ord_noad_type) {
3487 if (delimiterclass(q) <= inner_noad_type) {
3488 return delimiterclass(q);
3489 } else {
3490 return ord_noad_type;
3492 } else if (subtype(q) == no_noad_side) {
3493 return open_noad_type;
3494 } else if (subtype(q) == left_noad_side) {
3495 return open_noad_type;
3496 } else {
3497 return close_noad_type;
3501 @ @c
3502 #define TEXT_STYLES(A,B) do { \
3503 def_math_param(A,display_style,(B),level_one); \
3504 def_math_param(A,cramped_display_style,(B),level_one); \
3505 def_math_param(A,text_style,(B),level_one); \
3506 def_math_param(A,cramped_text_style,(B),level_one); \
3507 } while (0)
3509 #define SCRIPT_STYLES(A,B) do { \
3510 def_math_param(A,script_style,(B),level_one); \
3511 def_math_param(A,cramped_script_style,(B),level_one); \
3512 def_math_param(A,script_script_style,(B),level_one); \
3513 def_math_param(A,cramped_script_script_style,(B),level_one); \
3514 } while (0)
3516 #define ALL_STYLES(A,B) do { \
3517 TEXT_STYLES(A,(B)); \
3518 SCRIPT_STYLES(A,(B)); \
3519 } while (0)
3521 #define SPLIT_STYLES(A,B,C) do { \
3522 TEXT_STYLES(A,(B)); \
3523 SCRIPT_STYLES(A,(C)); \
3524 } while (0)
3527 void initialize_math_spacing(void)
3529 /* *INDENT-OFF* */
3530 ALL_STYLES (math_param_ord_ord_spacing, 0);
3531 ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code);
3532 SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0);
3533 SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0);
3534 ALL_STYLES (math_param_ord_open_spacing, 0);
3535 ALL_STYLES (math_param_ord_close_spacing, 0);
3536 ALL_STYLES (math_param_ord_punct_spacing, 0);
3537 SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0);
3539 ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code);
3540 ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code);
3541 ALL_STYLES (math_param_op_bin_spacing, 0);
3542 SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0);
3543 ALL_STYLES (math_param_op_open_spacing, 0);
3544 ALL_STYLES (math_param_op_close_spacing, 0);
3545 ALL_STYLES (math_param_op_punct_spacing, 0);
3546 SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0);
3548 SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0);
3549 SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0);
3550 ALL_STYLES (math_param_bin_bin_spacing, 0);
3551 ALL_STYLES (math_param_bin_rel_spacing, 0);
3552 SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0);
3553 ALL_STYLES (math_param_bin_close_spacing, 0);
3554 ALL_STYLES (math_param_bin_punct_spacing, 0);
3555 SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0);
3557 SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0);
3558 SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0);
3559 ALL_STYLES (math_param_rel_bin_spacing, 0);
3560 ALL_STYLES (math_param_rel_rel_spacing, 0);
3561 SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0);
3562 ALL_STYLES (math_param_rel_close_spacing, 0);
3563 ALL_STYLES (math_param_rel_punct_spacing, 0);
3564 SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0);
3566 ALL_STYLES (math_param_open_ord_spacing, 0);
3567 ALL_STYLES (math_param_open_op_spacing, 0);
3568 ALL_STYLES (math_param_open_bin_spacing, 0);
3569 ALL_STYLES (math_param_open_rel_spacing, 0);
3570 ALL_STYLES (math_param_open_open_spacing, 0);
3571 ALL_STYLES (math_param_open_close_spacing, 0);
3572 ALL_STYLES (math_param_open_punct_spacing, 0);
3573 ALL_STYLES (math_param_open_inner_spacing, 0);
3575 ALL_STYLES (math_param_close_ord_spacing, 0);
3576 ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code);
3577 SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0);
3578 SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0);
3579 ALL_STYLES (math_param_close_open_spacing, 0);
3580 ALL_STYLES (math_param_close_close_spacing, 0);
3581 ALL_STYLES (math_param_close_punct_spacing, 0);
3582 SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
3584 SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0);
3585 SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0);
3586 ALL_STYLES (math_param_punct_bin_spacing, 0);
3587 SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0);
3588 SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0);
3589 SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
3590 SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
3591 SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
3593 SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0);
3594 ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code);
3595 SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0);
3596 SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0);
3597 SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0);
3598 ALL_STYLES (math_param_inner_close_spacing, 0);
3599 SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
3600 SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
3601 /* *INDENT-ON* */
3604 @ @c
3605 #define both_types(A,B) ((A)*16+(B))
3607 static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
3609 int x = -1;
3610 pointer z = null;
3611 if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
3612 l_type = op_noad_type_normal;
3613 if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
3614 r_type = op_noad_type_normal;
3615 switch (both_types(l_type, r_type)) {
3616 /* *INDENT-OFF* */
3617 case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
3618 case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
3619 case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
3620 case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
3621 case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
3622 case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
3623 case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
3624 case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
3625 case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
3626 case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
3627 #if 0
3628 case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
3629 #endif
3630 case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
3631 case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
3632 case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
3633 case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
3634 case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
3635 case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
3636 case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
3637 #if 0
3638 case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
3639 case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
3640 #endif
3641 case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
3642 #if 0
3643 case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
3644 case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
3645 #endif
3646 case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
3647 case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
3648 case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
3649 #if 0
3650 case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
3651 #endif
3652 case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
3653 case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
3654 case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
3655 case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
3656 case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
3657 case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
3658 case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
3659 #if 0
3660 case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
3661 #endif
3662 case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
3663 case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
3664 case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
3665 case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
3666 case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
3667 case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
3668 case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
3669 case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
3670 case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
3671 case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
3672 case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
3673 case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
3674 case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
3675 case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
3676 case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
3677 #if 0
3678 case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
3679 #endif
3680 case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
3681 case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
3682 case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
3683 case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
3684 case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
3685 case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
3686 case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
3687 case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
3688 case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
3689 case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
3690 case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
3691 case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
3692 case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
3693 /* *INDENT-ON* */
3695 if (x < 0) {
3696 confusion("mathspacing");
3698 if (x != 0) {
3699 pointer y;
3700 if (x <= thick_mu_skip_code) {
3701 /* trap thin/med/thick settings cf. old TeX */
3702 y = math_glue(glue_par(x), mmu);
3703 z = new_glue(y);
3704 glue_ref_count(y) = null;
3705 /* store a symbolic subtype */
3706 subtype(z) = (quarterword) (x + 1);
3707 } else {
3708 y = math_glue(x, mmu);
3709 z = new_glue(y);
3710 glue_ref_count(y) = null;
3713 return z;
3716 @ @c
3717 static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style)
3719 pointer p = null;
3720 switch (type(nucleus(q))) {
3721 case math_char_node:
3722 case math_text_char_node:
3723 fetch(nucleus(q));
3724 if (char_exists(cur_f, cur_c)) {
3725 /* we could look at neighbours */
3726 if (is_new_mathfont(cur_f)) {
3727 *delta = 0 ; /* cf spec only the last one */
3728 } else {
3729 *delta = char_italic(cur_f, cur_c);
3731 p = new_glyph(cur_f, cur_c);
3732 reset_attributes(p, node_attr(nucleus(q)));
3733 if (is_new_mathfont(cur_f)) {
3734 if (! math_no_char_italic) {
3735 /* keep italic, but bad with two successive letters */
3736 } else if (get_char_cat_code(cur_c) == 11) {
3737 /* no italic correction in mid-word of text font */
3738 *delta = 0;
3740 } else {
3741 /* no italic correction in mid-word of text font */
3742 if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
3743 *delta = 0;
3746 /* so we only add italic correction when we have no scripts */
3747 if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
3748 pointer x = new_kern(*delta);
3749 reset_attributes(x, node_attr(nucleus(q)));
3750 couple_nodes(p,x);
3751 *delta = 0;
3753 if (is_new_mathfont(cur_f)) {
3754 *delta = char_italic(cur_f, cur_c); /* must be more selective */
3757 break;
3758 case sub_box_node:
3759 p = math_list(nucleus(q));
3760 break;
3761 case sub_mlist_node:
3762 mlist_to_hlist(math_list(nucleus(q)), false, cur_style); /* recursive call */
3763 setup_cur_size(cur_style);
3764 p = hpack(vlink(temp_head), 0, additional, -1);
3765 reset_attributes(p, node_attr(nucleus(q)));
3766 break;
3767 default:
3768 confusion("mlist2"); /* this can't happen mlist2 */
3770 return p;
3773 @ Here is the overall plan of |mlist_to_hlist|, and the list of its
3774 local variables.
3777 void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
3779 pointer q = mlist; /* runs through the mlist */
3780 pointer r = null; /* the most recent noad preceding |q| */
3781 int style = cur_style; /* tuck global parameter away as local variable */
3782 int r_type = simple_noad; /* the |type| of noad |r|, or |op_noad| if |r=null| */
3783 int r_subtype = op_noad_type_normal; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
3784 int t; /* the effective |type| of noad |q| during the second pass */
3785 int t_subtype; /* the effective |subtype| of noad |q| during the second pass */
3786 pointer p = null;
3787 pointer x = null;
3788 pointer y = null;
3789 pointer z = null;
3790 int pen; /* a penalty to be inserted */
3791 scaled max_hl = 0; /* maximum height of the list translated so far */
3792 scaled max_d = 0; /* maximum depth of the list translated so far */
3793 scaled delta; /* italic correction offset for subscript and superscript */
3794 scaled cur_mu; /* the math unit width corresponding to |cur_size| */
3795 r_subtype = op_noad_type_normal;
3796 setup_cur_size(cur_style);
3797 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3798 while (q != null) {
3800 we use the fact that no character nodes appear in an mlist, hence
3801 the field |type(q)| is always present.
3803 one of the things we must do on the first pass is change a |bin_noad| to
3804 an |ord_noad| if the |bin_noad| is not in the context of a binary operator
3806 the values of |r| and |r_type| make this fairly easy
3808 RESWITCH:
3809 delta = 0;
3810 switch (type(q)) {
3811 case simple_noad:
3812 switch (subtype(q)) {
3813 case bin_noad_type:
3814 switch (r_type) {
3815 case simple_noad:
3816 switch (r_subtype) {
3817 case bin_noad_type:
3818 case op_noad_type_normal:
3819 case op_noad_type_limits:
3820 case op_noad_type_no_limits:
3821 case rel_noad_type:
3822 case open_noad_type:
3823 case punct_noad_type:
3824 subtype(q) = ord_noad_type;
3825 goto RESWITCH;
3826 break;
3828 break;
3829 case fence_noad:
3830 if (r_subtype == left_noad_side) {
3831 subtype(q) = ord_noad_type; /* so these can best be the same size */
3832 goto RESWITCH;
3834 break;
3836 break;
3837 case over_noad_type:
3838 make_over(q, cur_style);
3839 break;
3840 case under_noad_type:
3841 make_under(q, cur_style);
3842 break;
3843 case vcenter_noad_type:
3844 make_vcenter(q);
3845 break;
3846 case rel_noad_type:
3847 case close_noad_type:
3848 case punct_noad_type:
3849 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3850 type(r) = simple_noad; /* assumes the same size .. can't this go */
3851 subtype(r) = ord_noad_type;
3853 break;
3854 case op_noad_type_normal:
3855 case op_noad_type_limits:
3856 case op_noad_type_no_limits:
3857 delta = make_op(q, cur_style);
3858 if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
3859 goto CHECK_DIMENSIONS;
3860 break;
3861 case ord_noad_type:
3862 make_ord(q);
3863 break;
3864 case open_noad_type:
3865 case inner_noad_type:
3866 break;
3868 break;
3869 case fence_noad:
3870 if (subtype(q) != left_noad_side)
3871 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3872 type(r) = simple_noad; /* assumes the same size */
3873 subtype(r) = ord_noad_type;
3875 goto DONE_WITH_NOAD;
3876 break;
3877 case fraction_noad:
3878 make_fraction(q, cur_style);
3879 goto CHECK_DIMENSIONS;
3880 break;
3881 case radical_noad:
3882 if (subtype(q) == 7)
3883 make_hextension(q, cur_style);
3884 else if (subtype(q) == 6)
3885 make_delimiter_over(q, cur_style);
3886 else if (subtype(q) == 5)
3887 make_delimiter_under(q, cur_style);
3888 else if (subtype(q) == 4)
3889 make_over_delimiter(q, cur_style);
3890 else if (subtype(q) == 3)
3891 make_under_delimiter(q, cur_style);
3892 else
3893 make_radical(q, cur_style);
3894 break;
3895 case accent_noad:
3896 make_math_accent(q, cur_style);
3897 break;
3898 case style_node:
3899 cur_style = subtype(q);
3900 setup_cur_size(cur_style);
3901 cur_mu = x_over_n(get_math_quad(cur_style), 18);
3902 goto DONE_WITH_NODE;
3903 break;
3904 case choice_node:
3905 switch (cur_style / 2) {
3906 case 0: /* |display_style=0| */
3907 choose_mlist(display_mlist);
3908 break;
3909 case 1: /* |text_style=2| */
3910 choose_mlist(text_mlist);
3911 break;
3912 case 2: /* |script_style=4| */
3913 choose_mlist(script_mlist);
3914 break;
3915 case 3: /* |script_script_style=6| */
3916 choose_mlist(script_script_mlist);
3917 break;
3919 flush_node_list(display_mlist(q));
3920 flush_node_list(text_mlist(q));
3921 flush_node_list(script_mlist(q));
3922 flush_node_list(script_script_mlist(q));
3923 type(q) = style_node;
3924 subtype(q) = (quarterword) cur_style;
3925 if (p != null) {
3926 z = vlink(q);
3927 couple_nodes(q,p);
3928 while (vlink(p) != null)
3929 p = vlink(p);
3930 try_couple_nodes(p,z);
3932 goto DONE_WITH_NODE;
3933 break;
3934 case ins_node:
3935 case mark_node:
3936 case adjust_node:
3937 case boundary_node:
3938 case whatsit_node:
3939 case penalty_node:
3940 case disc_node:
3941 goto DONE_WITH_NODE;
3942 break;
3943 case rule_node:
3944 if (height(q) > max_hl)
3945 max_hl = height(q);
3946 if (depth(q) > max_d)
3947 max_d = depth(q);
3948 goto DONE_WITH_NODE;
3949 break;
3950 case glue_node:
3952 conditional math glue (`\.{\\nonscript}') results in a |glue_node|
3953 pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
3954 the node following will be eliminated if it is a glue or kern node and if the
3955 current size is different from |text_size|
3957 unconditional math glue (`\.{\\muskip}') is converted to normal glue by
3958 multiplying the dimensions by |cur_mu|
3961 if (subtype(q) == mu_glue) {
3962 x = glue_ptr(q);
3963 y = math_glue(x, cur_mu);
3964 delete_glue_ref(x);
3965 glue_ptr(q) = y;
3966 subtype(q) = normal;
3967 } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
3968 p = vlink(q);
3969 if (p != null)
3970 if ((type(p) == glue_node) || (type(p) == kern_node)) {
3971 couple_nodes(q,vlink(p));
3972 vlink(p) = null;
3973 flush_node_list(p);
3976 goto DONE_WITH_NODE;
3977 break;
3978 case kern_node:
3979 math_kern(q, cur_mu);
3980 goto DONE_WITH_NODE;
3981 break;
3982 default:
3983 confusion("mlist1");
3986 When we get to the following part of the program, we have ``fallen through''
3987 from cases that did not lead to |check_dimensions| or |done_with_noad| or
3988 |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
3989 converted to an hlist, and whose subscripts and superscripts need to be
3990 appended if they are present.
3992 If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
3993 by which a superscript should be moved right with respect to a subscript
3994 when both are present.
3997 p = check_nucleus_complexity(q, &delta, cur_style);
3999 if ((subscr(q) == null) && (supscr(q) == null)) {
4000 assign_new_hlist(q, p);
4001 } else {
4002 /* top, bottom */
4003 make_scripts(q, p, delta, cur_style, 0, 0);
4005 CHECK_DIMENSIONS:
4006 z = hpack(new_hlist(q), 0, additional, -1);
4007 if (height(z) > max_hl)
4008 max_hl = height(z);
4009 if (depth(z) > max_d)
4010 max_d = depth(z);
4011 list_ptr(z) = null;
4012 /* only drop the \.{\\hbox} */
4013 flush_node(z);
4014 DONE_WITH_NOAD:
4015 r = q;
4016 r_type = type(r);
4017 r_subtype = subtype(r);
4018 if (r_type == fence_noad) {
4019 r_subtype = left_noad_side;
4020 cur_style = style;
4021 setup_cur_size(cur_style);
4022 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4024 DONE_WITH_NODE:
4025 q = vlink(q);
4027 if (r_type == simple_noad && r_subtype == bin_noad_type) {
4028 type(r) = simple_noad;
4029 subtype(r) = ord_noad_type;
4032 Make a second pass over the mlist, removing all noads and inserting the
4033 proper spacing and penalties.
4035 We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
4036 The second pass simply goes through and hooks everything together with the
4037 proper glue and penalties. It also handles the |fence_noad|s that
4038 might be present, since |max_hl| and |max_d| are now known. Variable |p| points
4039 to a node at the current end of the final hlist.
4041 p = temp_head;
4042 vlink(p) = null;
4043 q = mlist;
4044 r_type = 0;
4045 r_subtype = 0;
4046 cur_style = style;
4047 setup_cur_size(cur_style);
4048 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4049 NEXT_NODE:
4050 while (q != null) {
4052 If node |q| is a style node, change the style and |goto delete_q|;
4053 otherwise if it is not a noad, put it into the hlist,
4054 advance |q|, and |goto done|; otherwise set |s| to the size
4055 of noad |q|, set |t| to the associated type (|ord_noad..
4056 inner_noad|), and set |pen| to the associated penalty
4058 Just before doing the big |case| switch in the second pass, the program
4059 sets up default values so that most of the branches are short.
4061 t = simple_noad;
4062 t_subtype = ord_noad_type;
4063 pen = inf_penalty;
4064 switch (type(q)) {
4065 case simple_noad:
4066 t_subtype = subtype(q);
4067 switch (t_subtype) {
4068 case bin_noad_type:
4069 pen = bin_op_penalty;
4070 break;
4071 case rel_noad_type:
4072 pen = rel_penalty;
4073 break;
4074 case vcenter_noad_type:
4075 case over_noad_type:
4076 case under_noad_type:
4077 t_subtype = ord_noad_type;
4078 break;
4080 case radical_noad:
4081 break;
4082 case accent_noad:
4083 break;
4084 case fraction_noad:
4085 t = simple_noad;
4086 t_subtype = inner_noad_type;
4087 break;
4088 case fence_noad:
4089 t_subtype = make_left_right(q, style, max_d, max_hl);
4090 break;
4091 case style_node:
4092 /* Change the current style and |goto delete_q| */
4093 cur_style = subtype(q);
4094 setup_cur_size(cur_style);
4095 cur_mu = x_over_n(get_math_quad(cur_size), 18);
4096 goto DELETE_Q;
4097 break;
4098 case whatsit_node:
4099 case penalty_node:
4100 case rule_node:
4101 case disc_node:
4102 case adjust_node:
4103 case ins_node:
4104 case mark_node:
4105 case glue_node:
4106 case kern_node:
4107 couple_nodes(p,q);
4108 p = q;
4109 q = vlink(q);
4110 vlink(p) = null;
4111 goto NEXT_NODE;
4112 break;
4113 default:
4114 confusion("mlist3");
4116 /* Append inter-element spacing based on |r_type| and |t| */
4117 if (r_type > 0) {
4118 /* not the first noad */
4119 z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
4120 if (z != null) {
4121 reset_attributes(z, node_attr(p));
4122 couple_nodes(p,z);
4123 p = z;
4127 Append any |new_hlist| entries for |q|, and any appropriate penalties
4129 We insert a penalty node after the hlist entries of noad |q| if |pen|
4130 is not an ``infinite'' penalty, and if the node immediately following |q|
4131 is not a penalty node or a |rel_noad| or absent entirely.
4133 if (new_hlist(q) != null) {
4134 couple_nodes(p,new_hlist(q));
4135 do {
4136 p = vlink(p);
4137 } while (vlink(p) != null);
4139 if (penalties && vlink(q) != null && pen < inf_penalty) {
4140 r_type = type(vlink(q));
4141 r_subtype = subtype(vlink(q));
4142 if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) {
4143 z = new_penalty(pen);
4144 reset_attributes(z, node_attr(q));
4145 couple_nodes(p,z);
4146 p = z;
4149 if (type(q) == fence_noad && subtype(q) == right_noad_side) {
4150 t = simple_noad;
4151 t_subtype = open_noad_type;
4153 r_type = t;
4154 r_subtype = t_subtype;
4155 DELETE_Q:
4156 r = q;
4157 q = vlink(q);
4159 The m-to-hlist conversion takes place in-place, so the various dependant
4160 fields may not be freed (as would happen if |flush_node| was called).
4162 A low-level |free_node| is easier than attempting to nullify such dependant
4163 fields for all possible node and noad types.
4165 if (nodetype_has_attributes(type(r)))
4166 delete_attribute_ref(node_attr(r));
4167 reset_node_properties(r);
4168 free_node(r, get_node_size(type(r), subtype(r)));