-- Compiler Font/nb
-- Stone December 2, 1981  9:30 PM
-- Stone+Tiberi, February 7, 1980  11:40 AM
-- implementing module for griffin fonts

DIRECTORY
	GriffinMemoryDefs: FROM "GriffinMemoryDefs",
	StringDefs: FROM "StringDefs" USING [AppendString, EquivalentString, LowerCase],
	MiscDefs: FROM "MiscDefs" USING [Zero, SetBlock],
	AltoDefs: FROM "AltoDefs" USING [BytesPerPage],
	StyleDefs: FROM "StyleDefs",
	InlineDefs: FROM "InlineDefs"
		USING [BITOR, BITAND, LongMult, LongDiv,
		LongDivMod, LongNumber],
	GriffinFontDefs: FROM "GriffinFontDefs",
	IxDefs: FROM "IxDefs",
	GriffinFileDefs: FROM "GriffinFileDefs",
	AltoFileDefs: FROM "AltoFileDefs" USING [FA],
	DirectoryDefs: FROM "DirectoryDefs"
		USING [EnumerateDirectory],
	SegmentDefs: FROM "SegmentDefs"
		USING [FileNameError],
	ScreenDefs: FROM "ScreenDefs"
		USING [Bitmap, Block, BLTBlockInScreen, EraseBox],
	PointDefs: FROM "PointDefs"
		USING [ScrPt, ObjPt, X, Y, ObjToScr, ScrToObj,
		ObjValToScrVal, ScrValToObjVal],
	Real: FROM "Real" USING [RoundI,RoundC],
	StreamDefs: FROM "StreamDefs"
		USING [NewWordStream, Read, Append, Write,
		DiskHandle, ReadBlock, WriteBlock, GetFA,
		JumpToFA, FileLength, CleanupDiskStream,
		TruncateDiskStream, CreateWordStream, StreamIndex,
		SetIndex];



Font:  PROGRAM
	IMPORTS GriffinMemoryDefs, StreamDefs, StringDefs,
	MiscDefs, InlineDefs, ScreenDefs, GriffinFileDefs,
	DirectoryDefs, PointDefs, Real, SegmentDefs
	EXPORTS GriffinFontDefs =
BEGIN OPEN GriffinFontDefs, InlineDefs; 

X: CARDINAL = PointDefs.X;
Y: CARDINAL = PointDefs.Y;


StrikeHeader: TYPE = MACHINE DEPENDENT RECORD
	[
	ignore1: [0 .. 4), fixed: BOOLEAN, ignore2: [0 .. 8192),
	minascii: CARDINAL,
	maxascii: CARDINAL,
	maxwidth: CARDINAL,
	length: CARDINAL, -- wrong
	ascent: CARDINAL,
	descent: CARDINAL,
	xoffset: CARDINAL,
	raster: CARDINAL
	];

lStrikeHeader: CARDINAL = SIZE [StrikeHeader];

indexSize: CARDINAL = 128;
nameSize: CARDINAL = 19;

fontVersion: CARDINAL = 6;
fontPassword: CARDINAL = 143000B + fontVersion;

GriffinFontHeader: TYPE = MACHINE DEPENDENT RECORD
	[
	rotation: CARDINAL, -- in degrees
	face: CARDINAL, -- face code as in Fonts.Widths
	points: CARDINAL,
	name: PACKED ARRAY [0 .. nameSize] OF CHARACTER,
	height: CARDINAL,  -- or width, if rotated
	maxwidth: CARDINAL,  -- or height, if rotated
	ascent: CARDINAL,
	raster: CARDINAL,
	bitmapwords: CARDINAL,
	offset: ARRAY [0 .. indexSize+2] OF CARDINAL,
	displaywidth: ARRAY [0 .. indexSize+2] OF CARDINAL,
	presswidth: ARRAY [0 .. indexSize+2] OF CARDINAL
	]; -- followed by bitmap

--Width philosophy: width and height routines return
--physical height and width, as used in BitBlt.  The width table
--contains the logical width of the character; i.e., for 90 degree
--rotation, it contains the physical height. 


lGriffinFontHeader: CARDINAL = SIZE [GriffinFontHeader];

GriffinFont: TYPE = RECORD
	[
	next: GriffinFontHandle,
	fd: FontDescriptor,
	fa: AltoFileDefs.FA
	];

lGriffinFont: CARDINAL = SIZE [GriffinFont];

GriffinFontHandle: TYPE = POINTER TO GriffinFont;

-- -----------------------------------------------------------------
FontError: PUBLIC SIGNAL = CODE;
StartupFontError: PUBLIC SIGNAL[type: ErrorType] = CODE;

