1% mlist.w 2% 3% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> 4% 5% This file is part of LuaTeX. 6% 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. 11% 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. 16% 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/>. 19 20/* hh-ls: by using couple_nodes instead of vlink we make sure that we can backtrack as well as have valid prev links */ 21 22 23\def\LuaTeX{Lua\TeX} 24 25@ @c 26 27 28#include "ptexlib.h" 29#include "lua/luatex-api.h" 30 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) 40 41#define nDEBUG 42 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) 51 52 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) 65 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) 72 73 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)) 76 77 78#define font_MATH_par(a,b) \ 79 (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter) 80 81 82@ here are the math parameters that are font-dependant 83 84@ Before an mlist is converted to an hlist, \TeX\ makes sure that 85the fonts in family~2 have enough parameters to be math-symbol 86fonts, and that the fonts in family~3 have enough parameters to be 87math-extension fonts. The math-symbol parameters are referred to by using the 88following 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@> 92 93@c 94#define total_mathsy_params 22 95#define total_mathex_params 13 96 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 */ 115 116 117@ The math-extension parameters have similar macros, but the size code is 118omitted (since it is always |cur_size| when we refer to such parameters). 119@^parameters for symbols@> 120@^font parameters@> 121 122@c 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 */ 130 131@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of 132the MathConstants values have no matching usage in \LuaTeX\ right now. 133 134ScriptPercentScaleDown, 135ScriptScriptPercentScaleDown: 136 These should be handled by the macro package, on the engine 137 side there are three separate fonts 138 139DelimitedSubFormulaMinHeight: 140 This is perhaps related to word's natural math input? I have 141 no idea what to do about it 142 143MathLeading: 144 LuaTeX does not currently handle multi-line displays, and 145 the parameter does not seem to make much sense elsewhere 146 147FlattenedAccentBaseHeight: 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 152 153SkewedFractionHorizontalGap, 154SkewedFractionVerticalGap: 155 I am not sure it makes sense implementing skewed fractions, 156 so I would like to see an example first 157 158Also still TODO for OpenType Math: 159 * extensible large operators 160 * prescripts 161 162@ this is not really a math parameter at all 163 164@c 165static void math_param_error(const char *param, int style) 166{ 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 174 }; 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; 182} 183 184 185@ @c 186static scaled accent_base_height(int f) 187{ 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); 195 } 196 return a; 197} 198 199@ the non-staticness of this function is for the benefit of |texmath.w| 200 201@c 202scaled get_math_quad(int var) 203{ 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; 208 } 209 return a; 210} 211 212@ this parameter is different because it is called with a size 213 specifier instead of a style specifier. 214 215@c 216static scaled math_axis(int b) 217{ 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; 230 } 231 return a; 232} 233 234@ @c 235static scaled get_math_quad_size(int b) 236{ 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); 245} 246 247 248@ @c 249static scaled minimum_operator_size(int var) 250{ 251 scaled a = get_math_param(math_param_operator_size, var); 252 return a; 253} 254 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. 257 258@c 259static scaled radical_rule(int var) 260{ 261 scaled a = get_math_param(math_param_radical_rule, var); 262 return a; 263} 264 265@ now follow all the trivial math parameters 266 267@c 268#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b) 269 270static scaled do_get_math_param_or_error(int var, int param, const char *name) 271{ 272 scaled a = get_math_param(param, var); 273 if (a == undefined_math_parameter) { 274 math_param_error(name, var); 275 a = 0; 276 } 277 return a; 278} 279 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) 284 285#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min) 286 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) 290 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) 294 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) 297 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) 300 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) 303 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) 307 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) 314 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) 318 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) 322 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) 332 333#define space_after_script(a) get_math_param_or_error(a, space_after_script) 334 335@ @c 336void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) 337{ 338 if (is_new_mathfont(f)) { /* fix all known parameters */ 339 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); 370 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); 381 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); 390 391 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); 407 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); 412 413 DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id, 414 font_MATH_par(f, DisplayOperatorMinHeight), 415 lvl); 416 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); 462 } 463 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); 479 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); 495 } 496 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); 517 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); 542 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); 573 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); 580 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); 585 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); 590 591 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); 673 } 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); 694 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); 700 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); 717 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); 748 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); 753 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 800 */ 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); 805 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); 822 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); 828 } 829} 830 831@ This needs to be called just at the start of |mlist_to_hlist|, for 832backward compatibility with \.{\\scriptspace}. 833 834@c 835static void finalize_math_parameters(void) 836{ 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); 857 } 858 int_par(tracing_assigns_code) = saved_trace; 859 860} 861 862 863@ In order to convert mlists to hlists, i.e., noads to nodes, we need several 864subroutines that are conveniently dealt with now. 865 866Let us first introduce the macros that make it easy to get at the parameters and 867other font information. A size code, which is a multiple of 256, is added to a 868family number to get an index into the table of internal font numbers 869for each combination of family and size. (Be alert: Size codes get 870larger as the type gets smaller.) 871 872@c 873static const char *math_size_string(int s) 874{ 875 if (s == text_size) 876 return "textfont"; 877 else if (s == script_size) 878 return "scriptfont"; 879 else 880 return "scriptscriptfont"; 881} 882 883@ When the style changes, the following piece of program computes associated 884information: 885 886@c 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) 896 897 898@ a simple routine that creates a flat copy of a nucleus 899@c 900static pointer math_clone(pointer q) 901{ 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); 912 } 913 return x; 914} 915 916 917 918 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. 922 923@c 924static pointer do_fraction_rule(scaled t, pointer att) 925{ 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; 933} 934 935 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|. 939 940@c 941static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att) 942{ 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; 955} 956 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. 962 963@c 964static pointer char_box(internal_font_number f, int c, pointer bb) 965{ 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; 976} 977 978@ Another handy subroutine computes the height plus depth of 979 a given character: 980 981@c 982static scaled height_plus_depth(internal_font_number f, int c) 983{ 984 return (char_height(f, c) + char_depth(f, c)); 985} 986 987 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|: 991 992@c 993static scaled stack_into_box(pointer b, internal_font_number f, int c) 994{ 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); 999try_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); 1013 } 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); 1019 } 1020} 1021 1022 1023static 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); 1041 } 1042 } 1043} 1044 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. 1055 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@> 1060 1061@c 1062int cur_size; /* size code corresponding to |cur_style| */ 1063 1064@ @c 1065static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, 1066 pointer att, int boxtype, int cur_style) 1067{ 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; 1077 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; 1087 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 1095 }; 1096 tex_error("Variant part doesn't exist.", hlp); 1097 width(b) = null_delimiter_space; 1098 return b; 1099 } 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 1111 }; 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; 1119 } 1120 cur = cur->next; 1121 } 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 1128 }; 1129 tex_error("Extensible recipe has no fixed parts.", hlp); 1130 ext->extender = 0; 1131 num_normal = 1; 1132 num_extenders--; 1133 } 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. 1142 */ 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); 1164 } 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); 1183 } 1184 b_max += a - c; 1185 prev_overlap = cur->end_overlap; 1186 i--; 1187 } 1188 } 1189 } 1190 } 1191 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; 1209 } 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; 1226 } 1227 b_max += stack_into_box(b, f, cur->glyph); 1228 prev_overlap = cur->end_overlap; 1229 i--; 1230 } 1231 } 1232 } 1233 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; 1245 } 1246 1247 if (boxtype == vlist_node) 1248 height(b) = b_max; 1249 else 1250 width(b) = b_max; 1251 1252 return b; 1253} 1254 1255static pointer get_delim_vbox(extinfo * ext, internal_font_number f, scaled v, 1256 pointer att, int cur_style) 1257{ 1258 return get_delim_box(ext, f, v, att, vlist_node, cur_style); 1259} 1260 1261static pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v, 1262 pointer att, int cur_style) 1263{ 1264 return get_delim_box(ext, f, v, att, hlist_node, cur_style); 1265} 1266 1267 1268 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. 1278 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. 1283 1284@c 1285static void endless_loop_error(internal_font_number g, int y) 1286{ 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 1293 }; 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); 1297} 1298 1299static pointer do_var_delimiter(pointer d, int s, scaled v, scaled * ic, 1300 boolean flat, int cur_style) 1301{ 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; 1345 } 1346 if (char_tag(g, y) == ext_tag) { 1347 f = g; 1348 c = y; 1349 do_parts = true; 1350 goto FOUND; 1351 } 1352 if (i > 10000) { 1353 /* endless loop */ 1354 endless_loop_error(g, y); 1355 goto FOUND; 1356 } 1357 if (char_tag(g, y) == list_tag) { 1358 y = char_remainder(g, y); 1359 goto CONTINUE; 1360 } 1361 } 1362 } 1363 } 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); 1369 } 1370 FOUND: 1371 if (d != null) { 1372 att = node_attr(d); 1373 node_attr(d) = null; 1374 flush_node(d); 1375 } 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. 1379 */ 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); 1391 } 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. 1396 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. 1400 */ 1401 if (!is_new_mathfont(f)) { 1402 width(b) += char_italic(f, c); 1403 } 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; 1412 } 1413 if (!flat) 1414 shift_amount(b) = half(height(b) - depth(b)) - math_axis(s); 1415 delete_attribute_ref(att); 1416 return b; 1417} 1418 1419 1420static pointer var_delimiter(pointer d, int s, scaled v, scaled * ic, 1421 int cur_style) 1422{ 1423 return do_var_delimiter(d, s, v, ic, false, cur_style); 1424} 1425 1426static pointer flat_delimiter(pointer d, int s, scaled v, int cur_style) 1427{ 1428 return do_var_delimiter(d, s, v, NULL, true, cur_style); 1429} 1430 1431@ The next subroutine is much simpler; it is used for numerators and 1432denominators of fractions as well as for displayed operators and 1433their limits above and below. It takes a given box~|b| and 1434changes it so that the new box is centered in a box of width~|w|. 1435The centering is done by putting \.{\\hss} glue at the left and right 1436of the list inside |b|, then packaging the new box; thus, the 1437actual box might not really be centered, if it already contains 1438infinite glue. 1439 1440 1441The given box might contain a single character whose italic correction 1442has been added to the width of the box; in this case a compensating 1443kern is inserted. 1444 1445@c 1446static pointer rebox(pointer b, scaled w) 1447{ 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 */ 1451 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; 1457 } 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); 1468 } 1469 } 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; 1487 } 1488} 1489 1490@ Here is a subroutine that creates a new glue specification from another 1491one that is expressed in `\.{mu}', given the value of the math unit. 1492 1493@c 1494#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen) 1495 1496static pointer math_glue(pointer g, scaled m) 1497{ 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; 1506 } 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; 1520} 1521 1522@ The |math_kern| subroutine removes |mu_glue| from a kern node, given 1523the value of the math unit. 1524 1525@c 1526static void math_kern(pointer p, scaled m) 1527{ 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; 1536 } 1537 width(p) = mu_mult(width(p)); 1538 subtype(p) = explicit; 1539 } 1540} 1541 1542@ @c 1543void run_mlist_to_hlist(halfword p, int mstyle, boolean penalties) 1544{ 1545 int callback_id; 1546 int a, sfix; 1547 lua_State *L = Luas; 1548 if (p == null) { 1549 vlink(temp_head) = null; 1550 return; 1551 } 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; 1559 } 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; 1569 } 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; 1578 } 1579} 1580 1581@ The recursion in |mlist_to_hlist| is due primarily to a subroutine 1582called |clean_box| that puts a given noad field into a box using a given 1583math style; |mlist_to_hlist| can call |clean_box|, which can call 1584|mlist_to_hlist|. 1585@^recursion@> 1586 1587 1588The box returned by |clean_box| is ``clean'' in the 1589sense that its |shift_amount| is zero. 1590 1591@c 1592static pointer clean_box(pointer p, int s, int cur_style) 1593{ 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; 1614 } 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; 1639 } 1640 } 1641 } 1642 } 1643 } 1644 return x; 1645} 1646 1647@ It is convenient to have a procedure that converts a |math_char| 1648field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| 1649to the font code and character code of a given noad field. 1650It also takes care of issuing error messages for 1651nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false| 1652after |fetch| has acted, and the field will also have been reset to |null|. 1653 1654The outputs of |fetch| are placed in global variables. 1655 1656@c 1657internal_font_number cur_f; /* the |font| field of a |math_char| */ 1658int cur_c; /* the |character| field of a |math_char| */ 1659 1660static 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 1672 }; 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); 1681 } 1682 } 1683} 1684 1685 1686@ We need to do a lot of different things, so |mlist_to_hlist| makes two 1687passes over the given mlist. 1688 1689The first pass does most of the processing: It removes ``mu'' spacing from 1690glue, it recursively evaluates all subsidiary mlists so that only the 1691top-level mlist remains to be handled, it puts fractions and square roots 1692and such things into boxes, it attaches subscripts and superscripts, and 1693it computes the overall height and depth of the top-level mlist so that 1694the size of delimiters for a |fence_noad| will be known. 1695The hlist resulting from each noad is recorded in that noad's |new_hlist| 1696field, an integer field that replaces the |nucleus| or |thickness|. 1697@^recursion@> 1698 1699The second pass eliminates all noads and inserts the correct glue and 1700penalties between nodes. 1701 1702@c 1703static void assign_new_hlist(pointer q, pointer r) 1704{ 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; 1721 } 1722 break; 1723 } 1724 new_hlist(q) = r; 1725} 1726 1727@ @c 1728#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0) 1729 1730 1731@ Most of the actual construction work of |mlist_to_hlist| is done 1732by procedures with names 1733like |make_fraction|, |make_radical|, etc. To illustrate 1734the general setup of such procedures, let's begin with a couple of 1735simple ones. 1736 1737@c 1738static void make_over(pointer q, int cur_style) 1739{ 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; 1747} 1748 1749static void make_under(pointer q, int cur_style) 1750{ 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; 1766} 1767 1768static void make_vcenter(pointer q) 1769{ 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); 1778} 1779 1780@ According to the rules in the \.{DVI} file specifications, we ensure alignment 1781@^square roots@> 1782between a square root sign and the rule above its nucleus by assuming that the 1783baseline of the square-root symbol is the same as the bottom of the rule. The 1784height of the square-root symbol will be the thickness of the rule, and the 1785depth of the square-root symbol should exceed or equal the height-plus-depth 1786of the nucleus plus a certain minimum clearance~|psi|. The symbol will be 1787placed so that the actual clearance is |psi| plus half the excess. 1788 1789@c 1790static void make_radical(pointer q, int cur_style) 1791{ 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); 1811 } 1812 } else { 1813 y = var_delimiter(left_delimiter(q), cur_size, 1814 height(x) + depth(x) + clr + theta, NULL, cur_style); 1815 } 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; 1847 } 1848 math_list(degree(q)) = null; /* for \.{\\Uroot ..{<list>}{}} */ 1849 flush_node(degree(q)); 1850 } 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; 1855} 1856 1857 1858@ Construct a vlist box 1859@c 1860static pointer 1861wrapup_delimiter(pointer x, pointer y, pointer q, 1862 scaled shift_up, scaled shift_down) 1863{ 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; 1876} 1877 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); \ 1884 } \ 1885 } while (0) 1886 1887@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y| 1888 1889@c 1890static void make_over_delimiter(pointer q, int cur_style) 1891{ 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; 1904 } 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; 1909} 1910 1911@ this has the extensible delimiter |x| as a limit above |nucleus| box |y| 1912 1913@c 1914static void make_delimiter_over(pointer q, int cur_style) 1915{ 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); 1930 } 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; 1935} 1936 1937 1938@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x| 1939 1940@c 1941static void make_delimiter_under(pointer q, int cur_style) 1942{ 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); 1957 } 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; 1962 1963} 1964 1965@ this has the extensible delimiter |x| as a limit below |nucleus| box |y| 1966 1967@c 1968static void make_under_delimiter(pointer q, int cur_style) 1969{ 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; 1982 } 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; 1987 1988} 1989 1990 1991@ Slants are not considered when placing accents in math mode. The accenter is 1992centered over the accentee, and the accent width is treated as zero with 1993respect to the size of the final box. 1994 1995@c 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 2000 2001static boolean compute_accent_skew(pointer q, int top_or_bot, scaled *s) 2002{ 2003 pointer p; /* temporary register for box construction */ 2004 boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */ 2005 2006 s_is_absolute = false; 2007 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; 2015 } 2016 } else { 2017 *s = char_bot_accent(cur_f, cur_c); 2018 if (*s != INT_MIN) { 2019 s_is_absolute = true; 2020 } 2021 } 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; 2027 } 2028 } 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); 2037 } 2038 } 2039 2040 return s_is_absolute; 2041} 2042 2043static void do_make_math_accent(pointer q, internal_font_number f, int c, 2044 int flags, int cur_style) 2045{ 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; 2058 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); 2063 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; 2070 } 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; 2089 } 2090 } 2091 } 2092 if (y == null) { 2093 y = char_box(f, c, node_attr(attr_p)); 2094 } 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 */ 2102 } 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); 2119 } 2120 } 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); 2127 } 2128 if (sa == INT_MIN) { 2129 sa = half(width(y)); /* just take the center */ 2130 } 2131 shift_amount(y) = s - sa; 2132 } else { 2133 if (width(y)== 0) { 2134 shift_amount(y) = s + w; 2135 } else { 2136 shift_amount(y) = s + half(w - width(y)); 2137 } 2138 } 2139 width(y) = 0; 2140 if (top_or_bot == TOP_CODE) { 2141 p = new_kern(-delta); 2142 couple_nodes(p,x); 2143 couple_nodes(y,p); 2144 } else { 2145#if 0 2146 p = new_kern(-delta); 2147 couple_nodes(x,p); 2148 couple_nodes(p,y); 2149 y = x; 2150#endif 2151 couple_nodes(x,y); 2152 y = x; 2153 } 2154 r = vpackage(y, 0, additional, max_dimen, math_direction); 2155 reset_attributes(r, node_attr(q)); 2156 width(r) = width(x); 2157 y = r; 2158 if (top_or_bot == TOP_CODE) { 2159 if (height(y) < h) { 2160 /* Make the height of box |y| equal to |h| */ 2161 p = new_kern(h - height(y)); 2162 reset_attributes(p, node_attr(q)); 2163// vlink(p) = list_ptr(y); 2164try_couple_nodes(p,list_ptr(y)); 2165 list_ptr(y) = p; 2166 height(y) = h; 2167 } 2168 } else { 2169 shift_amount(y) = -(h - height(y)); 2170 } 2171 math_list(nucleus(q)) = y; 2172 type(nucleus(q)) = sub_box_node; 2173} 2174 2175static void make_math_accent(pointer q, int cur_style) 2176{ 2177 int topstretch = !(subtype(q) % 2); 2178 int botstretch = !(subtype(q) / 2); 2179 2180 if (accent_chr(q) != null) { 2181 fetch(accent_chr(q)); 2182 if (char_exists(cur_f, cur_c)) { 2183 do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style); 2184 } 2185 flush_node(accent_chr(q)); 2186 accent_chr(q) = null; 2187 } 2188 if (bot_accent_chr(q) != null) { 2189 fetch(bot_accent_chr(q)); 2190 if (char_exists(cur_f, cur_c)) { 2191 do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style); 2192 } 2193 flush_node(bot_accent_chr(q)); 2194 bot_accent_chr(q) = null; 2195 } 2196} 2197 2198@ The |make_fraction| procedure is a bit different because it sets 2199|new_hlist(q)| directly rather than making a sub-box. 2200 2201@c 2202static void make_fraction(pointer q, int cur_style) 2203{ 2204 pointer p, v, x, y, z; /* temporary registers for box construction */ 2205 scaled delta, delta1, delta2, shift_up, shift_down, clr; 2206 /* dimensions for box calculations */ 2207 if (thickness(q) == default_code) 2208 thickness(q) = fraction_rule(cur_style); 2209 /* Create equal-width boxes |x| and |z| for the numerator and denominator, 2210 and compute the default amounts |shift_up| and |shift_down| by which they 2211 are displaced from the baseline */ 2212 x = clean_box(numerator(q), num_style(cur_style), cur_style); 2213 z = clean_box(denominator(q), denom_style(cur_style), cur_style); 2214 if (width(x) < width(z)) 2215 x = rebox(x, width(z)); 2216 else 2217 z = rebox(z, width(x)); 2218 if (thickness(q) == 0) { 2219 shift_up = stack_num_up(cur_style); 2220 shift_down = stack_denom_down(cur_style); 2221 /* The numerator and denominator must be separated by a certain minimum 2222 clearance, called |clr| in the following program. The difference between 2223 |clr| and the actual clearance is |2delta|. */ 2224 clr = stack_vgap(cur_style); 2225 delta = half(clr - ((shift_up - depth(x)) - (height(z) - shift_down))); 2226 if (delta > 0) { 2227 shift_up = shift_up + delta; 2228 shift_down = shift_down + delta; 2229 } 2230 } else { 2231 shift_up = fraction_num_up(cur_style); 2232 shift_down = fraction_denom_down(cur_style); 2233 /* In the case of a fraction line, the minimum clearance depends on the actual 2234 thickness of the line. */ 2235 delta = half(thickness(q)); 2236 clr = fraction_num_vgap(cur_style); 2237 clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style)); 2238 delta1 = clr - ((shift_up - depth(x)) - (math_axis(cur_size) + delta)); 2239 if (delta1 > 0) 2240 shift_up = shift_up + delta1; 2241 clr = fraction_denom_vgap(cur_style); 2242 clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style)); 2243 delta2 = 2244 clr - ((math_axis(cur_size) - delta) - (height(z) - shift_down)); 2245 if (delta2 > 0) 2246 shift_down = shift_down + delta2; 2247 } 2248 /* Construct a vlist box for the fraction, according to |shift_up| and |shift_down| */ 2249 v = new_null_box(); 2250 type(v) = vlist_node; 2251 height(v) = shift_up + height(x); 2252 depth(v) = depth(z) + shift_down; 2253 width(v) = width(x); /* this also equals |width(z)| */ 2254 reset_attributes(v, node_attr(q)); 2255 if (thickness(q) == 0) { 2256 p = new_kern((shift_up - depth(x)) - (height(z) - shift_down)); 2257 couple_nodes(p,z); 2258 } else { 2259 y = do_fraction_rule(thickness(q), node_attr(q)); 2260 p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down)); 2261 reset_attributes(p, node_attr(q)); 2262 couple_nodes(y,p); 2263 couple_nodes(p,z); 2264 p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta)); 2265 couple_nodes(p,y); 2266 } 2267 reset_attributes(p, node_attr(q)); 2268 couple_nodes(x,p); 2269 list_ptr(v) = x; 2270 /* Put the fraction into a box with its delimiters, and make |new_hlist(q)| 2271 point to it */ 2272 delta = fraction_del_size(cur_style); 2273 x = var_delimiter(left_delimiter(q), cur_size, delta, NULL, cur_style); 2274 left_delimiter(q) = null; 2275 couple_nodes(x,v); 2276 z = var_delimiter(right_delimiter(q), cur_size, delta, NULL, cur_style); 2277 right_delimiter(q) = null; 2278 couple_nodes(v,z); 2279 y = hpack(x, 0, additional, -1); 2280 reset_attributes(y, node_attr(q)); 2281 assign_new_hlist(q, y); 2282} 2283 2284 2285@ If the nucleus of an |op_noad| is a single character, it is to be 2286centered vertically with respect to the axis, after first being enlarged 2287(via a character list in the font) if we are in display style. The normal 2288convention for placing displayed limits is to put them above and below the 2289operator in display style. 2290 2291The italic correction is removed from the character if there is a subscript 2292and the limits are not being displayed. The |make_op| 2293routine returns the value that should be used as an offset between 2294subscript and superscript. 2295 2296After |make_op| has acted, |subtype(q)| will be |limits| if and only if 2297the limits have been set above and below the operator. In that case, 2298|new_hlist(q)| will already contain the desired final box. 2299 2300@c 2301static scaled make_op(pointer q, int cur_style) 2302{ 2303 scaled delta; /* offset between subscript and superscript */ 2304 pointer p, v, x, y, z; /* temporary registers for box construction */ 2305 int c; /* register for character examination */ 2306 scaled shift_up, shift_down; /* dimensions for box calculation */ 2307 scaled ok_size; 2308 if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) 2309 subtype(q) = op_noad_type_limits; 2310 if (type(nucleus(q)) == math_char_node) { 2311 fetch(nucleus(q)); 2312 if (cur_style < text_style) { /* try to make it larger */ 2313 ok_size = minimum_operator_size(cur_style); 2314 if (ok_size != undefined_math_parameter) { 2315 /* creating a temporary delimiter is the cleanest way */ 2316 y = new_node(delim_node, 0); 2317 small_fam(y) = math_fam(nucleus(q)); 2318 small_char(y) = math_character(nucleus(q)); 2319 x = var_delimiter(y, text_size, ok_size, &delta, cur_style); 2320 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { 2321 width(x) -= delta; /* remove italic correction */ 2322 } 2323 /* For an OT MATH font, we may have to get rid of yet another italic 2324 correction because |make_scripts()| will add one. 2325 This test is somewhat more complicated because |x| can be a null 2326 delimiter */ 2327 if ((subscr(q) != null || supscr(q) != null) 2328 && (subtype(q) != op_noad_type_limits) 2329 && ((list_ptr(x) != null) 2330 && (type(list_ptr(x)) == glyph_node) 2331 && is_new_mathfont(font(list_ptr(x))))) { 2332 width(x) -= delta; /* remove another italic correction */ 2333 } 2334 } else { 2335 ok_size = height_plus_depth(cur_f, cur_c) + 1; 2336 while ((char_tag(cur_f, cur_c) == list_tag) && 2337 height_plus_depth(cur_f, cur_c) < ok_size) { 2338 c = char_remainder(cur_f, cur_c); 2339 if (!char_exists(cur_f, c)) 2340 break; 2341 cur_c = c; 2342 math_character(nucleus(q)) = c; 2343 } 2344 delta = char_italic(cur_f, cur_c); 2345 x = clean_box(nucleus(q), cur_style, cur_style); 2346 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) 2347 width(x) = width(x) - delta; /* remove italic correction */ 2348 2349 shift_amount(x) = 2350 half(height(x) - depth(x)) - math_axis(cur_size); 2351 /* center vertically */ 2352 } 2353 type(nucleus(q)) = sub_box_node; 2354 math_list(nucleus(q)) = x; 2355 2356 } else { /* normal size */ 2357 delta = char_italic(cur_f, cur_c); 2358 x = clean_box(nucleus(q), cur_style, cur_style); 2359 if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) 2360 width(x) = width(x) - delta; /* remove italic correction */ 2361 2362 /* For an OT MATH font, we may have to get rid of yet another italic 2363 correction because |make_scripts()| will add one. 2364 This test is somewhat more complicated because |x| can be a null 2365 delimiter */ 2366 if ((subscr(q) != null || supscr(q) != null) 2367 && (subtype(q) != op_noad_type_limits) 2368 && ((list_ptr(x) != null) 2369 && (type(list_ptr(x)) == glyph_node) 2370 && is_new_mathfont(font(list_ptr(x))))) { 2371 width(x) -= delta; /* remove another italic correction */ 2372 } 2373 2374 shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size); 2375 /* center vertically */ 2376 type(nucleus(q)) = sub_box_node; 2377 math_list(nucleus(q)) = x; 2378 } 2379 } else { 2380 delta = 0; 2381 } 2382 if (subtype(q) == op_noad_type_limits) { 2383 /* The following program builds a vlist box |v| for displayed limits. The 2384 width of the box is not affected by the fact that the limits may be skewed. */ 2385 x = clean_box(supscr(q), sup_style(cur_style), cur_style); 2386 y = clean_box(nucleus(q), cur_style, cur_style); 2387 z = clean_box(subscr(q), sub_style(cur_style), cur_style); 2388 v = new_null_box(); 2389 reset_attributes(v, node_attr(q)); 2390 type(v) = vlist_node; 2391 width(v) = width(y); 2392 if (width(x) > width(v)) 2393 width(v) = width(x); 2394 if (width(z) > width(v)) 2395 width(v) = width(z); 2396 x = rebox(x, width(v)); 2397 y = rebox(y, width(v)); 2398 z = rebox(z, width(v)); 2399 shift_amount(x) = half(delta); 2400 shift_amount(z) = -shift_amount(x); 2401 height(v) = height(y); 2402 depth(v) = depth(y); 2403 /* Attach the limits to |y| and adjust |height(v)|, |depth(v)| to 2404 account for their presence */ 2405 /* We use |shift_up| and |shift_down| in the following program for the 2406 amount of glue between the displayed operator |y| and its limits |x| and 2407 |z|. The vlist inside box |v| will consist of |x| followed by |y| followed 2408 by |z|, with kern nodes for the spaces between and around them. */ 2409 if (supscr(q) == null) { 2410 list_ptr(x) = null; 2411 flush_node(x); 2412 list_ptr(v) = y; 2413 } else { 2414 shift_up = limit_above_bgap(cur_style) - depth(x); 2415 if (shift_up < limit_above_vgap(cur_style)) 2416 shift_up = limit_above_vgap(cur_style); 2417 p = new_kern(shift_up); 2418 reset_attributes(p, node_attr(q)); 2419 couple_nodes(p,y); 2420 couple_nodes(x,p); 2421 p = new_kern(limit_above_kern(cur_style)); 2422 reset_attributes(p, node_attr(q)); 2423 couple_nodes(p,x); 2424 list_ptr(v) = p; 2425 height(v) = 2426 height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + 2427 shift_up; 2428 } 2429 if (subscr(q) == null) { 2430 list_ptr(z) = null; 2431 flush_node(z); 2432 } else { 2433 shift_down = limit_below_bgap(cur_style) - height(z); 2434 if (shift_down < limit_below_vgap(cur_style)) 2435 shift_down = limit_below_vgap(cur_style); 2436 p = new_kern(shift_down); 2437 reset_attributes(p, node_attr(q)); 2438 couple_nodes(y,p); 2439 couple_nodes(p,z); 2440 p = new_kern(limit_below_kern(cur_style)); 2441 reset_attributes(p, node_attr(q)); 2442 couple_nodes(z,p); 2443 depth(v) = 2444 depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + 2445 shift_down; 2446 } 2447 if (subscr(q) != null) { 2448 math_list(subscr(q)) = null; 2449 flush_node(subscr(q)); 2450 subscr(q) = null; 2451 } 2452 if (supscr(q) != null) { 2453 math_list(supscr(q)) = null; 2454 flush_node(supscr(q)); 2455 supscr(q) = null; 2456 } 2457 assign_new_hlist(q, v); 2458 } 2459 return delta; 2460} 2461 2462@ A ligature found in a math formula does not create a ligature, because 2463there is no question of hyphenation afterwards; the ligature will simply be 2464stored in an ordinary |glyph_node|, after residing in an |ord_noad|. 2465 2466The |type| is converted to |math_text_char| here if we would not want to 2467apply an italic correction to the current character unless it belongs 2468to a math font (i.e., a font with |space=0|). 2469 2470No boundary characters enter into these ligatures. 2471 2472@c 2473static void make_ord(pointer q) 2474{ 2475 int a; /* the left-side character for lig/kern testing */ 2476 pointer p, r, s; /* temporary registers for list manipulation */ 2477 scaled k; /* a kern */ 2478 liginfo lig; /* a ligature */ 2479 RESTART: 2480 if (subscr(q) == null && 2481 supscr(q) == null && type(nucleus(q)) == math_char_node) { 2482 p = vlink(q); 2483 if ((p != null) && 2484 (type(p) == simple_noad) && 2485 (subtype(p) <= punct_noad_type) && 2486 (type(nucleus(p)) == math_char_node) && 2487 (math_fam(nucleus(p)) == math_fam(nucleus(q)))) { 2488 type(nucleus(q)) = math_text_char_node; 2489 fetch(nucleus(q)); 2490 a = cur_c; 2491 if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) { 2492 cur_c = math_character(nucleus(p)); 2493 /* If character |a| has a kern with |cur_c|, attach 2494 the kern after~|q|; or if it has a ligature with |cur_c|, combine 2495 noads |q| and~|p| appropriately; then |return| if the cursor has 2496 moved past a noad, or |goto restart| */ 2497 2498 /* Note that a ligature between an |ord_noad| and another kind of noad 2499 is replaced by an |ord_noad|, when the two noads collapse into one. 2500 But we could make a parenthesis (say) change shape when it follows 2501 certain letters. Presumably a font designer will define such 2502 ligatures only when this convention makes sense. */ 2503 2504 if (disable_lig == 0 && has_lig(cur_f, a)) { 2505 lig = get_ligature(cur_f, a, cur_c); 2506 if (is_valid_ligature(lig)) { 2507 check_interrupt(); /* allow a way out of infinite ligature loop */ 2508 switch (lig_type(lig)) { 2509 case 1: 2510 case 5: 2511 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:\char`\|}, \.{=:\char`\|>} */ 2512 break; 2513 case 2: 2514 case 6: 2515 math_character(nucleus(p)) = lig_replacement(lig); /* \.{\char`\|=:}, \.{\char`\|=:>} */ 2516 break; 2517 case 3: 2518 case 7: 2519 case 11: 2520 r = new_noad(); /* \.{\char`\|=:\char`\|}, \.{\char`\|=:\char`\|>}, \.{\char`\|=:\char`\|>>} */ 2521 reset_attributes(r, node_attr(q)); 2522 s = new_node(math_char_node, 0); 2523 reset_attributes(s, node_attr(q)); 2524 nucleus(r) = s; 2525 math_character(nucleus(r)) = lig_replacement(lig); 2526 math_fam(nucleus(r)) = math_fam(nucleus(q)); 2527 couple_nodes(q,r); 2528 couple_nodes(r,p); 2529 if (lig_type(lig) < 11) 2530 type(nucleus(r)) = math_char_node; 2531 else 2532 type(nucleus(r)) = math_text_char_node; /* prevent combination */ 2533 break; 2534 default: 2535 try_couple_nodes(q,vlink(p)); 2536 math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */ 2537 s = math_clone(subscr(p)); 2538 subscr(q) = s; 2539 s = math_clone(supscr(p)); 2540 supscr(q) = s; 2541 math_reset(subscr(p)); /* just in case */ 2542 math_reset(supscr(p)); 2543 flush_node(p); 2544 break; 2545 } 2546 if (lig_type(lig) > 3) 2547 return; 2548 type(nucleus(q)) = math_char_node; 2549 goto RESTART; 2550 } 2551 } 2552 if (disable_kern == 0 && has_kern(cur_f, a)) { 2553 k = get_kern(cur_f, a, cur_c); /* todo: should this use mathkerns? */ 2554 if (k != 0) { 2555 p = new_kern(k); 2556 reset_attributes(p, node_attr(q)); 2557 couple_nodes(p,vlink(q)); 2558 couple_nodes(q,p); 2559 return; 2560 } 2561 } 2562 } 2563 } 2564 } 2565} 2566 2567@ If the fonts for the left and right bits of a mathkern are not 2568both new-style fonts, then return a sentinel value meaning: 2569please use old-style italic correction placement 2570 2571@c 2572#define MATH_KERN_NOT_FOUND 0x7FFFFFFF 2573 2574@ This function tries to find the kern needed for proper cut-ins. 2575 The left side doesn't move, but the right side does, so the first 2576 order of business is to create a staggered fence line on the 2577 left side of the right character. 2578 2579 The microsoft spec says that there are four quadrants, but the 2580 actual images say 2581 2582@c 2583static scaled math_kern_at(internal_font_number f, int c, int side, int v) 2584{ 2585 int h, k, numkerns; 2586 scaled *kerns_heights; 2587 scaled kern = 0; 2588 charinfo *co = char_info(f, c); /* known to exist */ 2589 numkerns = get_charinfo_math_kerns(co, side); 2590#ifdef DEBUG 2591 fprintf(stderr, " entries = %d, height = %d\n", numkerns, v); 2592#endif 2593 if (numkerns == 0) 2594 return kern; 2595 if (side == top_left_kern) { 2596 kerns_heights = co->top_left_math_kern_array; 2597 } else if (side == bottom_left_kern) { 2598 kerns_heights = co->bottom_left_math_kern_array; 2599 } else if (side == top_right_kern) { 2600 kerns_heights = co->top_right_math_kern_array; 2601 } else if (side == bottom_right_kern) { 2602 kerns_heights = co->bottom_right_math_kern_array; 2603 } else { 2604 confusion("math_kern_at"); 2605 kerns_heights = NULL; /* not reached */ 2606 } 2607#ifdef DEBUG 2608 fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]); 2609#endif 2610 if (v < kerns_heights[0]) 2611 return kerns_heights[1]; 2612 for (k = 0; k < numkerns; k++) { 2613 h = kerns_heights[(k * 2)]; 2614 kern = kerns_heights[(k * 2) + 1]; 2615#ifdef DEBUG 2616 if (k > 0) 2617 fprintf(stderr, " entry %d: %d,%d\n", k, h, kern); 2618#endif 2619 if (h > v) { 2620 return kern; 2621 } 2622 } 2623 return kern; 2624} 2625 2626@ @c 2627static scaled 2628find_math_kern(internal_font_number l_f, int l_c, 2629 internal_font_number r_f, int r_c, int cmd, scaled shift) 2630{ 2631 scaled corr_height_top = 0, corr_height_bot = 0; 2632 scaled krn_l = 0, krn_r = 0, krn = 0; 2633 if ((!is_new_mathfont(l_f)) || (!is_new_mathfont(r_f)) 2634 || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c))) 2635 return MATH_KERN_NOT_FOUND; 2636 2637 if (cmd == sup_mark_cmd) { 2638 corr_height_top = char_height(l_f, l_c); 2639 corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */ 2640 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top); 2641 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top); 2642#ifdef DEBUG 2643 fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, 2644 shift); 2645#endif 2646 krn = (krn_l + krn_r); 2647 krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot); 2648 krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot); 2649#ifdef DEBUG 2650 fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r); 2651#endif 2652 if ((krn_l + krn_r) < krn) 2653 krn = (krn_l + krn_r); 2654 return (krn); 2655 2656 } else if (cmd == sub_mark_cmd) { 2657 corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */ 2658 corr_height_bot = -char_depth(l_f, l_c); 2659 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top); 2660 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top); 2661#ifdef DEBUG 2662 fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r); 2663#endif 2664 krn = (krn_l + krn_r); 2665 krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot); 2666 krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot); 2667#ifdef DEBUG 2668 fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r); 2669#endif 2670 if ((krn_l + krn_r) < krn) 2671 krn = (krn_l + krn_r); 2672 return (krn); 2673 2674 } else { 2675 confusion("find_math_kern"); 2676 } 2677 return 0; /* not reached */ 2678} 2679 2680@ just a small helper 2681@c 2682static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) 2683{ 2684 pointer y; 2685 pointer z = new_kern(delta2); 2686 if (new_hlist(q) == null) { /* this is somewhat weird */ 2687 new_hlist(q) = z; 2688 } else { 2689 y = new_hlist(q); 2690 while (vlink(y) != null) 2691 y = vlink(y); 2692 couple_nodes(y,z); 2693 } 2694 return new_hlist(q); 2695} 2696 2697@ 2698@c 2699#ifdef DEBUG 2700void dump_simple_field(pointer q) 2701{ 2702 pointer p; 2703 printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q)); 2704 switch (type(q)) { 2705 case math_char_node: 2706 printf("mathchar "); 2707 break; 2708 case math_text_char_node: 2709 printf("texchar "); 2710 break; 2711 case sub_box_node: 2712 printf("box "); 2713 break; 2714 case sub_mlist_node: 2715 printf("mlist "); 2716 p = math_list(q); 2717 while (p != null) { 2718 dump_simple_field(p); 2719 p = vlink(p); 2720 } 2721 break; 2722 } 2723} 2724 2725 2726void dump_simple_node(pointer q) 2727{ 2728 printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q)); 2729 printf("nucleus: "); 2730 dump_simple_field(nucleus(q)); 2731 printf("\n"); 2732 printf("sub: "); 2733 dump_simple_field(subscr(q)); 2734 printf("\n"); 2735 printf("sup: "); 2736 dump_simple_field(supscr(q)); 2737 printf("\n\n"); 2738} 2739#endif 2740 2741@ The purpose of |make_scripts(q,it)| is to attach the subscript and/or 2742superscript of noad |q| to the list that starts at |new_hlist(q)|, 2743given that subscript and superscript aren't both empty. The superscript 2744will be horizontally shifted over |delta1|, the subscript over |delta2|. 2745 2746We set |shift_down| and |shift_up| to the minimum amounts to shift the 2747baseline of subscripts and superscripts based on the given nucleus. 2748 2749Note: We need to look at a character but also at the first one in a sub list 2750and there we ignore leading kerns and glue. Elsewhere is code that removes 2751kerns assuming that is italic correction. The heuristics are unreliable for 2752the new fonts so eventualy there will be an option to ignore such corrections. 2753 2754@ @c 2755#define analyze_script(init,su_n,su_f,su_c) do { \ 2756 su_n = init; \ 2757 if (su_n != null) { \ 2758 if (type(su_n) == sub_mlist_node) { \ 2759 su_n = math_list(su_n); \ 2760 if (su_n != null) { \ 2761 while (su_n) { \ 2762 if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\ 2763 su_n = vlink(su_n); \ 2764 } else if (type(su_n) == simple_noad) { \ 2765 su_n = nucleus(su_n); \ 2766 if (type(su_n) != math_char_node) { \ 2767 su_n = null; \ 2768 } \ 2769 break; \ 2770 } else { \ 2771 su_n = null; \ 2772 break; \ 2773 } \ 2774 } \ 2775 } \ 2776 } \ 2777 if (su_n != null) { \ 2778 fetch(su_n); \ 2779 if (char_exists(cur_f, cur_c)) { \ 2780 su_f = cur_f; \ 2781 su_c = cur_c; \ 2782 } else { \ 2783 su_n = null; \ 2784 } \ 2785 } \ 2786 } \ 2787 } while (0) 2788 2789 2790static void make_scripts(pointer q, pointer p, scaled it, int cur_style) 2791{ 2792 pointer x, y, z; /* temporary registers for box construction */ 2793 scaled shift_up, shift_down, clr; /* dimensions in the calculation */ 2794 scaled delta1, delta2; 2795 halfword sub_n, sup_n; 2796 internal_font_number sub_f, sup_f; 2797 int sub_c, sup_c; 2798 sub_n = null; 2799 sup_n = null; 2800 sub_f = 0; 2801 sup_f = 0; 2802 sub_c = 0; 2803 sup_c = 0; 2804 delta1 = it; 2805 delta2 = 0; 2806 2807#ifdef DEBUG 2808 printf("it: %d\n", it); 2809 dump_simple_node(q); 2810 printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p)); 2811#endif 2812 switch (type(nucleus(q))) { 2813 case math_char_node: 2814 case math_text_char_node: 2815 if ((subscr(q) == null) && (delta1 != 0)) { 2816 x = new_kern(delta1); 2817 reset_attributes(x, node_attr(nucleus(q))); 2818 couple_nodes(p,x); 2819 delta1 = 0; 2820 } 2821 } 2822 assign_new_hlist(q, p); 2823 if (is_char_node(p)) { 2824 shift_up = 0; 2825 shift_down = 0; 2826 } else { 2827 z = hpack(p, 0, additional, -1); 2828 shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */ 2829 shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */ 2830 list_ptr(z) = null; 2831 flush_node(z); 2832 } 2833 2834 if (is_char_node(p)) { 2835 /* we look at the subscript character (_i) or first character in a list (_{ij}) */ 2836 analyze_script(subscr(q),sub_n,sub_f,sub_c); 2837 /* we look at the superscript character (^i) or first character in a list (^{ij}) */ 2838 analyze_script(supscr(q),sup_n,sup_f,sup_c); 2839 } 2840 2841 if (supscr(q) == null) { 2842 2843 /* Construct a subscript box |x| when there is no superscript */ 2844 /* When there is a subscript without a superscript, the top of the subscript 2845 should not exceed the baseline plus four-fifths of the x-height. */ 2846 x = clean_box(subscr(q), sub_style(cur_style), cur_style); 2847 width(x) = width(x) + space_after_script(cur_style); 2848 if (shift_down < sub_shift_down(cur_style)) 2849 shift_down = sub_shift_down(cur_style); 2850 clr = height(x) - sub_top_max(cur_style); 2851 if (shift_down < clr) 2852 shift_down = clr; 2853 shift_amount(x) = shift_down; 2854 2855 /* now find and correct for horizontal shift */ 2856 if (sub_n != null) { 2857 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); 2858 if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) { 2859 p = attach_hkern_to_new_hlist(q, delta2); 2860 } 2861 } 2862 2863 } else { 2864 /* Construct a superscript box |x| */ 2865 /*The bottom of a superscript should never descend below the baseline plus 2866 one-fourth of the x-height. */ 2867 x = clean_box(supscr(q), sup_style(cur_style), cur_style); 2868 width(x) = width(x) + space_after_script(cur_style); 2869 clr = sup_shift_up(cur_style); 2870 if (shift_up < clr) 2871 shift_up = clr; 2872 clr = depth(x) + sup_bottom_min(cur_style); 2873 if (shift_up < clr) 2874 shift_up = clr; 2875 2876 if (subscr(q) == null) { 2877 shift_amount(x) = -shift_up; 2878 /* now find and correct for horizontal shift */ 2879 if (sup_n != null) { 2880 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); 2881 if (clr != MATH_KERN_NOT_FOUND && clr != 0) { 2882 p = attach_hkern_to_new_hlist(q, clr); 2883 } 2884 } 2885 } else { 2886 /* Construct a sub/superscript combination box |x|, with the 2887 superscript offset by |delta| */ 2888 /* When both subscript and superscript are present, the subscript must be 2889 separated from the superscript by at least four times |default_rule_thickness|. 2890 If this condition would be violated, the subscript moves down, after which 2891 both subscript and superscript move up so that the bottom of the superscript 2892 is at least as high as the baseline plus four-fifths of the x-height. */ 2893 2894 y = clean_box(subscr(q), sub_style(cur_style), cur_style); 2895 width(y) = width(y) + space_after_script(cur_style); 2896 if (shift_down < sub_sup_shift_down(cur_style)) 2897 shift_down = sub_sup_shift_down(cur_style); 2898 clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down)); 2899 if (clr > 0) { 2900 shift_down = shift_down + clr; 2901 clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x)); 2902 if (clr > 0) { 2903 shift_up = shift_up + clr; 2904 shift_down = shift_down - clr; 2905 } 2906 } 2907 /* now find and correct for horizontal shift */ 2908 if (sub_n != null) { 2909 delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); 2910 if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) { 2911 p = attach_hkern_to_new_hlist(q, delta2); 2912 } 2913 } 2914 /* now the horizontal shift for the superscript. */ 2915 /* the superscript is also to be shifted by |delta1| (the italic correction) */ 2916 clr = MATH_KERN_NOT_FOUND; 2917 if (sup_n != null) { 2918 clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); 2919 } 2920 2921 if (delta2 == MATH_KERN_NOT_FOUND) 2922 delta2 = 0; 2923 if (clr != MATH_KERN_NOT_FOUND) { 2924 shift_amount(x) = clr + delta1 - delta2; 2925 } else { 2926 shift_amount(x) = delta1 - delta2; 2927 } 2928 p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); 2929 reset_attributes(p, node_attr(q)); 2930 couple_nodes(x,p); 2931 couple_nodes(p,y); 2932 x = vpackage(x, 0, additional, max_dimen, math_direction); 2933 reset_attributes(x, node_attr(q)); 2934 shift_amount(x) = shift_down; 2935 } 2936 } 2937 2938 2939 if (new_hlist(q) == null) { 2940 new_hlist(q) = x; 2941 } else { 2942 p = new_hlist(q); 2943 while (vlink(p) != null) 2944 p = vlink(p); 2945 couple_nodes(p,x); 2946 } 2947 if (subscr(q) != null) { 2948 math_list(subscr(q)) = null; 2949 flush_node(subscr(q)); 2950 subscr(q) = null; 2951 } 2952 if (supscr(q) != null) { 2953 math_list(supscr(q)) = null; 2954 flush_node(supscr(q)); 2955 supscr(q) = null; 2956 } 2957 2958} 2959 2960@ The |make_left_right| function constructs a left or right delimiter of 2961the required size and returns the value |open_noad| or |close_noad|. The 2962|left_noad_side| and |right_noad_side| will both be based on the original |style|, 2963so they will have consistent sizes. 2964 2965@c 2966static small_number make_left_right(pointer q, int style, scaled max_d, 2967 scaled max_hv) 2968{ 2969 scaled delta, delta1, delta2; /* dimensions used in the calculation */ 2970 pointer tmp; 2971 setup_cur_size(style); 2972 delta2 = max_d + math_axis(cur_size); 2973 delta1 = max_hv + max_d - delta2; 2974 if (delta2 > delta1) 2975 delta1 = delta2; /* |delta1| is max distance from axis */ 2976 delta = (delta1 / 500) * delimiter_factor; 2977 delta2 = delta1 + delta1 - delimiter_shortfall; 2978 if (delta < delta2) 2979 delta = delta2; 2980 tmp = var_delimiter(delimiter(q), cur_size, delta, NULL, style); 2981 delimiter(q) = null; 2982 assign_new_hlist(q, tmp); 2983 if (subtype(q) == left_noad_side) 2984 return open_noad_type; 2985 else 2986 return close_noad_type; 2987} 2988 2989@ @c 2990#define TEXT_STYLES(A,B) do { \ 2991 def_math_param(A,display_style,(B),level_one); \ 2992 def_math_param(A,cramped_display_style,(B),level_one); \ 2993 def_math_param(A,text_style,(B),level_one); \ 2994 def_math_param(A,cramped_text_style,(B),level_one); \ 2995 } while (0) 2996 2997#define SCRIPT_STYLES(A,B) do { \ 2998 def_math_param(A,script_style,(B),level_one); \ 2999 def_math_param(A,cramped_script_style,(B),level_one); \ 3000 def_math_param(A,script_script_style,(B),level_one); \ 3001 def_math_param(A,cramped_script_script_style,(B),level_one); \ 3002 } while (0) 3003 3004#define ALL_STYLES(A,B) do { \ 3005 TEXT_STYLES(A,(B)); \ 3006 SCRIPT_STYLES(A,(B)); \ 3007 } while (0) 3008 3009#define SPLIT_STYLES(A,B,C) do { \ 3010 TEXT_STYLES(A,(B)); \ 3011 SCRIPT_STYLES(A,(C)); \ 3012 } while (0) 3013 3014 3015void initialize_math_spacing(void) 3016{ 3017 /* *INDENT-OFF* */ 3018 ALL_STYLES (math_param_ord_ord_spacing, 0); 3019 ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code); 3020 SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0); 3021 SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0); 3022 ALL_STYLES (math_param_ord_open_spacing, 0); 3023 ALL_STYLES (math_param_ord_close_spacing, 0); 3024 ALL_STYLES (math_param_ord_punct_spacing, 0); 3025 SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0); 3026 3027 ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code); 3028 ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code); 3029 ALL_STYLES (math_param_op_bin_spacing, 0); 3030 SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0); 3031 ALL_STYLES (math_param_op_open_spacing, 0); 3032 ALL_STYLES (math_param_op_close_spacing, 0); 3033 ALL_STYLES (math_param_op_punct_spacing, 0); 3034 SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0); 3035 3036 SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0); 3037 SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0); 3038 ALL_STYLES (math_param_bin_bin_spacing, 0); 3039 ALL_STYLES (math_param_bin_rel_spacing, 0); 3040 SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0); 3041 ALL_STYLES (math_param_bin_close_spacing, 0); 3042 ALL_STYLES (math_param_bin_punct_spacing, 0); 3043 SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0); 3044 3045 SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0); 3046 SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0); 3047 ALL_STYLES (math_param_rel_bin_spacing, 0); 3048 ALL_STYLES (math_param_rel_rel_spacing, 0); 3049 SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0); 3050 ALL_STYLES (math_param_rel_close_spacing, 0); 3051 ALL_STYLES (math_param_rel_punct_spacing, 0); 3052 SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0); 3053 3054 ALL_STYLES (math_param_open_ord_spacing, 0); 3055 ALL_STYLES (math_param_open_op_spacing, 0); 3056 ALL_STYLES (math_param_open_bin_spacing, 0); 3057 ALL_STYLES (math_param_open_rel_spacing, 0); 3058 ALL_STYLES (math_param_open_open_spacing, 0); 3059 ALL_STYLES (math_param_open_close_spacing, 0); 3060 ALL_STYLES (math_param_open_punct_spacing, 0); 3061 ALL_STYLES (math_param_open_inner_spacing, 0); 3062 3063 ALL_STYLES (math_param_close_ord_spacing, 0); 3064 ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code); 3065 SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0); 3066 SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0); 3067 ALL_STYLES (math_param_close_open_spacing, 0); 3068 ALL_STYLES (math_param_close_close_spacing, 0); 3069 ALL_STYLES (math_param_close_punct_spacing, 0); 3070 SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0); 3071 3072 SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0); 3073 SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0); 3074 ALL_STYLES (math_param_punct_bin_spacing, 0); 3075 SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0); 3076 SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0); 3077 SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0); 3078 SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0); 3079 SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0); 3080 3081 SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0); 3082 ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code); 3083 SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0); 3084 SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0); 3085 SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0); 3086 ALL_STYLES (math_param_inner_close_spacing, 0); 3087 SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0); 3088 SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0); 3089 /* *INDENT-ON* */ 3090} 3091 3092 3093@ @c 3094#define both_types(A,B) ((A)*16+(B)) 3095 3096static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) 3097{ 3098 int x = -1; 3099 pointer z = null; 3100 if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits) 3101 l_type = op_noad_type_normal; 3102 if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits) 3103 r_type = op_noad_type_normal; 3104 switch (both_types(l_type, r_type)) { 3105 /* *INDENT-OFF* */ 3106 case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break; 3107 case both_types(ord_noad_type, op_noad_type_normal ): x = get_math_param(math_param_ord_op_spacing,mstyle); break; 3108 case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break; 3109 case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break; 3110 case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break; 3111 case both_types(ord_noad_type, close_noad_type): x = get_math_param(math_param_ord_close_spacing,mstyle); break; 3112 case both_types(ord_noad_type, punct_noad_type): x = get_math_param(math_param_ord_punct_spacing,mstyle); break; 3113 case both_types(ord_noad_type, inner_noad_type): x = get_math_param(math_param_ord_inner_spacing,mstyle); break; 3114 case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break; 3115 case both_types(op_noad_type_normal, op_noad_type_normal ): x = get_math_param(math_param_op_op_spacing,mstyle); break; 3116#if 0 3117 case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break; 3118#endif 3119 case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break; 3120 case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break; 3121 case both_types(op_noad_type_normal, close_noad_type): x = get_math_param(math_param_op_close_spacing,mstyle); break; 3122 case both_types(op_noad_type_normal, punct_noad_type): x = get_math_param(math_param_op_punct_spacing,mstyle); break; 3123 case both_types(op_noad_type_normal, inner_noad_type): x = get_math_param(math_param_op_inner_spacing,mstyle); break; 3124 case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break; 3125 case both_types(bin_noad_type, op_noad_type_normal ): x = get_math_param(math_param_bin_op_spacing,mstyle); break; 3126#if 0 3127 case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break; 3128 case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break; 3129#endif 3130 case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break; 3131#if 0 3132 case both_types(bin_noad_type, close_noad_type): x = get_math_param(math_param_bin_close_spacing,mstyle); break; 3133 case both_types(bin_noad_type, punct_noad_type): x = get_math_param(math_param_bin_punct_spacing,mstyle); break; 3134#endif 3135 case both_types(bin_noad_type, inner_noad_type): x = get_math_param(math_param_bin_inner_spacing,mstyle); break; 3136 case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break; 3137 case both_types(rel_noad_type, op_noad_type_normal ): x = get_math_param(math_param_rel_op_spacing,mstyle); break; 3138#if 0 3139 case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break; 3140#endif 3141 case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break; 3142 case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break; 3143 case both_types(rel_noad_type, close_noad_type): x = get_math_param(math_param_rel_close_spacing,mstyle); break; 3144 case both_types(rel_noad_type, punct_noad_type): x = get_math_param(math_param_rel_punct_spacing,mstyle); break; 3145 case both_types(rel_noad_type, inner_noad_type): x = get_math_param(math_param_rel_inner_spacing,mstyle); break; 3146 case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break; 3147 case both_types(open_noad_type, op_noad_type_normal ): x = get_math_param(math_param_open_op_spacing,mstyle); break; 3148#if 0 3149 case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break; 3150#endif 3151 case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break; 3152 case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break; 3153 case both_types(open_noad_type, close_noad_type): x = get_math_param(math_param_open_close_spacing,mstyle); break; 3154 case both_types(open_noad_type, punct_noad_type): x = get_math_param(math_param_open_punct_spacing,mstyle); break; 3155 case both_types(open_noad_type, inner_noad_type): x = get_math_param(math_param_open_inner_spacing,mstyle); break; 3156 case both_types(close_noad_type,ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break; 3157 case both_types(close_noad_type,op_noad_type_normal ): x = get_math_param(math_param_close_op_spacing,mstyle); break; 3158 case both_types(close_noad_type,bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break; 3159 case both_types(close_noad_type,rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break; 3160 case both_types(close_noad_type,open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break; 3161 case both_types(close_noad_type,close_noad_type): x = get_math_param(math_param_close_close_spacing,mstyle); break; 3162 case both_types(close_noad_type,punct_noad_type): x = get_math_param(math_param_close_punct_spacing,mstyle); break; 3163 case both_types(close_noad_type,inner_noad_type): x = get_math_param(math_param_close_inner_spacing,mstyle); break; 3164 case both_types(punct_noad_type,ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break; 3165 case both_types(punct_noad_type,op_noad_type_normal ): x = get_math_param(math_param_punct_op_spacing,mstyle); break; 3166#if 0 3167 case both_types(punct_noad_type,bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break; 3168#endif 3169 case both_types(punct_noad_type,rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break; 3170 case both_types(punct_noad_type,open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break; 3171 case both_types(punct_noad_type,close_noad_type): x = get_math_param(math_param_punct_close_spacing,mstyle); break; 3172 case both_types(punct_noad_type,punct_noad_type): x = get_math_param(math_param_punct_punct_spacing,mstyle); break; 3173 case both_types(punct_noad_type,inner_noad_type): x = get_math_param(math_param_punct_inner_spacing,mstyle); break; 3174 case both_types(inner_noad_type,ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break; 3175 case both_types(inner_noad_type,op_noad_type_normal ): x = get_math_param(math_param_inner_op_spacing,mstyle); break; 3176 case both_types(inner_noad_type,bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break; 3177 case both_types(inner_noad_type,rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break; 3178 case both_types(inner_noad_type,open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break; 3179 case both_types(inner_noad_type,close_noad_type): x = get_math_param(math_param_inner_close_spacing,mstyle); break; 3180 case both_types(inner_noad_type,punct_noad_type): x = get_math_param(math_param_inner_punct_spacing,mstyle); break; 3181 case both_types(inner_noad_type,inner_noad_type): x = get_math_param(math_param_inner_inner_spacing,mstyle); break; 3182 /* *INDENT-ON* */ 3183 } 3184 if (x < 0) { 3185 confusion("mathspacing"); 3186 } 3187 if (x != 0) { 3188 pointer y; 3189 if (x <= thick_mu_skip_code) { /* trap thin/med/thick settings cf. old TeX */ 3190 y = math_glue(glue_par(x), mmu); 3191 z = new_glue(y); 3192 glue_ref_count(y) = null; 3193 subtype(z) = (quarterword) (x + 1); /* store a symbolic subtype */ 3194 } else { 3195 y = math_glue(x, mmu); 3196 z = new_glue(y); 3197 glue_ref_count(y) = null; 3198 } 3199 } 3200 return z; 3201} 3202 3203@ @c 3204static pointer check_nucleus_complexity(halfword q, scaled * delta, 3205 int cur_style) 3206{ 3207 pointer p = null; 3208 switch (type(nucleus(q))) { 3209 case math_char_node: 3210 case math_text_char_node: 3211 fetch(nucleus(q)); 3212 if (char_exists(cur_f, cur_c)) { 3213 *delta = char_italic(cur_f, cur_c); 3214 p = new_glyph(cur_f, cur_c); 3215 reset_attributes(p, node_attr(nucleus(q))); 3216 if ((is_new_mathfont(cur_f) && get_char_cat_code(cur_c) == 11) || 3217 (!is_new_mathfont(cur_f) && type(nucleus(q)) == math_text_char_node && space(cur_f)) != 0) { 3218 *delta = 0; /* no italic correction in mid-word of text font */ 3219 } 3220 if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) { 3221 pointer x = new_kern(*delta); 3222 reset_attributes(x, node_attr(nucleus(q))); 3223 couple_nodes(p,x); 3224 *delta = 0; 3225 } 3226 } 3227 break; 3228 case sub_box_node: 3229 p = math_list(nucleus(q)); 3230 break; 3231 case sub_mlist_node: 3232 mlist_to_hlist_args(math_list(nucleus(q)), cur_style, false); /* recursive call */ 3233 setup_cur_size(cur_style); 3234 p = hpack(vlink(temp_head), 0, additional, -1); 3235 reset_attributes(p, node_attr(nucleus(q))); 3236 break; 3237 default: 3238 confusion("mlist2"); /* this can't happen mlist2 */ 3239 } 3240 return p; 3241} 3242 3243@ Here is the overall plan of |mlist_to_hlist|, and the list of its 3244 local variables. 3245 3246@c 3247static void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) 3248{ 3249 pointer q; /* runs through the mlist */ 3250 pointer r; /* the most recent noad preceding |q| */ 3251 int style; 3252 int r_type; /* the |type| of noad |r|, or |op_noad| if |r=null| */ 3253 int r_subtype; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */ 3254 int t; /* the effective |type| of noad |q| during the second pass */ 3255 int t_subtype; /* the effective |subtype| of noad |q| during the second pass */ 3256 pointer p, x, y, z; /* temporary registers for list construction */ 3257 int pen; /* a penalty to be inserted */ 3258 scaled max_hl, max_d; /* maximum height and depth of the list translated so far */ 3259 scaled delta; /* italic correction offset for subscript and superscript */ 3260 scaled cur_mu; /* the math unit width corresponding to |cur_size| */ 3261 style = cur_style; /* tuck global parameter away as local variable */ 3262 q = mlist; 3263 r = null; 3264 r_type = simple_noad; 3265 r_subtype = op_noad_type_normal; 3266 max_hl = 0; 3267 max_d = 0; 3268 x = null; 3269 p = null; 3270 setup_cur_size(cur_style); 3271 cur_mu = x_over_n(get_math_quad(cur_size), 18); 3272 while (q != null) { 3273 /* We use the fact that no character nodes appear in an mlist, hence 3274 the field |type(q)| is always present. */ 3275 3276 /* One of the things we must do on the first pass is change a |bin_noad| to 3277 an |ord_noad| if the |bin_noad| is not in the context of a binary operator. 3278 The values of |r| and |r_type| make this fairly easy. */ 3279 RESWITCH: 3280 delta = 0; 3281 switch (type(q)) { 3282 case simple_noad: 3283 switch (subtype(q)) { 3284 case bin_noad_type: 3285 switch (r_type) { 3286 case simple_noad: 3287 switch (r_subtype) { 3288 case bin_noad_type: 3289 case op_noad_type_normal: 3290 case op_noad_type_limits: 3291 case op_noad_type_no_limits: 3292 case rel_noad_type: 3293 case open_noad_type: 3294 case punct_noad_type: 3295 subtype(q) = ord_noad_type; 3296 goto RESWITCH; 3297 break; 3298 } 3299 break; 3300 case fence_noad: 3301 if (r_subtype == left_noad_side) { 3302 subtype(q) = ord_noad_type; 3303 goto RESWITCH; 3304 } 3305 break; 3306 } 3307 break; 3308 case over_noad_type: 3309 make_over(q, cur_style); 3310 break; 3311 case under_noad_type: 3312 make_under(q, cur_style); 3313 break; 3314 case vcenter_noad_type: 3315 make_vcenter(q); 3316 break; 3317 case rel_noad_type: 3318 case close_noad_type: 3319 case punct_noad_type: 3320 if (r_type == simple_noad && r_subtype == bin_noad_type) { 3321 type(r) = simple_noad; 3322 subtype(r) = ord_noad_type; 3323 } 3324 break; 3325 case op_noad_type_normal: 3326 case op_noad_type_limits: 3327 case op_noad_type_no_limits: 3328 delta = make_op(q, cur_style); 3329 if (subtype(q) == op_noad_type_limits) 3330 goto CHECK_DIMENSIONS; 3331 break; 3332 case ord_noad_type: 3333 make_ord(q); 3334 break; 3335 case open_noad_type: 3336 case inner_noad_type: 3337 break; 3338 } 3339 break; 3340 case fence_noad: 3341 if (subtype(q) != left_noad_side) 3342 if (r_type == simple_noad && r_subtype == bin_noad_type) { 3343 type(r) = simple_noad; 3344 subtype(r) = ord_noad_type; 3345 } 3346 goto DONE_WITH_NOAD; 3347 break; 3348 case fraction_noad: 3349 make_fraction(q, cur_style); 3350 goto CHECK_DIMENSIONS; 3351 break; 3352 case radical_noad: 3353 if (subtype(q) == 4) 3354 make_under_delimiter(q, cur_style); 3355 else if (subtype(q) == 5) 3356 make_over_delimiter(q, cur_style); 3357 else if (subtype(q) == 6) 3358 make_delimiter_under(q, cur_style); 3359 else if (subtype(q) == 7) 3360 make_delimiter_over(q, cur_style); 3361 else 3362 make_radical(q, cur_style); 3363 break; 3364 case accent_noad: 3365 make_math_accent(q, cur_style); 3366 break; 3367 case style_node: 3368 cur_style = subtype(q); 3369 setup_cur_size(cur_style); 3370 /* HH-LS: was cur_mu = x_over_n(get_math_quad(cur_size), 18);*/ 3371 /* This is an old bug so the fix can influence outcome */ 3372 cur_mu = x_over_n(get_math_quad(cur_style), 18); 3373 goto DONE_WITH_NODE; 3374 break; 3375 case choice_node: 3376 switch (cur_style / 2) { 3377 case 0: 3378 choose_mlist(display_mlist); 3379 break; /* |display_style=0| */ 3380 case 1: 3381 choose_mlist(text_mlist); 3382 break; /* |text_style=2| */ 3383 case 2: 3384 choose_mlist(script_mlist); 3385 break; /* |script_style=4| */ 3386 case 3: 3387 choose_mlist(script_script_mlist); 3388 break; /* |script_script_style=6| */ 3389 } /* there are no other cases */ 3390 flush_node_list(display_mlist(q)); 3391 flush_node_list(text_mlist(q)); 3392 flush_node_list(script_mlist(q)); 3393 flush_node_list(script_script_mlist(q)); 3394 type(q) = style_node; 3395 subtype(q) = (quarterword) cur_style; 3396 if (p != null) { 3397 z = vlink(q); 3398 couple_nodes(q,p); 3399 while (vlink(p) != null) 3400 p = vlink(p); 3401 try_couple_nodes(p,z); 3402 } 3403 goto DONE_WITH_NODE; 3404 break; 3405 case ins_node: 3406 case mark_node: 3407 case adjust_node: 3408 case whatsit_node: 3409 case penalty_node: 3410 case disc_node: 3411 goto DONE_WITH_NODE; 3412 break; 3413 case rule_node: 3414 if (height(q) > max_hl) 3415 max_hl = height(q); 3416 if (depth(q) > max_d) 3417 max_d = depth(q); 3418 goto DONE_WITH_NODE; 3419 break; 3420 case glue_node: 3421 /* 3422 Conditional math glue (`\.{\\nonscript}') results in a |glue_node| 3423 pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case 3424 the node following will be eliminated if it is a glue or kern node and if the 3425 current size is different from |text_size|. Unconditional math glue 3426 (`\.{\\muskip}') is converted to normal glue by multiplying the dimensions 3427 by |cur_mu|. 3428 */ 3429 if (subtype(q) == mu_glue) { 3430 x = glue_ptr(q); 3431 y = math_glue(x, cur_mu); 3432 delete_glue_ref(x); 3433 glue_ptr(q) = y; 3434 subtype(q) = normal; 3435 } else if ((cur_size != text_size) 3436 && (subtype(q) == cond_math_glue)) { 3437 p = vlink(q); 3438 if (p != null) 3439 if ((type(p) == glue_node) || (type(p) == kern_node)) { 3440 couple_nodes(q,vlink(p)); 3441 vlink(p) = null; 3442 flush_node_list(p); 3443 } 3444 } 3445 goto DONE_WITH_NODE; 3446 break; 3447 case kern_node: 3448 math_kern(q, cur_mu); 3449 goto DONE_WITH_NODE; 3450 break; 3451 default: 3452 confusion("mlist1"); /* this can't happen mlist1 */ 3453 } 3454 /* When we get to the following part of the program, we have ``fallen through'' 3455 from cases that did not lead to |check_dimensions| or |done_with_noad| or 3456 |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be 3457 converted to an hlist, and whose subscripts and superscripts need to be 3458 appended if they are present. 3459 3460 If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount 3461 by which a superscript should be moved right with respect to a subscript 3462 when both are present. 3463 */ 3464 p = check_nucleus_complexity(q, &delta, cur_style); 3465 3466 if ((subscr(q) == null) && (supscr(q) == null)) { 3467 assign_new_hlist(q, p); 3468 } else { 3469 make_scripts(q, p, delta, cur_style); /* top, bottom */ 3470 } 3471 CHECK_DIMENSIONS: 3472 z = hpack(new_hlist(q), 0, additional, -1); 3473 if (height(z) > max_hl) 3474 max_hl = height(z); 3475 if (depth(z) > max_d) 3476 max_d = depth(z); 3477 list_ptr(z) = null; 3478 flush_node(z); /* only drop the \.{\\hbox} */ 3479 DONE_WITH_NOAD: 3480 r = q; 3481 r_type = type(r); 3482 r_subtype = subtype(r); 3483 if (r_type == fence_noad) { 3484 r_subtype = left_noad_side; 3485 cur_style = style; 3486 setup_cur_size(cur_style); 3487 cur_mu = x_over_n(get_math_quad(cur_size), 18); 3488 } 3489 DONE_WITH_NODE: 3490 q = vlink(q); 3491 } 3492 if (r_type == simple_noad && r_subtype == bin_noad_type) { 3493 type(r) = simple_noad; 3494 subtype(r) = ord_noad_type; 3495 } 3496 /* Make a second pass over the mlist, removing all noads and inserting the 3497 proper spacing and penalties */ 3498 3499 /* We have now tied up all the loose ends of the first pass of |mlist_to_hlist|. 3500 The second pass simply goes through and hooks everything together with the 3501 proper glue and penalties. It also handles the |fence_noad|s that 3502 might be present, since |max_hl| and |max_d| are now known. Variable |p| points 3503 to a node at the current end of the final hlist. 3504 */ 3505 p = temp_head; 3506 vlink(p) = null; 3507 q = mlist; 3508 r_type = 0; 3509 r_subtype = 0; 3510 cur_style = style; 3511 setup_cur_size(cur_style); 3512 cur_mu = x_over_n(get_math_quad(cur_size), 18); 3513 NEXT_NODE: 3514 while (q != null) { 3515 /* If node |q| is a style node, change the style and |goto delete_q|; 3516 otherwise if it is not a noad, put it into the hlist, 3517 advance |q|, and |goto done|; otherwise set |s| to the size 3518 of noad |q|, set |t| to the associated type (|ord_noad.. 3519 inner_noad|), and set |pen| to the associated penalty */ 3520 /* Just before doing the big |case| switch in the second pass, the program 3521 sets up default values so that most of the branches are short. */ 3522 t = simple_noad; 3523 t_subtype = ord_noad_type; 3524 pen = inf_penalty; 3525 switch (type(q)) { 3526 case simple_noad: 3527 t_subtype = subtype(q); 3528 switch (t_subtype) { 3529 case bin_noad_type: 3530 pen = bin_op_penalty; 3531 break; 3532 case rel_noad_type: 3533 pen = rel_penalty; 3534 break; 3535 case vcenter_noad_type: 3536 case over_noad_type: 3537 case under_noad_type: 3538 t_subtype = ord_noad_type; 3539 break; 3540 } 3541 case radical_noad: 3542 break; 3543 case accent_noad: 3544 break; 3545 case fraction_noad: 3546 t = simple_noad; 3547 t_subtype = inner_noad_type; 3548 break; 3549 case fence_noad: 3550 t_subtype = make_left_right(q, style, max_d, max_hl); 3551 break; 3552 case style_node: 3553 /* Change the current style and |goto delete_q| */ 3554 cur_style = subtype(q); 3555 setup_cur_size(cur_style); 3556 cur_mu = x_over_n(get_math_quad(cur_size), 18); 3557 goto DELETE_Q; 3558 break; 3559 case whatsit_node: 3560 case penalty_node: 3561 case rule_node: 3562 case disc_node: 3563 case adjust_node: 3564 case ins_node: 3565 case mark_node: 3566 case glue_node: 3567 case kern_node: 3568 couple_nodes(p,q); 3569 p = q; 3570 q = vlink(q); 3571 vlink(p) = null; 3572 goto NEXT_NODE; 3573 break; 3574 default: 3575 confusion("mlist3"); /* this can't happen mlist3 */ 3576 } 3577 /* Append inter-element spacing based on |r_type| and |t| */ 3578 if (r_type > 0) { /* not the first noad */ 3579 z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu); 3580 if (z != null) { 3581 reset_attributes(z, node_attr(p)); 3582 couple_nodes(p,z); 3583 p = z; 3584 } 3585 } 3586 3587 /* Append any |new_hlist| entries for |q|, and any appropriate penalties */ 3588 /* We insert a penalty node after the hlist entries of noad |q| if |pen| 3589 is not an ``infinite'' penalty, and if the node immediately following |q| 3590 is not a penalty node or a |rel_noad| or absent entirely. */ 3591 3592 if (new_hlist(q) != null) { 3593 couple_nodes(p,new_hlist(q)); 3594 do { 3595 p = vlink(p); 3596 } while (vlink(p) != null); 3597 } 3598 if (penalties && vlink(q) != null && pen < inf_penalty) { 3599 r_type = type(vlink(q)); 3600 r_subtype = subtype(vlink(q)); 3601 if (r_type != penalty_node 3602 && (r_type != simple_noad || r_subtype != rel_noad_type)) { 3603 z = new_penalty(pen); 3604 reset_attributes(z, node_attr(q)); 3605 couple_nodes(p,z); 3606 p = z; 3607 } 3608 } 3609 if (type(q) == fence_noad && subtype(q) == right_noad_side) { 3610 t = simple_noad; 3611 t_subtype = open_noad_type; 3612 } 3613 r_type = t; 3614 r_subtype = t_subtype; 3615 DELETE_Q: 3616 r = q; 3617 q = vlink(q); 3618 /* The m-to-hlist conversion takes place in-place, 3619 so the various dependant fields may not be freed 3620 (as would happen if |flush_node| was called). 3621 A low-level |free_node| is easier than attempting 3622 to nullify such dependant fields for all possible 3623 node and noad types. 3624 */ 3625 if (nodetype_has_attributes(type(r))) 3626 delete_attribute_ref(node_attr(r)); 3627 free_node(r, get_node_size(type(r), subtype(r))); 3628 } 3629} 3630 3631@ This used to be needed when |mlist_to_hlist| communicated via global 3632variables. 3633 3634@c 3635void mlist_to_hlist_args(pointer n, int w, boolean m) 3636{ 3637 mlist_to_hlist(n, m, w); 3638} 3639