-- TexPage.mesa -- last written by Doug Wyatt, January 10, 1980 2:43 PM DIRECTORY TexDebugDefs: FROM "TexDebugDefs", TexDefs: FROM "TexDefs", TexErrorDefs: FROM "TexErrorDefs", TexGlueDefs: FROM "TexGlueDefs", TexIODefs: FROM "TexIODefs", TexMainDefs: FROM "TexMainDefs", TexNodeDefs: FROM "TexNodeDefs", TexOutputDefs: FROM "TexOutputDefs", TexPackDefs: FROM "TexPackDefs", TexTableDefs: FROM "TexTableDefs", TexTokenDefs: FROM "TexTokenDefs", InlineDefs: FROM "InlineDefs"; TexPage: PROGRAM IMPORTS TexDebugDefs,TexErrorDefs,TexGlueDefs,TexIODefs,TexMainDefs, TexNodeDefs,TexOutputDefs,TexPackDefs,TexTableDefs,TexTokenDefs, InlineDefs EXPORTS TexMainDefs = BEGIN OPEN TexMainDefs,TexNodeDefs,TexDefs; pagelist: NodeListPtr←InitNodeList[]; contriblist: NodeListPtr←InitNodeList[]; waitinglist: NodeListPtr←NIL; curbreak: NodePtr; curbadness: Penalty; pagesize: Dimn; pagedepthmax: Dimn; pagetopbl: Dimn; pageheight: Dimn; pagedepth: Dimn; pageflex: TexGlueDefs.FlexSums; topmark,firstmark: TexTokenDefs.TokenListPtr←NIL; savedpage: BoxNodePtr←NIL; savedpageno: INTEGER←0; outputdormant: BOOLEAN ← TRUE; deadcycles: CARDINAL←0; AddToPage: PUBLIC PROCEDURE[list: NodeListPtr] = BEGIN -- Now the page builder is in operation; it continues to work until -- the contribution list has been emptied. -- Note: list's listhead is destroyed by this procedure! AppendNodeList[contriblist,list]; UNTIL contriblist.link=NIL DO IF EmptyPage[] THEN InitPage[]; -- initialization of page building Contribute[]; ENDLOOP; END; penaltyLimit: Penalty=1000; -- a penalty>=penaltyLimit is considered infinite Contribute: PROCEDURE = --INLINE-- BEGIN p: NodePtr←contriblist.link; -- The page builder adds the first node of the contribution list to -- the current page, checking to see if it is a decent place to break, -- and/or calling EjectPage if the current page is full. AddHD: PROCEDURE[h,d: Dimn] = BEGIN pageheight←pageheight+pagedepth+h; IF d>pagedepthmax THEN BEGIN pageheight←pageheight+d-pagedepthmax; d←pagedepthmax END; pagedepth←d; END; WITH pp:p SELECT FROM box => BEGIN AddHD[pp.height, pp.depth]; IF EmptyPage[] AND pageheight<pagetopbl THEN BEGIN OPEN TexGlueDefs; -- Put glue at top of page to adjust first baseline q: GluePtr←InterLineGlue[pagetopbl-pageheight, CommonGlue[zero]]; pageheight←pagetopbl; StoreNode[pagelist, MakeGlueNode[q]]; END; END; rule => AddHD[pp.height, pp.depth]; glue => BEGIN OPEN TexGlueDefs; q: GluePtr; IF EmptyPage[] THEN GOTO Delete; -- delete glue at beginning of page WITH pagelist.last SELECT FROM box,rule => IF TestPageBreak[0] THEN GOTO Eject; -- make a page ENDCASE; q←pp.g; -- pointer to glue specification AddHD[q.space, 0]; SumFlex[q, @pageflex]; END; penalty => BEGIN n: Penalty; IF EmptyPage[] THEN GOTO Delete; -- delete penalty at beginning of page IF (n←pp.pts)<penaltyLimit AND TestPageBreak[n] THEN GOTO Eject; END; eject => BEGIN IF EmptyPage[] THEN GOTO Delete; -- Non-null page should be ejected -- make sure there's a place to break IF curbreak=NIL THEN curbreak←pagelist.last; []←TestPageBreak[-200]; -- *** check this GOTO Eject; END; ins => BEGIN OPEN TexGlueDefs; q: GluePtr←pp.glue; tempflex: FlexSums←pageflex; -- remember current pageflex and tempheight: Dimn←pageheight; -- pageheight, in case we must back up shrink: Flex; epsilon: Dimn=1; pageheight←pageheight+q.space; SumFlex[q,@pageflex]; -- add the glue IF pp.dir=vlist AND NOT EmptyPage[] AND (shrink←DominantFlex[@pageflex[shr]]).order=regular AND (pageheight-shrink.val)>(pagesize+epsilon) THEN BEGIN -- it won't fit pageflex←tempflex; pageheight←tempheight; -- restore page state -- add insertion to waiting list IF waitinglist=NIL THEN waitinglist←InitNodeList[]; StoreNode[waitinglist, RemoveNode[contriblist]]; END ELSE BEGIN -- it will fit (this should be the common case) -- add insertion to page list StoreNode[pagelist, RemoveNode[contriblist]]; IF pp.dir=vlist AND TestPageBreak[0] THEN GOTO Eject; END; RETURN; -- we've taken care of this node END; mark,leader => NULL; ENDCASE => ERROR TexErrorDefs.Confusion; -- the contribution is contributed StoreNode[pagelist, RemoveNode[contriblist]]; EXITS Delete => DsNode[RemoveNode[contriblist]]; Eject => EjectPage; END; EmptyPage: PROCEDURE RETURNS[BOOLEAN] = INLINE BEGIN RETURN[pagelist.link=NIL] END; InitPage: PROCEDURE = BEGIN OPEN TexTableDefs; -- Initialization of page building curbreak←NIL; curbadness←maxPenalty; pagesize←DimnParam[vsize]; pagedepthmax←DimnParam[maxdepth]; pagetopbl←DimnParam[topbaseline]; pageheight←pagedepth←0; TexGlueDefs.ClearFlexSums[@pageflex]; END; EjectPage: PROCEDURE = BEGIN OPEN TexTokenDefs; -- Now curbreak specifies the best place to break the current page. -- We will break it there and ship it off to the output routine. page: BoxNodePtr; -- the page box produced by the output routine -- don't try to eject an empty page IF EmptyPage[] THEN ERROR TexErrorDefs.Confusion; -- break the page at curbreak and update contriblist BreakPage[curbreak]; -- update topmark and botmark IF topmark#NIL THEN DelRCLink[topmark]; IF (topmark←TexPackDefs.CurMark[])#NIL THEN AddRCLink[topmark]; savedpage←TexPackDefs.VPackage[list: pagelist, desiredheight: pagesize, xpand: FALSE, page: TRUE]; pagelist←InitNodeList[]; savedpageno←TexTableDefs.Kount[0]; IF TexTableDefs.CurTracing[].showpages THEN BEGIN OPEN TexIODefs; UseDisplay; Ps["Completed page "]; Wn[savedpageno]; Wc['.]; -- *** fix this -- Ps["Completed for page "]; Wn[savedpageno]; Wc[':]; -- TraceDump[savedpage]; END; outputdormant←FALSE; page←RunOutputRoutine[]; outputdormant←TRUE; IF savedpage#NIL THEN BEGIN TexErrorDefs.Error["\output routine didn't use \page"]; DsNodeList[savedpage]; savedpage←NIL; END; EndPageOut[page]; END; BreakPage: PROCEDURE[tail: NodePtr] = BEGIN -- tail points to the last node to be included in the pagelist -- break the page there and prune unwanted nodes at the break -- update pagelist, contriblist, and waitinglist -- first join pagelist and contriblist (there may be -- nodes at the head of contriblist that we want to prune) InsertNodeList[contriblist,pagelist]; -- split the combined list between tail and tail.link pagelist←RemoveNodeList[contriblist,tail]; -- now prune unwanted nodes at the head of contriblist PruneNodesAtBreak[]; -- and put any waiting insertions at the head of contriblist IF waitinglist#NIL THEN BEGIN InsertNodeList[contriblist,waitinglist]; waitinglist←NIL END; END; PruneNodesAtBreak: PROCEDURE = BEGIN t: NodePtr; WHILE (t←contriblist.link)#NIL DO WITH tt:t SELECT FROM glue,penalty => DsNode[RemoveNode[contriblist]]; eject => BEGIN DsNode[RemoveNode[contriblist]]; EXIT END; ENDCASE => EXIT; ENDLOOP; RETURN; END; Page: PUBLIC PROCEDURE RETURNS[page: BoxNodePtr] = BEGIN OutputOnly; page←savedpage; savedpage←NIL; RETURN[page]; END; Mark: PUBLIC PROCEDURE[t: MarkType] RETURNS[TexTokenDefs.TokenListPtr] = BEGIN OutputOnly; SELECT t FROM top => RETURN[topmark]; bot => RETURN[TexPackDefs.CurMark[]]; first => RETURN[firstmark]; ENDCASE => ERROR; -- bad TopBotType END; OutputOnly: PROCEDURE = BEGIN IF outputdormant THEN BEGIN TexErrorDefs.Error["This is allowed only in output routines"]; SIGNAL Continue; END; END; EndPageOut: PROCEDURE[page: BoxNodePtr] = BEGIN OPEN TexIODefs; IF page=NIL THEN BEGIN deadcycles←deadcycles+1; RETURN END; UseDisplay; Ws[" ["]; Wn[savedpageno]; TexDebugDefs.ShowMem[string: "before ShipOut"]; TexDebugDefs.ShowNodeStatistics[page,"page"]; TexOutputDefs.ShipOut[page]; deadcycles←0; DsNodeList[page]; TexDebugDefs.ShowMem[string: "after ShipOut"]; Ws["]"]; END; FlushOutput: PUBLIC PROCEDURE[maxdeadcycles: CARDINAL] = BEGIN DO IF EmptyPage[] THEN BEGIN OPEN TexTableDefs; dummypage: BoxNodePtr; IF deadcycles=0 OR deadcycles>maxdeadcycles THEN RETURN; dummypage←NullBox[]; -- dummy page dummypage.height←DimnParam[vsize]; dummypage.width←DimnParam[hsize]; StoreNode[pagelist,dummypage]; END; AddToPage[MakeNodeList[MakeEjectNode[]]]; -- The condition EmptyPage[] implies that the waiting and contribution -- lists are both empty. But if NOT EmptyPage[], we need to flush out -- everything that is still waiting to be output. ENDLOOP; END; TestPageBreak: PROCEDURE[penalt: Penalty] RETURNS[BOOLEAN] = BEGIN OPEN TexGlueDefs; delta: Dimn; dir: FlexDir; flex: Flex; badness: Penalty; LONGbadness: LONG INTEGER; IF EmptyPage[] THEN RETURN[FALSE]; delta←pagesize-pageheight; -- positive if stretching needed dir←IF delta<0 THEN shr ELSE str; flex←DominantFlex[@pageflex[dir]]; badness←GluePenalty[[dir: dir, order: flex.order, num: ABS[delta], den: flex.val]]; IF badness=maxPenalty AND dir=shr THEN BEGIN -- can't shrink this much IF curbreak=NIL THEN curbreak←pagelist.last; -- in this case the -- page will be too long, but we took the first possible break RETURN[TRUE]; END; LONGbadness←LONG[badness]+penalt; IF LONGbadness>maxPenalty THEN badness←maxPenalty ELSE badness←InlineDefs.LowHalf[LONGbadness]; IF badness<=curbadness THEN BEGIN curbreak←pagelist.last; curbadness←badness END; RETURN[FALSE]; END; END.