Fix math accents in TRT direction
[luatex.git] / source / texk / web2c / luatexdir / tex / mlist.w
blobbe0cf552378c0f23fad0f17b6edcb74438d2bd7d
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-ls: by using couple_nodes instead of vlink we make sure that we can backtrack as well as have valid prev links */
23 \def\LuaTeX{Lua\TeX}
25 @ @c
28 #include "ptexlib.h"
29 #include "lua/luatex-api.h"
31 @ @c
32 #define delimiter_factor int_par(delimiter_factor_code)
33 #define delimiter_shortfall dimen_par(delimiter_shortfall_code)
34 #define bin_op_penalty int_par(bin_op_penalty_code)
35 #define rel_penalty int_par(rel_penalty_code)
36 #define null_delimiter_space dimen_par(null_delimiter_space_code)
37 #define script_space dimen_par(script_space_code)
38 #define disable_lig int_par(disable_lig_code)
39 #define disable_kern int_par(disable_kern_code)
41 #define nDEBUG
43 #define reset_attributes(p,newatt) do { \
44 delete_attribute_ref(node_attr(p)); \
45 node_attr(p) = newatt; \
46 if (newatt!=null) { \
47 assert(type(newatt)==attribute_list_node); \
48 add_node_attr_ref(node_attr(p)); \
49 } \
50 } while (0)
53 #define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \
54 if (B==text_size) { \
55 def_math_param(A, text_style, (C),D); \
56 def_math_param(A, cramped_text_style, (C),D); \
57 } else if (B==script_size) { \
58 def_math_param(A, script_style, (C),D); \
59 def_math_param(A, cramped_script_style, (C),D); \
60 } else if (B==script_script_size) { \
61 def_math_param(A, script_script_style, (C),D); \
62 def_math_param(A, cramped_script_script_style, (C),D); \
63 } \
64 } while (0)
66 #define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \
67 if (B==text_size) { \
68 def_math_param(A, display_style,(C),D); \
69 def_math_param(A, cramped_display_style,(C),D); \
70 } \
71 } while (0)
74 #define is_new_mathfont(A) (font_math_params(A)>0)
75 #define is_old_mathfont(A,B) (font_math_params(A)==0 && font_params(A)>=(B))
78 #define font_MATH_par(a,b) \
79 (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
82 @ here are the math parameters that are font-dependant
84 @ Before an mlist is converted to an hlist, \TeX\ makes sure that
85 the fonts in family~2 have enough parameters to be math-symbol
86 fonts, and that the fonts in family~3 have enough parameters to be
87 math-extension fonts. The math-symbol parameters are referred to by using the
88 following macros, which take a size code as their parameter; for example,
89 |num1(cur_size)| gives the value of the |num1| parameter for the current size.
90 @^parameters for symbols@>
91 @^font parameters@>
94 #define total_mathsy_params 22
95 #define total_mathex_params 13
97 #define mathsy(A,B) font_param(fam_fnt(2,A),B)
98 #define math_x_height(A) mathsy(A,5) /* height of `\.x' */
99 #define math_quad(A) mathsy(A,6) /* \.{18mu} */
100 #define num1(A) mathsy(A,8) /* numerator shift-up in display styles */
101 #define num2(A) mathsy(A,9) /* numerator shift-up in non-display, non-\.{\\atop} */
102 #define num3(A) mathsy(A,10) /* numerator shift-up in non-display \.{\\atop} */
103 #define denom1(A) mathsy(A,11) /* denominator shift-down in display styles */
104 #define denom2(A) mathsy(A,12) /* denominator shift-down in non-display styles */
105 #define sup1(A) mathsy(A,13) /* superscript shift-up in uncramped display style */
106 #define sup2(A) mathsy(A,14) /* superscript shift-up in uncramped non-display */
107 #define sup3(A) mathsy(A,15) /* superscript shift-up in cramped styles */
108 #define sub1(A) mathsy(A,16) /* subscript shift-down if superscript is absent */
109 #define sub2(A) mathsy(A,17) /* subscript shift-down if superscript is present */
110 #define sup_drop(A) mathsy(A,18) /* superscript baseline below top of large box */
111 #define sub_drop(A) mathsy(A,19) /* subscript baseline below bottom of large box */
112 #define delim1(A) mathsy(A,20) /* size of \.{\\atopwithdelims} delimiters in display styles */
113 #define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */
114 #define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */
117 @ The math-extension parameters have similar macros, but the size code is
118 omitted (since it is always |cur_size| when we refer to such parameters).
119 @^parameters for symbols@>
120 @^font parameters@>
123 #define mathex(A,B) font_param(fam_fnt(3,A),B)
124 #define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */
125 #define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */
126 #define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
127 #define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
128 #define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
129 #define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
131 @ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
132 the MathConstants values have no matching usage in \LuaTeX\ right now.
134 ScriptPercentScaleDown,
135 ScriptScriptPercentScaleDown:
136 These should be handled by the macro package, on the engine
137 side there are three separate fonts
139 DelimitedSubFormulaMinHeight:
140 This is perhaps related to word's natural math input? I have
141 no idea what to do about it
143 MathLeading:
144 LuaTeX does not currently handle multi-line displays, and
145 the parameter does not seem to make much sense elsewhere
147 FlattenedAccentBaseHeight:
148 This is based on the 'flac' GSUB feature. It would not be hard
149 to support that, but proper math accent placements cf. MATH
150 needs support for MathTopAccentAttachment table to be
151 implemented first
153 SkewedFractionHorizontalGap,
154 SkewedFractionVerticalGap:
155 I am not sure it makes sense implementing skewed fractions,
156 so I would like to see an example first
158 Also still TODO for OpenType Math:
159 * extensible large operators
160 * prescripts
162 @ this is not really a math parameter at all
165 static void math_param_error(const char *param, int style)
167 char s[256];
168 const char *hlp[] = {
169 "Sorry, but I can't typeset math unless various parameters have",
170 "been set. This is normally done by loading special math fonts",
171 "into the math family slots. Your font set is lacking at least",
172 "the parameter mentioned earlier.",
173 NULL
175 snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
176 param, math_style_names[style]);
177 tex_error(s, hlp);
178 #if 0
179 flush_math();
180 #endif
181 return;
185 @ @c
186 static scaled accent_base_height(int f)
188 scaled a;
189 if (is_new_mathfont(f)) {
190 a = font_MATH_par(f, AccentBaseHeight);
191 if (a == undefined_math_parameter)
192 a = x_height(f);
193 } else {
194 a = x_height(f);
196 return a;
199 @ the non-staticness of this function is for the benefit of |texmath.w|
202 scaled get_math_quad(int var)
204 scaled a = get_math_param(math_param_quad, var);
205 if (a == undefined_math_parameter) {
206 math_param_error("quad", var);
207 a = 0;
209 return a;
212 @ this parameter is different because it is called with a size
213 specifier instead of a style specifier.
216 static scaled math_axis(int b)
218 scaled a;
219 int var;
220 if (b == script_size)
221 var = script_style;
222 else if (b == script_script_size)
223 var = script_script_style;
224 else
225 var = text_style;
226 a = get_math_param(math_param_axis, var);
227 if (a == undefined_math_parameter) {
228 math_param_error("axis", var);
229 a = 0;
231 return a;
234 @ @c
235 static scaled get_math_quad_size(int b)
237 int var;
238 if (b == script_size)
239 var = script_style;
240 else if (b == script_script_size)
241 var = script_script_style;
242 else
243 var = text_style;
244 return get_math_param(math_param_quad, var);
248 @ @c
249 static scaled minimum_operator_size(int var)
251 scaled a = get_math_param(math_param_operator_size, var);
252 return a;
255 @ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select
256 the backward compatibility code, and it means that we can't raise an error here.
259 static scaled radical_rule(int var)
261 scaled a = get_math_param(math_param_radical_rule, var);
262 return a;
265 @ now follow all the trivial math parameters
268 #define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
270 static scaled do_get_math_param_or_error(int var, int param, const char *name)
272 scaled a = get_math_param(param, var);
273 if (a == undefined_math_parameter) {
274 math_param_error(name, var);
275 a = 0;
277 return a;
280 @ @c
281 #define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
282 #define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after)
283 #define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise)
285 #define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
287 #define overbar_rule(a) get_math_param_or_error(a, overbar_rule)
288 #define overbar_kern(a) get_math_param_or_error(a, overbar_kern)
289 #define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap)
291 #define underbar_rule(a) get_math_param_or_error(a, underbar_rule)
292 #define underbar_kern(a) get_math_param_or_error(a, underbar_kern)
293 #define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap)
295 #define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap)
296 #define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap)
298 #define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap)
299 #define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap)
301 #define radical_vgap(a) get_math_param_or_error(a, radical_vgap)
302 #define radical_kern(a) get_math_param_or_error(a, radical_kern)
304 #define stack_vgap(a) get_math_param_or_error(a, stack_vgap)
305 #define stack_num_up(a) get_math_param_or_error(a, stack_num_up)
306 #define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down)
308 #define fraction_rule(a) get_math_param_or_error(a, fraction_rule)
309 #define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap)
310 #define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap)
311 #define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up)
312 #define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down)
313 #define fraction_del_size(a) get_math_param_or_error(a, fraction_del_size)
315 #define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap)
316 #define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap)
317 #define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern)
319 #define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap)
320 #define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap)
321 #define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern)
323 #define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop)
324 #define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop)
325 #define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down)
326 #define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down)
327 #define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up)
328 #define sub_top_max(a) get_math_param_or_error(a, sub_top_max)
329 #define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min)
330 #define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max)
331 #define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap)
333 #define space_after_script(a) get_math_param_or_error(a, space_after_script)
335 @ @c
336 void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
338 if (is_new_mathfont(f)) { /* fix all known parameters */
340 DEFINE_MATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl);
341 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl);
342 DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
343 font_MATH_par(f, AxisHeight), lvl);
344 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
345 font_MATH_par(f, AxisHeight), lvl);
346 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
347 font_MATH_par(f, OverbarExtraAscender), lvl);
348 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
349 font_MATH_par(f, OverbarExtraAscender), lvl);
350 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
351 font_MATH_par(f, OverbarRuleThickness), lvl);
352 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
353 font_MATH_par(f, OverbarRuleThickness), lvl);
354 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
355 font_MATH_par(f, OverbarVerticalGap), lvl);
356 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
357 font_MATH_par(f, OverbarVerticalGap), lvl);
358 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
359 font_MATH_par(f, UnderbarExtraDescender), lvl);
360 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
361 font_MATH_par(f, UnderbarExtraDescender), lvl);
362 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
363 font_MATH_par(f, UnderbarRuleThickness), lvl);
364 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
365 font_MATH_par(f, UnderbarRuleThickness), lvl);
366 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
367 font_MATH_par(f, UnderbarVerticalGap), lvl);
368 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
369 font_MATH_par(f, UnderbarVerticalGap), lvl);
371 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
372 font_MATH_par(f, StretchStackGapAboveMin), lvl);
373 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
374 font_MATH_par(f, StretchStackGapAboveMin), lvl);
375 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
376 font_MATH_par(f, StretchStackBottomShiftDown),
377 lvl);
378 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
379 font_MATH_par(f, StretchStackBottomShiftDown),
380 lvl);
382 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
383 font_MATH_par(f, StretchStackGapBelowMin), lvl);
384 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
385 font_MATH_par(f, StretchStackGapBelowMin), lvl);
386 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
387 font_MATH_par(f, StretchStackTopShiftUp), lvl);
388 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
389 font_MATH_par(f, StretchStackTopShiftUp), lvl);
392 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
393 font_MATH_par(f, StackTopShiftUp), lvl);
394 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
395 font_MATH_par(f, StackTopDisplayStyleShiftUp),
396 lvl);
397 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
398 font_MATH_par(f, StackBottomShiftDown), lvl);
399 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
400 font_MATH_par(f,
401 StackBottomDisplayStyleShiftDown),
402 lvl);
403 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
404 font_MATH_par(f, StackGapMin), lvl);
405 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
406 font_MATH_par(f, StackDisplayStyleGapMin), lvl);
408 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
409 font_MATH_par(f, RadicalExtraAscender), lvl);
410 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
411 font_MATH_par(f, RadicalExtraAscender), lvl);
413 DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
414 font_MATH_par(f, DisplayOperatorMinHeight),
415 lvl);
417 DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
418 font_MATH_par(f, RadicalRuleThickness), lvl);
419 DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
420 font_MATH_par(f, RadicalRuleThickness), lvl);
421 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
422 font_MATH_par(f, RadicalVerticalGap), lvl);
423 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
424 font_MATH_par(f,
425 RadicalDisplayStyleVerticalGap),
426 lvl);
427 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
428 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
429 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
430 font_MATH_par(f, RadicalKernBeforeDegree), lvl);
431 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
432 font_MATH_par(f, RadicalKernAfterDegree), lvl);
433 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
434 font_MATH_par(f, RadicalKernAfterDegree), lvl);
435 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
436 font_MATH_par(f,
437 RadicalDegreeBottomRaisePercent),
438 lvl);
439 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
440 font_MATH_par(f,
441 RadicalDegreeBottomRaisePercent),
442 lvl);
443 if (size_id == text_size) {
444 def_math_param(math_param_sup_shift_up, display_style,
445 font_MATH_par(f, SuperscriptShiftUp), lvl);
446 def_math_param(math_param_sup_shift_up, cramped_display_style,
447 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
448 def_math_param(math_param_sup_shift_up, text_style,
449 font_MATH_par(f, SuperscriptShiftUp), lvl);
450 def_math_param(math_param_sup_shift_up, cramped_text_style,
451 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
452 } else if (size_id == script_size) {
453 def_math_param(math_param_sup_shift_up, script_style,
454 font_MATH_par(f, SuperscriptShiftUp), lvl);
455 def_math_param(math_param_sup_shift_up, cramped_script_style,
456 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
457 } else if (size_id == script_script_size) {
458 def_math_param(math_param_sup_shift_up, script_script_style,
459 font_MATH_par(f, SuperscriptShiftUp), lvl);
460 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
461 font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
464 DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
465 font_MATH_par(f, SubscriptBaselineDropMin), lvl);
466 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
467 font_MATH_par(f, SubscriptBaselineDropMin),
468 lvl);
469 DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
470 font_MATH_par(f, SuperscriptBaselineDropMax),
471 lvl);
472 DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
473 font_MATH_par(f, SuperscriptBaselineDropMax),
474 lvl);
475 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
476 font_MATH_par(f, SubscriptShiftDown), lvl);
477 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
478 font_MATH_par(f, SubscriptShiftDown), lvl);
480 if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) !=
481 undefined_math_parameter) {
482 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
483 font_MATH_par(f,
484 SubscriptShiftDownWithSuperscript),
485 lvl);
486 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
487 font_MATH_par(f,
488 SubscriptShiftDownWithSuperscript),
489 lvl);
490 } else {
491 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
492 font_MATH_par(f, SubscriptShiftDown), lvl);
493 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
494 font_MATH_par(f, SubscriptShiftDown), lvl);
497 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
498 font_MATH_par(f, SubscriptTopMax), lvl);
499 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
500 font_MATH_par(f, SubscriptTopMax), lvl);
501 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
502 font_MATH_par(f, SuperscriptBottomMin), lvl);
503 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
504 font_MATH_par(f, SuperscriptBottomMin), lvl);
505 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
506 font_MATH_par(f,
507 SuperscriptBottomMaxWithSubscript),
508 lvl);
509 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
510 font_MATH_par(f,
511 SuperscriptBottomMaxWithSubscript),
512 lvl);
513 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
514 font_MATH_par(f, SubSuperscriptGapMin), lvl);
515 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
516 font_MATH_par(f, SubSuperscriptGapMin), lvl);
518 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
519 font_MATH_par(f, UpperLimitGapMin), lvl);
520 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
521 font_MATH_par(f, UpperLimitGapMin), lvl);
522 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
523 font_MATH_par(f, UpperLimitBaselineRiseMin),
524 lvl);
525 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
526 font_MATH_par(f, UpperLimitBaselineRiseMin),
527 lvl);
528 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl);
529 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl);
530 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
531 font_MATH_par(f, LowerLimitGapMin), lvl);
532 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
533 font_MATH_par(f, LowerLimitGapMin), lvl);
534 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
535 font_MATH_par(f, LowerLimitBaselineDropMin),
536 lvl);
537 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
538 font_MATH_par(f, LowerLimitBaselineDropMin),
539 lvl);
540 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl);
541 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl);
543 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
544 font_MATH_par(f, FractionRuleThickness), lvl);
545 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
546 font_MATH_par(f, FractionRuleThickness), lvl);
547 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
548 font_MATH_par(f, FractionNumeratorGapMin), lvl);
549 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
550 font_MATH_par(f,
551 FractionNumeratorDisplayStyleGapMin),
552 lvl);
553 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
554 font_MATH_par(f, FractionNumeratorShiftUp), lvl);
555 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
556 font_MATH_par(f,
557 FractionNumeratorDisplayStyleShiftUp),
558 lvl);
559 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
560 font_MATH_par(f, FractionDenominatorGapMin),
561 lvl);
562 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
563 font_MATH_par(f,
564 FractionDenominatorDisplayStyleGapMin),
565 lvl);
566 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
567 font_MATH_par(f, FractionDenominatorShiftDown),
568 lvl);
569 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
570 font_MATH_par(f,
571 FractionDenominatorDisplayStyleShiftDown),
572 lvl);
574 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
575 font_MATH_par(f, FractionDelimiterSize), lvl);
576 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
577 font_MATH_par(f,
578 FractionDelimiterDisplayStyleSize),
579 lvl);
581 DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
582 font_MATH_par(f, SpaceAfterScript), lvl);
583 DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
584 font_MATH_par(f, SpaceAfterScript), lvl);
586 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
587 font_MATH_par(f, MinConnectorOverlap), lvl);
588 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
589 font_MATH_par(f, MinConnectorOverlap), lvl);
592 } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
593 /* fix old-style |sy| parameters */
594 DEFINE_MATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id),
595 lvl);
596 DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id),
597 lvl);
598 DEFINE_MATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id),
599 lvl);
600 DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id),
601 lvl);
602 DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, num3(size_id),
603 lvl);
604 DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, num1(size_id),
605 lvl);
606 DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
607 denom2(size_id), lvl);
608 DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
609 denom1(size_id), lvl);
610 DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
611 num2(size_id), lvl);
612 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
613 num1(size_id), lvl);
614 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
615 denom2(size_id), lvl);
616 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
617 denom1(size_id), lvl);
618 DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
619 delim2(size_id), lvl);
620 DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
621 delim1(size_id), lvl);
622 if (size_id == text_size) {
623 def_math_param(math_param_sup_shift_up, display_style,
624 sup1(size_id), lvl);
625 def_math_param(math_param_sup_shift_up, cramped_display_style,
626 sup3(size_id), lvl);
627 def_math_param(math_param_sup_shift_up, text_style, sup2(size_id),
628 lvl);
629 def_math_param(math_param_sup_shift_up, cramped_text_style,
630 sup3(size_id), lvl);
631 } else if (size_id == script_size) {
632 def_math_param(math_param_sub_shift_drop, display_style,
633 sub_drop(size_id), lvl);
634 def_math_param(math_param_sub_shift_drop, cramped_display_style,
635 sub_drop(size_id), lvl);
636 def_math_param(math_param_sub_shift_drop, text_style,
637 sub_drop(size_id), lvl);
638 def_math_param(math_param_sub_shift_drop, cramped_text_style,
639 sub_drop(size_id), lvl);
640 def_math_param(math_param_sup_shift_drop, display_style,
641 sup_drop(size_id), lvl);
642 def_math_param(math_param_sup_shift_drop, cramped_display_style,
643 sup_drop(size_id), lvl);
644 def_math_param(math_param_sup_shift_drop, text_style,
645 sup_drop(size_id), lvl);
646 def_math_param(math_param_sup_shift_drop, cramped_text_style,
647 sup_drop(size_id), lvl);
648 def_math_param(math_param_sup_shift_up, script_style, sup2(size_id),
649 lvl);
650 def_math_param(math_param_sup_shift_up, cramped_script_style,
651 sup3(size_id), lvl);
652 } else if (size_id == script_script_size) {
653 def_math_param(math_param_sub_shift_drop, script_style,
654 sub_drop(size_id), lvl);
655 def_math_param(math_param_sub_shift_drop, cramped_script_style,
656 sub_drop(size_id), lvl);
657 def_math_param(math_param_sub_shift_drop, script_script_style,
658 sub_drop(size_id), lvl);
659 def_math_param(math_param_sub_shift_drop,
660 cramped_script_script_style, sub_drop(size_id), lvl);
661 def_math_param(math_param_sup_shift_drop, script_style,
662 sup_drop(size_id), lvl);
663 def_math_param(math_param_sup_shift_drop, cramped_script_style,
664 sup_drop(size_id), lvl);
665 def_math_param(math_param_sup_shift_drop, script_script_style,
666 sup_drop(size_id), lvl);
667 def_math_param(math_param_sup_shift_drop,
668 cramped_script_script_style, sup_drop(size_id), lvl);
669 def_math_param(math_param_sup_shift_up, script_script_style,
670 sup2(size_id), lvl);
671 def_math_param(math_param_sup_shift_up, cramped_script_script_style,
672 sup3(size_id), lvl);
674 DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
675 sub1(size_id), lvl);
676 DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
677 sub1(size_id), lvl);
678 DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
679 sub2(size_id), lvl);
680 DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
681 sub2(size_id), lvl);
682 DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
683 (abs(math_x_height(size_id) * 4) / 5), lvl);
684 DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
685 (abs(math_x_height(size_id) * 4) / 5), lvl);
686 DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
687 (abs(math_x_height(size_id)) / 4), lvl);
688 DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
689 (abs(math_x_height(size_id)) / 4), lvl);
690 DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
691 (abs(math_x_height(size_id) * 4) / 5), lvl);
692 DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
693 (abs(math_x_height(size_id) * 4) / 5), lvl);
695 /* The display-size |radical_vgap| is done twice because it needs
696 values from both the sy and the ex font. */
697 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
698 (default_rule_thickness(size_id) +
699 (abs(math_x_height(size_id)) / 4)), lvl);
701 DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
702 60, lvl);
703 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
704 60, lvl);
705 DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
706 xn_over_d(get_math_quad_size(size_id), 5, 18),
707 lvl);
708 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
709 xn_over_d(get_math_quad_size(size_id), 5, 18),
710 lvl);
711 DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
712 (-xn_over_d
713 (get_math_quad_size(size_id), 10, 18)), lvl);
714 DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
715 (-xn_over_d
716 (get_math_quad_size(size_id), 10, 18)), lvl);
718 } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
719 /* fix old-style |ex| parameters */
720 DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
721 default_rule_thickness(size_id), lvl);
722 DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
723 default_rule_thickness(size_id), lvl);
724 DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
725 3 * default_rule_thickness(size_id), lvl);
726 DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
727 default_rule_thickness(size_id), lvl);
728 DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
729 default_rule_thickness(size_id), lvl);
730 DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
731 3 * default_rule_thickness(size_id), lvl);
732 DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
733 default_rule_thickness(size_id), lvl);
734 DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
735 default_rule_thickness(size_id), lvl);
736 DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
737 3 * default_rule_thickness(size_id), lvl);
738 DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
739 default_rule_thickness(size_id), lvl);
740 DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
741 default_rule_thickness(size_id), lvl);
742 DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
743 3 * default_rule_thickness(size_id), lvl);
744 DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
745 default_rule_thickness(size_id), lvl);
746 DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
747 default_rule_thickness(size_id), lvl);
749 DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
750 (default_rule_thickness(size_id) +
751 (abs(default_rule_thickness(size_id)) / 4)),
752 lvl);
754 DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
755 3 * default_rule_thickness(size_id), lvl);
756 DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
757 7 * default_rule_thickness(size_id), lvl);
758 DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
759 default_rule_thickness(size_id), lvl);
760 DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
761 default_rule_thickness(size_id), lvl);
762 DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
763 default_rule_thickness(size_id), lvl);
764 DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
765 3 * default_rule_thickness(size_id), lvl);
766 DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
767 default_rule_thickness(size_id), lvl);
768 DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
769 3 * default_rule_thickness(size_id), lvl);
770 DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
771 big_op_spacing1(size_id), lvl);
772 DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
773 big_op_spacing1(size_id), lvl);
774 DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
775 big_op_spacing3(size_id), lvl);
776 DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
777 big_op_spacing3(size_id), lvl);
778 DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
779 big_op_spacing5(size_id), lvl);
780 DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
781 big_op_spacing5(size_id), lvl);
782 DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
783 big_op_spacing2(size_id), lvl);
784 DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
785 big_op_spacing2(size_id), lvl);
786 DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
787 big_op_spacing4(size_id), lvl);
788 DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
789 big_op_spacing4(size_id), lvl);
790 DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
791 big_op_spacing5(size_id), lvl);
792 DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
793 big_op_spacing5(size_id), lvl);
794 DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
795 4 * default_rule_thickness(size_id), lvl);
796 DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
797 4 * default_rule_thickness(size_id), lvl);
798 /* All of the |space_after_script|s are done in |finalize_math_parameters| because the
799 \.{\\scriptspace} may have been altered by the user
801 DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0,
802 lvl);
803 DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0,
804 lvl);
806 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
807 big_op_spacing2(size_id), lvl);
808 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
809 big_op_spacing2(size_id), lvl);
810 DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
811 big_op_spacing4(size_id), lvl);
812 DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
813 big_op_spacing4(size_id), lvl);
814 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
815 big_op_spacing1(size_id), lvl);
816 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
817 big_op_spacing1(size_id), lvl);
818 DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
819 big_op_spacing3(size_id), lvl);
820 DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
821 big_op_spacing3(size_id), lvl);
823 /* The display-size |radical_vgap| is done twice because it needs
824 values from both the sy and the ex font. */
825 DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
826 (default_rule_thickness(size_id) +
827 (abs(math_x_height(size_id)) / 4)), lvl);
831 @ This needs to be called just at the start of |mlist_to_hlist|, for
832 backward compatibility with \.{\\scriptspace}.
835 static void finalize_math_parameters(void)
837 int saved_trace = int_par(tracing_assigns_code);
838 int_par(tracing_assigns_code) = 0;
839 if (get_math_param(math_param_space_after_script, display_style) ==
840 undefined_math_parameter) {
841 def_math_param(math_param_space_after_script, display_style,
842 script_space, level_one);
843 def_math_param(math_param_space_after_script, text_style,
844 script_space, level_one);
845 def_math_param(math_param_space_after_script, script_style,
846 script_space, level_one);
847 def_math_param(math_param_space_after_script, script_script_style,
848 script_space, level_one);
849 def_math_param(math_param_space_after_script, cramped_display_style,
850 script_space, level_one);
851 def_math_param(math_param_space_after_script, cramped_text_style,
852 script_space, level_one);
853 def_math_param(math_param_space_after_script, cramped_script_style,
854 script_space, level_one);
855 def_math_param(math_param_space_after_script,
856 cramped_script_script_style, script_space, level_one);
858 int_par(tracing_assigns_code) = saved_trace;
863 @ In order to convert mlists to hlists, i.e., noads to nodes, we need several
864 subroutines that are conveniently dealt with now.
866 Let us first introduce the macros that make it easy to get at the parameters and
867 other font information. A size code, which is a multiple of 256, is added to a
868 family number to get an index into the table of internal font numbers
869 for each combination of family and size. (Be alert: Size codes get
870 larger as the type gets smaller.)
873 static const char *math_size_string(int s)
875 if (s == text_size)
876 return "textfont";
877 else if (s == script_size)
878 return "scriptfont";
879 else
880 return "scriptscriptfont";
883 @ When the style changes, the following piece of program computes associated
884 information:
887 #define setup_cur_size(a) do { \
888 if (a==script_style || \
889 a==cramped_script_style) \
890 cur_size=script_size; \
891 else if (a==script_script_style || \
892 a==cramped_script_script_style) \
893 cur_size=script_script_size; \
894 else cur_size=text_size; \
895 } while (0)
898 @ a simple routine that creates a flat copy of a nucleus
900 static pointer math_clone(pointer q)
902 pointer x;
903 if (q == null)
904 return null;
905 x = new_node(type(q), 0);
906 reset_attributes(x, node_attr(q));
907 if (type(q) == math_char_node) {
908 math_fam(x) = math_fam(q);
909 math_character(x) = math_character(q);
910 } else {
911 math_list(x) = math_list(q);
913 return x;
919 @ Here is a function that returns a pointer to a rule node having a given
920 thickness |t|. The rule will extend horizontally to the boundary of the vlist
921 that eventually contains it.
924 static pointer do_fraction_rule(scaled t, pointer att)
926 pointer p; /* the new node */
927 p = new_rule();
928 rule_dir(p) = math_direction;
929 height(p) = t;
930 depth(p) = 0;
931 reset_attributes(p, att);
932 return p;
936 @ The |overbar| function returns a pointer to a vlist box that consists of
937 a given box |b|, above which has been placed a kern of height |k| under a
938 fraction rule of thickness |t| under additional space of height |ht|.
941 static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att)
943 pointer p, q; /* nodes being constructed */
944 p = new_kern(k);
945 couple_nodes(p,b);
946 reset_attributes(p, att);
947 q = do_fraction_rule(t, att);
948 couple_nodes(q,p);
949 p = new_kern(ht);
950 reset_attributes(p, att);
951 couple_nodes(p,q);
952 q = vpackage(p, 0, additional, max_dimen, math_direction);
953 reset_attributes(q, att);
954 return q;
957 @ Here is a subroutine that creates a new box, whose list contains a
958 single character, and whose width includes the italic correction for
959 that character. The height or depth of the box will be negative, if
960 the height or depth of the character is negative; thus, this routine
961 may deliver a slightly different result than |hpack| would produce.
964 static pointer char_box(internal_font_number f, int c, pointer bb)
966 pointer b, p; /* the new box and its character node */
967 b = new_null_box();
968 width(b) = char_width(f, c) + char_italic(f, c);
969 height(b) = char_height(f, c);
970 depth(b) = char_depth(f, c);
971 reset_attributes(b, bb);
972 p = new_char(f, c);
973 reset_attributes(p, bb);
974 list_ptr(b) = p;
975 return b;
978 @ Another handy subroutine computes the height plus depth of
979 a given character:
982 static scaled height_plus_depth(internal_font_number f, int c)
984 return (char_height(f, c) + char_depth(f, c));
988 @ When we build an extensible character, it's handy to have the
989 following subroutine, which puts a given character on top
990 of the characters already in box |b|:
993 static scaled stack_into_box(pointer b, internal_font_number f, int c)
995 pointer p, q; /* new node placed into |b| */
996 p = char_box(f, c, node_attr(b));
997 if (type(b) == vlist_node) {
998 // vlink(p) = list_ptr(b);
999 try_couple_nodes(p,list_ptr(b));
1000 list_ptr(b) = p;
1001 height(b) = height(p);
1002 if (width(b) < width(p))
1003 width(b) = width(p);
1004 return height_plus_depth(f, c);
1005 } else {
1006 q = list_ptr(b);
1007 if (q == null) {
1008 list_ptr(b) = p;
1009 } else {
1010 while (vlink(q) != null)
1011 q = vlink(q);
1012 couple_nodes(q,p);
1014 if (height(b) < height(p))
1015 height(b) = height(p);
1016 if (depth(b) < depth(p))
1017 depth(b) = depth(p);
1018 return char_width(f, c);
1023 static void stack_glue_into_box(pointer b, scaled min, scaled max) {
1024 pointer p, q; /* new node placed into |b| */
1025 q = new_spec(zero_glue);
1026 width(q) = min;
1027 stretch(q) = max-min;
1028 p = new_glue(q);
1029 reset_attributes(p, node_attr(b));
1030 if (type(b) == vlist_node) {
1031 try_couple_nodes(p,list_ptr(b));
1032 list_ptr(b) = p;
1033 } else {
1034 q = list_ptr(b);
1035 if (q == null) {
1036 list_ptr(b) = p;
1037 } else {
1038 while (vlink(q) != null)
1039 q = vlink(q);
1040 couple_nodes(q,p);
1045 @ \TeX's most important routine for dealing with formulas is called
1046 |mlist_to_hlist|. After a formula has been scanned and represented
1047 as an mlist, this routine converts it to an hlist that can be placed
1048 into a box or incorporated into the text of a paragraph. The
1049 explicit parameter |cur_mlist| points to the first node or noad in
1050 the given mlist (and it might be |null|); the parameter |penalties|
1051 is |true| if penalty nodes for potential line breaks are to be
1052 inserted into the resulting hlist, the parameter |cur_style| is a
1053 style code. After |mlist_to_hlist| has acted, |vlink(temp_head)|
1054 points to the translated hlist.
1056 Since mlists can be inside mlists, the procedure is recursive. And since this
1057 is not part of \TeX's inner loop, the program has been written in a manner
1058 that stresses compactness over efficiency.
1059 @^recursion@>
1062 int cur_size; /* size code corresponding to |cur_style| */
1064 @ @c
1065 static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v,
1066 pointer att, int boxtype, int cur_style)
1068 pointer b; /* new box */
1069 scaled b_max; /* natural (maximum) size of the stack */
1070 scaled s_max; /* amount of possible shrink in the stack */
1071 extinfo *cur;
1072 scaled min_overlap, prev_overlap;
1073 int i; /* a temporary counter number of extensible pieces */
1074 int with_extenders; /* number of times to repeat each repeatable item in |ext| */
1075 int num_extenders, num_normal;
1076 scaled a, c, d;
1078 assert(ext != NULL);
1079 b = new_null_box();
1080 type(b) = (quarterword) boxtype;
1081 reset_attributes(b, att);
1082 min_overlap = connector_overlap_min(cur_style);
1083 assert(min_overlap >= 0);
1084 with_extenders = -1;
1085 num_extenders = 0;
1086 num_normal = 0;
1088 cur = ext;
1089 while (cur != NULL) {
1090 if (!char_exists(f, cur->glyph)) {
1091 const char *hlp[] = {
1092 "Each glyph part in an extensible item should exist in the font.",
1093 "I will give up trying to find a suitable size for now. Fix your font!",
1094 NULL
1096 tex_error("Variant part doesn't exist.", hlp);
1097 width(b) = null_delimiter_space;
1098 return b;
1100 if (cur->extender > 0)
1101 num_extenders++;
1102 else
1103 num_normal++;
1104 /* no negative overlaps or advances are allowed */
1105 if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
1106 const char *hlp[] = {
1107 "All measurements in extensible items should be positive.",
1108 "To get around this problem, I have changed the font metrics.",
1109 "Fix your font!",
1110 NULL
1112 tex_error("Extensible recipe has negative fields.", hlp);
1113 if (cur->start_overlap < 0)
1114 cur->start_overlap = 0;
1115 if (cur->end_overlap < 0)
1116 cur->end_overlap = 0;
1117 if (cur->advance < 0)
1118 cur->advance = 0;
1120 cur = cur->next;
1122 if (num_normal == 0) {
1123 const char *hlp[] = {
1124 "Each extensible recipe should have at least one non-repeatable part.",
1125 "To get around this problem, I have changed the first part to be",
1126 "non-repeatable. Fix your font!",
1127 NULL
1129 tex_error("Extensible recipe has no fixed parts.", hlp);
1130 ext->extender = 0;
1131 num_normal = 1;
1132 num_extenders--;
1134 /* |ext| holds a linked list of numerous items that may or may not be
1135 repeatable. For the total height, we have to figure out how many items
1136 are needed to create a stack of at least |v|.
1137 The next |while| loop does that. It has two goals: it finds out
1138 the natural height |b_max| of the all the parts needed to reach
1139 at least |v|, and it sets |with_extenders| to the number of times
1140 each of the repeatable items in |ext| has to be repeated to reach
1141 that height.
1143 cur = ext;
1144 b_max = 0;
1145 while (b_max < v && num_extenders > 0) {
1146 b_max = 0;
1147 prev_overlap = 0;
1148 with_extenders++;
1149 for (cur = ext; cur != NULL; cur = cur->next) {
1150 if (cur->extender == 0) {
1151 c = cur->start_overlap;
1152 if (min_overlap < c)
1153 c = min_overlap;
1154 if (prev_overlap < c)
1155 c = prev_overlap;
1156 a = cur->advance;
1157 if (a == 0) {
1158 /* for tfm fonts */
1159 if (boxtype == vlist_node)
1160 a = height_plus_depth(f, cur->glyph);
1161 else
1162 a = char_width(f, cur->glyph);
1163 assert(a >= 0);
1165 b_max += a - c;
1166 prev_overlap = cur->end_overlap;
1167 } else {
1168 i = with_extenders;
1169 while (i > 0) {
1170 c = cur->start_overlap;
1171 if (min_overlap < c)
1172 c = min_overlap;
1173 if (prev_overlap < c)
1174 c = prev_overlap;
1175 a = cur->advance;
1176 if (a == 0) {
1177 /* for tfm fonts */
1178 if (boxtype == vlist_node)
1179 a = height_plus_depth(f, cur->glyph);
1180 else
1181 a = char_width(f, cur->glyph);
1182 assert(a >= 0);
1184 b_max += a - c;
1185 prev_overlap = cur->end_overlap;
1186 i--;
1192 /* assemble box using |with_extenders| copies of each extender, with
1193 appropriate glue wherever an overlap occurs */
1194 prev_overlap = 0;
1195 b_max = 0;
1196 s_max = 0;
1197 for (cur = ext; cur != NULL; cur = cur->next) {
1198 if (cur->extender == 0) {
1199 c = cur->start_overlap;
1200 if (prev_overlap < c)
1201 c = prev_overlap;
1202 d = c;
1203 if (min_overlap < c)
1204 c = min_overlap;
1205 if (d > 0) {
1206 stack_glue_into_box(b, -d, -c);
1207 s_max += (-c) - (-d);
1208 b_max -= d;
1210 b_max += stack_into_box(b, f, cur->glyph);
1211 prev_overlap = cur->end_overlap;
1212 i--;
1213 } else {
1214 i = with_extenders;
1215 while (i > 0) {
1216 c = cur->start_overlap;
1217 if (prev_overlap < c)
1218 c = prev_overlap;
1219 d = c;
1220 if (min_overlap < c)
1221 c = min_overlap;
1222 if (d > 0) {
1223 stack_glue_into_box(b, -d, -c);
1224 s_max += (-c) - (-d);
1225 b_max -= d;
1227 b_max += stack_into_box(b, f, cur->glyph);
1228 prev_overlap = cur->end_overlap;
1229 i--;
1234 /* set glue so as to stretch the connections if needed */
1235 d = 0;
1236 if (v > b_max && s_max > 0) {
1237 d = v-b_max;
1238 /* don't stretch more than |s_max| */
1239 if (d > s_max)
1240 d = s_max;
1241 glue_order(b) = normal;
1242 glue_sign(b) = stretching;
1243 glue_set(b) = unfloat(d/(float) s_max);
1244 b_max += d;
1247 if (boxtype == vlist_node)
1248 height(b) = b_max;
1249 else
1250 width(b) = b_max;
1252 return b;
1255 static pointer get_delim_vbox(extinfo * ext, internal_font_number f, scaled v,
1256 pointer att, int cur_style)
1258 return get_delim_box(ext, f, v, att, vlist_node, cur_style);
1261 static pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v,
1262 pointer att, int cur_style)
1264 return get_delim_box(ext, f, v, att, hlist_node, cur_style);
1269 @ The |var_delimiter| function, which finds or constructs a sufficiently
1270 large delimiter, is the most interesting of the auxiliary functions that
1271 currently concern us. Given a pointer |d| to a delimiter field in some noad,
1272 together with a size code |s| and a vertical distance |v|, this function
1273 returns a pointer to a box that contains the smallest variant of |d| whose
1274 height plus depth is |v| or more. (And if no variant is large enough, it
1275 returns the largest available variant.) In particular, this routine will
1276 construct arbitrarily large delimiters from extensible components, if
1277 |d| leads to such characters.
1279 The value returned is a box whose |shift_amount| has been set so that
1280 the box is vertically centered with respect to the axis in the given size.
1281 If a built-up symbol is returned, the height of the box before shifting
1282 will be the height of its topmost component.
1285 static void endless_loop_error(internal_font_number g, int y)
1287 char s[256];
1288 const char *hlp[] = {
1289 "You managed to create a seemingly endless charlist chain in the current",
1290 "font. I have counted until 10000 already and still have not escaped, so"
1291 "I will jump out of the loop all by myself now. Fix your font!",
1292 NULL
1294 snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
1295 (int) y, font_name(g));
1296 tex_error(s, hlp);
1299 static pointer do_var_delimiter(pointer d, int s, scaled v, scaled * ic,
1300 boolean flat, int cur_style)
1302 /* label found,continue; */
1303 pointer b; /* the box that will be constructed */
1304 internal_font_number f, g; /* best-so-far and tentative font codes */
1305 int c, i, x, y; /* best-so-far and tentative character codes */
1306 scaled u; /* height-plus-depth of a tentative character */
1307 scaled w; /* largest height-plus-depth so far */
1308 int z; /* runs through font family members */
1309 boolean large_attempt; /* are we trying the ``large'' variant? */
1310 pointer att; /* to save the current attribute list */
1311 boolean do_parts;
1312 extinfo *ext;
1313 att = null;
1314 f = null_font;
1315 c = 0;
1316 w = 0;
1317 do_parts = false;
1318 large_attempt = false;
1319 if (d == null)
1320 goto FOUND;
1321 z = small_fam(d);
1322 x = small_char(d);
1323 i = 0;
1324 while (true) {
1325 /* The search process is complicated slightly by the facts that some of the
1326 characters might not be present in some of the fonts, and they might not
1327 be probed in increasing order of height. */
1328 if ((z != 0) || (x != 0)) {
1329 g = fam_fnt(z, s);
1330 if (g != null_font) {
1331 y = x;
1332 CONTINUE:
1333 i++;
1334 if (char_exists(g, y)) {
1335 if (flat)
1336 u = char_width(g, y);
1337 else
1338 u = height_plus_depth(g, y);
1339 if (u > w) {
1340 f = g;
1341 c = y;
1342 w = u;
1343 if (u >= v)
1344 goto FOUND;
1346 if (char_tag(g, y) == ext_tag) {
1347 f = g;
1348 c = y;
1349 do_parts = true;
1350 goto FOUND;
1352 if (i > 10000) {
1353 /* endless loop */
1354 endless_loop_error(g, y);
1355 goto FOUND;
1357 if (char_tag(g, y) == list_tag) {
1358 y = char_remainder(g, y);
1359 goto CONTINUE;
1364 if (large_attempt)
1365 goto FOUND; /* there were none large enough */
1366 large_attempt = true;
1367 z = large_fam(d);
1368 x = large_char(d);
1370 FOUND:
1371 if (d != null) {
1372 att = node_attr(d);
1373 node_attr(d) = null;
1374 flush_node(d);
1376 if (f != null_font) {
1377 /* When the following code is executed, |do_parts| will be true
1378 if a built-up symbol is supposed to be returned.
1380 ext = NULL;
1381 if ((do_parts) &&
1382 ((!flat
1383 && (ext = get_charinfo_vert_variants(char_info(f, c))) != NULL)
1384 || (flat
1385 && (ext =
1386 get_charinfo_hor_variants(char_info(f, c))) != NULL))) {
1387 b = (flat ? get_delim_hbox(ext, f, v, att, cur_style) :
1388 get_delim_vbox(ext, f, v, att, cur_style));
1389 } else {
1390 b = char_box(f, c, att);
1392 /* This next test is because for OT MATH fonts, the italic correction of an
1393 extensible character is only used for the placement of a subscript
1394 (in negated form), and it is not supposed to be added to the
1395 width of the character box at all.
1397 This has an effect later on in |make_op| as well, where it has to do
1398 an extra correction for |make_script|'s addition of yet another italic
1399 correction.
1401 if (!is_new_mathfont(f)) {
1402 width(b) += char_italic(f, c);
1404 if (ic != NULL)
1405 *ic = char_italic(f, c);
1406 } else {
1407 b = new_null_box();
1408 reset_attributes(b, att);
1409 width(b) = (flat ? 0 : null_delimiter_space); /* use this width if no delimiter was found */
1410 if (ic != NULL)
1411 *ic = 0;
1413 if (!flat)
1414 shift_amount(b) = half(height(b) - depth(b)) - math_axis(s);
1415 delete_attribute_ref(att);
1416 return b;
1420 static pointer var_delimiter(pointer d, int s, scaled v, scaled * ic,
1421 int cur_style)
1423 return do_var_delimiter(d, s, v, ic, false, cur_style);
1426 static pointer flat_delimiter(pointer d, int s, scaled v, int cur_style)
1428 return do_var_delimiter(d, s, v, NULL, true, cur_style);
1431 @ The next subroutine is much simpler; it is used for numerators and
1432 denominators of fractions as well as for displayed operators and
1433 their limits above and below. It takes a given box~|b| and
1434 changes it so that the new box is centered in a box of width~|w|.
1435 The centering is done by putting \.{\\hss} glue at the left and right
1436 of the list inside |b|, then packaging the new box; thus, the
1437 actual box might not really be centered, if it already contains
1438 infinite glue.
1441 The given box might contain a single character whose italic correction
1442 has been added to the width of the box; in this case a compensating
1443 kern is inserted.
1446 static pointer rebox(pointer b, scaled w)
1448 pointer p, q, r, att; /* temporary registers for list manipulation */
1449 internal_font_number f; /* font in a one-character box */
1450 scaled v; /* width of a character without italic correction */
1452 if ((width(b) != w) && (list_ptr(b) != null)) {
1453 if (type(b) == vlist_node) {
1454 p = hpack(b, 0, additional, -1);
1455 reset_attributes(p, node_attr(b));
1456 b = p;
1458 p = list_ptr(b);
1459 att = node_attr(b);
1460 add_node_attr_ref(att);
1461 if ((is_char_node(p)) && (vlink(p) == null)) {
1462 f = font(p);
1463 v = char_width(f, character(p));
1464 if (v != width(b)) {
1465 q = new_kern(width(b) - v);
1466 reset_attributes(q, att);
1467 couple_nodes(p,q);
1470 list_ptr(b) = null;
1471 flush_node(b);
1472 b = new_glue(ss_glue);
1473 reset_attributes(b, att);
1474 couple_nodes(b,p);
1475 while (vlink(p) != null)
1476 p = vlink(p);
1477 q = new_glue(ss_glue);
1478 reset_attributes(q, att);
1479 couple_nodes(p,q);
1480 r = hpack(b, w, exactly, -1);
1481 reset_attributes(r, att);
1482 delete_attribute_ref(att);
1483 return r;
1484 } else {
1485 width(b) = w;
1486 return b;
1490 @ Here is a subroutine that creates a new glue specification from another
1491 one that is expressed in `\.{mu}', given the value of the math unit.
1494 #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
1496 static pointer math_glue(pointer g, scaled m)
1498 pointer p; /* the new glue specification */
1499 int n; /* integer part of |m| */
1500 scaled f; /* fraction part of |m| */
1501 n = x_over_n(m, unity);
1502 f = tex_remainder;
1503 if (f < 0) {
1504 decr(n);
1505 f = f + unity;
1507 p = new_node(glue_spec_node, 0);
1508 width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
1509 stretch_order(p) = stretch_order(g);
1510 if (stretch_order(p) == normal)
1511 stretch(p) = mu_mult(stretch(g));
1512 else
1513 stretch(p) = stretch(g);
1514 shrink_order(p) = shrink_order(g);
1515 if (shrink_order(p) == normal)
1516 shrink(p) = mu_mult(shrink(g));
1517 else
1518 shrink(p) = shrink(g);
1519 return p;
1522 @ The |math_kern| subroutine removes |mu_glue| from a kern node, given
1523 the value of the math unit.
1526 static void math_kern(pointer p, scaled m)
1528 int n; /* integer part of |m| */
1529 scaled f; /* fraction part of |m| */
1530 if (subtype(p) == mu_glue) {
1531 n = x_over_n(m, unity);
1532 f = tex_remainder;
1533 if (f < 0) {
1534 decr(n);
1535 f = f + unity;
1537 width(p) = mu_mult(width(p));
1538 subtype(p) = explicit;
1542 @ @c
1543 void run_mlist_to_hlist(halfword p, int mstyle, boolean penalties)
1545 int callback_id;
1546 int a, sfix;
1547 lua_State *L = Luas;
1548 if (p == null) {
1549 vlink(temp_head) = null;
1550 return;
1552 finalize_math_parameters();
1553 callback_id = callback_defined(mlist_to_hlist_callback);
1554 if (callback_id > 0) {
1555 sfix = lua_gettop(L);
1556 if (!get_callback(L, callback_id)) {
1557 lua_settop(L, sfix);
1558 return;
1560 alink(p) = null ;
1561 nodelist_to_lua(L, p); /* arg 1 */
1562 lua_pushstring(L, math_style_names[mstyle]); /* arg 2 */
1563 lua_pushboolean(L, penalties); /* arg 3 */
1564 if (lua_pcall(L, 3, 1, 0) != 0) { /* 3 args, 1 result */
1565 fprintf(stdout, "error: %s\n", lua_tostring(L, -1));
1566 lua_settop(L, sfix);
1567 error();
1568 return;
1570 a = nodelist_from_lua(L);
1571 /* alink(vlink(a)) = null; */ /* hh-ls: not need to null here */
1572 lua_settop(L, sfix);
1573 vlink(temp_head) = a;
1574 } else if (callback_id == 0) {
1575 mlist_to_hlist_args(p, mstyle, penalties);
1576 } else {
1577 vlink(temp_head) = null;
1581 @ The recursion in |mlist_to_hlist| is due primarily to a subroutine
1582 called |clean_box| that puts a given noad field into a box using a given
1583 math style; |mlist_to_hlist| can call |clean_box|, which can call
1584 |mlist_to_hlist|.
1585 @^recursion@>
1588 The box returned by |clean_box| is ``clean'' in the
1589 sense that its |shift_amount| is zero.
1592 static pointer clean_box(pointer p, int s, int cur_style)
1594 pointer q; /* beginning of a list to be boxed */
1595 pointer x; /* box to be returned */
1596 pointer r; /* temporary pointer */
1597 pointer mlist = null; /* beginning of mlist to be translated */
1598 switch (type(p)) {
1599 case math_char_node:
1600 mlist = new_noad();
1601 r = math_clone(p);
1602 nucleus(mlist) = r;
1603 break;
1604 case sub_box_node:
1605 q = math_list(p);
1606 goto FOUND;
1607 break;
1608 case sub_mlist_node:
1609 mlist = math_list(p);
1610 break;
1611 default:
1612 q = new_null_box();
1613 goto FOUND;
1615 mlist_to_hlist_args(mlist, s, false);
1616 q = vlink(temp_head); /* recursive call */
1617 setup_cur_size(cur_style);
1618 FOUND:
1619 if (is_char_node(q) || (q == null))
1620 x = hpack(q, 0, additional, -1);
1621 else if ((vlink(q) == null) && (type(q) <= vlist_node)
1622 && (shift_amount(q) == 0))
1623 x = q; /* it's already clean */
1624 else
1625 x = hpack(q, 0, additional, -1);
1626 if (x != q && q != null)
1627 reset_attributes(x, node_attr(q));
1628 /* Here we save memory space in a common case. */
1629 q = list_ptr(x);
1630 if (is_char_node(q)) {
1631 r = vlink(q);
1632 if (r != null) {
1633 if (vlink(r) == null) {
1634 if (!is_char_node(r)) {
1635 if (type(r) == kern_node) {
1636 /* unneeded italic correction */
1637 flush_node(r);
1638 vlink(q) = null;
1644 return x;
1647 @ It is convenient to have a procedure that converts a |math_char|
1648 field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
1649 to the font code and character code of a given noad field.
1650 It also takes care of issuing error messages for
1651 nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
1652 after |fetch| has acted, and the field will also have been reset to |null|.
1654 The outputs of |fetch| are placed in global variables.
1657 internal_font_number cur_f; /* the |font| field of a |math_char| */
1658 int cur_c; /* the |character| field of a |math_char| */
1660 static void fetch(pointer a)
1661 { /* unpack the |math_char| field |a| */
1662 cur_c = math_character(a);
1663 cur_f = fam_fnt(math_fam(a), cur_size);
1664 if (cur_f == null_font) {
1665 char *msg;
1666 const char *hlp[] = {
1667 "Somewhere in the math formula just ended, you used the",
1668 "stated character from an undefined font family. For example,",
1669 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
1670 "and I'll try to forget that I needed that character.",
1671 NULL
1673 msg = xmalloc(256);
1674 snprintf(msg, 255, "\\%s%d is undefined (character %d)",
1675 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
1676 tex_error(msg, hlp);
1677 free(msg);
1678 } else {
1679 if (!(char_exists(cur_f, cur_c))) {
1680 char_warning(cur_f, cur_c);
1686 @ We need to do a lot of different things, so |mlist_to_hlist| makes two
1687 passes over the given mlist.
1689 The first pass does most of the processing: It removes ``mu'' spacing from
1690 glue, it recursively evaluates all subsidiary mlists so that only the
1691 top-level mlist remains to be handled, it puts fractions and square roots
1692 and such things into boxes, it attaches subscripts and superscripts, and
1693 it computes the overall height and depth of the top-level mlist so that
1694 the size of delimiters for a |fence_noad| will be known.
1695 The hlist resulting from each noad is recorded in that noad's |new_hlist|
1696 field, an integer field that replaces the |nucleus| or |thickness|.
1697 @^recursion@>
1699 The second pass eliminates all noads and inserts the correct glue and
1700 penalties between nodes.
1703 static void assign_new_hlist(pointer q, pointer r)
1705 switch (type(q)) {
1706 case fraction_noad:
1707 math_list(numerator(q)) = null;
1708 flush_node(numerator(q));
1709 numerator(q) = null;
1710 math_list(denominator(q)) = null;
1711 flush_node(denominator(q));
1712 denominator(q) = null;
1713 break;
1714 case radical_noad:
1715 case simple_noad:
1716 case accent_noad:
1717 if (nucleus(q) != null) {
1718 math_list(nucleus(q)) = null;
1719 flush_node(nucleus(q));
1720 nucleus(q) = null;
1722 break;
1724 new_hlist(q) = r;
1727 @ @c
1728 #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
1731 @ Most of the actual construction work of |mlist_to_hlist| is done
1732 by procedures with names
1733 like |make_fraction|, |make_radical|, etc. To illustrate
1734 the general setup of such procedures, let's begin with a couple of
1735 simple ones.
1738 static void make_over(pointer q, int cur_style)
1740 pointer p;
1741 p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
1742 overbar_vgap(cur_style),
1743 overbar_rule(cur_style),
1744 overbar_kern(cur_style), node_attr(nucleus(q)));
1745 math_list(nucleus(q)) = p;
1746 type(nucleus(q)) = sub_box_node;
1749 static void make_under(pointer q, int cur_style)
1751 pointer p, x, y, r; /* temporary registers for box construction */
1752 scaled delta; /* overall height plus depth */
1753 x = clean_box(nucleus(q), cur_style, cur_style);
1754 p = new_kern(underbar_vgap(cur_style));
1755 reset_attributes(p, node_attr(q));
1756 couple_nodes(x,p);
1757 r = do_fraction_rule(underbar_rule(cur_style), node_attr(q));
1758 couple_nodes(p,r);
1759 y = vpackage(x, 0, additional, max_dimen, math_direction);
1760 reset_attributes(y, node_attr(q));
1761 delta = height(y) + depth(y) + underbar_kern(cur_style);
1762 height(y) = height(x);
1763 depth(y) = delta - height(y);
1764 math_list(nucleus(q)) = y;
1765 type(nucleus(q)) = sub_box_node;
1768 static void make_vcenter(pointer q)
1770 pointer v; /* the box that should be centered vertically */
1771 scaled delta; /* its height plus depth */
1772 v = math_list(nucleus(q));
1773 if (type(v) != vlist_node)
1774 confusion("vcenter"); /* this can't happen vcenter */
1775 delta = height(v) + depth(v);
1776 height(v) = math_axis(cur_size) + half(delta);
1777 depth(v) = delta - height(v);
1780 @ According to the rules in the \.{DVI} file specifications, we ensure alignment
1781 @^square roots@>
1782 between a square root sign and the rule above its nucleus by assuming that the
1783 baseline of the square-root symbol is the same as the bottom of the rule. The
1784 height of the square-root symbol will be the thickness of the rule, and the
1785 depth of the square-root symbol should exceed or equal the height-plus-depth
1786 of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
1787 placed so that the actual clearance is |psi| plus half the excess.
1790 static void make_radical(pointer q, int cur_style)
1792 pointer x, y, p; /* temporary registers for box construction */
1793 scaled delta, clr, theta, h; /* dimensions involved in the calculation */
1794 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
1795 clr = radical_vgap(cur_style);
1796 theta = radical_rule(cur_style);
1797 if (theta == undefined_math_parameter) {
1798 theta = fraction_rule(cur_style);
1799 y = var_delimiter(left_delimiter(q), cur_size,
1800 height(x) + depth(x) + clr + theta, NULL, cur_style);
1801 /* If |y| is a composite then set |theta| to the height of its top
1802 character, else set it to the height of |y|. */
1803 if (list_ptr(y) != null
1804 && type(list_ptr(y)) == hlist_node
1805 && list_ptr(list_ptr(y)) != null
1806 && type(list_ptr(list_ptr(y))) == glyph_node) { /* and it should be */
1807 theta = char_height(font(list_ptr(list_ptr(y))),
1808 character(list_ptr(list_ptr(y))));
1809 } else {
1810 theta = height(y);
1812 } else {
1813 y = var_delimiter(left_delimiter(q), cur_size,
1814 height(x) + depth(x) + clr + theta, NULL, cur_style);
1816 left_delimiter(q) = null;
1817 delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
1818 if (delta > 0)
1819 clr = clr + half(delta); /* increase the actual clearance */
1820 shift_amount(y) = (height(y) - theta) - (height(x) + clr);
1821 h = depth(y) + height(y);
1822 p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y));
1823 couple_nodes(y,p);
1824 if (degree(q) != null) {
1825 scaled wr, br, ar;
1826 pointer r = clean_box(degree(q), script_script_style, cur_style);
1827 reset_attributes(r, node_attr(degree(q)));
1828 wr = width(r);
1829 if (wr == 0) {
1830 flush_node(r);
1831 } else {
1832 br = radical_degree_before(cur_style);
1833 ar = radical_degree_after(cur_style);
1834 if (-ar > (wr + br))
1835 ar = -(wr + br);
1836 x = new_kern(ar);
1837 reset_attributes(x, node_attr(degree(q)));
1838 couple_nodes(x,y);
1839 shift_amount(r) =
1840 -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
1841 depth(y) - shift_amount(y));
1842 couple_nodes(r,x);
1843 x = new_kern(br);
1844 reset_attributes(x, node_attr(degree(q)));
1845 couple_nodes(x,r);
1846 y = x;
1848 math_list(degree(q)) = null; /* for \.{\\Uroot ..{<list>}{}} */
1849 flush_node(degree(q));
1851 p = hpack(y, 0, additional, -1);
1852 reset_attributes(p, node_attr(q));
1853 math_list(nucleus(q)) = p;
1854 type(nucleus(q)) = sub_box_node;
1858 @ Construct a vlist box
1860 static pointer
1861 wrapup_delimiter(pointer x, pointer y, pointer q,
1862 scaled shift_up, scaled shift_down)
1864 pointer p; /* temporary register for box construction */
1865 pointer v = new_null_box();
1866 type(v) = vlist_node;
1867 height(v) = shift_up + height(x);
1868 depth(v) = depth(y) + shift_down;
1869 reset_attributes(v, node_attr(q));
1870 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
1871 reset_attributes(p, node_attr(q));
1872 couple_nodes(p,y);
1873 couple_nodes(x,p);
1874 list_ptr(v) = x;
1875 return v;
1878 @ @c
1879 #define fixup_widths(x,y) do { \
1880 if (width(y) >= width(x)) { \
1881 width(x) = width(y); \
1882 } else { \
1883 width(y) = width(x); \
1885 } while (0)
1887 @ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
1890 static void make_over_delimiter(pointer q, int cur_style)
1892 pointer x, y, v; /* temporary registers for box construction */
1893 scaled shift_up, shift_down, clr, delta;
1894 x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
1895 y = flat_delimiter(left_delimiter(q), cur_size, width(x), cur_style);
1896 left_delimiter(q) = null;
1897 fixup_widths(x, y);
1898 shift_up = over_delimiter_bgap(cur_style);
1899 shift_down = 0;
1900 clr = over_delimiter_vgap(cur_style);
1901 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
1902 if (delta > 0) {
1903 shift_up = shift_up + delta;
1905 v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1906 width(v) = width(x); /* this also equals |width(y)| */
1907 math_list(nucleus(q)) = v;
1908 type(nucleus(q)) = sub_box_node;
1911 @ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
1914 static void make_delimiter_over(pointer q, int cur_style)
1916 pointer x, y, v; /* temporary registers for box construction */
1917 scaled shift_up, shift_down, clr, actual;
1918 y = clean_box(nucleus(q), cur_style, cur_style);
1919 x = flat_delimiter(left_delimiter(q),
1920 cur_size + (cur_size == script_script_size ? 0 : 1),
1921 width(y), cur_style);
1922 left_delimiter(q) = null;
1923 fixup_widths(x, y);
1924 shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
1925 shift_down = 0;
1926 clr = over_delimiter_vgap(cur_style);
1927 actual = shift_up - height(y);
1928 if (actual < clr) {
1929 shift_up = shift_up + (clr-actual);
1931 v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1932 width(v) = width(x); /* this also equals |width(y)| */
1933 math_list(nucleus(q)) = v;
1934 type(nucleus(q)) = sub_box_node;
1938 @ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
1941 static void make_delimiter_under(pointer q, int cur_style)
1943 pointer x, y, v; /* temporary registers for box construction */
1944 scaled shift_up, shift_down, clr, actual;
1945 x = clean_box(nucleus(q), cur_style, cur_style);
1946 y = flat_delimiter(left_delimiter(q),
1947 cur_size + (cur_size == script_script_size ? 0 : 1),
1948 width(x), cur_style);
1949 left_delimiter(q) = null;
1950 fixup_widths(x, y);
1951 shift_up = 0;
1952 shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
1953 clr = under_delimiter_vgap(cur_style);
1954 actual = shift_down - depth(x);
1955 if (actual<clr) {
1956 shift_down += (clr-actual);
1958 v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1959 width(v) = width(y); /* this also equals |width(y)| */
1960 math_list(nucleus(q)) = v;
1961 type(nucleus(q)) = sub_box_node;
1965 @ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
1968 static void make_under_delimiter(pointer q, int cur_style)
1970 pointer x, y, v; /* temporary registers for box construction */
1971 scaled shift_up, shift_down, clr, delta;
1972 y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
1973 x = flat_delimiter(left_delimiter(q), cur_size, width(y), cur_style);
1974 left_delimiter(q) = null;
1975 fixup_widths(x, y);
1976 shift_up = 0;
1977 shift_down = under_delimiter_bgap(cur_style);
1978 clr = under_delimiter_vgap(cur_style);
1979 delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
1980 if (delta > 0) {
1981 shift_down = shift_down + delta;
1983 v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1984 width(v) = width(y); /* this also equals |width(y)| */
1985 math_list(nucleus(q)) = v;
1986 type(nucleus(q)) = sub_box_node;
1991 @ Slants are not considered when placing accents in math mode. The accenter is
1992 centered over the accentee, and the accent width is treated as zero with
1993 respect to the size of the final box.
1996 #define TOP_CODE 1
1997 #define BOT_CODE 2
1998 #define TOP_OR_BOT_MASK ((TOP_CODE) | (BOT_CODE))
1999 #define STRETCH_ACCENT_CODE 4
2001 static boolean compute_accent_skew(pointer q, int top_or_bot, scaled *s)
2003 pointer p; /* temporary register for box construction */
2004 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
2006 s_is_absolute = false;
2008 if (type(nucleus(q)) == math_char_node) {
2009 fetch(nucleus(q));
2010 if (is_new_mathfont(cur_f)) {
2011 if (top_or_bot == TOP_CODE) {
2012 *s = char_top_accent(cur_f, cur_c);
2013 if (*s != INT_MIN) {
2014 s_is_absolute = true;
2016 } else {
2017 *s = char_bot_accent(cur_f, cur_c);
2018 if (*s != INT_MIN) {
2019 s_is_absolute = true;
2022 } else {
2023 if (top_or_bot == TOP_CODE) {
2024 *s = get_kern(cur_f, cur_c, skew_char(cur_f));
2025 } else {
2026 *s = 0;
2029 } else if (type(nucleus(q)) == sub_mlist_node) {
2030 /* if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
2031 * use the positioning of the nucleus of that noad, recursing until
2032 * the inner most |accent_noad|. This way multiple stacked accents are
2033 * aligned to the inner most one. */
2034 p = math_list(nucleus(q));
2035 if (type(p) == accent_noad) {
2036 s_is_absolute = compute_accent_skew(p, top_or_bot, s);
2040 return s_is_absolute;
2043 static void do_make_math_accent(pointer q, internal_font_number f, int c,
2044 int flags, int cur_style)
2046 pointer p, r, x, y; /* temporary registers for box construction */
2047 scaled s; /* amount to skew the accent to the right */
2048 scaled h; /* height of character being accented */
2049 scaled delta; /* space to remove between accent and accentee */
2050 scaled w; /* width of the accentee, not including sub/superscripts */
2051 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
2052 extinfo *ext;
2053 pointer attr_p;
2054 const int top_or_bot = flags & TOP_OR_BOT_MASK;
2055 attr_p = (top_or_bot == TOP_CODE ? accent_chr(q) : bot_accent_chr(q));
2056 c = cur_c;
2057 f = cur_f;
2059 s = 0;
2060 s_is_absolute = false;
2061 /* Compute the amount of skew, or set |s| to an alignment point */
2062 s_is_absolute = compute_accent_skew(q, top_or_bot, &s);
2064 x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
2065 w = width(x);
2066 h = height(x);
2067 if (is_new_mathfont(cur_f) && !s_is_absolute) {
2068 s = half(w);
2069 s_is_absolute = true;
2071 /* Switch to a larger accent if available and appropriate */
2072 y = null;
2073 ext = NULL;
2074 if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
2075 while (1) {
2076 if ((char_tag(f, c) == ext_tag) &&
2077 ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
2078 y = get_delim_hbox(ext, f, w, node_attr(attr_p), cur_style);
2079 break;
2080 } else if (char_tag(f, c) != list_tag) {
2081 break;
2082 } else {
2083 int yy = char_remainder(f, c);
2084 if (!char_exists(f, yy))
2085 break;
2086 if (char_width(f, yy) > w)
2087 break;
2088 c = yy;
2092 if (y == null) {
2093 y = char_box(f, c, node_attr(attr_p));
2095 if (top_or_bot == TOP_CODE) {
2096 if (h < accent_base_height(f))
2097 delta = h;
2098 else
2099 delta = accent_base_height(f);
2100 } else {
2101 delta = 0; /* hm */
2103 if ((supscr(q) != null) || (subscr(q) != null)) {
2104 if (type(nucleus(q)) == math_char_node) {
2105 /* Swap the subscript and superscript into box |x| */
2106 flush_node_list(x);
2107 x = new_noad();
2108 r = math_clone(nucleus(q));
2109 nucleus(x) = r;
2110 supscr(x) = supscr(q);
2111 supscr(q) = null;
2112 subscr(x) = subscr(q);
2113 subscr(q) = null;
2114 type(nucleus(q)) = sub_mlist_node;
2115 math_list(nucleus(q)) = x;
2116 x = clean_box(nucleus(q), cur_style, cur_style);
2117 delta = delta + height(x) - h;
2118 h = height(x);
2121 if (s_is_absolute) {
2122 scaled sa;
2123 if (ext != NULL) {
2124 sa = half(width(y)); /* if the accent is extensible just take the center */
2125 } else {
2126 sa = char_top_accent(f, c);
2128 if (sa == INT_MIN) {
2129 sa = half(width(y)); /* just take the center */
2131 if (math_direction == dir_TRT) {
2132 shift_amount(y) = s + sa - width(y);
2133 } else {
2134 shift_amount(y) = s - sa;
2136 } else {
2137 if (width(y)== 0) {
2138 shift_amount(y) = s + w;
2139 } else {
2140 if (math_direction == dir_TRT) {
2141 shift_amount(y) = s + width(y);
2142 } else {
2143 shift_amount(y) = s + half(w - width(y));
2147 width(y) = 0;
2148 if (top_or_bot == TOP_CODE) {
2149 p = new_kern(-delta);
2150 couple_nodes(p,x);
2151 couple_nodes(y,p);
2152 } else {
2153 #if 0
2154 p = new_kern(-delta);
2155 couple_nodes(x,p);
2156 couple_nodes(p,y);
2157 y = x;
2158 #endif
2159 couple_nodes(x,y);
2160 y = x;
2162 r = vpackage(y, 0, additional, max_dimen, math_direction);
2163 reset_attributes(r, node_attr(q));
2164 width(r) = width(x);
2165 y = r;
2166 if (top_or_bot == TOP_CODE) {
2167 if (height(y) < h) {
2168 /* Make the height of box |y| equal to |h| */
2169 p = new_kern(h - height(y));
2170 reset_attributes(p, node_attr(q));
2171 // vlink(p) = list_ptr(y);
2172 try_couple_nodes(p,list_ptr(y));
2173 list_ptr(y) = p;
2174 height(y) = h;
2176 } else {
2177 shift_amount(y) = -(h - height(y));
2179 math_list(nucleus(q)) = y;
2180 type(nucleus(q)) = sub_box_node;
2183 static void make_math_accent(pointer q, int cur_style)
2185 int topstretch = !(subtype(q) % 2);
2186 int botstretch = !(subtype(q) / 2);
2188 if (accent_chr(q) != null) {
2189 fetch(accent_chr(q));
2190 if (char_exists(cur_f, cur_c)) {
2191 do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2193 flush_node(accent_chr(q));
2194 accent_chr(q) = null;
2196 if (bot_accent_chr(q) != null) {
2197 fetch(bot_accent_chr(q));
2198 if (char_exists(cur_f, cur_c)) {
2199 do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2201 flush_node(bot_accent_chr(q));
2202 bot_accent_chr(q) = null;
2206 @ The |make_fraction| procedure is a bit different because it sets
2207 |new_hlist(q)| directly rather than making a sub-box.
2210 static void make_fraction(pointer q, int cur_style)
2212 pointer p, v, x, y, z; /* temporary registers for box construction */
2213 scaled delta, delta1, delta2, shift_up, shift_down, clr;
2214 /* dimensions for box calculations */
2215 if (thickness(q) == default_code)
2216 thickness(q) = fraction_rule(cur_style);
2217 /* Create equal-width boxes |x| and |z| for the numerator and denominator,
2218 and compute the default amounts |shift_up| and |shift_down| by which they
2219 are displaced from the baseline */
2220 x = clean_box(numerator(q), num_style(cur_style), cur_style);
2221 z = clean_box(denominator(q), denom_style(cur_style), cur_style);
2222 if (width(x) < width(z))
2223 x = rebox(x, width(z));
2224 else
2225 z = rebox(z, width(x));
2226 if (thickness(q) == 0) {
2227 shift_up = stack_num_up(cur_style);
2228 shift_down = stack_denom_down(cur_style);
2229 /* The numerator and denominator must be separated by a certain minimum
2230 clearance, called |clr| in the following program. The difference between
2231 |clr| and the actual clearance is |2delta|. */
2232 clr = stack_vgap(cur_style);
2233 delta = half(clr - ((shift_up - depth(x)) - (height(z) - shift_down)));
2234 if (delta > 0) {
2235 shift_up = shift_up + delta;
2236 shift_down = shift_down + delta;
2238 } else {
2239 shift_up = fraction_num_up(cur_style);
2240 shift_down = fraction_denom_down(cur_style);
2241 /* In the case of a fraction line, the minimum clearance depends on the actual
2242 thickness of the line. */
2243 delta = half(thickness(q));
2244 clr = fraction_num_vgap(cur_style);
2245 clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style));
2246 delta1 = clr - ((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2247 if (delta1 > 0)
2248 shift_up = shift_up + delta1;
2249 clr = fraction_denom_vgap(cur_style);
2250 clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style));
2251 delta2 =
2252 clr - ((math_axis(cur_size) - delta) - (height(z) - shift_down));
2253 if (delta2 > 0)
2254 shift_down = shift_down + delta2;
2256 /* Construct a vlist box for the fraction, according to |shift_up| and |shift_down| */
2257 v = new_null_box();
2258 type(v) = vlist_node;
2259 height(v) = shift_up + height(x);
2260 depth(v) = depth(z) + shift_down;
2261 width(v) = width(x); /* this also equals |width(z)| */
2262 reset_attributes(v, node_attr(q));
2263 if (thickness(q) == 0) {
2264 p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
2265 couple_nodes(p,z);
2266 } else {
2267 y = do_fraction_rule(thickness(q), node_attr(q));
2268 p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down));
2269 reset_attributes(p, node_attr(q));
2270 couple_nodes(y,p);
2271 couple_nodes(p,z);
2272 p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2273 couple_nodes(p,y);
2275 reset_attributes(p, node_attr(q));
2276 couple_nodes(x,p);
2277 list_ptr(v) = x;
2278 /* Put the fraction into a box with its delimiters, and make |new_hlist(q)|
2279 point to it */
2280 delta = fraction_del_size(cur_style);
2281 x = var_delimiter(left_delimiter(q), cur_size, delta, NULL, cur_style);
2282 left_delimiter(q) = null;
2283 couple_nodes(x,v);
2284 z = var_delimiter(right_delimiter(q), cur_size, delta, NULL, cur_style);
2285 right_delimiter(q) = null;
2286 couple_nodes(v,z);
2287 y = hpack(x, 0, additional, -1);
2288 reset_attributes(y, node_attr(q));
2289 assign_new_hlist(q, y);
2293 @ If the nucleus of an |op_noad| is a single character, it is to be
2294 centered vertically with respect to the axis, after first being enlarged
2295 (via a character list in the font) if we are in display style. The normal
2296 convention for placing displayed limits is to put them above and below the
2297 operator in display style.
2299 The italic correction is removed from the character if there is a subscript
2300 and the limits are not being displayed. The |make_op|
2301 routine returns the value that should be used as an offset between
2302 subscript and superscript.
2304 After |make_op| has acted, |subtype(q)| will be |limits| if and only if
2305 the limits have been set above and below the operator. In that case,
2306 |new_hlist(q)| will already contain the desired final box.
2309 static scaled make_op(pointer q, int cur_style)
2311 scaled delta; /* offset between subscript and superscript */
2312 pointer p, v, x, y, z; /* temporary registers for box construction */
2313 int c; /* register for character examination */
2314 scaled shift_up, shift_down; /* dimensions for box calculation */
2315 scaled ok_size;
2316 if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style))
2317 subtype(q) = op_noad_type_limits;
2318 if (type(nucleus(q)) == math_char_node) {
2319 fetch(nucleus(q));
2320 if (cur_style < text_style) { /* try to make it larger */
2321 ok_size = minimum_operator_size(cur_style);
2322 if (ok_size != undefined_math_parameter) {
2323 /* creating a temporary delimiter is the cleanest way */
2324 y = new_node(delim_node, 0);
2325 small_fam(y) = math_fam(nucleus(q));
2326 small_char(y) = math_character(nucleus(q));
2327 x = var_delimiter(y, text_size, ok_size, &delta, cur_style);
2328 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2329 width(x) -= delta; /* remove italic correction */
2331 /* For an OT MATH font, we may have to get rid of yet another italic
2332 correction because |make_scripts()| will add one.
2333 This test is somewhat more complicated because |x| can be a null
2334 delimiter */
2335 if ((subscr(q) != null || supscr(q) != null)
2336 && (subtype(q) != op_noad_type_limits)
2337 && ((list_ptr(x) != null)
2338 && (type(list_ptr(x)) == glyph_node)
2339 && is_new_mathfont(font(list_ptr(x))))) {
2340 width(x) -= delta; /* remove another italic correction */
2342 } else {
2343 ok_size = height_plus_depth(cur_f, cur_c) + 1;
2344 while ((char_tag(cur_f, cur_c) == list_tag) &&
2345 height_plus_depth(cur_f, cur_c) < ok_size) {
2346 c = char_remainder(cur_f, cur_c);
2347 if (!char_exists(cur_f, c))
2348 break;
2349 cur_c = c;
2350 math_character(nucleus(q)) = c;
2352 delta = char_italic(cur_f, cur_c);
2353 x = clean_box(nucleus(q), cur_style, cur_style);
2354 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits))
2355 width(x) = width(x) - delta; /* remove italic correction */
2357 shift_amount(x) =
2358 half(height(x) - depth(x)) - math_axis(cur_size);
2359 /* center vertically */
2361 type(nucleus(q)) = sub_box_node;
2362 math_list(nucleus(q)) = x;
2364 } else { /* normal size */
2365 delta = char_italic(cur_f, cur_c);
2366 x = clean_box(nucleus(q), cur_style, cur_style);
2367 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits))
2368 width(x) = width(x) - delta; /* remove italic correction */
2370 /* For an OT MATH font, we may have to get rid of yet another italic
2371 correction because |make_scripts()| will add one.
2372 This test is somewhat more complicated because |x| can be a null
2373 delimiter */
2374 if ((subscr(q) != null || supscr(q) != null)
2375 && (subtype(q) != op_noad_type_limits)
2376 && ((list_ptr(x) != null)
2377 && (type(list_ptr(x)) == glyph_node)
2378 && is_new_mathfont(font(list_ptr(x))))) {
2379 width(x) -= delta; /* remove another italic correction */
2382 shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size);
2383 /* center vertically */
2384 type(nucleus(q)) = sub_box_node;
2385 math_list(nucleus(q)) = x;
2387 } else {
2388 delta = 0;
2390 if (subtype(q) == op_noad_type_limits) {
2391 /* The following program builds a vlist box |v| for displayed limits. The
2392 width of the box is not affected by the fact that the limits may be skewed. */
2393 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2394 y = clean_box(nucleus(q), cur_style, cur_style);
2395 z = clean_box(subscr(q), sub_style(cur_style), cur_style);
2396 v = new_null_box();
2397 reset_attributes(v, node_attr(q));
2398 type(v) = vlist_node;
2399 width(v) = width(y);
2400 if (width(x) > width(v))
2401 width(v) = width(x);
2402 if (width(z) > width(v))
2403 width(v) = width(z);
2404 x = rebox(x, width(v));
2405 y = rebox(y, width(v));
2406 z = rebox(z, width(v));
2407 shift_amount(x) = half(delta);
2408 shift_amount(z) = -shift_amount(x);
2409 height(v) = height(y);
2410 depth(v) = depth(y);
2411 /* Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
2412 account for their presence */
2413 /* We use |shift_up| and |shift_down| in the following program for the
2414 amount of glue between the displayed operator |y| and its limits |x| and
2415 |z|. The vlist inside box |v| will consist of |x| followed by |y| followed
2416 by |z|, with kern nodes for the spaces between and around them. */
2417 if (supscr(q) == null) {
2418 list_ptr(x) = null;
2419 flush_node(x);
2420 list_ptr(v) = y;
2421 } else {
2422 shift_up = limit_above_bgap(cur_style) - depth(x);
2423 if (shift_up < limit_above_vgap(cur_style))
2424 shift_up = limit_above_vgap(cur_style);
2425 p = new_kern(shift_up);
2426 reset_attributes(p, node_attr(q));
2427 couple_nodes(p,y);
2428 couple_nodes(x,p);
2429 p = new_kern(limit_above_kern(cur_style));
2430 reset_attributes(p, node_attr(q));
2431 couple_nodes(p,x);
2432 list_ptr(v) = p;
2433 height(v) =
2434 height(v) + limit_above_kern(cur_style) + height(x) + depth(x) +
2435 shift_up;
2437 if (subscr(q) == null) {
2438 list_ptr(z) = null;
2439 flush_node(z);
2440 } else {
2441 shift_down = limit_below_bgap(cur_style) - height(z);
2442 if (shift_down < limit_below_vgap(cur_style))
2443 shift_down = limit_below_vgap(cur_style);
2444 p = new_kern(shift_down);
2445 reset_attributes(p, node_attr(q));
2446 couple_nodes(y,p);
2447 couple_nodes(p,z);
2448 p = new_kern(limit_below_kern(cur_style));
2449 reset_attributes(p, node_attr(q));
2450 couple_nodes(z,p);
2451 depth(v) =
2452 depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) +
2453 shift_down;
2455 if (subscr(q) != null) {
2456 math_list(subscr(q)) = null;
2457 flush_node(subscr(q));
2458 subscr(q) = null;
2460 if (supscr(q) != null) {
2461 math_list(supscr(q)) = null;
2462 flush_node(supscr(q));
2463 supscr(q) = null;
2465 assign_new_hlist(q, v);
2467 return delta;
2470 @ A ligature found in a math formula does not create a ligature, because
2471 there is no question of hyphenation afterwards; the ligature will simply be
2472 stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
2474 The |type| is converted to |math_text_char| here if we would not want to
2475 apply an italic correction to the current character unless it belongs
2476 to a math font (i.e., a font with |space=0|).
2478 No boundary characters enter into these ligatures.
2481 static void make_ord(pointer q)
2483 int a; /* the left-side character for lig/kern testing */
2484 pointer p, r, s; /* temporary registers for list manipulation */
2485 scaled k; /* a kern */
2486 liginfo lig; /* a ligature */
2487 RESTART:
2488 if (subscr(q) == null &&
2489 supscr(q) == null && type(nucleus(q)) == math_char_node) {
2490 p = vlink(q);
2491 if ((p != null) &&
2492 (type(p) == simple_noad) &&
2493 (subtype(p) <= punct_noad_type) &&
2494 (type(nucleus(p)) == math_char_node) &&
2495 (math_fam(nucleus(p)) == math_fam(nucleus(q)))) {
2496 type(nucleus(q)) = math_text_char_node;
2497 fetch(nucleus(q));
2498 a = cur_c;
2499 if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
2500 cur_c = math_character(nucleus(p));
2501 /* If character |a| has a kern with |cur_c|, attach
2502 the kern after~|q|; or if it has a ligature with |cur_c|, combine
2503 noads |q| and~|p| appropriately; then |return| if the cursor has
2504 moved past a noad, or |goto restart| */
2506 /* Note that a ligature between an |ord_noad| and another kind of noad
2507 is replaced by an |ord_noad|, when the two noads collapse into one.
2508 But we could make a parenthesis (say) change shape when it follows
2509 certain letters. Presumably a font designer will define such
2510 ligatures only when this convention makes sense. */
2512 if (disable_lig == 0 && has_lig(cur_f, a)) {
2513 lig = get_ligature(cur_f, a, cur_c);
2514 if (is_valid_ligature(lig)) {
2515 check_interrupt(); /* allow a way out of infinite ligature loop */
2516 switch (lig_type(lig)) {
2517 case 1:
2518 case 5:
2519 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:\char`\|}, \.{=:\char`\|>} */
2520 break;
2521 case 2:
2522 case 6:
2523 math_character(nucleus(p)) = lig_replacement(lig); /* \.{\char`\|=:}, \.{\char`\|=:>} */
2524 break;
2525 case 3:
2526 case 7:
2527 case 11:
2528 r = new_noad(); /* \.{\char`\|=:\char`\|}, \.{\char`\|=:\char`\|>}, \.{\char`\|=:\char`\|>>} */
2529 reset_attributes(r, node_attr(q));
2530 s = new_node(math_char_node, 0);
2531 reset_attributes(s, node_attr(q));
2532 nucleus(r) = s;
2533 math_character(nucleus(r)) = lig_replacement(lig);
2534 math_fam(nucleus(r)) = math_fam(nucleus(q));
2535 couple_nodes(q,r);
2536 couple_nodes(r,p);
2537 if (lig_type(lig) < 11)
2538 type(nucleus(r)) = math_char_node;
2539 else
2540 type(nucleus(r)) = math_text_char_node; /* prevent combination */
2541 break;
2542 default:
2543 try_couple_nodes(q,vlink(p));
2544 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
2545 s = math_clone(subscr(p));
2546 subscr(q) = s;
2547 s = math_clone(supscr(p));
2548 supscr(q) = s;
2549 math_reset(subscr(p)); /* just in case */
2550 math_reset(supscr(p));
2551 flush_node(p);
2552 break;
2554 if (lig_type(lig) > 3)
2555 return;
2556 type(nucleus(q)) = math_char_node;
2557 goto RESTART;
2560 if (disable_kern == 0 && has_kern(cur_f, a)) {
2561 k = get_kern(cur_f, a, cur_c); /* todo: should this use mathkerns? */
2562 if (k != 0) {
2563 p = new_kern(k);
2564 reset_attributes(p, node_attr(q));
2565 couple_nodes(p,vlink(q));
2566 couple_nodes(q,p);
2567 return;
2575 @ If the fonts for the left and right bits of a mathkern are not
2576 both new-style fonts, then return a sentinel value meaning:
2577 please use old-style italic correction placement
2580 #define MATH_KERN_NOT_FOUND 0x7FFFFFFF
2582 @ This function tries to find the kern needed for proper cut-ins.
2583 The left side doesn't move, but the right side does, so the first
2584 order of business is to create a staggered fence line on the
2585 left side of the right character.
2587 The microsoft spec says that there are four quadrants, but the
2588 actual images say
2591 static scaled math_kern_at(internal_font_number f, int c, int side, int v)
2593 int h, k, numkerns;
2594 scaled *kerns_heights;
2595 scaled kern = 0;
2596 charinfo *co = char_info(f, c); /* known to exist */
2597 numkerns = get_charinfo_math_kerns(co, side);
2598 #ifdef DEBUG
2599 fprintf(stderr, " entries = %d, height = %d\n", numkerns, v);
2600 #endif
2601 if (numkerns == 0)
2602 return kern;
2603 if (side == top_left_kern) {
2604 kerns_heights = co->top_left_math_kern_array;
2605 } else if (side == bottom_left_kern) {
2606 kerns_heights = co->bottom_left_math_kern_array;
2607 } else if (side == top_right_kern) {
2608 kerns_heights = co->top_right_math_kern_array;
2609 } else if (side == bottom_right_kern) {
2610 kerns_heights = co->bottom_right_math_kern_array;
2611 } else {
2612 confusion("math_kern_at");
2613 kerns_heights = NULL; /* not reached */
2615 #ifdef DEBUG
2616 fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
2617 #endif
2618 if (v < kerns_heights[0])
2619 return kerns_heights[1];
2620 for (k = 0; k < numkerns; k++) {
2621 h = kerns_heights[(k * 2)];
2622 kern = kerns_heights[(k * 2) + 1];
2623 #ifdef DEBUG
2624 if (k > 0)
2625 fprintf(stderr, " entry %d: %d,%d\n", k, h, kern);
2626 #endif
2627 if (h > v) {
2628 return kern;
2631 return kern;
2634 @ @c
2635 static scaled
2636 find_math_kern(internal_font_number l_f, int l_c,
2637 internal_font_number r_f, int r_c, int cmd, scaled shift)
2639 scaled corr_height_top = 0, corr_height_bot = 0;
2640 scaled krn_l = 0, krn_r = 0, krn = 0;
2641 if ((!is_new_mathfont(l_f)) || (!is_new_mathfont(r_f))
2642 || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
2643 return MATH_KERN_NOT_FOUND;
2645 if (cmd == sup_mark_cmd) {
2646 corr_height_top = char_height(l_f, l_c);
2647 corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
2648 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
2649 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
2650 #ifdef DEBUG
2651 fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r,
2652 shift);
2653 #endif
2654 krn = (krn_l + krn_r);
2655 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
2656 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
2657 #ifdef DEBUG
2658 fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
2659 #endif
2660 if ((krn_l + krn_r) < krn)
2661 krn = (krn_l + krn_r);
2662 return (krn);
2664 } else if (cmd == sub_mark_cmd) {
2665 corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
2666 corr_height_bot = -char_depth(l_f, l_c);
2667 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
2668 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
2669 #ifdef DEBUG
2670 fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
2671 #endif
2672 krn = (krn_l + krn_r);
2673 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
2674 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
2675 #ifdef DEBUG
2676 fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
2677 #endif
2678 if ((krn_l + krn_r) < krn)
2679 krn = (krn_l + krn_r);
2680 return (krn);
2682 } else {
2683 confusion("find_math_kern");
2685 return 0; /* not reached */
2688 @ just a small helper
2690 static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
2692 pointer y;
2693 pointer z = new_kern(delta2);
2694 if (new_hlist(q) == null) { /* this is somewhat weird */
2695 new_hlist(q) = z;
2696 } else {
2697 y = new_hlist(q);
2698 while (vlink(y) != null)
2699 y = vlink(y);
2700 couple_nodes(y,z);
2702 return new_hlist(q);
2707 #ifdef DEBUG
2708 void dump_simple_field(pointer q)
2710 pointer p;
2711 printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q));
2712 switch (type(q)) {
2713 case math_char_node:
2714 printf("mathchar ");
2715 break;
2716 case math_text_char_node:
2717 printf("texchar ");
2718 break;
2719 case sub_box_node:
2720 printf("box ");
2721 break;
2722 case sub_mlist_node:
2723 printf("mlist ");
2724 p = math_list(q);
2725 while (p != null) {
2726 dump_simple_field(p);
2727 p = vlink(p);
2729 break;
2734 void dump_simple_node(pointer q)
2736 printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
2737 printf("nucleus: ");
2738 dump_simple_field(nucleus(q));
2739 printf("\n");
2740 printf("sub: ");
2741 dump_simple_field(subscr(q));
2742 printf("\n");
2743 printf("sup: ");
2744 dump_simple_field(supscr(q));
2745 printf("\n\n");
2747 #endif
2749 @ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
2750 superscript of noad |q| to the list that starts at |new_hlist(q)|,
2751 given that subscript and superscript aren't both empty. The superscript
2752 will be horizontally shifted over |delta1|, the subscript over |delta2|.
2754 We set |shift_down| and |shift_up| to the minimum amounts to shift the
2755 baseline of subscripts and superscripts based on the given nucleus.
2757 Note: We need to look at a character but also at the first one in a sub list
2758 and there we ignore leading kerns and glue. Elsewhere is code that removes
2759 kerns assuming that is italic correction. The heuristics are unreliable for
2760 the new fonts so eventualy there will be an option to ignore such corrections.
2762 @ @c
2763 #define analyze_script(init,su_n,su_f,su_c) do { \
2764 su_n = init; \
2765 if (su_n != null) { \
2766 if (type(su_n) == sub_mlist_node && math_list(su_n)) { \
2767 su_n = math_list(su_n); \
2768 if (su_n != null) { \
2769 while (su_n) { \
2770 if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\
2771 su_n = vlink(su_n); \
2772 } else if (type(su_n) == simple_noad) { \
2773 su_n = nucleus(su_n); \
2774 if (type(su_n) != math_char_node) { \
2775 su_n = null; \
2777 break; \
2778 } else { \
2779 su_n = null; \
2780 break; \
2785 if ((su_n != null) && (type(su_n) == math_char_node)) { \
2786 fetch(su_n); \
2787 if (char_exists(cur_f, cur_c)) { \
2788 su_f = cur_f; \
2789 su_c = cur_c; \
2790 } else { \
2791 su_n = null; \
2795 } while (0)
2798 static void make_scripts(pointer q, pointer p, scaled it, int cur_style)
2800 pointer x, y, z; /* temporary registers for box construction */
2801 scaled shift_up, shift_down, clr; /* dimensions in the calculation */
2802 scaled delta1, delta2;
2803 halfword sub_n, sup_n;
2804 internal_font_number sub_f, sup_f;
2805 int sub_c, sup_c;
2806 sub_n = null;
2807 sup_n = null;
2808 sub_f = 0;
2809 sup_f = 0;
2810 sub_c = 0;
2811 sup_c = 0;
2812 delta1 = it;
2813 delta2 = 0;
2815 #ifdef DEBUG
2816 printf("it: %d\n", it);
2817 dump_simple_node(q);
2818 printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
2819 #endif
2820 switch (type(nucleus(q))) {
2821 case math_char_node:
2822 case math_text_char_node:
2823 if ((subscr(q) == null) && (delta1 != 0)) {
2824 x = new_kern(delta1);
2825 reset_attributes(x, node_attr(nucleus(q)));
2826 couple_nodes(p,x);
2827 delta1 = 0;
2830 assign_new_hlist(q, p);
2831 if (is_char_node(p)) {
2832 shift_up = 0;
2833 shift_down = 0;
2834 } else {
2835 z = hpack(p, 0, additional, -1);
2836 shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */
2837 shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
2838 list_ptr(z) = null;
2839 flush_node(z);
2842 if (is_char_node(p)) {
2843 /* we look at the subscript character (_i) or first character in a list (_{ij}) */
2844 analyze_script(subscr(q),sub_n,sub_f,sub_c);
2845 /* we look at the superscript character (^i) or first character in a list (^{ij}) */
2846 analyze_script(supscr(q),sup_n,sup_f,sup_c);
2849 if (supscr(q) == null) {
2851 /* Construct a subscript box |x| when there is no superscript */
2852 /* When there is a subscript without a superscript, the top of the subscript
2853 should not exceed the baseline plus four-fifths of the x-height. */
2854 x = clean_box(subscr(q), sub_style(cur_style), cur_style);
2855 width(x) = width(x) + space_after_script(cur_style);
2856 if (shift_down < sub_shift_down(cur_style))
2857 shift_down = sub_shift_down(cur_style);
2858 clr = height(x) - sub_top_max(cur_style);
2859 if (shift_down < clr)
2860 shift_down = clr;
2861 shift_amount(x) = shift_down;
2863 /* now find and correct for horizontal shift */
2864 if (sub_n != null) {
2865 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
2866 if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) {
2867 p = attach_hkern_to_new_hlist(q, delta2);
2871 } else {
2872 /* Construct a superscript box |x| */
2873 /*The bottom of a superscript should never descend below the baseline plus
2874 one-fourth of the x-height. */
2875 x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2876 width(x) = width(x) + space_after_script(cur_style);
2877 clr = sup_shift_up(cur_style);
2878 if (shift_up < clr)
2879 shift_up = clr;
2880 clr = depth(x) + sup_bottom_min(cur_style);
2881 if (shift_up < clr)
2882 shift_up = clr;
2884 if (subscr(q) == null) {
2885 shift_amount(x) = -shift_up;
2886 /* now find and correct for horizontal shift */
2887 if (sup_n != null) {
2888 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
2889 if (clr != MATH_KERN_NOT_FOUND && clr != 0) {
2890 p = attach_hkern_to_new_hlist(q, clr);
2893 } else {
2894 /* Construct a sub/superscript combination box |x|, with the
2895 superscript offset by |delta| */
2896 /* When both subscript and superscript are present, the subscript must be
2897 separated from the superscript by at least four times |default_rule_thickness|.
2898 If this condition would be violated, the subscript moves down, after which
2899 both subscript and superscript move up so that the bottom of the superscript
2900 is at least as high as the baseline plus four-fifths of the x-height. */
2902 y = clean_box(subscr(q), sub_style(cur_style), cur_style);
2903 width(y) = width(y) + space_after_script(cur_style);
2904 if (shift_down < sub_sup_shift_down(cur_style))
2905 shift_down = sub_sup_shift_down(cur_style);
2906 clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
2907 if (clr > 0) {
2908 shift_down = shift_down + clr;
2909 clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
2910 if (clr > 0) {
2911 shift_up = shift_up + clr;
2912 shift_down = shift_down - clr;
2915 /* now find and correct for horizontal shift */
2916 if (sub_n != null) {
2917 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
2918 if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) {
2919 p = attach_hkern_to_new_hlist(q, delta2);
2922 /* now the horizontal shift for the superscript. */
2923 /* the superscript is also to be shifted by |delta1| (the italic correction) */
2924 clr = MATH_KERN_NOT_FOUND;
2925 if (sup_n != null) {
2926 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
2929 if (delta2 == MATH_KERN_NOT_FOUND)
2930 delta2 = 0;
2931 if (clr != MATH_KERN_NOT_FOUND) {
2932 shift_amount(x) = clr + delta1 - delta2;
2933 } else {
2934 shift_amount(x) = delta1 - delta2;
2936 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
2937 reset_attributes(p, node_attr(q));
2938 couple_nodes(x,p);
2939 couple_nodes(p,y);
2940 x = vpackage(x, 0, additional, max_dimen, math_direction);
2941 reset_attributes(x, node_attr(q));
2942 shift_amount(x) = shift_down;
2947 if (new_hlist(q) == null) {
2948 new_hlist(q) = x;
2949 } else {
2950 p = new_hlist(q);
2951 while (vlink(p) != null)
2952 p = vlink(p);
2953 couple_nodes(p,x);
2955 if (subscr(q) != null) {
2956 math_list(subscr(q)) = null;
2957 flush_node(subscr(q));
2958 subscr(q) = null;
2960 if (supscr(q) != null) {
2961 math_list(supscr(q)) = null;
2962 flush_node(supscr(q));
2963 supscr(q) = null;
2968 @ The |make_left_right| function constructs a left or right delimiter of
2969 the required size and returns the value |open_noad| or |close_noad|. The
2970 |left_noad_side| and |right_noad_side| will both be based on the original |style|,
2971 so they will have consistent sizes.
2974 static small_number make_left_right(pointer q, int style, scaled max_d,
2975 scaled max_hv)
2977 scaled delta, delta1, delta2; /* dimensions used in the calculation */
2978 pointer tmp;
2979 setup_cur_size(style);
2980 delta2 = max_d + math_axis(cur_size);
2981 delta1 = max_hv + max_d - delta2;
2982 if (delta2 > delta1)
2983 delta1 = delta2; /* |delta1| is max distance from axis */
2984 delta = (delta1 / 500) * delimiter_factor;
2985 delta2 = delta1 + delta1 - delimiter_shortfall;
2986 if (delta < delta2)
2987 delta = delta2;
2988 tmp = var_delimiter(delimiter(q), cur_size, delta, NULL, style);
2989 delimiter(q) = null;
2990 assign_new_hlist(q, tmp);
2991 if (subtype(q) == left_noad_side)
2992 return open_noad_type;
2993 else
2994 return close_noad_type;
2997 @ @c
2998 #define TEXT_STYLES(A,B) do { \
2999 def_math_param(A,display_style,(B),level_one); \
3000 def_math_param(A,cramped_display_style,(B),level_one); \
3001 def_math_param(A,text_style,(B),level_one); \
3002 def_math_param(A,cramped_text_style,(B),level_one); \
3003 } while (0)
3005 #define SCRIPT_STYLES(A,B) do { \
3006 def_math_param(A,script_style,(B),level_one); \
3007 def_math_param(A,cramped_script_style,(B),level_one); \
3008 def_math_param(A,script_script_style,(B),level_one); \
3009 def_math_param(A,cramped_script_script_style,(B),level_one); \
3010 } while (0)
3012 #define ALL_STYLES(A,B) do { \
3013 TEXT_STYLES(A,(B)); \
3014 SCRIPT_STYLES(A,(B)); \
3015 } while (0)
3017 #define SPLIT_STYLES(A,B,C) do { \
3018 TEXT_STYLES(A,(B)); \
3019 SCRIPT_STYLES(A,(C)); \
3020 } while (0)
3023 void initialize_math_spacing(void)
3025 /* *INDENT-OFF* */
3026 ALL_STYLES (math_param_ord_ord_spacing, 0);
3027 ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code);
3028 SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0);
3029 SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0);
3030 ALL_STYLES (math_param_ord_open_spacing, 0);
3031 ALL_STYLES (math_param_ord_close_spacing, 0);
3032 ALL_STYLES (math_param_ord_punct_spacing, 0);
3033 SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0);
3035 ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code);
3036 ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code);
3037 ALL_STYLES (math_param_op_bin_spacing, 0);
3038 SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0);
3039 ALL_STYLES (math_param_op_open_spacing, 0);
3040 ALL_STYLES (math_param_op_close_spacing, 0);
3041 ALL_STYLES (math_param_op_punct_spacing, 0);
3042 SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0);
3044 SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0);
3045 SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0);
3046 ALL_STYLES (math_param_bin_bin_spacing, 0);
3047 ALL_STYLES (math_param_bin_rel_spacing, 0);
3048 SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0);
3049 ALL_STYLES (math_param_bin_close_spacing, 0);
3050 ALL_STYLES (math_param_bin_punct_spacing, 0);
3051 SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0);
3053 SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0);
3054 SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0);
3055 ALL_STYLES (math_param_rel_bin_spacing, 0);
3056 ALL_STYLES (math_param_rel_rel_spacing, 0);
3057 SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0);
3058 ALL_STYLES (math_param_rel_close_spacing, 0);
3059 ALL_STYLES (math_param_rel_punct_spacing, 0);
3060 SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0);
3062 ALL_STYLES (math_param_open_ord_spacing, 0);
3063 ALL_STYLES (math_param_open_op_spacing, 0);
3064 ALL_STYLES (math_param_open_bin_spacing, 0);
3065 ALL_STYLES (math_param_open_rel_spacing, 0);
3066 ALL_STYLES (math_param_open_open_spacing, 0);
3067 ALL_STYLES (math_param_open_close_spacing, 0);
3068 ALL_STYLES (math_param_open_punct_spacing, 0);
3069 ALL_STYLES (math_param_open_inner_spacing, 0);
3071 ALL_STYLES (math_param_close_ord_spacing, 0);
3072 ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code);
3073 SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0);
3074 SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0);
3075 ALL_STYLES (math_param_close_open_spacing, 0);
3076 ALL_STYLES (math_param_close_close_spacing, 0);
3077 ALL_STYLES (math_param_close_punct_spacing, 0);
3078 SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
3080 SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0);
3081 SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0);
3082 ALL_STYLES (math_param_punct_bin_spacing, 0);
3083 SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0);
3084 SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0);
3085 SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
3086 SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
3087 SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
3089 SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0);
3090 ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code);
3091 SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0);
3092 SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0);
3093 SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0);
3094 ALL_STYLES (math_param_inner_close_spacing, 0);
3095 SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
3096 SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
3097 /* *INDENT-ON* */
3101 @ @c
3102 #define both_types(A,B) ((A)*16+(B))
3104 static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
3106 int x = -1;
3107 pointer z = null;
3108 if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
3109 l_type = op_noad_type_normal;
3110 if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
3111 r_type = op_noad_type_normal;
3112 switch (both_types(l_type, r_type)) {
3113 /* *INDENT-OFF* */
3114 case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
3115 case both_types(ord_noad_type, op_noad_type_normal ): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
3116 case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
3117 case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
3118 case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
3119 case both_types(ord_noad_type, close_noad_type): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
3120 case both_types(ord_noad_type, punct_noad_type): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
3121 case both_types(ord_noad_type, inner_noad_type): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
3122 case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
3123 case both_types(op_noad_type_normal, op_noad_type_normal ): x = get_math_param(math_param_op_op_spacing,mstyle); break;
3124 #if 0
3125 case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
3126 #endif
3127 case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
3128 case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
3129 case both_types(op_noad_type_normal, close_noad_type): x = get_math_param(math_param_op_close_spacing,mstyle); break;
3130 case both_types(op_noad_type_normal, punct_noad_type): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
3131 case both_types(op_noad_type_normal, inner_noad_type): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
3132 case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
3133 case both_types(bin_noad_type, op_noad_type_normal ): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
3134 #if 0
3135 case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
3136 case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
3137 #endif
3138 case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
3139 #if 0
3140 case both_types(bin_noad_type, close_noad_type): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
3141 case both_types(bin_noad_type, punct_noad_type): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
3142 #endif
3143 case both_types(bin_noad_type, inner_noad_type): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
3144 case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
3145 case both_types(rel_noad_type, op_noad_type_normal ): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
3146 #if 0
3147 case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
3148 #endif
3149 case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
3150 case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
3151 case both_types(rel_noad_type, close_noad_type): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
3152 case both_types(rel_noad_type, punct_noad_type): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
3153 case both_types(rel_noad_type, inner_noad_type): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
3154 case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
3155 case both_types(open_noad_type, op_noad_type_normal ): x = get_math_param(math_param_open_op_spacing,mstyle); break;
3156 #if 0
3157 case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
3158 #endif
3159 case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
3160 case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
3161 case both_types(open_noad_type, close_noad_type): x = get_math_param(math_param_open_close_spacing,mstyle); break;
3162 case both_types(open_noad_type, punct_noad_type): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
3163 case both_types(open_noad_type, inner_noad_type): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
3164 case both_types(close_noad_type,ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
3165 case both_types(close_noad_type,op_noad_type_normal ): x = get_math_param(math_param_close_op_spacing,mstyle); break;
3166 case both_types(close_noad_type,bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
3167 case both_types(close_noad_type,rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
3168 case both_types(close_noad_type,open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
3169 case both_types(close_noad_type,close_noad_type): x = get_math_param(math_param_close_close_spacing,mstyle); break;
3170 case both_types(close_noad_type,punct_noad_type): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
3171 case both_types(close_noad_type,inner_noad_type): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
3172 case both_types(punct_noad_type,ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
3173 case both_types(punct_noad_type,op_noad_type_normal ): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
3174 #if 0
3175 case both_types(punct_noad_type,bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
3176 #endif
3177 case both_types(punct_noad_type,rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
3178 case both_types(punct_noad_type,open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
3179 case both_types(punct_noad_type,close_noad_type): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
3180 case both_types(punct_noad_type,punct_noad_type): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
3181 case both_types(punct_noad_type,inner_noad_type): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
3182 case both_types(inner_noad_type,ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
3183 case both_types(inner_noad_type,op_noad_type_normal ): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
3184 case both_types(inner_noad_type,bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
3185 case both_types(inner_noad_type,rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
3186 case both_types(inner_noad_type,open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
3187 case both_types(inner_noad_type,close_noad_type): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
3188 case both_types(inner_noad_type,punct_noad_type): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
3189 case both_types(inner_noad_type,inner_noad_type): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
3190 /* *INDENT-ON* */
3192 if (x < 0) {
3193 confusion("mathspacing");
3195 if (x != 0) {
3196 pointer y;
3197 if (x <= thick_mu_skip_code) { /* trap thin/med/thick settings cf. old TeX */
3198 y = math_glue(glue_par(x), mmu);
3199 z = new_glue(y);
3200 glue_ref_count(y) = null;
3201 subtype(z) = (quarterword) (x + 1); /* store a symbolic subtype */
3202 } else {
3203 y = math_glue(x, mmu);
3204 z = new_glue(y);
3205 glue_ref_count(y) = null;
3208 return z;
3211 @ @c
3212 static pointer check_nucleus_complexity(halfword q, scaled * delta,
3213 int cur_style)
3215 pointer p = null;
3216 switch (type(nucleus(q))) {
3217 case math_char_node:
3218 case math_text_char_node:
3219 fetch(nucleus(q));
3220 if (char_exists(cur_f, cur_c)) {
3221 *delta = char_italic(cur_f, cur_c);
3222 p = new_glyph(cur_f, cur_c);
3223 reset_attributes(p, node_attr(nucleus(q)));
3224 if ((is_new_mathfont(cur_f) && get_char_cat_code(cur_c) == 11) ||
3225 (!is_new_mathfont(cur_f) && type(nucleus(q)) == math_text_char_node && space(cur_f)) != 0) {
3226 *delta = 0; /* no italic correction in mid-word of text font */
3228 if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
3229 pointer x = new_kern(*delta);
3230 reset_attributes(x, node_attr(nucleus(q)));
3231 couple_nodes(p,x);
3232 *delta = 0;
3235 break;
3236 case sub_box_node:
3237 p = math_list(nucleus(q));
3238 break;
3239 case sub_mlist_node:
3240 mlist_to_hlist_args(math_list(nucleus(q)), cur_style, false); /* recursive call */
3241 setup_cur_size(cur_style);
3242 p = hpack(vlink(temp_head), 0, additional, -1);
3243 reset_attributes(p, node_attr(nucleus(q)));
3244 break;
3245 default:
3246 confusion("mlist2"); /* this can't happen mlist2 */
3248 return p;
3251 @ Here is the overall plan of |mlist_to_hlist|, and the list of its
3252 local variables.
3255 static void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
3257 pointer q; /* runs through the mlist */
3258 pointer r; /* the most recent noad preceding |q| */
3259 int style;
3260 int r_type; /* the |type| of noad |r|, or |op_noad| if |r=null| */
3261 int r_subtype; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
3262 int t; /* the effective |type| of noad |q| during the second pass */
3263 int t_subtype; /* the effective |subtype| of noad |q| during the second pass */
3264 pointer p, x, y, z; /* temporary registers for list construction */
3265 int pen; /* a penalty to be inserted */
3266 scaled max_hl, max_d; /* maximum height and depth of the list translated so far */
3267 scaled delta; /* italic correction offset for subscript and superscript */
3268 scaled cur_mu; /* the math unit width corresponding to |cur_size| */
3269 style = cur_style; /* tuck global parameter away as local variable */
3270 q = mlist;
3271 r = null;
3272 r_type = simple_noad;
3273 r_subtype = op_noad_type_normal;
3274 max_hl = 0;
3275 max_d = 0;
3276 x = null;
3277 p = null;
3278 setup_cur_size(cur_style);
3279 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3280 while (q != null) {
3281 /* We use the fact that no character nodes appear in an mlist, hence
3282 the field |type(q)| is always present. */
3284 /* One of the things we must do on the first pass is change a |bin_noad| to
3285 an |ord_noad| if the |bin_noad| is not in the context of a binary operator.
3286 The values of |r| and |r_type| make this fairly easy. */
3287 RESWITCH:
3288 delta = 0;
3289 switch (type(q)) {
3290 case simple_noad:
3291 switch (subtype(q)) {
3292 case bin_noad_type:
3293 switch (r_type) {
3294 case simple_noad:
3295 switch (r_subtype) {
3296 case bin_noad_type:
3297 case op_noad_type_normal:
3298 case op_noad_type_limits:
3299 case op_noad_type_no_limits:
3300 case rel_noad_type:
3301 case open_noad_type:
3302 case punct_noad_type:
3303 subtype(q) = ord_noad_type;
3304 goto RESWITCH;
3305 break;
3307 break;
3308 case fence_noad:
3309 if (r_subtype == left_noad_side) {
3310 subtype(q) = ord_noad_type;
3311 goto RESWITCH;
3313 break;
3315 break;
3316 case over_noad_type:
3317 make_over(q, cur_style);
3318 break;
3319 case under_noad_type:
3320 make_under(q, cur_style);
3321 break;
3322 case vcenter_noad_type:
3323 make_vcenter(q);
3324 break;
3325 case rel_noad_type:
3326 case close_noad_type:
3327 case punct_noad_type:
3328 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3329 type(r) = simple_noad;
3330 subtype(r) = ord_noad_type;
3332 break;
3333 case op_noad_type_normal:
3334 case op_noad_type_limits:
3335 case op_noad_type_no_limits:
3336 delta = make_op(q, cur_style);
3337 if (subtype(q) == op_noad_type_limits)
3338 goto CHECK_DIMENSIONS;
3339 break;
3340 case ord_noad_type:
3341 make_ord(q);
3342 break;
3343 case open_noad_type:
3344 case inner_noad_type:
3345 break;
3347 break;
3348 case fence_noad:
3349 if (subtype(q) != left_noad_side)
3350 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3351 type(r) = simple_noad;
3352 subtype(r) = ord_noad_type;
3354 goto DONE_WITH_NOAD;
3355 break;
3356 case fraction_noad:
3357 make_fraction(q, cur_style);
3358 goto CHECK_DIMENSIONS;
3359 break;
3360 case radical_noad:
3361 if (subtype(q) == 6)
3362 make_delimiter_over(q, cur_style);
3363 else if (subtype(q) == 5)
3364 make_delimiter_under(q, cur_style);
3365 else if (subtype(q) == 4)
3366 make_over_delimiter(q, cur_style);
3367 else if (subtype(q) == 3)
3368 make_under_delimiter(q, cur_style);
3369 else
3370 make_radical(q, cur_style);
3371 break;
3372 case accent_noad:
3373 make_math_accent(q, cur_style);
3374 break;
3375 case style_node:
3376 cur_style = subtype(q);
3377 setup_cur_size(cur_style);
3378 /* HH-LS: was cur_mu = x_over_n(get_math_quad(cur_size), 18);*/
3379 /* This is an old bug so the fix can influence outcome */
3380 cur_mu = x_over_n(get_math_quad(cur_style), 18);
3381 goto DONE_WITH_NODE;
3382 break;
3383 case choice_node:
3384 switch (cur_style / 2) {
3385 case 0:
3386 choose_mlist(display_mlist);
3387 break; /* |display_style=0| */
3388 case 1:
3389 choose_mlist(text_mlist);
3390 break; /* |text_style=2| */
3391 case 2:
3392 choose_mlist(script_mlist);
3393 break; /* |script_style=4| */
3394 case 3:
3395 choose_mlist(script_script_mlist);
3396 break; /* |script_script_style=6| */
3397 } /* there are no other cases */
3398 flush_node_list(display_mlist(q));
3399 flush_node_list(text_mlist(q));
3400 flush_node_list(script_mlist(q));
3401 flush_node_list(script_script_mlist(q));
3402 type(q) = style_node;
3403 subtype(q) = (quarterword) cur_style;
3404 if (p != null) {
3405 z = vlink(q);
3406 couple_nodes(q,p);
3407 while (vlink(p) != null)
3408 p = vlink(p);
3409 try_couple_nodes(p,z);
3411 goto DONE_WITH_NODE;
3412 break;
3413 case ins_node:
3414 case mark_node:
3415 case adjust_node:
3416 case whatsit_node:
3417 case penalty_node:
3418 case disc_node:
3419 goto DONE_WITH_NODE;
3420 break;
3421 case rule_node:
3422 if (height(q) > max_hl)
3423 max_hl = height(q);
3424 if (depth(q) > max_d)
3425 max_d = depth(q);
3426 goto DONE_WITH_NODE;
3427 break;
3428 case glue_node:
3430 Conditional math glue (`\.{\\nonscript}') results in a |glue_node|
3431 pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
3432 the node following will be eliminated if it is a glue or kern node and if the
3433 current size is different from |text_size|. Unconditional math glue
3434 (`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
3435 by |cur_mu|.
3437 if (subtype(q) == mu_glue) {
3438 x = glue_ptr(q);
3439 y = math_glue(x, cur_mu);
3440 delete_glue_ref(x);
3441 glue_ptr(q) = y;
3442 subtype(q) = normal;
3443 } else if ((cur_size != text_size)
3444 && (subtype(q) == cond_math_glue)) {
3445 p = vlink(q);
3446 if (p != null)
3447 if ((type(p) == glue_node) || (type(p) == kern_node)) {
3448 couple_nodes(q,vlink(p));
3449 vlink(p) = null;
3450 flush_node_list(p);
3453 goto DONE_WITH_NODE;
3454 break;
3455 case kern_node:
3456 math_kern(q, cur_mu);
3457 goto DONE_WITH_NODE;
3458 break;
3459 default:
3460 confusion("mlist1"); /* this can't happen mlist1 */
3462 /* When we get to the following part of the program, we have ``fallen through''
3463 from cases that did not lead to |check_dimensions| or |done_with_noad| or
3464 |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
3465 converted to an hlist, and whose subscripts and superscripts need to be
3466 appended if they are present.
3468 If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
3469 by which a superscript should be moved right with respect to a subscript
3470 when both are present.
3472 p = check_nucleus_complexity(q, &delta, cur_style);
3474 if ((subscr(q) == null) && (supscr(q) == null)) {
3475 assign_new_hlist(q, p);
3476 } else {
3477 make_scripts(q, p, delta, cur_style); /* top, bottom */
3479 CHECK_DIMENSIONS:
3480 z = hpack(new_hlist(q), 0, additional, -1);
3481 if (height(z) > max_hl)
3482 max_hl = height(z);
3483 if (depth(z) > max_d)
3484 max_d = depth(z);
3485 list_ptr(z) = null;
3486 flush_node(z); /* only drop the \.{\\hbox} */
3487 DONE_WITH_NOAD:
3488 r = q;
3489 r_type = type(r);
3490 r_subtype = subtype(r);
3491 if (r_type == fence_noad) {
3492 r_subtype = left_noad_side;
3493 cur_style = style;
3494 setup_cur_size(cur_style);
3495 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3497 DONE_WITH_NODE:
3498 q = vlink(q);
3500 if (r_type == simple_noad && r_subtype == bin_noad_type) {
3501 type(r) = simple_noad;
3502 subtype(r) = ord_noad_type;
3504 /* Make a second pass over the mlist, removing all noads and inserting the
3505 proper spacing and penalties */
3507 /* We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
3508 The second pass simply goes through and hooks everything together with the
3509 proper glue and penalties. It also handles the |fence_noad|s that
3510 might be present, since |max_hl| and |max_d| are now known. Variable |p| points
3511 to a node at the current end of the final hlist.
3513 p = temp_head;
3514 vlink(p) = null;
3515 q = mlist;
3516 r_type = 0;
3517 r_subtype = 0;
3518 cur_style = style;
3519 setup_cur_size(cur_style);
3520 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3521 NEXT_NODE:
3522 while (q != null) {
3523 /* If node |q| is a style node, change the style and |goto delete_q|;
3524 otherwise if it is not a noad, put it into the hlist,
3525 advance |q|, and |goto done|; otherwise set |s| to the size
3526 of noad |q|, set |t| to the associated type (|ord_noad..
3527 inner_noad|), and set |pen| to the associated penalty */
3528 /* Just before doing the big |case| switch in the second pass, the program
3529 sets up default values so that most of the branches are short. */
3530 t = simple_noad;
3531 t_subtype = ord_noad_type;
3532 pen = inf_penalty;
3533 switch (type(q)) {
3534 case simple_noad:
3535 t_subtype = subtype(q);
3536 switch (t_subtype) {
3537 case bin_noad_type:
3538 pen = bin_op_penalty;
3539 break;
3540 case rel_noad_type:
3541 pen = rel_penalty;
3542 break;
3543 case vcenter_noad_type:
3544 case over_noad_type:
3545 case under_noad_type:
3546 t_subtype = ord_noad_type;
3547 break;
3549 case radical_noad:
3550 break;
3551 case accent_noad:
3552 break;
3553 case fraction_noad:
3554 t = simple_noad;
3555 t_subtype = inner_noad_type;
3556 break;
3557 case fence_noad:
3558 t_subtype = make_left_right(q, style, max_d, max_hl);
3559 break;
3560 case style_node:
3561 /* Change the current style and |goto delete_q| */
3562 cur_style = subtype(q);
3563 setup_cur_size(cur_style);
3564 cur_mu = x_over_n(get_math_quad(cur_size), 18);
3565 goto DELETE_Q;
3566 break;
3567 case whatsit_node:
3568 case penalty_node:
3569 case rule_node:
3570 case disc_node:
3571 case adjust_node:
3572 case ins_node:
3573 case mark_node:
3574 case glue_node:
3575 case kern_node:
3576 couple_nodes(p,q);
3577 p = q;
3578 q = vlink(q);
3579 vlink(p) = null;
3580 goto NEXT_NODE;
3581 break;
3582 default:
3583 confusion("mlist3"); /* this can't happen mlist3 */
3585 /* Append inter-element spacing based on |r_type| and |t| */
3586 if (r_type > 0) { /* not the first noad */
3587 z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
3588 if (z != null) {
3589 reset_attributes(z, node_attr(p));
3590 couple_nodes(p,z);
3591 p = z;
3595 /* Append any |new_hlist| entries for |q|, and any appropriate penalties */
3596 /* We insert a penalty node after the hlist entries of noad |q| if |pen|
3597 is not an ``infinite'' penalty, and if the node immediately following |q|
3598 is not a penalty node or a |rel_noad| or absent entirely. */
3600 if (new_hlist(q) != null) {
3601 couple_nodes(p,new_hlist(q));
3602 do {
3603 p = vlink(p);
3604 } while (vlink(p) != null);
3606 if (penalties && vlink(q) != null && pen < inf_penalty) {
3607 r_type = type(vlink(q));
3608 r_subtype = subtype(vlink(q));
3609 if (r_type != penalty_node
3610 && (r_type != simple_noad || r_subtype != rel_noad_type)) {
3611 z = new_penalty(pen);
3612 reset_attributes(z, node_attr(q));
3613 couple_nodes(p,z);
3614 p = z;
3617 if (type(q) == fence_noad && subtype(q) == right_noad_side) {
3618 t = simple_noad;
3619 t_subtype = open_noad_type;
3621 r_type = t;
3622 r_subtype = t_subtype;
3623 DELETE_Q:
3624 r = q;
3625 q = vlink(q);
3626 /* The m-to-hlist conversion takes place in-place,
3627 so the various dependant fields may not be freed
3628 (as would happen if |flush_node| was called).
3629 A low-level |free_node| is easier than attempting
3630 to nullify such dependant fields for all possible
3631 node and noad types.
3633 if (nodetype_has_attributes(type(r)))
3634 delete_attribute_ref(node_attr(r));
3635 free_node(r, get_node_size(type(r), subtype(r)));
3639 @ This used to be needed when |mlist_to_hlist| communicated via global
3640 variables.
3643 void mlist_to_hlist_args(pointer n, int w, boolean m)
3645 mlist_to_hlist(n, m, w);