DIRECTORY
AltoFileDefs: FROM "AltoFileDefs",
BitBltDefs: FROM "BitBltDefs",
LongDefs: FROM "LongDefs", --long LongDefs.BITBLT different for D0,Alto
GraphicsDefs: FROM "GraphicsDefs",
InlineDefs: FROM "InlineDefs",
SegmentDefs: FROM "SegmentDefs",
StreamDefs: FROM "StreamDefs",
StringDefs: FROM "StringDefs",
SystemDefs: FROM "SystemDefs";

TextFns: PROGRAM IMPORTS LongDefs, GraphicsDefs, InlineDefs, StreamDefs, StringDefs, SegmentDefs, SystemDefs EXPORTS GraphicsDefs =
BEGIN
--stuff for screen font manipulation
TypeLen: TYPE = MACHINE DEPENDENT RECORD
[type: [0..77B],
len: [0..1777B]
];
FPName: TYPE = MACHINE DEPENDENT RECORD
[ directory,random,nolog: [0..1],
serial1: [0..17777B],
serial2: CARDINAL,
version: CARDINAL,
blank: UNSPECIFIED,
leaderDA: AltoFileDefs.vDA,
ch: PACKED ARRAY [-1..77B] OF CHARACTER
];
fontDescriptor: TYPE = RECORD
[link: POINTER TO fontDescriptor,
fp: AltoFileDefs.FP,
name: STRING,
strikeHandle: POINTER TO GraphicsDefs.StrikeFont
];
fontListHandle: POINTER TO fontDescriptor ← ReadALFonts[];
SelfRelative: TYPE = SelfRelativeBase RELATIVE POINTER TO ALChar;
SelfRelativeBase: TYPE = BASE POINTER TO SelfRelative;
ALFontHead: TYPE = RECORD
[height: CARDINAL,
proportional: [0..1],
baseline: [0..177B],
maxWidth: [0..377B],
Pointers: ARRAY [0..377B] OF SelfRelative
];
ALChar: TYPE = RECORD
[XW: CARDINAL,
HD: [0..377B],
XH: [0..377B]
];
ALCharHandle: TYPE = BASE POINTER TO ALChar;
RelativeBitmapPointer: TYPE = ALCharHandle RELATIVE POINTER TO ARRAY OF CARDINAL;
xx: ARRAY [0..SIZE[BitBltDefs.BBTable]] OF UNSPECIFIED;
textBLT: POINTER TO BitBltDefs.BBTable ←
@xx[LOOPHOLE[@xx,CARDINAL] MOD 2];

Allocate: PROCEDURE [nwords: CARDINAL] RETURNS [LONG POINTER];
Free: PROCEDURE [LONG POINTER];