-- -----------------------------------------------------------------
gFontHead, currentFont, lastFont: GriffinFontHandle ← NIL;

fontHeader: GriffinFontHeader;
bitMap: ScreenDefs.Bitmap ← [0, 0, 0, 0, NIL]; -- because it mustn't go away

gHandle: StreamDefs.DiskHandle ← StreamDefs.NewWordStream ["Griffin.Fonts",
	StreamDefs.Read + StreamDefs.Write + StreamDefs.Append];

fwHandle: StreamDefs.DiskHandle = StreamDefs.NewWordStream ["Fonts.Widths",
	StreamDefs.Read ! SegmentDefs.FileNameError => StartupFontError[nowidths]];

-- -----------------------------------------------------------------
DisplayString: PUBLIC PROCEDURE [string: STRING, screenpt: PointDefs.ScrPt, anchor: StyleDefs.Anchor, orientation: StyleDefs.Orientation, fd: FontDescriptorHandle] =
BEGIN
j: CARDINAL;
twiddle: REAL;
rotation: CARDINAL=fd.rotation;
bltpt: PointDefs.ObjPt;
char: CHARACTER;
block: ScreenDefs.Block;
height, width: CARDINAL;
aligned, vertical: BOOLEAN;
tl,br: PointDefs.ScrPt;

SetUpFont [fd];
block.bitmap ← @bitMap;
height ← StringHeight[string, fd, orientation];
width ← StringWidth[string, fd, orientation];
tl ← PointDefs.ObjToScr[TopLeft[PointDefs.ScrToObj[screenpt], height,
	width, anchor, orientation, fontHeader.rotation]];

bltpt ← PointDefs.ScrToObj[tl];
br ← [tl[X]+PointDefs.ObjValToScrVal[width]-1,
	tl[Y]+PointDefs.ObjValToScrVal[height]-1];
ScreenDefs.EraseBox [tl, br];

vertical ← orientation = or90 OR orientation = or270;
aligned ← IF vertical
	THEN rotation = Rot0Degrees OR rotation = Rot180Degrees
	ELSE rotation = Rot90Degrees OR rotation = Rot270Degrees;
SELECT orientation FROM
or180 => bltpt[X] ← bltpt[X]+width;
or90 => bltpt[Y] ← bltpt[Y]-height;
ENDCASE;

FOR j IN [0 .. string.length)
	DO
	char ← string [j];
	block.h ← FDHeight [char];
	block.w ← FDWidth [char];
	block.ty ← 0;
	block.lx ← FLeft [char] ;
	twiddle ← IF vertical THEN (MaxWidth[fd] - FPWidth[char])/2
			ELSE (MaxHeight[fd] - FPHeight[char])/2;
	IF aligned THEN IF vertical
		THEN bltpt[X] ← bltpt[X] + twiddle
		ELSE bltpt[Y] ← bltpt[Y] - twiddle;
	SELECT orientation FROM
		or90 => bltpt[Y] ← bltpt[Y] + FPHeight[char];
		or180 => bltpt[X] ← bltpt[X] - FPWidth[char];
	ENDCASE;
	ScreenDefs.BLTBlockInScreen [@block,
		PointDefs.ObjToScr[bltpt], paint];
	SELECT orientation FROM
		or0 => bltpt[X] ← bltpt[X] + FPWidth[char];
		or270 => bltpt[Y] ← bltpt[Y] - FPHeight[char];
	ENDCASE;
	IF aligned THEN IF vertical
		THEN bltpt[X] ← bltpt[X] - twiddle
		ELSE bltpt[Y] ← bltpt[Y] + twiddle;
	ENDLOOP;
END;


-- -----------------------------------------------------------------
TopLeft: PUBLIC PROCEDURE [anchorPt: PointDefs.ObjPt,
		height, width: CARDINAL,
		anchor: StyleDefs.Anchor,
		orientation: StyleDefs.Orientation,
		rotation: CARDINAL]
	RETURNS [tl: PointDefs.ObjPt]=
BEGIN
vertical, aligned, reverse: BOOLEAN;
shift, amount: INTEGER;

tl ← anchorPt;
vertical ← orientation = or90 OR orientation = or270;
reverse ← orientation=or90 OR orientation=or180;
aligned ← IF vertical
	THEN rotation = Rot0Degrees OR rotation = Rot180Degrees
	ELSE rotation = Rot90Degrees OR rotation = Rot270Degrees;
