-- TexVMode.mesa -- last written by Doug Wyatt, December 5, 1979 2:54 PM DIRECTORY TexAlignDefs: FROM "TexAlignDefs", TexDefs: FROM "TexDefs", TexErrorDefs: FROM "TexErrorDefs", TexGlueDefs: FROM "TexGlueDefs", TexIODefs: FROM "TexIODefs", TexJustifyDefs: FROM "TexJustifyDefs", TexMainDefs: FROM "TexMainDefs", TexNodeDefs: FROM "TexNodeDefs", TexPackDefs: FROM "TexPackDefs", TexSynDefs: FROM "TexSynDefs", TexTableDefs: FROM "TexTableDefs", TexTokenDefs: FROM "TexTokenDefs"; TexVMode: PROGRAM IMPORTS TexAlignDefs,TexErrorDefs,TexGlueDefs,TexIODefs,TexJustifyDefs, TexMainDefs,TexNodeDefs,TexPackDefs,TexSynDefs,TexTableDefs,TexTokenDefs EXPORTS TexMainDefs = BEGIN OPEN TexMainDefs,TexTableDefs,TexNodeDefs,TexSynDefs,TexDefs; -- ***************** -- * Vertical mode * -- ***************** VAppend: PUBLIC PROCEDURE[vh: VHeadPtr, b: BoxNodePtr] = BEGIN OPEN vh; -- append a box node to the current vlist -- the inter-line glue is also appended between boxes IF prevdepth#pflag THEN BEGIN OPEN TexGlueDefs; -- appending to a vlist with previous depth prevdepth p: GluePtr←GlueParam[baselineskip]; q: GluePtr←InterLineGlue[p.space-prevdepth-b.height, p]; StoreNode[vlist, MakeGlueNode[q]]; -- append the glue END; StoreNode[vlist, b]; -- append the box prevdepth←b.depth; -- and remember its depth END; VMode: PUBLIC PROCEDURE[vhead: VHeadPtr, restricted: BOOLEAN] = BEGIN OPEN vhead; CheckPriv: PROCEDURE = BEGIN IF restricted THEN SIGNAL FallThru END; Store: PROCEDURE[p: NodePtr] = BEGIN StoreNode[vlist, p] END; Append: PROCEDURE[b: BoxNodePtr] = INLINE BEGIN VAppend[vhead,b]; END; nlines,l: CARDINAL; begin: CARDINAL; width0,width1,indent0,indent1: Dimn; LineWidth: TexJustifyDefs.LineWidthProc = BEGIN RETURN[IF (nlines+line)<begin THEN width0 ELSE width1] END; LineIndent: TexJustifyDefs.LineWidthProc = BEGIN RETURN[IF (nlines+line)<begin THEN indent0 ELSE indent1] END; -- Note: FinishParagraph destroys hlist's listhead! FinishParagraph: PROCEDURE[hlist: NodeListPtr, penalt: BOOLEAN] RETURNS[lastwidth: Dimn] = BEGIN OPEN TexGlueDefs,TexJustifyDefs; parfillglue: GluePtr←CommonGlue[fil]; wPen: Penalty←CurPenalty[wpen]; bPen: Penalty←CurPenalty[bpen]; NewLine: NewLineProc = BEGIN OPEN info; -- justification by the page builder: insert the topinsert, botinsert, -- and eject nodes removed from the line by hpackaging, then check if -- there is any special penalty for breaking after this line pen: Penalty←0; linebox.shiftamt←LineIndent[l]; Append[linebox]; l←l+1; -- append the line AppendNodeList[vlist,inserts]; -- append any inserts IF widow AND (first OR penalt) THEN pen←pen+wPen; -- widow penalty IF broken THEN pen←pen+bPen; -- broken line penalty IF pen#0 THEN Store[MakePenaltyNode[pen]]; END; WITH pp:hlist.last SELECT FROM glue => BEGIN DelGlueLink[pp.g]; pp.g←parfillglue END; ENDCASE => StoreNode[hlist,MakeGlueNode[parfillglue]]; l←0; lastwidth←Justification[hlist, LineWidth, NewLine]; nlines←nlines+l; RETURN[lastwidth]; END; AppendDisplay: PROCEDURE[abovedisplaywidth: Dimn] = BEGIN OPEN TexPackDefs,TexGlueDefs; dlist: NodeListPtr←InitNodeList[]; b,eqnobox: BoxNodePtr; -- boxes containing the equation and eqno inserts: NodeListPtr←InitNodeList[]; flexsums: FlexSums; -- flex totals for the equation shr: Flex; w: Dimn; -- width of the equation dw: Dimn; -- desired line width nw: Dimn; -- width of equation number to append to the equation lmar: Dimn; -- width of left indent shift: Dimn; -- amount to shift equation right for centering quad: Dimn; -- quad width for current math fonts qd: Dimn; -- space for equation number plus quad width q1,q2: GluePtr; -- pointers to glue spec for above and below singleline: BOOLEAN←TRUE; CanShrink: PROCEDURE[shr: Flex, w: Dimn] RETURNS[BOOLEAN] = --INLINE-- BEGIN RETURN[FALSE]; -- ***** fix this ***** END; RePack: PROCEDURE[b: BoxNodePtr, w: Dimn] RETURNS[BoxNodePtr] = BEGIN list: NodeListPtr←MakeNodeList[b.head]; b.head←NIL; DsNode[b]; RETURN[HPack[list,w]]; END; eqnobox←GetDisplay[dlist]; -- scan a formula in display MMode quad←MathQuad[]; -- ignore empty display (probably was $$\halign{...}$$) IF dlist.link=NIL THEN BEGIN FreeNodeList[dlist]; RETURN END; b←HPackage[dlist,0,TRUE,inserts,@flexsums]; FreeNodeList[inserts]; w←b.width; -- determine the equations's natural width shr←DominantFlex[@flexsums[shr]]; -- remember total shrink lmar←LineIndent[0]; dw←LineWidth[0]; IF eqnobox=NIL THEN nw←qd←0 ELSE BEGIN nw←eqnobox.width; qd←nw+quad END; IF (w+qd)>dw THEN BEGIN -- the equation doesn't fit with its natural width -- we will squeeze it as best we can IF CanShrink[shr, w+qd-dw] THEN b←RePack[b, dw-qd] -- it will fit on one line ELSE BEGIN -- too big, drop equation number to separate line singleline←FALSE; IF w>dw THEN b←RePack[b, dw]; END; END; w←b.width; -- now we have an equation b of width w and a possible equation number -- eqnobox of width nw, and they are to be centered appropriately on a -- line of width dw. (If NOT singleline, the equation number will appear -- on a separate line below the display) nlines←nlines+3; -- treat as three lines output w.r.t. hanging indents shift←(dw-w)/2; -- prepare to center the equation on the line IF singleline AND shift<2*nw THEN shift←0; -- but put it flush left if -- centering would make it too close to the equation number -- At this point shift will be negative if the equation is too large; -- it will extend into the margins IF shift+lmar<=abovedisplaywidth THEN BEGIN -- for large formulas use dispskip glue above and below q1←q2←GlueParam[dispskip]; END ELSE BEGIN -- otherwise use dispaskip, dispbskip and delete a virtual line nlines←nlines-1; q1←GlueParam[dispaskip]; q2←GlueParam[dispbskip]; END; Store[MakeGlueNode[q1]]; IF singleline THEN BEGIN -- attach equation number hlist: NodeListPtr←InitNodeList[]; StoreNode[hlist, b]; StoreNode[hlist, MakeGlueNode[CommonGlue[fill]]]; StoreNode[hlist, eqnobox]; b←HPack[hlist, dw-shift]; -- eqno will be right-justified END; b.shiftamt←shift+lmar; Append[b]; IF singleline THEN Store[MakeGlueNode[q2]] -- put chosen glue after display ELSE BEGIN -- append the equation number on a separate line eqnobox.shiftamt←lmar+dw-eqnobox.width; Store[MakePenaltyNode[maxPenalty]]; -- no break may occur here Append[eqnobox]; END; END; DO GetNext[]; BEGIN ENABLE BEGIN FallThru => BEGIN SIGNAL CantDoThat[v, restricted]; CONTINUE END; Reswitch => RETRY; Continue => CONTINUE; END; WITH cc:curchar SELECT curcmd FROM lbrace => NewSaveLevel[simpleblock]; rbrace => SELECT SaveCode[] FROM simpleblock => UnSave[simpleblock]; -- just pop the savestack trueend => TrueEnd[]; -- skip over the \else part falseend => FalseEnd[]; -- skip over a spacer, if any bottomlevel => TexErrorDefs.Error["Too many }'s"]; alignentry,noalignend => EXIT; -- let the alignment handle this outputend => RETURN; -- end of \output routine topinsend,botinsend => RETURN; -- end of insertion endvcenter => RETURN; -- end of \vcenter or \vtop justend => RETURN; -- end of \vbox aligncode,mathcode,mathleft,mathblock,endscanmath => ERROR TexErrorDefs.Confusion; -- invalid endcode in VMode ENDCASE => ERROR; -- bad EndingCode mathbr,letter,otherchar,ascii,noindent,accent,nonmathletter,caseshift => BEGIN hlist: NodeListPtr←InitNodeList[]; hhead: HHead; abovedisplaywidth: Dimn; CheckPriv; -- beginning of a paragraph, must be in the page builder Store[MakeGlueNode[GlueParam[parskip]]]; -- inter-paragraph glue IF curcmd#noindent THEN BEGIN BackInput; -- put it back for HMode to scan StoreNode[hlist, IndentBox[DimnParam[parindent]]]; END; nlines←0; DO hhead←[hlist,sfOne]; HMode[@hhead, FALSE]; -- call the paragraph builder -- get current hangindent and hsize -- (they might have been changed by HMode) [begin,width0,width1,indent0,indent1]← HangVals[GlobalHangIndent[],DimnParam[hsize]]; SELECT curcmd FROM parend => EXIT; -- end of the paragraph mathbr => NULL; -- paragraph interrupted by math display ENDCASE => SIGNAL TexErrorDefs.Confusion; IF hlist.link=NIL THEN abovedisplaywidth←LAST[Dimn] ELSE -- output the paragraph so far BEGIN lastwidth: Dimn←FinishParagraph[hlist,FALSE]; abovedisplaywidth←lastwidth+2*MathQuad[]; hlist←InitNodeList[]; Store[MakePenaltyNode[CurPenalty[disppen]]]; -- penalty for break END; AppendDisplay[abovedisplaywidth]; -- ignore space after closing $$ GetNCTok; IF curcmd#spacer THEN BackInput; ENDLOOP; []←FinishParagraph[hlist,TRUE]; SetGlobalHangIndent[nullHang]; -- reset hanging indent END; tabmrk,carret => BEGIN CheckAlignment; SIGNAL TexErrorDefs.Confusion END; spacer,parend => NULL; -- ignore these in vmode endv => BEGIN CheckAlignment; IF SaveCode[]=alignentry THEN EXIT ELSE MissingBrace; -- will SIGNAL Reswitch END; font => SetFont[ScanFont[]]; innput => InputFile[]; stop => BEGIN lev: Lev; CheckPriv; AddToPage[vlist]; vlist←NIL; -- if the page is not empty, flush out everything waiting to be output FlushOutput[maxdeadcycles: 25]; IF (lev←CurLev[])>1 THEN BEGIN OPEN TexIODefs; Ps["\end occured on level "]; Wn[lev]; END; RETURN; -- return to MainControl END; fntfam => DoFntFam[cc.mfont]; hmove => Append[ScanMovedBox[cc.neg]]; leaders => Store[ScanLeaders[]]; halign => TexAlignDefs.HAlign[vhead,FALSE]; vskip => Store[SkipGlue[cc.gluetype]]; hrule => BEGIN Store[ScanRuleNode[]]; prevdepth←pflag END; box => Append[GetBox[cc.boxtype]]; topbotins => BEGIN CheckPriv; DoTopBotIns[cc.topbot, vlist, Store] END; mark => BEGIN CheckPriv; Store[MakeMarkNode[ScanToks[mark]]] END; hangindent => SetGlobalHangIndent[ScanHang[]]; penlty => Store[ScanPenltyNode[]]; eject => Store[MakeEjectNode[]]; ENDCASE => CommonCmd; END; IF NOT restricted THEN BEGIN AddToPage[vlist]; vlist←InitNodeList[] END; ENDLOOP; END; IndentBox: PROCEDURE[indent: Dimn] RETURNS[BoxNodePtr] = --INLINE-- BEGIN q: BoxNodePtr←NullBox[]; q.width←indent; q.altered←TRUE; RETURN[q]; END; outputroutine: TexTokenDefs.TokenListPtr←NIL; SetOutputRoutine: PUBLIC PROCEDURE[t: TexTokenDefs.TokenListPtr] = BEGIN IF outputroutine#NIL THEN TexTokenDefs.DelRCLink[outputroutine]; outputroutine←t; END; RunOutputRoutine: PUBLIC PROCEDURE RETURNS[BoxNodePtr] = BEGIN IF outputroutine=NIL THEN RETURN[Page[]] ELSE BEGIN vlist: NodeListPtr←InitNodeList[]; vhead: VHead←[vlist,pflag]; InsRCList[outputroutine]; NewSaveLevel[outputend]; VMode[@vhead, TRUE]; -- run the output routine in restricted VMode UnSave[outputend]; IF vlist.link=NIL THEN BEGIN FreeNodeList[vlist]; RETURN[NIL] END ELSE RETURN[TexPackDefs.VBox[vlist]]; END; END; END.