-- TexMathB.mesa -- Tex routines for dealing with math mode -- last written by Doug Wyatt, December 5, 1979 2:23 AM DIRECTORY TexDefs: FROM "TexDefs", TexErrorDefs: FROM "TexErrorDefs", TexFontDefs: FROM "TexFontDefs", TexGlueDefs: FROM "TexGlueDefs", TexMathDefs: FROM "TexMathDefs", TexMathOpDefs: FROM "TexMathOpDefs", TexMemDefs: FROM "TexMemDefs", TexNoadDefs: FROM "TexNoadDefs", TexNodeDefs: FROM "TexNodeDefs", TexPackDefs: FROM "TexPackDefs", TexTableDefs: FROM "TexTableDefs"; TexMathB: PROGRAM IMPORTS TexErrorDefs,TexFontDefs,TexGlueDefs,TexMathOpDefs,TexMemDefs, TexNoadDefs,TexNodeDefs,TexPackDefs,TexTableDefs EXPORTS TexMathOpDefs = BEGIN OPEN TexNodeDefs,TexNoadDefs,TexDefs,TexMathOpDefs,TexMathDefs; -- **************************** -- Major math mode procedures -- **************************** -- these global variables are maintained by MlistToHlist as it -- runs through an mlist curstyle: MathStyle; -- the style used at noad q cursize: MathSize; -- FontSize[curstyle], the type size used at noad q drt: Dimn; -- the default rule thickness penalties: BOOLEAN; maxh,maxd: Dimn; -- the maximum height,depth of the mlist -- the table that governs inter-element mlist spacing SpaceArray: TYPE = ARRAY MType OF ARRAY MType OF MathSpace; spaceArray: POINTER TO SpaceArray←NIL; MathBInit: PROCEDURE = BEGIN spaceArray←TexMemDefs.AllocMem[SIZE[SpaceArray]]; spaceArray↑← [ [ no, thin, op, thick, no, no, no], -- Ord [ thin, thin, nil, thick, no, no, no], -- Op [ op, op, nil, nil, op, nil, nil], -- Bin [thick, thick, nil, no, thick, no, no], -- Rel [ no, no, nil, no, no, no, no], -- Open [ no, thin, op, thick, no, no, no], -- Close [ th, th, nil, thick, th, th, th] -- Punct ]; -- Ord, Op, Bin, Rel, Open, Close, Punct END; SpaceTable: PROCEDURE[a,b: MType] RETURNS[MathSpace] = --INLINE-- BEGIN RETURN[spaceArray[a][b]] END; MCharToFChar: PROCEDURE[mc: MChar, size: MathSize] RETURNS[FChar] = --INLINE-- BEGIN RETURN[[font: MathFontTable[size, mc.mfont], char: mc.char]]; END; WrongMFieldType: SIGNAL = CODE; -- this asserts that the given MField contains a box -- after debugging, it could be replaced by a LOOPHOLE BoxF: PROCEDURE[f: MFieldPtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN WITH ff:f SELECT FROM box => RETURN[ff.box]; ENDCASE => ERROR WrongMFieldType; END; -- this is used for vtop TopHeight: PROCEDURE[p: NodePtr] RETURNS[Dimn] = --INLINE-- BEGIN WHILE p#NIL DO WITH pp:p SELECT FROM char => RETURN[TexFontDefs.CharHt[pp.c]]; box => RETURN[pp.height]; rule => RETURN[pp.height]; glue => EXIT; ENDCASE; -- continue looping p←p.link; ENDLOOP; RETURN[0]; END; SetStyle: PROCEDURE[s: MathStyle] = --INLINE-- BEGIN cursize←FontSize[curstyle←s] END; DispStyle: PROCEDURE RETURNS[BOOLEAN] = --INLINE-- BEGIN RETURN[curstyle.style=disp] END; DoVctr: PROCEDURE[r: BoxNodePtr, vctr: VctrType] = --INLINE-- BEGIN SELECT vctr FROM vcenter => r.shiftamt←(r.height-r.depth)/2-MathPar[axisheight,cursize]; vtop => r.shiftamt←r.height-TopHeight[r.head]; ENDCASE => ERROR; -- invalid VctrType END; OpKern: PROCEDURE[b: BoxNodePtr] RETURNS[kern: Dimn] = --INLINE-- BEGIN p: CharNodePtr; -- now set kern nonzero if the operator in box b is a single -- character in the mathex font, having a nonzero MathKern IF (p←SingleCharBox[b])#NIL THEN BEGIN ch: FChar←p.c; -- ch is the character in the char Node IF ch.font=MathFontTable[text,ex] THEN RETURN[TexFontDefs.CharIc[ch]]; -- ch is in the mathex font END; RETURN[0]; END; CancelBin: PROCEDURE[r: CommonNoadPtr] = --INLINE-- BEGIN IF r#NIL AND r.mtype=Bin THEN r.mtype←Ord; END; NotBinContext: PROCEDURE[r: CommonNoadPtr] RETURNS[BOOLEAN] = --INLINE-- BEGIN RETURN[r=NIL OR (SELECT r.mtype FROM Bin,Op,Rel,Open,Punct => TRUE, ENDCASE => FALSE)]; END; DoSqrt: PROCEDURE[b: BoxNodePtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN radical: Delimiter=[small:[TRUE,[sy,160C]],large:[TRUE,[ex,160C]]]; r: BoxNodePtr; -- box containing the radical symbol clr: Dimn; -- extra blank space above operand list: NodeListPtr←InitNodeList[]; b←CleanBox[b]; IF DispStyle[] THEN clr←MathFontPar[xheight,cursize]/4+drt ELSE clr←(5*drt)/4; r←VarSymbol[radical, curstyle, b.height+b.depth+clr+drt]; -- Now r points to a box containing a radical sign of sufficient -- size. The upper left corner of the corresponding rule should -- touch the upper right corner of this box. We still need to -- raise or lower the box appropriately. r.shiftamt←(r.height-r.depth-b.height+b.depth-clr-drt)/2; -- Now top of box minus drt = b.height+clr plus half the excess r.link←OverBar[b, r.height-r.shiftamt, drt, (IF penalties THEN drt ELSE 2*drt)]; StoreNode[list,r]; RETURN[TexPackDefs.HBox[list]]; END; DoOver: PROCEDURE[b: BoxNodePtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN b←CleanBox[b]; RETURN[OverBar[b, b.height+3*drt, drt, (IF penalties THEN drt ELSE 2*drt)]]; END; DoUnder: PROCEDURE[b: BoxNodePtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN list: NodeListPtr←InitNodeList[]; StoreNode[list,b←CleanBox[b]]; -- first the operand StoreNode[list,MakeSpace[2*drt]]; -- then some glue StoreNode[list,FractionRule[drt]]; -- finally, the underbar RETURN[MakeBoxNode[dir: vlist, head: FinishNodeList[list], h: b.height, d: b.depth+(IF penalties THEN 4 ELSE 5)*drt, w: b.width]]; END; DoAccent: PROCEDURE[b: BoxNodePtr, accent: MChar] RETURNS[BoxNodePtr] = --INLINE-- BEGIN -- Slants are not taken into account in mathmode accents, since -- the sizes of math characters are already adjusted for slant p: BoxNodePtr; g: GlueNodePtr; h,t: Dimn; list: NodeListPtr←InitNodeList[]; p←BoxChar[accent, curstyle, FALSE].box; -- the accent char, in proper size b←CleanBox[b]; g←MakeGlueNode[TexGlueDefs.CommonGlue[lowerfill]]; -- make a vlist StoreNode[list,p]; -- the accent StoreNode[list,g]; -- some lowerfill glue StoreNode[list,b]; -- the accentee p.shiftamt←(b.width-p.width)/2; -- center the accent h←b.height; t←MathFontPar[xheight,cursize]; -- we will raise the accent by h-t p.width←0; -- the accent won't count in determining the new width RETURN[TexPackDefs.VPack[list, p.height+h-t]]; END; DoOpLimits: PROCEDURE[middle,upper,lower: BoxNodePtr, kern: Dimn] RETURNS[BoxNodePtr] = --INLINE-- BEGIN shiftup,shiftdown,maxw,h,d,extra: Dimn; list: NodeListPtr←InitNodeList[]; IF upper=NIL AND lower=NIL THEN RETURN[middle]; -- no limits to do IF middle=NIL THEN middle←NullBox[]; -- limits are to be centered above and below the operator -- (except modified by kern, the upper limit being shifted -- right and the lower limit shifted left by kern/2 each) IF upper#NIL THEN BEGIN upper←CleanBox[upper]; shiftup←MAX[MathExPar[bigopspacing3]-upper.depth, MathExPar[bigopspacing1]]; END ELSE BEGIN upper←NullBox[]; shiftup←0 END; IF lower#NIL THEN BEGIN lower←CleanBox[lower]; shiftdown←MAX[MathExPar[bigopspacing4]-lower.height, MathExPar[bigopspacing2]]; END ELSE BEGIN lower←NullBox[]; shiftdown←0 END; maxw←MAX[middle.width-kern, lower.width, upper.width]; upper←ReBox[upper,maxw,kern]; middle←ReBox[CleanBox[middle],maxw,kern/2]; lower←ReBox[lower,maxw,0]; extra←MathExPar[bigopspacing5]; -- extra space above and below limits h←middle.height-middle.shiftamt+upper.depth+upper.height; IF shiftup#0 THEN h←h+shiftup+extra; d←middle.depth+middle.shiftamt+lower.height+lower.depth; IF shiftdown#0 THEN d←d+shiftdown+extra; IF shiftup=0 THEN StoreNode[list,upper] ELSE BEGIN StoreNode[list,MakeSpace[extra]]; StoreNode[list,upper]; StoreNode[list,MakeSpace[shiftup]]; END; StoreNode[list,middle]; IF shiftdown=0 THEN StoreNode[list,lower] ELSE BEGIN StoreNode[list,MakeSpace[shiftdown]]; StoreNode[list,lower]; StoreNode[list,MakeSpace[extra]]; END; RETURN[MakeBoxNode[dir: vlist, head: FinishNodeList[list], h: h, d: d, w: maxw+kern]]; END; SupTable: PROCEDURE[s: MathStyle] RETURNS[MathParType] = --INLINE-- BEGIN RETURN[IF s.variant=atop THEN sup3 ELSE (IF s.style=disp THEN sup1 ELSE sup2)] END; DoScripts: PROCEDURE[b,bsup,bsub: BoxNodePtr, kern: Dimn, op: BOOLEAN] RETURNS[BoxNodePtr] = --INLINE-- BEGIN -- Process the sub/superscripts of noad q d: BoxNodePtr; shiftup,shiftdown: Dimn←0; IF bsup=NIL AND bsub=NIL THEN RETURN[b]; -- no super or subscript IF b#NIL AND (op OR SingleCharBox[b]=NIL OR b.shiftamt#0) THEN BEGIN -- the operand is not simply a character siz: MathSize←FontSize[ScrStyle[curstyle]]; shiftup←MAX[0, b.height-b.shiftamt-MathPar[supdrop,siz]]; shiftdown←MAX[0, b.depth+b.shiftamt+MathPar[subdrop,siz]]; END; -- shiftup and shiftdown are minimum amounts to shift baselines IF bsup=NIL THEN BEGIN -- subscript but no superscript d←CleanBox[bsub]; shiftdown←MAX[shiftdown, MathPar[sub1,cursize]]; -- make sure that the subscript doesn't get above the baseline -- plus four-fifths the xheight shiftdown←MAX[shiftdown, d.height-(4*MathFontPar[xheight,cursize])/5]; d.shiftamt←shiftdown; IF kern#0 THEN BEGIN list: NodeListPtr←InitNodeList[]; StoreNode[list,MakeSpace[-kern]]; StoreNode[list,d]; d←TexPackDefs.HBox[list]; END; END ELSE BEGIN -- superscript present shiftup←MAX[shiftup, MathPar[SupTable[curstyle],cursize]]; d←CleanBox[bsup]; -- make sure that the exponent doesn't get below the basline plus -- one-fourth the xheight shiftup←MAX[shiftup, MathFontPar[xheight,cursize]/4+d.depth]; IF bsub=NIL THEN d.shiftamt←-shiftup -- superscript but no subscript ELSE BEGIN -- both subscript and superscript c: BoxNodePtr←CleanBox[bsub]; delta: Dimn; list: NodeListPtr←InitNodeList[]; shiftdown←MAX[shiftdown, MathPar[sub2,cursize]]; delta←(d.depth+c.height+drt)-(shiftup+shiftdown); IF delta>0 THEN BEGIN -- adjust scripts to ensure minimum clearance drt shiftup←shiftup+delta/2; shiftdown←shiftdown+delta/2; END; StoreNode[list,d]; StoreNode[list,MakeGlueNode[TexGlueDefs.CommonGlue[fill]]]; StoreNode[list,c]; c.shiftamt←-kern; -- kern might be nonzero if np.ctype=op d←TexPackDefs.VPack[list, shiftdown+shiftup+d.height]; d.shiftamt←shiftdown; END; END; -- Now d points to a box representing the sub/superscripts -- and b is the box to attach it to IF b=NIL THEN RETURN[d] ELSE BEGIN list: NodeListPtr←InitNodeList[]; StoreNode[list,b]; StoreNode[list,d]; RETURN[TexPackDefs.HBox[list]]; END; END; DoAbove: PROCEDURE[np: AboveNoadPtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN num: BoxNodePtr←CleanBox[BoxF[@np.numerator]]; denom: BoxNodePtr←CleanBox[BoxF[@np.denominator]]; rt: Dimn←np.aboverule; -- rule thickness axis: Dimn←MathPar[axisheight,cursize]; pn,pd: MathParType; shiftup,shiftdown,maxw,s: Dimn; r: BoxNodePtr; ld,rd: BoxNodePtr; -- left and right delimiters dispstyle: BOOLEAN←DispStyle[]; list: NodeListPtr←InitNodeList[]; IF dispstyle THEN BEGIN pn←num1; pd←denom1 END ELSE BEGIN pn←IF rt=0 THEN num3 ELSE num2; pd←denom2 END; shiftup←MathPar[pn,cursize]; shiftdown←MathPar[pd,cursize]; -- Now axis is the distance from the base line to the center of the -- bar line, while shiftup and shiftdown are the standard baseline -- displacements for numerator and denominator in the current style. -- These standard displacements will be increased, if necessary, to -- avoid interference between numerator and denominator. -- Center the numerator and denominator by reboxing the smaller one maxw←MAX[num.width,denom.width]; IF num.width<maxw THEN num←ReBox[num,maxw,0]; IF denom.width<maxw THEN denom←ReBox[denom,maxw,0]; -- compute actual baseline displacements IF rt=0 THEN BEGIN -- the case of no fraction line -- clr is the minimum clearance desired between num and denom clr: Dimn←IF dispstyle THEN 7*drt ELSE 3*drt; delta: Dimn←(num.depth+denom.height+clr)-(shiftup+shiftdown); IF delta>0 THEN BEGIN shiftup←shiftup+delta/2; shiftdown←shiftdown+delta/2 END; END ELSE BEGIN -- the case of a fraction line -- clr is the min clearance desired between num, denom, and rule clr: Dimn←IF dispstyle THEN 3*rt ELSE rt; delta1,delta2: Dimn; -- possible additions to shiftup, shiftdown delta1←(num.depth+clr+rt/2)-(shiftup-axis); delta2←(denom.height+clr+rt/2)-(shiftdown+axis); SELECT TRUE FROM (delta1>0 AND delta2>0) => -- both get minimum clearance BEGIN shiftup←shiftup+delta1; shiftdown←shiftdown+delta2 END; -- otherwise, both get clearance of the good one (delta1>0) => shiftup←shiftup+delta1-delta2; (delta2>0) => shiftdown←shiftdown+delta2-delta1; ENDCASE; END; -- make the vlist box for the fraction StoreNode[list,num]; -- first comes the numerator IF rt=0 THEN BEGIN -- no rule inserted h: Dimn←shiftup+shiftdown-num.depth-denom.height; StoreNode[list,MakeSpace[h]]; -- glue inbetween num and denom END ELSE BEGIN h1: Dimn←shiftup-num.depth-rt/2-axis; h2: Dimn←shiftdown+axis-denom.height-rt/2; StoreNode[list,MakeSpace[h1]]; -- glue above fraction bar StoreNode[list,FractionRule[rt]]; -- the fraction bar StoreNode[list,MakeSpace[h2]]; -- glue below fraction bar END; StoreNode[list,denom]; -- last comes the denominator r←MakeBoxNode[dir: vlist, head: FinishNodeList[list], h: num.height+shiftup, d: denom.depth+shiftdown, w: maxw]; -- Finally, put the fraction into a box with its delimiters s←MathPar[(IF dispstyle THEN delim1 ELSE delim2),cursize]; ld←VarSymbol[np.ldelim, curstyle, s]; rd←VarSymbol[np.rdelim, curstyle, s]; ld.shiftamt←(ld.height-ld.depth)/2-axis; rd.shiftamt←(rd.height-rd.depth)/2-axis; list←InitNodeList[]; -- ldelim, fraction, rdelim StoreNode[list,ld]; StoreNode[list,r]; StoreNode[list,rd]; RETURN[TexPackDefs.HBox[list]]; END; -- This procedure does most of the mathematics formatting: It converts -- an mlist to an hlist, provided that the noads of the mlist contain no -- references to other mlists. (The procedure "evalmlist" below makes it -- possible to assume that this condition is satisfied.) If "penalties" -- is true, penalty nodes that indicate permissible breaks in the main -- mlist will be inserted MlistToHlist: PUBLIC PROCEDURE[p: NoadPtr, style: MathStyle, penlt: BOOLEAN] RETURNS[NodePtr] = BEGIN mlist: NoadListPtr←InitNoadList[]; -- the mlist being scanned hlist: NodeListPtr←InitNodeList[]; -- the hlist being formed -- We make two passes over the mlist. On the first pass, boxes are -- constructed for square roots and fractions, etc., and -- sub/superscripts are attached. A few other minor operations are -- also done (e.g., binnoads are changed to boxnoads if they don't -- appear in the context of binary operators, and the height and depth -- are calculated so that left and right delimiters of the appropriate -- size will be fabricated. The second pass gets rid of all noads, -- and hooks together the desired hlist including appropriate -- glue and penalty nodes IF p=NIL THEN RETURN[NIL]; -- avoid degenerate case mlist.link←p; drt←MathExPar[defaultrulethickness]; penalties←penlt; maxh←maxd←0; ScanMlist[mlist,style]; MakeHlist[mlist,hlist,style]; RETURN[FinishNodeList[hlist]]; END; -- the first pass of MlistToHlist ScanMlist: PROCEDURE[mlist: NoadListPtr, style: MathStyle] = --INLINE-- BEGIN q: NoadPtr←mlist; -- runs through the mlist prevq: NoadPtr←NIL; -- the noad preceding q (prevq.link=q) r: CommonNoadPtr←NIL; -- the previous CommonNoad -- On this first pass, boxes are constructed for square roots and -- fractions, etc., and sub/superscripts are attached. A few other -- minor operations are also done (e.g., binnoads are changed to -- boxnoads if they don't appear in the context of binary operators, -- and the height and depth are calculated so that left and right -- delimiters of the appropriate size will be fabricated. SetStyle[style]; FOR prevq←mlist,q UNTIL (q←prevq.link)=NIL DO -- the first pass WITH qq:q SELECT FROM common => BEGIN b,sup,sub: BoxNodePtr; WITH qqq:qq SELECT FROM scripted => BEGIN kern: Dimn←0; opflag,oplimits: BOOLEAN←FALSE; SELECT qq.mtype FROM Rel,Close,Punct => CancelBin[r]; Bin => IF NotBinContext[r] THEN qq.mtype←Ord; ENDCASE; b←BoxF[@qqq.operand]; WITH qqqq:qqq SELECT FROM none => NULL; vctr => DoVctr[b,qqqq.vctr]; op => BEGIN opflag←TRUE; kern←OpKern[b]; oplimits←kern=0; IF qqqq.limitswitch THEN oplimits←NOT oplimits; END; sqrt => b←DoSqrt[b]; over => b←DoOver[b]; under => b←DoUnder[b]; accent => b←DoAccent[b,qqqq.accent]; ENDCASE => ERROR; -- bad NType -- attach sub/superscripts if present sup←BoxF[@qqq.supscr]; sub←BoxF[@qqq.subscr]; IF oplimits AND DispStyle[] THEN b←DoOpLimits[b,sup,sub,kern] ELSE b←DoScripts[b,sup,sub,kern,opflag]; END; above => b←DoAbove[@qqq]; delim => b←NIL; ENDCASE; IF b#NIL THEN BEGIN qq.box←b; maxh←MAX[maxh, b.height-b.shiftamt]; maxd←MAX[maxd, b.depth+b.shiftamt]; END; r←@qq; END; node,disc => NULL; style => SetStyle[qq.s]; space => BEGIN -- substitute for this space Noad a node Noad for g -- g is a pointer to a glue node or NIL g: NodePtr←MakeMathGlue[qq.sp, cursize]; n: NoadPtr←MakeNodeNoad[g]; n.link←qq.link; prevq.link←q←n; END; ENDCASE => ERROR; -- invalid noad type ENDLOOP; -- end of first pass loop END; -- The second pass of MlistToHlist MakeHlist: PROCEDURE[mlist: NoadListPtr, hlist: NodeListPtr, style: MathStyle] = BEGIN q: NoadPtr; -- runs through the mlist nextq: NoadPtr; -- holds q.link while q is freed rtype,t: MType; -- previous and current node types, for spacing binpen: Penalty←TexTableDefs.CurPenalty[mbpen]; -- penalty for break at Bin relpen: Penalty←TexTableDefs.CurPenalty[mrpen]; -- penalty for break at Rel pen: Penalty; -- penalty for breaking after q break: BOOLEAN; -- TRUE if a break is allowed and pen has been set Appnd: PROCEDURE[x: NodePtr] = --INLINE-- BEGIN IF x#NIL THEN StoreNode[hlist,x] END; -- The second pass simply goes through and inserts the appropriate -- spacing, returning the noads to free storage. It also handles -- leftnoads and rightnoads, since we now know maxh and maxd rtype←Open; SetStyle[style]; FOR q←mlist.link,nextq WHILE q#NIL DO -- second pass loop break←FALSE; WITH qq:q SELECT FROM common => BEGIN WITH qqq:qq SELECT FROM scripted,above => NULL; delim => BEGIN axis: Dimn←MathPar[axisheight,FontSize[style]]; s: Dimn←MAX[maxh-axis,maxd+axis]; -- max distance from axis b: BoxNodePtr←VarSymbol[qqq.delim, curstyle, 2*s]; b.shiftamt←(b.height-b.depth)/2-axis; qq.box←b; END; ENDCASE => ERROR; -- bad CommonNoad type SELECT (t←qq.mtype) FROM Bin => BEGIN pen←binpen; break←TRUE END; Rel => BEGIN pen←relpen; break←TRUE END; ENDCASE; Appnd[InterElementGlue[rtype,t,cursize]]; Appnd[qq.box]; IF break AND penalties AND NOT PenaltyNodeFollows[@qq] THEN Appnd[MakePenaltyNode[pen]]; rtype←t; END; node => Appnd[qq.p]; disc => Appnd[MakeDiscNode[MCharToFChar[qq.c, cursize]]]; style => SetStyle[qq.s]; ENDCASE => ERROR; -- invalid Noad type nextq←q.link; ENDLOOP; END; EvalMlist: PUBLIC PROCEDURE[p: NoadPtr, style: MathStyle, penalties: BOOLEAN] RETURNS[NodePtr] = BEGIN -- This procedure converts the general mlist pointed to by p into -- an hlist, using the given style for the main mlist. The effect is -- like MlistToHlist except that the given mlist may have sub-mlists, or it -- might refer to math characters that aren't already in boxes. This is the -- procedure that controls the implicit styles in math formulas. Recursion -- occurs when evalmlist calls boxfield which calls evalmlist q: NoadPtr; curstyle: MathStyle←style; FOR q←p,q.link WHILE q#NIL DO WITH qq:q SELECT FROM -- we must remove non-box fields from noad q common => WITH qqq:qq SELECT FROM scripted => BEGIN scrstyle: MathStyle←ScrStyle[curstyle]; SELECT qqq.stype FROM sqrt,over => BoxField[@qqq.operand, UndStyle[curstyle], TRUE]; op => BoxOp[@qqq.operand,curstyle]; ENDCASE => BoxField[@qqq.operand, curstyle, ~KernScript[@qqq]]; -- The last parameter to BoxField essentially makes a "kerned" -- symbol when there is a subscript but no superscript. -- Otherwise the italic correction is included as the box is made BoxField[@qqq.supscr,scrstyle,TRUE]; BoxField[@qqq.subscr,UndStyle[scrstyle],TRUE]; END; above => BEGIN BoxField[@qqq.numerator,NumStyle[curstyle],TRUE]; BoxField[@qqq.denominator,DenomStyle[curstyle],TRUE]; END; delim => NULL; ENDCASE => ERROR; -- bad SType in common Noad node,disc,space => NULL; style => curstyle←qq.s; ENDCASE => ERROR; -- bad Noad type ENDLOOP; RETURN[MlistToHlist[p, style, penalties]]; END; PenaltyNodeFollows: PROCEDURE[p: NoadPtr] RETURNS[BOOLEAN] = --INLINE-- BEGIN q: NoadPtr←p.link; IF q#NIL THEN WITH qq:q SELECT FROM node => IF qq.p#NIL AND qq.p.type=penalty THEN RETURN[TRUE]; ENDCASE; RETURN[FALSE]; END; InterElementGlue: PROCEDURE[a,b: MType, size: MathSize] RETURNS[GlueNodePtr] = --INLINE-- BEGIN g: GlueNodePtr←NIL; quad: Dimn←MathFontPar[quad,size]; SELECT SpaceTable[a,b] FROM no => NULL; -- no space thin => g←MathGlue[thinGlue, quad]; th => IF size=text THEN g←MathGlue[thinGlue, quad]; thick => IF size=text THEN g←MathGlue[thickGlue, quad]; op => IF size=text THEN g←MathGlue[opGlue, quad]; ENDCASE => ERROR TexErrorDefs.Confusion; -- invalid SpaceTable entry RETURN[g]; END; NullMField: PROCEDURE[f: MFieldPtr] RETURNS[BOOLEAN] = --INLINE-- BEGIN WITH ff:f SELECT FROM box => RETURN[ff.box=NIL]; ENDCASE; RETURN[FALSE]; END; KernScript: PROCEDURE[np: ScriptedNoadPtr] RETURNS[BOOLEAN] = --INLINE-- BEGIN RETURN[NullMField[@np.supscr] AND NOT NullMField[@np.subscr]]; END; BoxOp: PROCEDURE[op: MFieldPtr, style: MathStyle] = --INLINE-- BEGIN -- check for a single character op in \mathex singlchrxop: BOOLEAN←FALSE; WITH ff:op SELECT FROM mchar => IF ff.mchar.mfont=ex THEN BEGIN singlchrxop←TRUE; IF style.style=disp THEN BEGIN OPEN TexFontDefs; ltype: LargerType; linfo: LargerInfo; [ltype,linfo]←NextLarger[MCharToFChar[ff.mchar,text]]; WITH linfo SELECT ltype FROM nextlarger => ff.mchar.char←next; -- use larger size if available ENDCASE; END; END; ENDCASE; BoxField[op, style, TRUE]; IF singlchrxop THEN WITH ff:op SELECT FROM box => BEGIN b: BoxNodePtr←ff.box; b.shiftamt←-MathPar[axisheight,FontSize[style]]-b.depth/2; -- shift the character so that its height above the axis -- exceeds its depth below the axis by the character height END; ENDCASE => ERROR WrongMFieldType; END; BoxField: PUBLIC PROCEDURE[f: MFieldPtr, style: MathStyle, corr: BOOLEAN] = BEGIN -- This procedure converts a noad field into the corresponding box. -- If corr is true, the italic correction occurs at the right of a -- single-character box. Recursion comes about when BoxField calls -- EvalMlist which calls BoxField. WITH ff:f SELECT FROM box => RETURN; -- nothing to do if already boxed mlist => f↑←[box[BoxNode[EvalMlist[ff.mlist, style, FALSE]]]]; mchar => f↑←[box[BoxChar[ff.mchar, style, corr].box]]; ENDCASE => ERROR; -- invalid MField tag END; BoxNode: PROCEDURE[q: NodePtr] RETURNS[BoxNodePtr] = --INLINE-- BEGIN list: NodeListPtr; IF q=NIL THEN RETURN[NIL]; WITH qq:q SELECT FROM box => IF qq.link=NIL THEN RETURN[@qq]; ENDCASE; list←InitNodeList[]; list.link←q; list.last←NIL; RETURN[TexPackDefs.HBox[list]]; END; -- initialization MathBInit; END.