amount ← IF vertical THEN height ELSE width;
shift ← SELECT anchor FROM
		right => IF reverse THEN 0 ELSE amount,
		left => IF reverse THEN amount ELSE 0,
		ENDCASE --center-- => amount/2;
IF vertical	--adjust for Anchor:
	THEN tl [Y] ← tl [Y] + shift
	ELSE tl [X] ← tl [X] - shift;

IF aligned THEN
-- here, if characters are rotated 90 degrees to the line, we must
-- center around the narrow amount
	BEGIN
	shift ← (IF vertical THEN width ELSE height)/2;
	IF vertical
		THEN tl [X] ← tl [X] - shift
		ELSE tl [Y] ← tl [Y] + shift;
	END;
END;

-- -----------------------------------------------------------------
BltChar: PUBLIC PROCEDURE [char: CHARACTER, tl: PointDefs.ScrPt,
	fd: FontDescriptorHandle]=
BEGIN
block: ScreenDefs.Block;

SetUpFont [fd];
block.bitmap ← @bitMap;
block.h ← FDHeight[char];
block.w ← FDWidth [char];
block.ty ← 0;
block.lx ← FLeft [char] ;
ScreenDefs.BLTBlockInScreen [@block, tl, replace];
END;

-- -----------------------------------------------------------------
Width: PUBLIC PROCEDURE [c: CHARACTER, fd: FontDescriptorHandle]
		RETURNS [CARDINAL] =
BEGIN
SetUpFont [fd];
RETURN [FPWidth [c]];
END;

-- -----------------------------------------------------------------
StringWidth: PUBLIC PROCEDURE [string: STRING, fd: FontDescriptorHandle, orientation: StyleDefs.Orientation]
		RETURNS [w: CARDINAL] =
BEGIN
j: CARDINAL;

SetUpFont [fd];
SELECT orientation FROM
or0, or180 =>
	BEGIN
	IF string=NIL THEN RETURN [0];
	w ← 0;
	FOR j IN [0 .. string.length)
		DO w ← w + FPWidth [string[j]] ENDLOOP;
	END;
or90, or270 => w ← MaxWidth[fd];
ENDCASE;
IF w#0 THEN w←w-1;
END;

-- -----------------------------------------------------------------
MaxWidth: PUBLIC PROCEDURE [fd: FontDescriptorHandle]
			RETURNS [CARDINAL] =
BEGIN
SetUpFont [fd];
RETURN [SELECT fontHeader.rotation FROM
	Rot0Degrees, Rot180Degrees =>
		Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.maxwidth]],
	ENDCASE => Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.height]]];
END;

-- -----------------------------------------------------------------
Height: PUBLIC PROCEDURE [c: CHARACTER, fd: FontDescriptorHandle] RETURNS [CARDINAL] =
BEGIN
SetUpFont [fd];
RETURN [FPHeight [c]];
END;

-- -----------------------------------------------------------------
StringHeight: PUBLIC PROCEDURE[string: STRING,
	fd:FontDescriptorHandle,
	orientation: StyleDefs.Orientation]
		RETURNS [h: CARDINAL] =
BEGIN
j: CARDINAL;

SetUpFont [fd];
SELECT orientation FROM
or0, or180 => h ← MaxHeight[fd];
or90, or270 =>
	BEGIN
	IF string=NIL THEN RETURN [0];
	h ← 0;
	FOR j IN [0 .. string.length)
		DO h ← h + FPHeight [string[j]] ENDLOOP;
	END;
ENDCASE;
IF h#0 THEN h←h-1;
END;

-- -----------------------------------------------------------------
MaxHeight: PUBLIC PROCEDURE [fd: FontDescriptorHandle]
			RETURNS [CARDINAL] =
BEGIN
SetUpFont [fd];
RETURN [SELECT fontHeader.rotation FROM
	Rot0Degrees, Rot180Degrees =>
		Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.height]],
	ENDCASE => Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.maxwidth]]];
END;

-- -----------------------------------------------------------------
FDWidth: PROCEDURE [char: CHARACTER] RETURNS [CARDINAL] = INLINE
BEGIN -- returns actual x dimension of char
rot: CARDINAL = fontHeader.rotation;
RETURN [IF rot = Rot0Degrees OR rot = Rot180Degrees
	THEN fontHeader.displaywidth[LOOPHOLE[char, CARDINAL]]
	ELSE fontHeader.height
	];
END;

-- -----------------------------------------------------------------
FDHeight: PROCEDURE [char: CHARACTER] RETURNS [CARDINAL] = INLINE
BEGIN -- returns actual y dimension of char for the display
rot: CARDINAL = fontHeader.rotation;
RETURN [IF rot = Rot0Degrees OR rot = Rot180Degrees
	THEN fontHeader.height
	ELSE fontHeader.displaywidth[LOOPHOLE[char,CARDINAL]]
	];
