-- Presser.mesa; edit by Johnsson; 16-Apr-81 14:25:18 -- edit by Schmidt, July 8, 1982 2:28 pm DIRECTORY Heap USING [systemMDSZone], Inline USING [HighByte, LongCOPY, LongDiv, LongMult, LowByte, LowHalf], LongString USING [AppendDecimal, AppendString], LongStorage USING [Node, String, Words, Free, FreeWords], 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 [FontNotInFontsDotWidths, ErrorReadingFontWidths, FindFontWidths], Segments USING [FileNameProblem], Streams USING [GetIndex, Handle, PutBlock, PutByte, PutChar, SetIndex], Time USING [Append, Unpack, defaultTime, Packed, Current]; Presser: PROGRAM IMPORTS Heap, Inline, LongString, LongStorage, PressFormat, PressUtilities, Segments, Streams, Time EXPORTS Press = BEGIN OPEN Press, PressFormat; MoreThan16Fonts: PUBLIC ERROR = CODE; ELBufferOverflow: PUBLIC ERROR = CODE; PartBufferOverflow: PUBLIC ERROR = CODE; BadParameters: PUBLIC ERROR = CODE; InternalError: PUBLIC ERROR = CODE; 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; widthGacha6: Mica = 129; -- default character width in landscape mode widthGacha8: Mica = 173; -- default character width in portrait mode -- big buffers entityIndex: [0..bytesPerEntityList); partsPerRecord: CARDINAL = wppr/SIZE[PressFormat.PE]; -- is 127 pages/doc enough? partsPerDocument: CARDINAL = recordsPerItemList*partsPerRecord; partIndex: [0..partsPerDocument); -- edit by Schmidt, used to be .. partsPerRecord -- global data Data: TYPE = RECORD [ currentX, currentY: Mica ← NULL, pressFile: Streams.Handle, height: Mica ← defaultHeight, width: Mica ← defaultWidth, entityList: LONG POINTER TO PACKED ARRAY [0..bytesPerEntityList) OF BYTE ← NIL, partList: LONG POINTER TO ARRAY [0..partsPerDocument) OF PressFormat.PE ← NIL, lineLeading: Mica ← defaultLineLeading, tabWidth: Mica ← defaultTabSpacing, spaceWidth: Mica ← NULL, documentFileName: LONG STRING ← NIL, -- max length 51 documentUserName: LONG STRING ← NIL, -- max length 31 documentCreationDate: LONG STRING ← NIL, -- max length 39 pageHeader, pageTrailer: LONG STRING ← NIL, numberOfColumns: CARDINAL ← 1, spaceBetweenColumns: Mica ← 0, leftMargin: Mica ← defaultLeft, rightMargin: Mica ← defaultRight, topMargin: Mica ← defaultTop, bottomMargin: Mica ← defaultBottom, -- 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 ← NULL, lineCharacters: CARDINAL ← 0, firstEntityIndexOfPage: CARDINAL ← 0, firstPageCharacter, pageCharacters: CARDINAL ← 0, currentPageNumber: CARDINAL ← 1, currentColumn: CARDINAL ← 0, currentRecordNumber: CARDINAL ← 0, firstDataRecordOfPaper: CARDINAL ← 0, paperCharacters: CARDINAL ← 0, userFonts: LONG POINTER TO ARRAY FontIndex OF UserFontInfo ← NIL, pressFonts: LONG POINTER TO ARRAY FontIndex OF PressFontInfo ← NIL, currentFontPointer: LONG POINTER TO PressFontInfo ← NIL, heightOfHeaderLine: Mica ← 0, widthOfHeaderDigit: Mica ← 0, numberOfCopies: CARDINAL ← 1, fontZeroPointer: LONG POINTER TO PressFontInfo, active: BOOLEAN ← FALSE, landscape: BOOLEAN ← FALSE, lineActive: BOOLEAN ← FALSE, pageActive: BOOLEAN ← FALSE, paperActive: BOOLEAN ← FALSE, headerPageNumbers, trailerPageNumbers: BOOLEAN ← FALSE, vSpaceWidth: BOOLEAN ← FALSE]; -- TRUE after SetWidthOfSpace data: POINTER TO Data ← NIL; UserFontInfo: TYPE = RECORD [ family: LONG 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 LONG 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: LONG POINTER TO WidthArray, -- NIL if not used yet rotation: CARDINAL, user: LONG 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 Initialize: PUBLIC PROCEDURE = BEGIN OPEN data; Reset[]; data ← Heap.systemMDSZone.NEW[Data]; userFonts ← LongStorage.Node[numberOfFonts*SIZE[UserFontInfo]]; userFonts↑ ← ALL[[NIL, , 0, [NIL, NIL, NIL, NIL], [NIL, NIL, NIL, NIL]]]; pressFonts ← LongStorage.Node[numberOfFonts*SIZE[PressFontInfo]]; pressFonts↑ ← ALL[[0, 0, 0, 0, 0, 0, NIL, 0, NIL]]; entityList ← LongStorage.Words[wordsPerEntityList]; Zero[entityList, wordsPerEntityList]; partList ← LongStorage.Words[wordsPerItemList]; Zero[partList, wordsPerItemList]; END; Reset: PUBLIC PROCEDURE = BEGIN OPEN LongStorage, data; i: FontIndex; IF data = NIL THEN RETURN; LongStorage.Free[pageHeader]; LongStorage.Free[pageTrailer]; LongStorage.Free[documentFileName]; LongStorage.Free[documentUserName]; LongStorage.Free[documentCreationDate]; IF userFonts # NIL THEN BEGIN FOR i IN FontIndex DO LongStorage.Free[userFonts[i].family]; ENDLOOP; Free[userFonts]; END; IF pressFonts # NIL THEN {FlushFontBuffers[]; Free[pressFonts]}; IF entityList # NIL THEN {FreeWords[entityList]; entityList ← NIL}; IF partList # NIL THEN {FreeWords[partList]; partList ← NIL}; Heap.systemMDSZone.FREE[@data] END; SetDocumentCreationDate: PUBLIC PROCEDURE [date: LONG STRING] = {MakeMeACopy[@data.documentCreationDate, date]}; SetDocumentUserName: PUBLIC PROCEDURE [user: LONG STRING] = {MakeMeACopy[@data.documentUserName, user]}; SetHeaderText: PUBLIC PROCEDURE [header: LONG STRING, pageNumbers: BOOLEAN] = {MakeMeACopy[@data.pageHeader, header]; data.headerPageNumbers ← pageNumbers}; SetTrailerText: PUBLIC PROCEDURE [trailer: LONG STRING, pageNumbers: BOOLEAN] = {MakeMeACopy[@data.pageTrailer, trailer]; data.trailerPageNumbers ← pageNumbers}; SetNumberOfCopies: PUBLIC PROCEDURE [copies: CARDINAL] = {data.numberOfCopies ← copies}; PutFontInTable: PUBLIC PROCEDURE [ index: FontIndex, family: LONG STRING, size: Points] = BEGIN OPEN data; IF userFonts = NIL THEN ERROR BadParameters; IF index NOT IN FontIndex THEN ERROR BadParameters; IF family.length NOT IN [1..19] THEN ERROR 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 => ERROR BadParameters; ENDLOOP; END; SetMargins: PUBLIC PROCEDURE [l, r, t, b: Mica] = BEGIN OPEN data; IF pageActive THEN ERROR 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] = {data.tabWidth ← tab}; SetCurrentLineLeading: PUBLIC PROCEDURE [lead: Mica] = {data.lineLeading ← lead}; Start: PUBLIC PROCEDURE [docName: LONG STRING, file: Streams.Handle] = BEGIN OPEN data; IF data = NIL THEN Initialize[]; IF active THEN RETURN; pressFile ← file; MakeMeACopy[@documentFileName, docName]; IF documentCreationDate = NIL THEN BEGIN OPEN Time; time: STRING = [18]; Append[time, Unpack[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; Streams.SetIndex[pressFile, 0]; 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 OPEN data; fd: LONG POINTER = entityList; -- build Font Directory in random buffer fp: LONG POINTER TO PressFormat.FE ← fd; dd: LONG POINTER TO PressFormat.DDV = fd; -- build Document Directory in random buffer now: Time.Packed ← Time.Current[]; numberOfPartRecords, firstPartRecord: CARDINAL; IF lineActive THEN EndCurrentLine[]; IF pageActive THEN EndCurrentPage[]; IF paperActive THEN EndCurrentPaper[]; -- send Font Directory 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 [] ← Streams.PutBlock[pressFile, fd, wppr]; Zero[fd, wppr]; fp ← fd; END; END; ENDLOOP; [] ← Streams.PutBlock[pressFile, fd, wppr]; AppendPartItem[PressFormat.PETypeFont, 0]; -- send off Part Directory firstPartRecord ← currentRecordNumber; numberOfPartRecords ← (partIndex*SIZE[PressFormat.PE] + wppr - 1)/wppr; [] ← Streams.PutBlock[pressFile, partList, numberOfPartRecords*wppr]; -- send off Document Directory - use entity buffer Zero[dd, wppr]; 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 PackString[documentFileName, @dd.FileStr]; PackString[documentUserName, @dd.CreatStr]; PackString[documentCreationDate, @dd.DateStr]; currentRecordNumber ← currentRecordNumber + 1; [] ← Streams.PutBlock[pressFile, dd, wppr]; active ← FALSE; END; Abort: PUBLIC PROCEDURE = {data.active ← FALSE}; String: PUBLIC PROCEDURE [s: LONG STRING] = {FOR i: CARDINAL IN [0..s.length) DO Character[s[i]] ENDLOOP}; PieceOfLine: PUBLIC PROCEDURE [s: LONG STRING, width: Mica] = BEGIN -- can't OPEN data; because of width: IF ~data.lineActive THEN BEGIN OPEN data; -- 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 Streams.PutChar[data.pressFile, s[i]]; ENDLOOP; data.lineCharacters ← data.lineCharacters + s.length; data.currentX ← data.currentX + width; END; GetWidthOfString: PUBLIC PROCEDURE [s: LONG 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 OPEN data; IF vSpaceWidth AND c = SP THEN RETURN[spaceWidth]; w ← currentFontPointer.widths[c]; IF w = magicNonPrintingWidth THEN w ← 0; END; GetHeightOfFont: PUBLIC PROCEDURE [font: FontIndex] RETURNS [Mica] = BEGIN OPEN data; IF font NOT IN FontIndex OR userFonts[font].family = NIL THEN ERROR BadParameters; RETURN[PointsToMicas[userFonts[font].size]] END; Character: PUBLIC PROCEDURE [c: CHARACTER] = BEGIN OPEN data; 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[]; Streams.PutChar[pressFile, c]; lineCharacters ← lineCharacters + 1; currentX ← currentX + charWidth; END; END; DoTAB: PROCEDURE = BEGIN OPEN data; 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 OPEN data; 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 OPEN data; 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["**"L]; END; DoFF: PROCEDURE = -- FF, FF will get you an empty page {IF ~data.pageActive THEN BeginPage[]; EndCurrentPage[]}; DoPageOverflow: PROCEDURE = {EndCurrentPage[]; BeginPage[]}; -- leave page active SetMode: PUBLIC PROCEDURE [columns: CARDINAL, between: Mica, mode: Mode] = BEGIN OPEN data; 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 => ERROR; width ← width - (numberOfColumns - 1)*(spaceBetweenColumns); width ← LOOPHOLE[width, CARDINAL]/numberOfColumns; END; SetCurrentPosition: PUBLIC PROCEDURE [x, y: Mica] = BEGIN OPEN data; IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; currentX ← x; currentY ← y; END; GetCurrentPageNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {RETURN[data.currentPageNumber]}; SetCurrentPageNumber: PUBLIC PROCEDURE [pn: CARDINAL] = {data.currentPageNumber ← pn}; GetCurrentPosition: PUBLIC PROCEDURE RETURNS [x, y: Mica] = {IF ~data.pageActive THEN BeginPage[]; RETURN[data.currentX, data.currentY]}; SetWidthOfSpace: PUBLIC PROCEDURE [w: Mica] = BEGIN OPEN data; 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 OPEN data; IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; AppendEntityByte[EResetSpace]; vSpaceWidth ← FALSE; END; DrawRectangle: PUBLIC PROCEDURE [w, h: Mica] = BEGIN OPEN data; IF ~pageActive THEN BeginPage[]; IF lineActive THEN EndCurrentLine[]; AppendEntityByte[ESetX]; AppendEntityWord[IF landscape THEN height - currentY ELSE currentX]; AppendEntityByte[ESetY]; AppendEntityWord[IF landscape THEN currentX ELSE currentY]; AppendEntityByte[EShowRectangle]; AppendEntityWord[IF landscape THEN h ELSE w]; AppendEntityWord[IF landscape THEN w ELSE h]; END; BeginLine: PROCEDURE = BEGIN OPEN data; 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 OPEN data; SELECT lineCharacters FROM 0 => RETURN; IN [1..40B] => BEGIN AppendEntityByte[EShowShort + lineCharacters - 1]; END; IN [40B..400B] => BEGIN AppendEntityByte[EShow]; AppendEntityByte[lineCharacters]; END; ENDCASE => ERROR InternalError; paperCharacters ← paperCharacters + lineCharacters; pageCharacters ← pageCharacters + lineCharacters; lineCharacters ← 0; END; EndCurrentLine: PROCEDURE = BEGIN OPEN data; temp: Mica; IF ~lineActive THEN ERROR InternalError; FlushBuffer[]; IF landscape THEN BEGIN temp ← height - currentY; entityList[fixupXIndex] ← Inline.HighByte[temp]; entityList[fixupXIndex + 1] ← Inline.LowByte[temp]; END ELSE BEGIN temp ← currentY - lineHeight - lineLeading; entityList[fixupYIndex] ← Inline.HighByte[temp]; entityList[fixupYIndex + 1] ← Inline.LowByte[temp]; END; lineActive ← FALSE; END; BeginPage: PROCEDURE = BEGIN OPEN data; IF pageActive THEN ERROR InternalError; IF ~paperActive THEN BeginPaper[]; firstEntityIndexOfPage ← entityIndex; firstPageCharacter ← paperCharacters; pageCharacters ← 0; pageActive ← TRUE; Headers[]; currentX ← 0; currentY ← height; IF currentFontPointer.index NOT IN FontIndex THEN ERROR InternalError; IF currentFontPointer.index # 0 THEN AppendEntityByte[EFont + currentFontPointer.index]; END; EndCurrentPage: PROCEDURE = BEGIN OPEN data; cardinal: CARDINAL; header, trailer: Mica ← 0; -- fudge for headers/trailers IF ~pageActive THEN ERROR 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 OPEN data; buffer: STRING = [4]; fontPointer: LONG 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[]; LongString.AppendDecimal[buffer, currentPageNumber]; SkipSomeSpace[width - buffer.length*widthOfHeaderDigit - currentX]; String[buffer]; EndCurrentLine[]; currentFontPointer ← fontPointer; END; Trailers: PROCEDURE = BEGIN OPEN data; buffer: STRING = [4]; fontPointer: LONG 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[]; LongString.AppendDecimal[buffer, currentPageNumber]; SkipSomeSpace[width - buffer.length*widthOfHeaderDigit - currentX]; String[buffer]; EndCurrentLine[]; currentFontPointer ← fontPointer; END; BeginPaper: PROCEDURE = BEGIN OPEN data; IF paperActive THEN ERROR InternalError; Zero[entityList, wordsPerEntityList]; entityIndex ← 0; AppendEntityWord[0]; -- marker firstDataRecordOfPaper ← currentRecordNumber; paperActive ← TRUE; paperCharacters ← 0; currentColumn ← 0; END; EndCurrentPaper: PROCEDURE = BEGIN OPEN data; pad: CARDINAL; IF pageActive THEN EndCurrentPage[]; THROUGH [0..2 + (Inline.LowHalf[Streams.GetIndex[pressFile]] MOD 2)) DO -- word boundary + 2 zeros Streams.PutByte[pressFile, 0] ENDLOOP; [] ← Streams.PutBlock[pressFile, entityList, entityIndex/2]; pad ← wppr - (CARDINAL[Inline.LowHalf[Streams.GetIndex[pressFile]]] MOD bppr)/2; THROUGH [0..pad) DO Streams.PutByte[pressFile, 0]; Streams.PutByte[pressFile, 0]; ENDLOOP; AppendPartItem[PressFormat.PETypePage, pad]; paperActive ← FALSE; END; SetCurrentFont: PUBLIC PROCEDURE [font: FontIndex, w: FontWeight, s: FontSlope] = BEGIN OPEN data; ff: FontFace ← 0; new: LONG POINTER TO PressFontInfo; IF data = NIL THEN ERROR BadParameters; IF font NOT IN FontIndex OR userFonts[font].family = NIL THEN ERROR BadParameters; SELECT w FROM medium => ff ← ff + 0; bold => ff ← ff + 2; --light => ff ← ff+4; ENDCASE => ERROR BadParameters; SELECT s FROM regular => ff ← ff + 0; italic => ff ← ff + 1; ENDCASE => ERROR BadParameters; --SELECT expansion FROM -- regular => ff ← ff+0; -- condensed => ff ← ff+6; -- expanded => ff ← ff+12; -- ENDCASE => ERROR 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 NOT IN FontIndex THEN ERROR 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 OPEN data; i: FontIndex; pf: LONG POINTER TO PressFontInfo; rot: CARDINAL ← IF landscape THEN 60*90 ELSE 0; family: LONG 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 => ERROR MoreThan16Fonts; ENDLOOP; IF pf.user = NIL THEN BEGIN OPEN pf; pf↑ ← [index: i, face: ff, widths: LongStorage.Words[SIZE[WidthArray]], rotation: rot, user: @userFonts[font]]; -- initialize to something legal height ← PointsToMicas[points]; width ← IF landscape THEN widthGacha6 ELSE widthGacha8; pf.widths↑ ← ALL[width]; [fBBox, fBBoy, width, ] ← PressUtilities.FindFontWidths[ family, points, w, s, pf.widths ! Segments.FileNameProblem[], PressUtilities.ErrorReadingFontWidths, PressUtilities.FontNotInFontsDotWidths => CONTINUE]; END; IF landscape THEN userFonts[font].landscape[ff] ← pf ELSE userFonts[font].portrait[ff] ← pf; END; PointsToMicas: PROCEDURE [p: Points] RETURNS [Mica] = BEGIN OPEN Inline; RETURN[LongDiv[LongMult[micasPerInch, p], pointsPerInch]]; END; BeSureFontZeroExists: PROCEDURE = BEGIN OPEN data; 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"L, 8]; pressFonts[i] ← [i, ff, , , , , LongStorage.Words[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 OPEN data; FOR i: FontIndex IN FontIndex DO IF pressFonts[i].widths # NIL THEN LongStorage.FreeWords[pressFonts[i].widths]; pressFonts[i] ← [0, 0, 0, 0, 0, 0, NIL, 0, NIL]; ENDLOOP; END; AppendEntityByte: PROCEDURE [b: BYTE] = BEGIN OPEN data; IF entityIndex = bytesPerEntityList THEN ERROR ELBufferOverflow; entityList[entityIndex] ← b; entityIndex ← entityIndex + 1; END; AppendEntityWord: PROCEDURE [w: UNSPECIFIED] = {AppendEntityByte[Inline.HighByte[w]]; AppendEntityByte[Inline.LowByte[w]]}; AppendPartItem: PROCEDURE [type: PressFormat.PartType, last: CARDINAL] = BEGIN OPEN data; page: CARDINAL; IF partIndex = partsPerDocument THEN ERROR PartBufferOverflow; page ← Inline.LowHalf[Streams.GetIndex[pressFile] / bppr]; partList[partIndex] ← [Type: type, pStart: firstDataRecordOfPaper, pRecs: page - firstDataRecordOfPaper, Padding: last]; partIndex ← partIndex + 1; currentRecordNumber ← page; END; KillString: PROCEDURE [where: LONG POINTER TO LONG STRING] = {LongStorage.Free[where↑]; where↑ ← NIL}; MakeMeACopy: PROCEDURE [where: LONG POINTER TO LONG STRING, newString: LONG STRING] = BEGIN OPEN LongStorage; KillString[where]; IF newString # NIL THEN BEGIN where↑ ← String[newString.length]; LongString.AppendString[where↑, newString]; END; END; Zero: PROCEDURE [p: LONG POINTER, nwords: CARDINAL] = BEGIN IF nwords = 0 THEN RETURN; p↑ ← 0; Inline.LongCOPY[from: p, to: p+1, nwords: nwords-1]; END; PackString: PROCEDURE [s: LONG STRING, p: LONG POINTER] = BEGIN ps: LONG POINTER TO PACKED ARRAY OF UNSPECIFIED[0..255] = p; FOR i: CARDINAL IN [0..s.length) DO ps[i+1] ← s[i] ENDLOOP; ps[0] ← s.length; END; -- initialization END.