-- Presser.mesa; edit by Johnsson; June 26, 1980 2:33 PM -- Converted to Laurel by Ken Pier, July 29, 1981 10:03 PM -- Last Edited by Pier, August 10, 1981 8:16 PM DIRECTORY csD: FROM "CoreStreamDefs", InlineDefs USING [LongDiv, LongMult, HighByte, LowByte], MiscDefs USING [SetBlock, Zero], Press USING [ defaultBottom, defaultCharWidth, defaultHeight, defaultLeft, defaultLineHeight, defaultLineLeading, defaultRight, defaultTabSpacing, defaultTop, defaultWidth, FontIndex, FontSlope, FontWeight, magicNonPrintingWidth, Mica, Mode, numberOfFonts, pageHeight, pageWidth, Points, pointsPerInch], PressFormat USING [ BYTE, DDV, EFont, ENop, EResetSpace, ESetX, ESetY, EShow, EShowRectangle, EShowShort, ESpaceX, ESpaceXShort, ESpaceY, ESpaceYShort, FE, LCToDouble, Mica, micasPerInch, PartType, PE, PETypeFont, PETypePage, PressPasswd], PressUtilities USING [FindFontWidths], StringDefs USING [MesaToBcplString, AppendDecimal, AppendString], SystemDefs USING [ AllocateHeapNode, AllocateHeapString, AllocateSegment, FreeHeapNode, FreeHeapString, FreeSegment], PrintDefs USING[PError], TimeDefs USING [ AppendDayTime, UnpackDT, DefaultTime, PackedTime, CurrentDayTime]; Presser: PROGRAM IMPORTS PrintDefs, InlineDefs, MiscDefs, PressFormat, PressUtilities, csD, StringDefs, SystemDefs, TimeDefs EXPORTS Press = BEGIN OPEN Press, PressFormat; wppr: CARDINAL = 256; -- words per press record bppr: CARDINAL = 2*wppr; recordsPerEntityList: CARDINAL = 10; wordsPerEntityList: CARDINAL = recordsPerEntityList*wppr; bytesPerEntityList: CARDINAL = recordsPerEntityList*bppr; recordsPerItemList: CARDINAL = 2; wordsPerItemList: CARDINAL = recordsPerItemList*wppr; BYTE: TYPE = [0..377B]; CR: CHARACTER = 15C; FF: CHARACTER = 14C; SP: CHARACTER = 40C; TAB: CHARACTER = 11C; onePoint: Mica = micasPerInch/pointsPerInch; -- big buffers EntityIndex: TYPE = [0..bytesPerEntityList); entityList: POINTER TO PACKED ARRAY EntityIndex OF BYTE ← NIL; entityIndex: EntityIndex; partsPerRecord: CARDINAL = wppr/SIZE[PressFormat.PE]; partsPerDocument: CARDINAL = recordsPerItemList*partsPerRecord; PartIndex: TYPE = [0..partsPerDocument); -- is 127 pages/doc enough? partList: POINTER TO ARRAY PartIndex OF PressFormat.PE ← NIL; partIndex: PartIndex ← 0; UserFontInfo: TYPE = RECORD [ family: STRING, -- mostly for debugging name: FamilyName, -- BCPL string body in caps size: Points, -- pointers below are NIL if not used yet portrait, landscape: ARRAY FontFace OF POINTER TO PressFontInfo]; WidthArray: TYPE = ARRAY CHARACTER OF Mica; PressFontInfo: TYPE = RECORD [ index: FontIndex, -- the one we feed to press face: FontFace, fBBox, fBBoy, width, height: Mica ← NULL, widths: POINTER TO WidthArray, -- NIL if not used yet rotation: CARDINAL, user: POINTER TO UserFontInfo]; -- to build Font Directory FamilyName: TYPE = PACKED ARRAY [0..20) OF BYTE; FontFace: TYPE = [0..2*2); -- should really be 2*3*3, See FontFormats memo initialized: BOOLEAN ← FALSE; Initialize: PUBLIC PROCEDURE = BEGIN i: FontIndex; Reset[]; userFonts ← SystemDefs.AllocateHeapNode[numberOfFonts*SIZE[UserFontInfo]]; pressFonts ← SystemDefs.AllocateHeapNode[numberOfFonts*SIZE[PressFontInfo]]; FOR i IN FontIndex DO userFonts[i] ← [NIL, , 0, [NIL, NIL, NIL, NIL], [NIL, NIL, NIL, NIL]]; pressFonts[i] ← [0, 0, 0, 0, 0, 0, NIL, 0, NIL]; ENDLOOP; entityList ← SystemDefs.AllocateSegment[wordsPerEntityList]; MiscDefs.Zero[entityList, wordsPerEntityList]; partList ← SystemDefs.AllocateSegment[wordsPerItemList]; MiscDefs.Zero[partList, wordsPerItemList]; initialized ← TRUE; END; Reset: PUBLIC PROCEDURE = BEGIN OPEN SystemDefs; i: FontIndex; active ← FALSE; KillString[@pageHeader]; KillString[@pageTrailer]; KillString[@documentFileName]; KillString[@documentUserName]; KillString[@documentCreationDate]; IF userFonts # NIL THEN BEGIN FOR i IN FontIndex DO KillString[@userFonts[i].family]; ENDLOOP; FreeHeapNode[userFonts]; userFonts ← NIL; END; IF pressFonts # NIL THEN BEGIN FlushFontBuffers[]; FreeHeapNode[pressFonts]; pressFonts ← NIL; END; IF entityList # NIL THEN BEGIN FreeSegment[entityList]; entityList ← NIL; END; IF partList # NIL THEN BEGIN FreeSegment[partList]; partList ← NIL; END; pageActive ← paperActive ← active ← initialized ← FALSE; END; lineLeading: Mica ← defaultLineLeading; tabWidth: Mica ← defaultTabSpacing; vSpaceWidth: BOOLEAN ← FALSE; -- TRUE after SetWidthOfSpace spaceWidth: Mica; documentFileName: STRING ← NIL; -- max length 51 documentUserName: STRING ← NIL; -- max length 31 documentCreationDate: STRING ← NIL; -- max length 39 pageHeader, pageTrailer: STRING ← NIL; headerPageNumbers, trailerPageNumbers: BOOLEAN ← FALSE; numberOfCopies: CARDINAL ← 1; SetDocumentCreationDate: PUBLIC PROCEDURE [date: STRING] = {MakeMeACopy[@documentCreationDate, date]}; SetDocumentUserName: PUBLIC PROCEDURE [user: STRING] = {MakeMeACopy[@documentUserName, user]}; SetHeaderText: PUBLIC PROCEDURE [header: STRING, pageNumbers: BOOLEAN] = {MakeMeACopy[@pageHeader, header]; headerPageNumbers ← pageNumbers}; SetTrailerText: PUBLIC PROCEDURE [trailer: STRING, pageNumbers: BOOLEAN] = {MakeMeACopy[@pageTrailer, trailer]; trailerPageNumbers ← pageNumbers}; SetNumberOfCopies: PUBLIC PROCEDURE [copies: CARDINAL] = {numberOfCopies ← copies}; PutFontInTable: PUBLIC PROCEDURE [ index: FontIndex, family: STRING, size: Points] = BEGIN IF userFonts = NIL THEN PrintDefs.PError[BadParameters]; IF index~ IN FontIndex THEN PrintDefs.PError[BadParameters]; IF family.length~ IN [1..19] THEN PrintDefs.PError[BadParameters]; -- only 20 bytes words MakeMeACopy[@userFonts[index].family, family]; userFonts[index].size ← size; FOR i: CARDINAL IN [0..20) DO userFonts[index].name[i] ← 0; ENDLOOP; userFonts[index].name[0] ← family.length; FOR i: CARDINAL IN [0..family.length) DO SELECT family[i] FROM IN ['A..'Z], IN ['0..'9] => userFonts[index].name[i + 1] ← LOOPHOLE[family[i]]; IN ['a..'z] => userFonts[index].name[i + 1] ← LOOPHOLE[family[i], BYTE] - 40B; ENDCASE => PrintDefs.PError[BadParameters]; ENDLOOP; END; SetMargins: PUBLIC PROCEDURE [l, r, t, b: Mica] = BEGIN IF pageActive THEN PrintDefs.PError[BadParameters]; leftMargin ← l; rightMargin ← r; topMargin ← t; bottomMargin ← b; IF landscape THEN BEGIN height ← pageWidth - rightMargin - leftMargin; width ← pageHeight - topMargin - bottomMargin; END ELSE BEGIN height ← pageHeight - topMargin - bottomMargin; width ← pageWidth - rightMargin - leftMargin; END; width ← width - (numberOfColumns - 1)*(spaceBetweenColumns); width ← LOOPHOLE[width, CARDINAL]/numberOfColumns; END; SetCurrentTabWidth: PUBLIC PROCEDURE [tab: Mica] = {tabWidth ← tab}; SetCurrentLineLeading: PUBLIC PROCEDURE [lead: Mica] = BEGIN lineLeading ← lead; END; active: BOOLEAN ← FALSE; pressFile: csD.StreamHandle ← NIL; Start: PUBLIC PROCEDURE [docName: STRING, file: csD.StreamHandle] = BEGIN IF active THEN RETURN; IF ~initialized THEN Initialize[]; pressFile ← file; MakeMeACopy[@documentFileName, docName]; IF documentCreationDate = NIL THEN BEGIN OPEN TimeDefs; time: STRING = [18]; AppendDayTime[time, UnpackDT[DefaultTime]]; MakeMeACopy[@documentCreationDate, time]; END; IF documentUserName = NIL THEN MakeMeACopy[@documentUserName, "NoName"L]; IF documentFileName = NIL THEN MakeMeACopy[@documentFileName, "NoName"L]; IF documentFileName.length > 51 THEN documentFileName.length ← 51; currentRecordNumber ← 0; csD.Reset[pressFile]; entityIndex ← partIndex ← 0; FOR i: FontIndex IN FontIndex DO userFonts[i].portrait ← [NIL, NIL, NIL, NIL]; userFonts[i].landscape ← [NIL, NIL, NIL, NIL]; ENDLOOP; currentPageNumber ← 1; lineActive ← pageActive ← paperActive ← FALSE; currentX ← 0; currentY ← height; BeSureFontZeroExists[]; active ← TRUE; END; Finish: PUBLIC PROCEDURE = BEGIN fd: POINTER = entityList; -- build Font Directory in random buffer fp: POINTER TO PressFormat.FE ← fd; dd: POINTER TO PressFormat.DDV = fd; -- build Document Directory in random buffer now: TimeDefs.PackedTime ← TimeDefs.CurrentDayTime[]; numberOfPartRecords, firstPartRecord: CARDINAL; IF lineActive THEN EndCurrentLine[]; IF pageActive THEN EndCurrentPage[]; IF paperActive THEN EndCurrentPaper[]; -- send Font Directory MiscDefs.Zero[fd, wppr]; firstDataRecordOfPaper ← currentRecordNumber; -- for AppendPartItem FOR i: CARDINAL IN FontIndex DO IF pressFonts[i].user # NIL THEN BEGIN fp↑ ← PressFormat.FE[ length: SIZE[PressFormat.FE], set: 0, -- we only use one font set fno: pressFonts[i].index, destm: 0, destn: 177B, fam: pressFonts[i].user.name, face: pressFonts[i].face, source: 0, siz: pressFonts[i].user.size, rotn: pressFonts[i].rotation]; fp ← fp + SIZE[PressFormat.FE]; IF fp = fd + wppr THEN -- opps, record exactly full BEGIN [] ← csD.WriteBlock[pressFile, fd, 0, bppr]; MiscDefs.Zero[fd, wppr]; fp ← fd; END; END; ENDLOOP; [] ← csD.WriteBlock[pressFile, fd, 0, bppr]; AppendPartItem[PressFormat.PETypeFont, 0]; -- send off Part Directory firstPartRecord ← currentRecordNumber; numberOfPartRecords ← (partIndex*SIZE[PressFormat.PE] + wppr - 1)/wppr; [] ← csD.WriteBlock[pressFile, partList, 0, numberOfPartRecords*bppr]; -- send off Document Directory - use entity buffer MiscDefs.SetBlock[p: dd, l: wppr, v: -1]; dd.Passwd ← PressFormat.PressPasswd; -- General Password dd.nRecs ← firstPartRecord + numberOfPartRecords + 1; -- total number of records dd.nParts ← partIndex; dd.pdStart ← firstPartRecord; dd.pdRecs ← numberOfPartRecords; dd.Backp ← 0; -- ?? funny backpointer dd.date ← PressFormat.LCToDouble[now]; dd.fCopy ← 1; dd.lCopy ← numberOfCopies; -- first, last copy dd.fPage ← 1; dd.lPage ← 0; -- first, last page StringDefs.MesaToBcplString[documentFileName, LOOPHOLE[@dd.FileStr]]; StringDefs.MesaToBcplString[documentUserName, LOOPHOLE[@dd.CreatStr]]; StringDefs.MesaToBcplString[documentCreationDate, LOOPHOLE[@dd.DateStr]]; currentRecordNumber ← currentRecordNumber + 1; [] ← csD.WriteBlock[pressFile, dd, 0, bppr]; csD.Checkpoint[pressFile];--flush everything to the disk active ← FALSE; END; Abort: PUBLIC PROCEDURE = {active ← FALSE}; String: PUBLIC PROCEDURE [s: STRING] = {FOR i: CARDINAL IN [0..s.length) DO Character[s[i]] ENDLOOP}; PieceOfLine: PUBLIC PROCEDURE [s: STRING, width: Mica] = BEGIN IF ~lineActive THEN BEGIN -- If we switch to a taller font, this test might miss. IF pageActive AND currentY < (currentFontPointer.height + lineLeading) THEN DoPageOverflow[]; BeginLine[]; END; FOR i: CARDINAL IN [0..s.length) DO csD.Write[pressFile, s[i]]; ENDLOOP; lineCharacters ← lineCharacters + s.length; currentX ← currentX + width; END; GetWidthOfString: PUBLIC PROCEDURE [s: STRING] RETURNS [w: Mica] = BEGIN w ← 0; FOR i: CARDINAL IN [0..s.length) DO w ← w + GetWidthOfCharacter[s[i]]; ENDLOOP; END; GetWidthOfCharacter: PUBLIC PROCEDURE [c: CHARACTER] RETURNS [w: Mica] = BEGIN IF vSpaceWidth AND c = SP THEN RETURN[spaceWidth]; w ← currentFontPointer.widths[c]; IF w = magicNonPrintingWidth THEN w ← 0; END; Character: PUBLIC PROCEDURE [c: CHARACTER] = BEGIN charWidth: Mica; SELECT c FROM CR => DoCR[]; FF => DoFF[]; TAB => DoTAB[]; ENDCASE => BEGIN IF vSpaceWidth AND c = SP THEN charWidth ← spaceWidth ELSE charWidth ← currentFontPointer.widths[c]; IF charWidth = magicNonPrintingWidth THEN charWidth ← 0; IF ~lineActive THEN BEGIN -- If we switch to a taller font, this test might miss. IF pageActive AND currentY < (currentFontPointer.height + lineLeading) THEN DoPageOverflow[]; BeginLine[]; END; IF (currentX + charWidth) > width AND c # SP THEN DoLineOverflow[]; csD.Write[pressFile, c]; lineCharacters ← lineCharacters + 1; currentX ← currentX + charWidth; END; END; DoTAB: PROCEDURE = BEGIN IF ~lineActive AND currentY < (currentFontPointer.height + lineLeading) THEN DoPageOverflow[]; -- else this TAB gets lost SkipSomeSpace[(((currentX + 20)/tabWidth) + 1)*tabWidth - currentX]; END; SkipSomeSpace: PUBLIC PROCEDURE [mica: Mica] = BEGIN IF mica = 0 THEN RETURN; IF ~pageActive THEN BeginPage[]; IF lineActive THEN FlushBuffer[] ELSE IF currentY < (currentFontPointer.height + lineLeading) THEN DoPageOverflow[]; currentX ← currentX + mica; IF currentX > width THEN BEGIN DoLineOverflow[]; RETURN; END; IF ~lineActive THEN RETURN; -- BeginLine will set position -- TAB - TAB will generate an extra Set-? IF landscape THEN AppendEntityByte[ESetY] ELSE AppendEntityByte[ESetX]; AppendEntityWord[currentX]; END; DoCR: PROCEDURE = BEGIN IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; currentX ← 0; currentY ← currentY - lineHeight - lineLeading; END; DoLineOverflow: PROCEDURE = BEGIN Character[CR]; -- Leave line active, but overflow test is done by PrintCharacter String["**"]; END; DoFF: PROCEDURE = -- FF, FF will get you an empty page {IF ~pageActive THEN BeginPage[]; EndCurrentPage[]}; DoPageOverflow: PROCEDURE = {EndCurrentPage[]; BeginPage[]}; -- leave page active landscape: BOOLEAN ← FALSE; numberOfColumns: CARDINAL ← 1; spaceBetweenColumns: Mica ← 0; height: Mica ← defaultHeight; width: Mica ← defaultWidth; currentX, currentY: Mica ← 0; leftMargin: Mica ← defaultLeft; rightMargin: Mica ← defaultRight; topMargin: Mica ← defaultTop; bottomMargin: Mica ← defaultBottom; SetMode: PUBLIC PROCEDURE [columns: CARDINAL, between: Mica, mode: Mode] = BEGIN numberOfColumns ← columns; spaceBetweenColumns ← between; SELECT mode FROM portrait => BEGIN landscape ← FALSE; height ← pageHeight - topMargin - bottomMargin; width ← pageWidth - rightMargin - leftMargin; END; landscape => BEGIN landscape ← TRUE; height ← pageWidth - rightMargin - leftMargin; width ← pageHeight - topMargin - bottomMargin; END; ENDCASE => PrintDefs.PError[PageModeError]; width ← width - (numberOfColumns - 1)*(spaceBetweenColumns); width ← LOOPHOLE[width, CARDINAL]/numberOfColumns; END; SetCurrentPosition: PUBLIC PROCEDURE [x, y: Mica] = BEGIN IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; currentX ← x; currentY ← y; END; GetCurrentPageNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {RETURN[currentPageNumber]}; SetCurrentPageNumber: PUBLIC PROCEDURE [pn: CARDINAL] = {currentPageNumber ← pn}; GetCurrentPosition: PUBLIC PROCEDURE RETURNS [x, y: Mica] = {IF ~pageActive THEN BeginPage[]; RETURN[currentX, currentY]}; SetWidthOfSpace: PUBLIC PROCEDURE [w: Mica] = BEGIN IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; IF w < 2048 THEN AppendEntityWord[ (IF landscape THEN ESpaceYShort ELSE ESpaceXShort)*400B + w] ELSE BEGIN AppendEntityByte[IF landscape THEN ESpaceY ELSE ESpaceX]; AppendEntityWord[w]; END; vSpaceWidth ← TRUE; spaceWidth ← w; END; ResetWidthOfSpace: PUBLIC PROCEDURE = BEGIN IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; AppendEntityByte[EResetSpace]; vSpaceWidth ← FALSE; END; DrawRectangle: PUBLIC PROCEDURE [w, h: Mica] = BEGIN IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; AppendEntityByte[EShowRectangle]; AppendEntityWord[IF landscape THEN h ELSE w]; AppendEntityWord[IF landscape THEN w ELSE h]; END; -- lineActive means that we don't have to do a Set-x and Set-y -- lineCharacters#0 means that we need to do a Show-characters -- We don't know how high the line is until we have seen it go past. -- This kludge remembers the entityIndex for the Y data word so we can fix it later. -- When we switch fonts, we save the height of the tallest one. lineHeight: Mica ← defaultLineHeight; fixupXIndex, fixupYIndex: CARDINAL ← 0; lineActive: BOOLEAN ← FALSE; lineCharacters: CARDINAL ← 0; BeginLine: PROCEDURE = BEGIN IF ~pageActive THEN BeginPage[]; lineCharacters ← 0; lineHeight ← currentFontPointer.height; AppendEntityByte[ESetX]; fixupXIndex ← entityIndex; AppendEntityWord[IF landscape THEN height - currentY ELSE currentX]; AppendEntityByte[ESetY]; fixupYIndex ← entityIndex; AppendEntityWord[IF landscape THEN currentX ELSE currentY]; lineActive ← TRUE; END; -- called by SkipSomeSpace, EndCurrentLine, BeginPage (headers), and SetCurrentFont FlushBuffer: PROCEDURE = BEGIN SELECT lineCharacters FROM 0 => RETURN; IN [1..40B] => BEGIN AppendEntityByte[EShowShort + lineCharacters - 1]; END; IN [40B..400B] => BEGIN AppendEntityByte[EShow]; AppendEntityByte[lineCharacters]; END; ENDCASE => PrintDefs.PError[InternalError]; paperCharacters ← paperCharacters + lineCharacters; pageCharacters ← pageCharacters + lineCharacters; lineCharacters ← 0; END; EndCurrentLine: PROCEDURE = BEGIN temp: INTEGER; IF ~lineActive THEN PrintDefs.PError[InternalError]; FlushBuffer[]; temp ← currentY - lineHeight - lineLeading; IF landscape THEN BEGIN temp ← height - currentY; entityList[fixupXIndex] ← InlineDefs.HighByte[temp]; entityList[fixupXIndex + 1] ← InlineDefs.LowByte[temp]; END ELSE BEGIN entityList[fixupYIndex] ← InlineDefs.HighByte[temp]; entityList[fixupYIndex + 1] ← InlineDefs.LowByte[temp]; END; lineActive ← FALSE; END; pageActive: BOOLEAN ← FALSE; firstEntityIndexOfPage: CARDINAL ← 0; firstPageCharacter, pageCharacters: CARDINAL ← 0; currentPageNumber: CARDINAL ← 0; currentColumn: CARDINAL ← 0; -- starts at 0 BeginPage: PROCEDURE = BEGIN IF pageActive THEN PrintDefs.PError[InternalError]; IF ~paperActive THEN BeginPaper[]; firstEntityIndexOfPage ← entityIndex; firstPageCharacter ← paperCharacters; pageCharacters ← 0; pageActive ← TRUE; Headers[]; currentX ← 0; currentY ← height; IF currentFontPointer.index~ IN FontIndex THEN PrintDefs.PError[InternalError]; IF currentFontPointer.index # 0 THEN AppendEntityByte[EFont + currentFontPointer.index]; END; EndCurrentPage: PROCEDURE = BEGIN cardinal: CARDINAL; header, trailer: Mica ← 0; -- fudge for headers/trailers IF ~pageActive THEN PrintDefs.PError[InternalError]; IF lineActive THEN EndCurrentLine[]; Trailers[]; pageActive ← FALSE; -- header must start on a word boundry IF (entityIndex MOD 2) = 1 THEN AppendEntityByte[ENop]; -- build entity header AppendEntityWord[0]; -- type and font-set AppendEntityWord[0]; AppendEntityWord[firstPageCharacter]; -- begin byte AppendEntityWord[0]; AppendEntityWord[pageCharacters]; -- length IF landscape THEN BEGIN -- Xe, Ye AppendEntityWord[leftMargin]; AppendEntityWord[ bottomMargin + currentColumn*(width + spaceBetweenColumns)]; IF pageHeader # NIL THEN header ← 3*heightOfHeaderLine; IF pageTrailer # NIL THEN trailer ← 3*heightOfHeaderLine; AppendEntityWord[-header]; AppendEntityWord[0]; -- left, bottom AppendEntityWord[height + header + trailer]; -- width AppendEntityWord[width]; -- height END ELSE BEGIN -- Xe, Ye AppendEntityWord[leftMargin + currentColumn*(width + spaceBetweenColumns)]; AppendEntityWord[bottomMargin]; IF pageHeader # NIL THEN header ← 3*heightOfHeaderLine; IF pageTrailer # NIL THEN trailer ← 3*heightOfHeaderLine; AppendEntityWord[0]; AppendEntityWord[-trailer]; -- left, bottom AppendEntityWord[width]; -- width AppendEntityWord[height + header + trailer]; -- height END; cardinal ← entityIndex - firstEntityIndexOfPage; AppendEntityWord[1 + cardinal/2]; -- entity-length currentPageNumber ← currentPageNumber + 1; IF (currentColumn ← currentColumn + 1) = numberOfColumns THEN EndCurrentPaper[]; END; Headers: PROCEDURE = BEGIN buffer: STRING = [20]; fontPointer: POINTER TO PressFontInfo ← currentFontPointer; -- Each new page starts out in font 0, but we have to switch the pointer so that the width calculations work out ok. If not, and headers are in a bigger font than the current font, the page number will overflow its line. IF pageHeader = NIL THEN RETURN; currentFontPointer ← fontZeroPointer; currentX ← 0; currentY ← height + 3*heightOfHeaderLine; BeginLine[]; String[pageHeader]; FlushBuffer[]; StringDefs.AppendDecimal[buffer, currentPageNumber]; SkipSomeSpace[width - buffer.length*widthOfHeaderDigit - currentX]; String[buffer]; EndCurrentLine[]; currentFontPointer ← fontPointer; END; Trailers: PROCEDURE = BEGIN buffer: STRING = [20]; fontPointer: POINTER TO PressFontInfo ← currentFontPointer; IF pageTrailer = NIL THEN RETURN; IF currentFontPointer.index # 0 THEN AppendEntityByte[EFont + 0]; -- font 0 currentFontPointer ← fontZeroPointer; currentX ← 0; currentY ← 0 - 2*heightOfHeaderLine; BeginLine[]; String[pageTrailer]; FlushBuffer[]; StringDefs.AppendDecimal[buffer, currentPageNumber]; SkipSomeSpace[width - buffer.length*widthOfHeaderDigit - currentX]; String[buffer]; EndCurrentLine[]; currentFontPointer ← fontPointer; END; paperActive: BOOLEAN ← FALSE; currentRecordNumber: CARDINAL ← 0; firstDataRecordOfPaper: CARDINAL ← 0; paperCharacters: CARDINAL ← 0; BeginPaper: PROCEDURE = BEGIN IF paperActive THEN PrintDefs.PError[InternalError]; MiscDefs.Zero[entityList, wordsPerEntityList]; entityIndex ← 0; AppendEntityWord[0]; -- marker firstDataRecordOfPaper ← currentRecordNumber; paperActive ← TRUE; paperCharacters ← 0; currentColumn ← 0; END; EndCurrentPaper: PROCEDURE = BEGIN pad: CARDINAL; pb: RECORD[page: CARDINAL, byte: [0..777B]]; IF pageActive THEN EndCurrentPage[]; [pb.page, pb.byte] ← csD.MapPositionToPageByte[csD.GetPosition[pressFile]]; THROUGH [0..2 + (pb.byte MOD 2)) DO -- word boundary + 2 zeros csD.Write[pressFile, 0] ENDLOOP; [] ← csD.WriteBlock[pressFile, entityList, 0, entityIndex]; [pb.page, pb.byte] ← csD.MapPositionToPageByte[csD.GetPosition[pressFile]]; pad ← wppr - pb.byte/2; THROUGH [0..pad) DO csD.Write[pressFile, 0]; csD.Write[pressFile, 0]; ENDLOOP; AppendPartItem[PressFormat.PETypePage, pad]; paperActive ← FALSE; END; userFonts: POINTER TO ARRAY FontIndex OF UserFontInfo ← NIL; pressFonts: POINTER TO ARRAY FontIndex OF PressFontInfo ← NIL; currentFontPointer: POINTER TO PressFontInfo ← NIL; SetCurrentFont: PUBLIC PROCEDURE [ font: FontIndex, w: FontWeight, s: FontSlope] = BEGIN ff: FontFace ← 0; new: POINTER TO PressFontInfo; IF ~initialized THEN PrintDefs.PError[BadParameters]; IF font~ IN FontIndex OR userFonts[font].family = NIL THEN PrintDefs.PError[BadParameters]; SELECT w FROM medium => ff ← ff + 0; bold => ff ← ff + 2; --light => ff ← ff+4; ENDCASE => PrintDefs.PError[BadParameters]; SELECT s FROM regular => ff ← ff + 0; italic => ff ← ff + 1; ENDCASE => PrintDefs.PError[BadParameters]; --SELECT expansion FROM -- regular => ff ← ff+0; -- condensed => ff ← ff+6; -- expanded => ff ← ff+12; -- ENDCASE => PrintDefs.PError[BadParameters]; IF ~landscape THEN BEGIN IF userFonts[font].portrait[ff] = NIL THEN FindPressSlot[font, ff, w, s]; new ← userFonts[font].portrait[ff]; END ELSE BEGIN IF userFonts[font].landscape[ff] = NIL THEN FindPressSlot[font, ff, w, s]; new ← userFonts[font].landscape[ff]; END; IF new = currentFontPointer THEN RETURN; currentFontPointer ← new; IF lineActive THEN FlushBuffer[]; IF currentFontPointer.index~ IN FontIndex THEN PrintDefs.PError[InternalError]; IF ~pageActive THEN RETURN; AppendEntityByte[EFont + currentFontPointer.index]; -- Font lineHeight ← MAX[lineHeight, currentFontPointer.height]; END; FindPressSlot: PROCEDURE [ font: FontIndex, ff: FontFace, w: FontWeight, s: FontSlope] = BEGIN i: FontIndex; pf: POINTER TO PressFontInfo; rot: CARDINAL ← IF landscape THEN 60*90 ELSE 0; family: STRING = userFonts[font].family; points: Points = userFonts[font].size; FOR i IN FontIndex DO pf ← @pressFonts[i]; IF pf.user = NIL THEN EXIT; -- empty slot - use it IF pf.user = @userFonts[font] AND pf.face = ff AND pf.rotation = rot THEN EXIT; REPEAT FINISHED => PrintDefs.PError[MoreThan16Fonts]; ENDLOOP; IF pf.user = NIL THEN BEGIN OPEN pf; pf↑ ← [index: i, face: ff, widths: SystemDefs.AllocateSegment[SIZE[WidthArray]], rotation: rot, user: @userFonts[font]]; -- initialize to something legal height ← PointsToMicas[userFonts[font].size]; width ← PointsToMicas[userFonts[font].size]; pressFonts[i].widths↑ ← ALL[magicNonPrintingWidth]; [fBBox, fBBoy, width, ] ← PressUtilities.FindFontWidths[ family, points, w, s, pf.widths]; END; IF landscape THEN userFonts[font].landscape[ff] ← pf ELSE userFonts[font].portrait[ff] ← pf; END; PointsToMicas: PROCEDURE [p: Points] RETURNS [Mica] = BEGIN OPEN InlineDefs; RETURN[LongDiv[LongMult[micasPerInch, p], pointsPerInch]]; END; heightOfHeaderLine: Mica; widthOfHeaderDigit: Mica; fontZeroPointer: POINTER TO PressFontInfo ← NIL; BeSureFontZeroExists: PROCEDURE = BEGIN fontZero: FontIndex = FIRST[FontIndex]; i: FontIndex = FIRST[FontIndex]; c: CHARACTER; ff: FontFace ← 0; -- medium, regular IF userFonts[fontZero].family # NIL THEN -- try for normal font 0 BEGIN -- The client has already specified a font 0. -- Activate it now so it will be the press font 0 that we can use for page headers. SetCurrentFont[fontZero, medium, regular]; fontZeroPointer ← currentFontPointer; heightOfHeaderLine ← currentFontPointer.height; widthOfHeaderDigit ← currentFontPointer.widths['0]; RETURN; END; -- ARGH! The idiot user didn't give us any font 0. The default is Gacha 8. -- We brew up the constants so it will work without Fonts.widhts. PutFontInTable[fontZero, "Gacha", 8]; pressFonts[i] ← [i, ff, , , , , SystemDefs.AllocateSegment[200B], , @userFonts[fontZero]]; pressFonts[i].height ← defaultLineHeight; pressFonts[i].width ← defaultCharWidth; FOR c IN [40C..176C] DO pressFonts[i].widths[c] ← defaultCharWidth; ENDLOOP; IF landscape THEN BEGIN userFonts[fontZero].landscape[ff] ← @pressFonts[i]; pressFonts[i].rotation ← 90*60; END ELSE BEGIN userFonts[fontZero].portrait[ff] ← @pressFonts[i]; pressFonts[i].rotation ← 0; END; currentFontPointer ← @pressFonts[i]; fontZeroPointer ← currentFontPointer; heightOfHeaderLine ← currentFontPointer.height; widthOfHeaderDigit ← currentFontPointer.widths['0]; AppendEntityByte[EFont + i]; -- Font END; FlushFontBuffers: PUBLIC PROCEDURE = BEGIN FOR i: FontIndex IN FontIndex DO IF pressFonts[i].widths # NIL THEN SystemDefs.FreeSegment[pressFonts[i].widths]; pressFonts[i] ← [0, 0, 0, 0, 0, 0, NIL, 0, NIL]; ENDLOOP; END; AppendEntityByte: PROCEDURE [b: BYTE] = BEGIN IF entityIndex = bytesPerEntityList THEN PrintDefs.PError[ELBufferOverflow]; entityList[entityIndex] ← b; entityIndex ← entityIndex + 1; END; AppendEntityWord: PROCEDURE [w: INTEGER] = {AppendEntityByte[InlineDefs.HighByte[w]]; AppendEntityByte[InlineDefs.LowByte[w]]}; AppendPartItem: PROCEDURE [type: PressFormat.PartType, last: CARDINAL] = BEGIN pb: RECORD[page: CARDINAL, byte: [0..777B]]; [pb.page, pb.byte] ← csD.MapPositionToPageByte[csD.GetPosition[pressFile]]; IF partIndex = partsPerDocument THEN PrintDefs.PError[ELBufferOverflow]; partList[partIndex] ← [Type: type, pStart: firstDataRecordOfPaper, pRecs: pb.page - firstDataRecordOfPaper, Padding: last]; partIndex ← partIndex + 1; currentRecordNumber ← pb.page; END; KillString: PROCEDURE [where: POINTER TO STRING] = {IF where↑ # NIL THEN {SystemDefs.FreeHeapString[where↑]; where↑ ← NIL}}; MakeMeACopy: PROCEDURE [where: POINTER TO STRING, newString: STRING] = BEGIN OPEN SystemDefs; KillString[where]; IF newString # NIL THEN BEGIN where↑ ← AllocateHeapString[newString.length]; StringDefs.AppendString[where↑, newString]; END; END; -- initialization END. LOG --Former errors MoreThan16Fonts: PUBLIC ERROR = CODE; ELBufferOverflow: PUBLIC ERROR = CODE; PartBufferOverflow: PUBLIC ERROR = CODE; BadParameters: PUBLIC ERROR = CODE; InternalError: PUBLIC ERROR = CODE; August 10, 1981 8:19 PM Defined EntityIndex and PartIndex, fixing bugs in index types Used InlineDefs.High/LowByte for AppendEntityWord