END;

-- -----------------------------------------------------------------
FPWidth: PROCEDURE [char: CHARACTER] RETURNS [CARDINAL] = INLINE
BEGIN -- returns actual x dimension of char
rot: CARDINAL = fontHeader.rotation;
RETURN [IF rot = Rot0Degrees OR rot = Rot180Degrees
	THEN fontHeader.presswidth[LOOPHOLE[char, CARDINAL]]
	ELSE Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.height]]
	];
END;

-- -----------------------------------------------------------------
FPHeight: PROCEDURE [char: CHARACTER] RETURNS [CARDINAL] = INLINE
BEGIN -- returns actual y dimension of char for the display
rot: CARDINAL = fontHeader.rotation;
RETURN [IF rot = Rot0Degrees OR rot = Rot180Degrees
	THEN Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.height]]
	ELSE fontHeader.presswidth[LOOPHOLE[char,CARDINAL]]
	];
END;

-- -----------------------------------------------------------------
BaseLine: PUBLIC PROCEDURE [fd: FontDescriptorHandle]
			RETURNS [CARDINAL] =
BEGIN
SetUpFont [fd];
RETURN [Real.RoundI[PointDefs.ScrValToObjVal[fontHeader.ascent]]];
END;

-- -----------------------------------------------------------------
FLeft: PROCEDURE [c: CHARACTER] RETURNS [CARDINAL] = INLINE
BEGIN
RETURN [fontHeader.offset [LOOPHOLE [c, CARDINAL]]];
END;

-- -----------------------------------------------------------------
ForAllFonts: PUBLIC PROCEDURE[do: PROCEDURE[font: FontDescriptorHandle]] = 
BEGIN
rover: GriffinFontHandle;
FOR rover ← gFontHead, rover.next UNTIL rover = NIL
	DO do[@rover.fd] ENDLOOP;
END;

-- -----------------------------------------------------------------
SetUpFont: PROCEDURE [fd: FontDescriptorHandle] =
BEGIN
OPEN GriffinMemoryDefs;
rover, precursor: GriffinFontHandle;
sameface, samerotation, faceprecursor: BOOLEAN;
rprecursor, fprecursor, rfprecursor: GriffinFontHandle ← NIL;
IF currentFont.fd.rotation = fd.rotation AND currentFont.fd.points = fd.points AND
	currentFont.fd.face= fd.face AND
	StringDefs.EquivalentString [currentFont.fd.name, fd.name]  THEN RETURN;
rover ← gFontHead;
WHILE rover # NIL DO
	IF NOT (StringDefs.EquivalentString [rover.fd.name, fd.name]
		AND rover.fd.points = fd.points)
		   THEN BEGIN rover ← rover.next; LOOP END;
	sameface ← rover.fd.face = fd.face;
	samerotation ← rover.fd.rotation = fd.rotation;
	faceprecursor ← rover.fd.face = 0 OR rover.fd.face = fd.face OR fd.face = 3;
	IF sameface AND samerotation THEN BEGIN
		currentFont ← rover; ReadGFont [currentFont]; EXIT END;
	IF sameface AND rover.fd.rotation = Rot0Degrees THEN rprecursor ← rover;
	IF samerotation AND faceprecursor THEN fprecursor ←rover;
	IF faceprecursor THEN rfprecursor ← rover;
	rover ← rover.next;
