-- 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 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) 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.