PutText
: PUBLIC PROCEDURE [s: STRING,x,y: CARDINAL,font: POINTER TO GraphicsDefs.StrikeFont,mode: GraphicsDefs.textMode,b: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [nextX: CARDINAL] =
BEGIN RETURN[SetText[s,x,y,paint,font,mode,b]];END;
ReplaceText: PUBLIC PROCEDURE [s: STRING,x,y: CARDINAL,font: POINTER TO GraphicsDefs.StrikeFont,mode: GraphicsDefs.textMode,b: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [nextX: CARDINAL] =
BEGIN RETURN[SetText[s,x,y,replace,font,mode,b]];END;
EraseText: PUBLIC PROCEDURE [s: STRING,x,y: CARDINAL,font: POINTER TO GraphicsDefs.StrikeFont,mode: GraphicsDefs.textMode,b: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [nextX: CARDINAL] =
BEGIN RETURN[SetText[s,x,y,erase,font,mode,b]];END;
XorText: PUBLIC PROCEDURE [s: STRING,x,y: CARDINAL,font: POINTER TO GraphicsDefs.StrikeFont,mode: GraphicsDefs.textMode,b: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [nextX: CARDINAL] =
BEGIN RETURN[SetText[s,x,y,invert,font,mode,b]];END;

MakeLongPointer
: PROCEDURE [ptr: POINTER,bank: CARDINAL] RETURNS [LONG POINTER] = MACHINE CODE BEGIN END;

SetText: PUBLIC PROCEDURE [s: STRING,x,y: CARDINAL,fn: BitBltDefs.BBoperation,font: POINTER TO GraphicsDefs.StrikeFont,mode: GraphicsDefs.textMode,b: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [nextX: CARDINAL] =
BEGIN
i: CARDINAL;
width: CARDINAL;
IF b = NIL THEN b ← GraphicsDefs.GetDefaultBitmapHandle[];

nextX ← x;
IF INTEGER[y+font.descent] < 0 THEN RETURN[nextX];
y ← y - font.ascent;
--the (x,y) coordinate refers to the baseline
IF INTEGER[y] > INTEGER[b.nLines] THEN RETURN[nextX];

textBLT.function ← fn;
textBLT.dlbca ← MakeLongPointer[b.bits,b.bank];
textBLT.dbmr ← b.nWords;
textBLT.sty ← textBLT.dw ← textBLT.dh ← 0;
textBLT.slbca ← MakeLongPointer[font.bitmap,font.bank];
textBLT.sbmr ← font.wordsPerScanLine;

SELECT mode FROM
GraphicsDefs.normal,GraphicsDefs.bold =>
BEGIN
textBLT.dh ← MIN[font.ascent+font.descent,MAX[0,INTEGER[b.nLines-y]]];
IF INTEGER[y] < 0 THEN
BEGIN
textBLT.dh ← textBLT.dh + y;
IF INTEGER[textBLT.dh] < 0 THEN RETURN[nextX];
textBLT.sty ← textBLT.sty - y;
y←0;
END;
FOR i IN [0..s.length) DO
IF INTEGER[nextX] >= INTEGER[b.nBits] THEN RETURN[nextX];
textBLT.dlx ← nextX;
textBLT.dty ← y;
width ← MIN[font.xInSegment[LOOPHOLE[s[i],CARDINAL]+1] -
font.xInSegment[LOOPHOLE[s[i],CARDINAL]],
MAX[0,INTEGER[b.nBits-nextX]]];
textBLT.dw ← width;
textBLT.slx ← font.xInSegment[LOOPHOLE[s[i],CARDINAL]];
IF INTEGER[nextX] < 0 THEN
BEGIN
textBLT.dw ← MAX[0,INTEGER[textBLT.dw+nextX]];
textBLT.slx ← textBLT.slx - nextX;
textBLT.dlx ← 0;
END;
LongDefs.BITBLT[textBLT];
IF mode = GraphicsDefs.bold THEN
BEGIN
textBLT.dlx ← textBLT.dlx+1;
textBLT.dw ← MIN[textBLT.dw,MAX[0,INTEGER[b.nBits-nextX]]];
LongDefs.BITBLT[textBLT];
END;
nextX ← nextX + width;
ENDLOOP;
END; -- case normal
GraphicsDefs.italic,GraphicsDefs.bold+GraphicsDefs.italic =>
BEGIN
yStep: CARDINAL = 4;
numYSteps: CARDINAL = (font.ascent+font.descent+yStep-1)/yStep;
firstStep: CARDINAL ← font.ascent+font.descent - (numYSteps-1)*yStep;
iMin: INTEGER = -((numYSteps-1)/2);
iMax: INTEGER = numYSteps/2;
italicStep: INTEGER;
FOR i IN [0..s.length) DO
IF INTEGER[nextX] >= INTEGER[b.nBits] THEN RETURN[nextX];
textBLT.dty ← y;
width ← MIN[font.xInSegment[LOOPHOLE[s[i],CARDINAL]+1] -
font.xInSegment[LOOPHOLE[s[i],CARDINAL]],
MAX[0,INTEGER[b.nBits-nextX]]];
textBLT.dw ← width;
textBLT.slx ← font.xInSegment[LOOPHOLE[s[i],CARDINAL]];
textBLT.sty ← 0;
textBLT.dh ← MIN[MAX[0,INTEGER[b.nLines-y]],firstStep];
FOR italicStep DECREASING IN [iMin..iMax] DO
textBLT.dlx ← nextX + italicStep;
textBLT.dw ← MIN[textBLT.dw,MAX[0,INTEGER[b.nBits-textBLT.dlx]]];
IF INTEGER[textBLT.dlx] < 0 THEN
BEGIN
textBLT.dw ← MAX[0,INTEGER[textBLT.dw+textBLT.dlx]];
textBLT.slx ← textBLT.slx - textBLT.dlx;
textBLT.dlx ← 0;
END;
IF INTEGER[textBLT.dty + textBLT.dh] > 0 THEN
BEGIN
IF INTEGER[textBLT.dty] < 0 THEN
BEGIN
textBLT.sty ← textBLT.sty - textBLT.dty;
textBLT.dh ← textBLT.dh + textBLT.dty;
textBLT.dty ← 0;
END;
LongDefs.BITBLT[textBLT];
IF mode = GraphicsDefs.bold+GraphicsDefs.italic THEN
BEGIN
textBLT.dlx ← textBLT.dlx+1;
textBLT.dw ← MIN[textBLT.dw,MAX[0,INTEGER[b.nBits-textBLT.dlx]]];
LongDefs.BITBLT[textBLT];
END;
END;
textBLT.sty ← textBLT.sty + textBLT.dh;
textBLT.dty ← textBLT.dty + textBLT.dh;
textBLT.dh ← MIN[MAX[0,INTEGER[b.nLines-textBLT.dty]],yStep];
ENDLOOP;
nextX ← nextX + width;
ENDLOOP;
END;--end of italic case
ENDCASE;
END;

GetStrikeHandle
: PUBLIC PROCEDURE [fontName: STRING] RETURNS [POINTER TO GraphicsDefs.StrikeFont] =
BEGIN
fileList: POINTER TO fontDescriptor ← fontListHandle;
UNTIL fileList = NIL DO
IF StringDefs.EquivalentString[fileList.name,fontName] THEN
BEGIN
IF fileList.strikeHandle = NIL THEN fileList↑.strikeHandle ← ALtoStrike[@fileList.fp];
RETURN[fileList.strikeHandle];
END;
fileList ← fileList.link;
ENDLOOP;
RETURN[NIL];
END;
GetFontNameArray: PUBLIC PROCEDURE RETURNS [POINTER TO ARRAY [0..0) OF STRING,CARDINAL] =
BEGIN
i: CARDINAL ← 0;
p: POINTER TO ARRAY [0..0) OF STRING;
fileList: POINTER TO fontDescriptor ← fontListHandle;
UNTIL fileList = NIL DO
i←i+1;
fileList ← fileList.link;
ENDLOOP;
p ← SystemDefs.AllocateHeapNode[i];
i←0;
fileList ← fontListHandle;
UNTIL fileList = NIL DO
p[i] ← fileList.name;
i←i+1;
fileList ← fileList.link;
ENDLOOP;
RETURN[p,i];
END;

ReadALFonts: PROCEDURE RETURNS [fontDescriptorHandle: POINTER TO fontDescriptor] =
BEGIN
DVHead: TypeLen;
DVBody: FPName;
name: STRING ← [100];
ext: STRING ← [100];
newString: STRING;
firstLinkPointer: POINTER TO fontDescriptor ← NIL;
linkPointer: POINTER TO UNSPECIFIED ← @firstLinkPointer;
i: CARDINAL;
BCPLLen: CARDINAL;
mem: POINTER TO fontDescriptor;
S: StreamDefs.DiskHandle ← StreamDefs.NewWordStream["sysdir",StreamDefs.Read];
S.reset[S];

UNTIL S.endof[S] DO
DVHead←S.get[S];
[] ← StreamDefs.ReadBlock[S,@DVBody,MIN[SIZE[FPName],DVHead.len-1]];
FOR i IN [SIZE[FPName]..DVHead.len-1) DO [] ← S.get[S];ENDLOOP;
IF DVHead.type = 1 THEN--its a file
BEGIN
name.length←ext.length←0;
BCPLLen ← LOOPHOLE[DVBody.ch[-1],CARDINAL];
FOR i IN [0..BCPLLen) DO
IF DVBody.ch[i] = ’. THEN EXIT;
IF DVBody.ch[i] = ’! THEN EXIT;--strip version
StringDefs.AppendChar[name,DVBody.ch[i]];
ENDLOOP;
IF DVBody.ch[i]=’. THEN--grab
FOR i IN [name.length+1..BCPLLen) DO
IF DVBody.ch[i] = ’. THEN EXIT;
IF DVBody.ch[i] = ’! THEN EXIT;--strip version
StringDefs.AppendChar[ext,DVBody.ch[i]];
ENDLOOP;
IF NOT StringDefs.EquivalentString["AL",ext] THEN LOOP;--not me
mem ← SystemDefs.AllocateHeapNode[SIZE[fontDescriptor]];
newString ← SystemDefs.AllocateHeapString[name.length];
mem↑.link←NIL;
mem↑.fp.serial.bits.directory ← DVBody.directory;
mem↑.fp.serial.bits.random ← DVBody.random;
mem↑.fp.serial.bits.nolog ← DVBody.nolog;
mem↑.fp.serial.part1 ← DVBody.serial1;
mem↑.fp.serial.part2 ← DVBody.serial2;
mem↑.fp.leaderDA ← DVBody.leaderDA;
mem↑.name ← newString;
mem↑.strikeHandle ← NIL;
newString.length ← 0;
StringDefs.AppendString[newString,name];
linkPointer↑ ← mem;
linkPointer ← mem;
END;
ENDLOOP;
S.destroy[S];
RETURN[firstLinkPointer];
END;

ALtoStrike: PUBLIC PROCEDURE [fp: POINTER TO AltoFileDefs.FP] RETURNS [POINTER TO GraphicsDefs.StrikeFont] =
BEGIN
i: CARDINAL;
strikeWidth: CARDINAL ← 0;
currentWidth: CARDINAL;
strikeWidthMax: CARDINAL ← 0;
ALFileHandle: SegmentDefs.FileHandle ← SegmentDefs.InsertFile[fp,SegmentDefs.Read];
ALSegment: SegmentDefs.FileSegmentHandle ←
SegmentDefs.NewFileSegment[ALFileHandle,SegmentDefs.DefaultBase,
SegmentDefs.DefaultPages,SegmentDefs.Read];
--array big enough for 18 point fonts
ALData: POINTER TO ALFontHead;
XHData: ALCharHandle;
sf: POINTER TO GraphicsDefs.StrikeFont;
srbase: SelfRelativeBase;
strikeIndexLen: CARDINAL = 256;
xIndex: POINTER TO ARRAY [0..strikeIndexLen) OF CARDINAL ←
SystemDefs.AllocateHeapNode[strikeIndexLen];
x: ARRAY [0..SIZE[BitBltDefs.BBTable]] OF UNSPECIFIED;
BLT: POINTER TO BitBltDefs.BBTable ←
@x[LOOPHOLE[@x,CARDINAL] MOD 2];

SegmentDefs.SwapIn[ALSegment];
ALData ← SegmentDefs.FileSegmentAddress[ALSegment];

--calculate width of strike raster
FOR i IN [0..377B] DO
xIndex[i] ← strikeWidth;
srbase ← @ALData.Pointers[i];
XHData ← @srbase[srbase↑];
currentWidth←0;
UNTIL InlineDefs.BITAND[XHData.XW,1] = 1 DO--Read extension characters
currentWidth ← currentWidth+16;
srbase ← @ALData.Pointers[XHData.XW/2];
XHData ← @srbase[srbase↑];
ENDLOOP;
currentWidth ← currentWidth + XHData.XW/2;
strikeWidth ← strikeWidth + currentWidth;
IF currentWidth > strikeWidthMax THEN strikeWidthMax ← currentWidth;
ENDLOOP;
xIndex[377B] ← strikeWidth;

sf ← SystemDefs.AllocateHeapNode[SIZE[GraphicsDefs.StrikeFont]];
sf↑ ← [oneBit: 1,bank: ,index: 0,fixed: 1-ALData↑.proportional,
minCode: 0,maxCode: strikeIndexLen-1,maxWidth: strikeWidthMax,
strikeBodyWordLength: ((strikeWidth+15)/16)*ALData↑.height,
ascent: ALData↑.baseline,descent: ALData↑.height - ALData↑.baseline,
wordsPerScanLine: ((strikeWidth+15)/16),bitmap: NIL,xInSegment: LOOPHOLE[xIndex]];
BLT↑ ← [ptrs: long,pad: 0,sourcealt: FALSE,destalt: FALSE,sourcetype: block,
function: replace,unused: 0,dbca: NIL,dbmr: sf.wordsPerScanLine,dlx: 0,dty: 0,
dw: strikeWidth,dh: sf.ascent+sf.descent,sbca: NIL,sbmr: 1,slx: 0,sty: 0,
gray0: 0,gray1: 0,gray2: 0,gray3: 0,dlbca:,slbca:];

[Allocate,Free] ← GraphicsDefs.GetXMAlloc[];
BLT.dlbca ← Allocate[sf.strikeBodyWordLength];
sf.bank ← InlineDefs.HighHalf[BLT.dlbca];
sf.bitmap ← InlineDefs.LowHalf[BLT.dlbca];
BLT.sourcetype←gray;LongDefs.BITBLT[BLT]; --erase area
BLT.sourcetype ← block;


--now, slam in all the characters
FOR i IN [0..377B] DO
BEGIN
offset: RelativeBitmapPointer;
srbase ← @ALData.Pointers[i];
XHData ← @srbase[srbase↑];
BLT.dlx ← xIndex[i];
BLT.dty ← XHData.HD;
BLT.dh ← XHData.XH;
offset ← LOOPHOLE[-XHData.XH];
BLT.slbca ← @XHData[offset];
UNTIL InlineDefs.BITAND[XHData.XW,1] = 1 DO
BLT.dw ← 16;
LongDefs.BITBLT[BLT];
srbase ← @ALData.Pointers[XHData.XW/2];
XHData ← @srbase[srbase↑];
BLT.dlx ← BLT.dlx + 16;
BLT.dty ← XHData.HD;
BLT.dh ← XHData.XH;
offset ← LOOPHOLE[-XHData.XH];
BLT.slbca ← @XHData[offset];
ENDLOOP;
BLT.dw ← XHData.XW/2;
LongDefs.BITBLT[BLT];

END;
ENDLOOP;

SegmentDefs.Unlock[ALSegment];
SegmentDefs.DeleteFileSegment[ALSegment];

RETURN[sf];
END;
--initialization of textBLT
textBLT↑ ← [ptrs: long,pad: 0,sourcealt: FALSE,destalt: FALSE,
sourcetype: block,function: ,unused: 0,dbca: NIL,dbmr: ,dlx: ,dty: ,dw: 0,dh: 0,
sbca: NIL,sbmr: ,slx: ,sty: 0,gray0: ,gray1: ,gray2: ,gray3: ,slbca: ,dlbca: ];
END.