REPEAT FINISHED => BEGIN
	precursor ← gFontHead;
	IF rfprecursor # NIL THEN precursor ← rfprecursor;
	IF fprecursor # NIL THEN precursor ← fprecursor;
	IF rprecursor # NIL THEN precursor ← rprecursor;
	IF precursor = NIL THEN SIGNAL FontError;
	ReadGFont [precursor];
	SELECT fd.rotation - fontHeader.rotation FROM
		Rot0Degrees => NULL;
		Rot90Degrees => BEGIN Rotate180D; Rotate90D END;
		Rot180Degrees => Rotate180D;
		Rot270Degrees => Rotate90D;
		ENDCASE => SIGNAL FontError;
	IF (fd.face = 2 OR fd.face = 3) AND
		(fontHeader.face # 2 AND fontHeader.face # 3) THEN Bolden;
	IF (fd.face = 1 OR fd.face =3) AND
		(fontHeader.face # 1 AND fontHeader.face # 3) THEN Italicize;
	AddGFont [fd];
	END;
	ENDLOOP;
END;



-- -----------------------------------------------------------------
ReadGFont: PROCEDURE [gfh: GriffinFontHandle] =
BEGIN
StreamDefs.JumpToFA [gHandle, @gfh.fa];
IF StreamDefs.ReadBlock [gHandle, @fontHeader, lGriffinFontHeader] #lGriffinFontHeader THEN
	SIGNAL FontError;
GriffinMemoryDefs.FreeSegment [bitMap.bits];
bitMap ← [0, fontHeader.raster, fontHeader.raster*16, fontHeader.height,
	GriffinMemoryDefs.AllocateSegment [fontHeader.bitmapwords]];
IF StreamDefs.ReadBlock [gHandle, bitMap.bits, fontHeader.bitmapwords] #
	fontHeader.bitmapwords THEN SIGNAL FontError;
END;


-- -----------------------------------------------------------------
AddGFont: PROCEDURE [fd: FontDescriptorHandle] =
BEGIN OPEN GriffinMemoryDefs;
[] ← StreamDefs.FileLength [gHandle];
IF gFontHead = NIL THEN gFontHead ← lastFont ← Allocate [lGriffinFont] ELSE
	lastFont ← lastFont.next ← Allocate [lGriffinFont];
currentFont ← lastFont;
currentFont.fd.rotation ← fd.rotation;
currentFont.fd.points ← fd.points;
currentFont.fd.face ← fd.face;
currentFont.fd.name ← AllocateString [fd.name.length];
currentFont.next ← NIL;
StringDefs.AppendString [currentFont.fd.name, fd.name];
StreamDefs.GetFA [gHandle, @currentFont.fa];
IF StreamDefs.WriteBlock [gHandle, @fontHeader, lGriffinFontHeader] # lGriffinFontHeader THEN SIGNAL FontError;
IF StreamDefs.WriteBlock [gHandle, bitMap.bits, fontHeader.bitmapwords] # fontHeader.bitmapwords THEN SIGNAL FontError;
StreamDefs.CleanupDiskStream [gHandle]
END;




-- -----------------------------------------------------------------
Rotate90D: PROCEDURE =
BEGIN
goodchars, newjindex, j, x, y, width, begin, end, oldraster: CARDINAL;
oldbitmap: POINTER;

SELECT fontHeader.rotation FROM
	Rot0Degrees => fontHeader.rotation ← Rot270Degrees;
	Rot180Degrees => fontHeader.rotation ← Rot90Degrees;
ENDCASE => FontError;
oldraster ← fontHeader.raster;
goodchars ← 0;
FOR j IN [0 .. indexSize + 1] DO
	IF fontHeader.offset [j+1] # fontHeader.offset [j] THEN goodchars ← goodchars + 1;
	ENDLOOP;
fontHeader.raster ← (goodchars*fontHeader.height+15)/16;
fontHeader.bitmapwords ← fontHeader.raster*fontHeader.maxwidth;
oldbitmap ← bitMap.bits;
bitMap ← [0, fontHeader.raster, fontHeader.raster*16, fontHeader.maxwidth,
	GriffinMemoryDefs.AllocateSegment [fontHeader.bitmapwords]];
MiscDefs.Zero [bitMap.bits, fontHeader.bitmapwords];
newjindex ← 0;
FOR j IN [0 .. indexSize]
	DO
	begin ← fontHeader.offset [j];
	end ← fontHeader.offset [j + 1];
	fontHeader.offset [j] ← newjindex;
	width ← end - begin;
	IF width = 0 THEN  LOOP; -- illegal character
	FOR x IN [0 .. width)
	   DO FOR y IN [0 .. fontHeader.height)
		DO
		IF IsOneBit [oldbitmap, x+begin, y, oldraster]
		THEN SetBit [bitMap.bits, newjindex+fontHeader.height-1-y, x, fontHeader.raster];
		ENDLOOP;
	   ENDLOOP; -- done with this character
	newjindex ← newjindex + fontHeader.height; -- end of the current character
	ENDLOOP; -- this is the end of the character loop
GriffinMemoryDefs.FreeSegment [oldbitmap];
END;

-- -----------------------------------------------------------------
Rotate180D: PROCEDURE =
BEGIN
j, x, y, width, begin, end: CARDINAL;
newbitmap: POINTER;

SELECT fontHeader.rotation FROM
	Rot0Degrees => fontHeader.rotation ← Rot180Degrees;
ENDCASE => FontError;
newbitmap ← GriffinMemoryDefs.AllocateSegment [fontHeader.bitmapwords];
MiscDefs.Zero [newbitmap, fontHeader.bitmapwords];
FOR j IN [0 .. indexSize]
	DO
	begin ← fontHeader.offset [j];
	end ← fontHeader.offset [j + 1];
	width ← end - begin;
	IF width  # 0
	   THEN FOR x IN [0 .. width)
		DO
		FOR y IN [0 .. fontHeader.height)
	  	   DO
		   IF IsOneBit [bitMap.bits, x+begin, y, fontHeader.raster]
			THEN SetBit [newbitmap, end-1-x, fontHeader.height-1-y, fontHeader.raster];
		   ENDLOOP;
		ENDLOOP; -- done with this character
	ENDLOOP; -- this is the end of the character loop
GriffinMemoryDefs.FreeSegment [bitMap.bits];
bitMap.bits ← newbitmap;
END;

oneBits: ARRAY [0 .. 15] OF WORD = [100000B, 40000B, 20000B, 10000B, 4000B, 2000B, 1000B,
	400B, 200B, 100B, 40B, 20B, 10B, 4B, 2B, 1B];


-- -----------------------------------------------------------------
SetBit: PROCEDURE [base: POINTER, x, y, raster: CARDINAL] = INLINE
BEGIN
WHILE x > 15 DO base ← base + 1; x ← x - 16; ENDLOOP;
WHILE y > 0 DO base ← base + raster; y ← y - 1; ENDLOOP;
base↑ ← InlineDefs.BITOR [base↑, oneBits [x]];
END;



-- -----------------------------------------------------------------
IsOneBit: PROCEDURE [base: POINTER, x, y, raster: CARDINAL] RETURNS [BOOLEAN] = INLINE
BEGIN
WHILE x > 15 DO base ← base + 1; x ← x - 16; ENDLOOP;
WHILE y > 0 DO base ← base + raster; y ← y - 1; ENDLOOP;
RETURN [InlineDefs.BITAND [base↑, oneBits [x]] # 0];
END;

-- -----------------------------------------------------------------
Bolden, Italicize: PROCEDURE = BEGIN SIGNAL FontError END;

-- -----------------------------------------------------------------
ReadStrike: PUBLIC PROCEDURE [fp: POINTER, s: STRING]
	RETURNS [false: BOOLEAN] =
BEGIN
fonthandle: StreamDefs.DiskHandle;
strikeheader: StrikeHeader;
strikebits: POINTER;
rover: GriffinFontHandle ← gFontHead;
fd: FontDescriptor ← [NIL,0,0,0];
j, numoffsets, oldlen, newlen: CARDINAL;
false ← FALSE;
newlen ← GriffinFileDefs.IsTail[s, ".Strike."];
IF newlen = 0 THEN RETURN;
IF s [newlen-1] = 'I OR s[newlen-1] = 'i THEN { fd.face ← fd.face + 1; newlen ← newlen - 1};
IF s [newlen-1] = 'B OR s[newlen-1] = 'b THEN { fd.face ← fd.face + 2; newlen ← newlen - 1};
IF s [newlen -1] IN ['0 .. '9] THEN
	{fd.points ← s [newlen-1] - '0; newlen ← newlen - 1};
IF s [newlen -1] IN ['0 .. '9] THEN 
	{fd.points ← fd.points + 10*(s [newlen-1]-'0); newlen ← newlen - 1};
oldlen ← s.length; -- strip junk off s for scan through prev fonts
s.length ← newlen;

--have we already stored this one?
UNTIL rover = NIL DO IF rover.fd.points = fd.points AND rover.fd.face = fd.face AND
	rover.fd.rotation = fd.rotation AND StringDefs.EquivalentString [rover.fd.name, s]
	THEN RETURN ELSE rover ← rover.next; ENDLOOP;

--is new to Griffin.fonts
fd.name ← GriffinMemoryDefs.AllocateString [newlen];
StringDefs.AppendString [fd.name, s];

--now check if it is in Fonts.widths and RETURN if not
IF ~ReadHWidths [@fd] THEN {GriffinMemoryDefs.FreeString[fd.name]; RETURN};

--is a good font, put it in
s.length ← oldlen; -- restore s
fonthandle ← StreamDefs.NewWordStream [s, StreamDefs.Read ];
IF StreamDefs.ReadBlock [fonthandle, @strikeheader, lStrikeHeader] # lStrikeHeader
	THEN SIGNAL FontError;
fontHeader.height ← strikeheader.ascent + strikeheader.descent;
fontHeader.ascent ← strikeheader.ascent;
fontHeader.raster ← strikeheader.raster;
fontHeader.bitmapwords ← fontHeader.raster*fontHeader.height;
fontHeader.rotation ← fd.rotation;
fontHeader.face ← fd.face;
fontHeader.points ← fd.points;
fontHeader.maxwidth ← strikeheader.maxwidth;
fd.name.length ← MIN [nameSize, fd.name.length];
fontHeader.name [0] ← LOOPHOLE [fd.name.length, CHARACTER];
FOR j IN [0 .. fd.name.length) DO fontHeader.name [j+1] ← fd.name [j]; ENDLOOP;
strikebits ← GriffinMemoryDefs.AllocateSegment [fontHeader.bitmapwords];
IF StreamDefs.ReadBlock [fonthandle, strikebits, fontHeader.bitmapwords] # fontHeader.bitmapwords THEN SIGNAL FontError;
MiscDefs.Zero [@fontHeader.offset, indexSize+2];
MiscDefs.Zero [@fontHeader.displaywidth, indexSize+2];
numoffsets ← strikeheader.maxascii+3 - strikeheader.minascii;
IF StreamDefs.ReadBlock [fonthandle, @fontHeader.offset + strikeheader.minascii, numoffsets]
	# numoffsets THEN SIGNAL FontError;
FOR j IN [strikeheader.minascii+numoffsets .. indexSize+2)
	DO
	fontHeader.offset [j] ← fontHeader.offset [strikeheader.minascii + numoffsets - 1];
	ENDLOOP;
FOR j IN [strikeheader.minascii .. MIN[indexSize+1,strikeheader.maxascii+1]]
	DO
	fontHeader.displaywidth[j] ←
		fontHeader.offset[j+1] - fontHeader.offset[j];
	ENDLOOP;
fonthandle.destroy [fonthandle];
GriffinMemoryDefs.FreeSegment [bitMap.bits];
bitMap ← [0, fontHeader.raster, fontHeader.raster*16, fontHeader.height, strikebits];
AddGFont [@fd];
END;

-- -----------------------------------------------------------------
ReadHWidths: PROCEDURE [fd: FontDescriptorHandle]  RETURNS [BOOLEAN]=
BEGIN OPEN IxDefs;
j: CARDINAL;
ix,best: IX;
size: CARDINAL ← PointsToMicas[fd.points];
family: WORD;

fwHandle.reset [fwHandle];
	DO
	ReadIX[@ix];
	SELECT ix.header.type FROM
	IXTypeEnd => RETURN[FALSE];
	IXTypeName => FOR j IN [0 .. fd.name.length)
		DO OPEN StringDefs;
		IF LowerCase[fd.name[j]]
			# LowerCase[ix.name [j+1]] THEN EXIT;
		REPEAT FINISHED => EXIT; --from the outer loop
		ENDLOOP;
	ENDCASE;
	ENDLOOP;
--now we've found the family, look for the right widths
family ← ix.code;
best.header.type ← IXTypeEnd;
	DO
	ReadIX[@ix];
	SELECT ix.header.type FROM
	IXTypeEnd => EXIT;
	IXTypeWidths =>
		IF ix.fam = family
			AND ix.face = fd.face
			AND (ix.siz = 0 --splines--
			    OR (fd.points=MicasToPoints[ix.siz] 
				AND ix.rotation = fd.rotation))
		THEN	BEGIN
			best ← ix;
			IF ix.siz # 0 THEN EXIT;
				--may be non-spline later
			END;
	ENDCASE;
	ENDLOOP;
IF best.header.type = IXTypeEnd THEN RETURN[FALSE] ;
CalculateWidths[@best, fd, size];
RETURN[TRUE];
END;

-- -----------------------------------------------------------------
CalculateWidths: PROCEDURE [best: POINTER TO IxDefs.IX,
		font: FontDescriptorHandle, size: CARDINAL] =
BEGIN OPEN IxDefs, MiscDefs, AltoDefs, InlineDefs;
sa: LongNumber;
index: StreamDefs.StreamIndex;
wt: WTB;
bufl: CARDINAL = LENGTH[fontHeader.presswidth];
i: CARDINAL;

SetBlock[@fontHeader.presswidth, 0, bufl];

[sa.highbits, sa.lowbits] ← best.sa;  --Bcpl to Mesa LONG
[index.page, index.byte] ← LongDivMod[sa.lc*2, BytesPerPage];
StreamDefs.SetIndex[fwHandle, index];
[] ← StreamDefs.ReadBlock[fwHandle, @wt, SIZE[WTB]];

-- now read either one word or many for the widths
IF font.rotation#Rot0Degrees THEN FontError;
IF best.ec>bufl THEN FontError;
IF wt.XWidthFixed
 THEN	BEGIN
	w: CARDINAL ← fwHandle.get[fwHandle];
	SetBlock[@fontHeader.presswidth, w, bufl];
	END
 ELSE	[] ← StreamDefs.ReadBlock[fwHandle,
		@fontHeader.presswidth[best.bc], best.ec-best.bc+1];

--IF wt.YWidthFixed
-- THEN fontHeader.height ← fwHandle.get[fwHandle]
-- ELSE FontError;

-- now scale, if necessary
IF best.siz = 0 THEN
	BEGIN
	FOR i IN [best.bc..best.ec]
		DO fontHeader.presswidth[i] ← LongDiv[
		   LongMult[fontHeader.presswidth[i],size], 1000];
		ENDLOOP;
--	fontHeader.height ← LongDiv[LongMult[fontHeader.height,
--					size], 1000]
	END;
END;

-- -----------------------------------------------------------------
ReadIX: PROCEDURE[ ix: POINTER TO IxDefs.IX] =
BEGIN
ix.header ← fwHandle.get[fwHandle];
IF ix.header.length>0
   THEN IF StreamDefs.ReadBlock[fwHandle, ix+1, ix.header.length-1]+1
		# ix.header.length THEN FontError;
END;

-- -----------------------------------------------------------------
PointsToMicas: PROCEDURE[points: CARDINAL] RETURNS[CARDINAL] = 
BEGIN OPEN InlineDefs;
RETURN [LongDiv[LongMult[points,635],18]]
END;

-- -----------------------------------------------------------------
mtp: REAL ← 18.0/635.0;
MicasToPoints: PROCEDURE[micas: CARDINAL] RETURNS[CARDINAL] = 
BEGIN
RETURN [Real.RoundC[mtp*micas]]
END;

-- -----------------------------------------------------------------
EachFile: PROCEDURE [fp: POINTER, s: STRING]
	RETURNS [BOOLEAN] =
BEGIN
IF GriffinFileDefs.IsTail[s, ".Strike."] # 0
	THEN []←ReadStrike[fp,s];
RETURN[FALSE];
END;

-- -----------------------------------------------------------------
foo, baz: CARDINAL;

IF gHandle.endof [gHandle]
 THEN gHandle.put[gHandle, fontPassword]
 ELSE IF gHandle.get[gHandle] # fontPassword THEN
	BEGIN OPEN StreamDefs;
	old: DiskHandle←gHandle;
	gHandle←CreateWordStream[old.file, Read+Append];
	old.reset[old];
	old.put[old, fontPassword];
	TruncateDiskStream [old]; --destroys stream
	[]←FileLength[gHandle]; --position to end
	END;
bitMap.bits ← GriffinMemoryDefs.AllocateSegment [256];
UNTIL gHandle.endof [gHandle] DO
	IF gFontHead = NIL THEN
		gFontHead←lastFont←GriffinMemoryDefs.Allocate [lGriffinFont]
	ELSE lastFont←lastFont.next←GriffinMemoryDefs.Allocate [lGriffinFont];
	StreamDefs.GetFA [gHandle, @lastFont.fa];
	IF StreamDefs.ReadBlock [gHandle, @fontHeader, lGriffinFontHeader]
		#lGriffinFontHeader THEN SIGNAL FontError;
	foo ← fontHeader.bitmapwords;
	UNTIL foo =0 DO -- skip bitmap
		IF StreamDefs.ReadBlock [gHandle, bitMap.bits, MIN [foo, 256]] #
			MIN [foo, 256] THEN SIGNAL FontError;
		IF foo < 256 THEN foo ← 0 ELSE foo ← foo - 256;
		ENDLOOP;
	lastFont.fd.rotation ← fontHeader.rotation;
	lastFont.fd.points ← fontHeader.points;
	lastFont.fd.face ← fontHeader.face;
	lastFont.next ← NIL;
	foo ← LOOPHOLE [fontHeader.name [0], CARDINAL];
	lastFont.fd.name ← GriffinMemoryDefs.AllocateString [foo];
	FOR baz IN [ 1 .. foo] DO
		lastFont.fd.name [baz-1] ← fontHeader.name [baz]; ENDLOOP;
	lastFont.fd.name.length ← foo;
	ENDLOOP;

IF gFontHead # NIL THEN ReadGFont [gFontHead];
currentFont ← gFontHead;
DirectoryDefs.EnumerateDirectory [EachFile];
IF gFontHead = NIL THEN StartupFontError[nofonts];
END.