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