-- TEditFontImpl.mesa; Last Edited by McGregor, January 29, 1982 10:19 am

DIRECTORY
Directory USING [Error, Lookup],
File USING [Capability, GetSize, PageCount],
Inline USING [LowHalf],
LongString USING [AppendDecimal, AppendString],
NodeStyle USING [Dist],
Real USING [RoundC],
Runtime USING [CallDebugger],
Space USING [Create, Handle, LongPointer, Map, PageCount, virtualMemory],
TEditFont,
NameSymbolTable USING [FromName, Name];

TEditFontImpl: PROGRAM
IMPORTS Directory, File, Inline, LongString, NameSymbolTable, Real, Runtime,
	TEditFont, Space
EXPORTS TEditFont = BEGIN OPEN TEditFont;

-- declarations for strike format font files

defaultFont: PUBLIC TEditFont.Font ← NIL;
Error: PUBLIC ERROR [code: TEditFont.ErrorCode] = CODE;
  
Strike: TYPE = LONG POINTER TO StrikeObject;
StrikeObject: TYPE = RECORD [header: StrikeHeader, body: StrikeBody];

StrikeHeader: TYPE = RECORD [
	bits: StrikeBits,
	min: CARDINAL,
	max: CARDINAL,
	maxwidth: CARDINAL];

StrikeBits: TYPE = RECORD [
	newStyle: BOOLEAN,
	indexed: BOOLEAN,
	fixed: BOOLEAN,
	kerned: BOOLEAN,
	pad: [0..7777B]];

BoundingBox: TYPE = RECORD [
	FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER];

StrikeBody: TYPE = RECORD [
	length: CARDINAL,
	ascent: CARDINAL,
	descent: CARDINAL,
	xoffset: CARDINAL,
	raster: CARDINAL,
	bitmap: ARRAY [0..0) OF WORD];

-- Procedures

StringWidth: PUBLIC PROCEDURE [string: STRING, font: TEditFont.Font ← defaultFont]
	RETURNS [width: [0..LAST[INTEGER]]] = BEGIN
	i: CARDINAL;
	width ← 0;
	FOR i IN [0..string.length) DO
		width ← width + TEditFont.CharWidth[string[i], font]
		ENDLOOP;
	END;

Initialize: PROCEDURE [font: TEditFont.Font] =
	BEGIN
	strike: Strike = font.address;
	c: CHARACTER;
	bits: StrikeBits = strike.header.bits;
	IF ~bits.newStyle THEN ERROR Error[illegalFormat];
	IF bits.indexed THEN ERROR Error[illegalFormat];
	font.kerned ← bits.kerned;
	font.min ← LOOPHOLE[strike.header.min];
	font.max ← LOOPHOLE[strike.header.max];
	IF font.min ~IN [0C..177C] THEN ERROR Error[illegalFormat];
	IF font.max ~IN [0C..177C] THEN ERROR Error[illegalFormat];
	font.height ← strike.body.ascent + strike.body.descent;
	font.raster ← strike.body.raster;
	font.maxWidth ← strike.header.maxwidth;
	SetFontAddresses[font];
	font.width[40C] ← 0;
	FOR c IN [font.min..MIN[font.max, 176C]] DO
		font.width[c] ← XInSegment[c+1, font] - XInSegment[c, font]
		ENDLOOP;
	FOR c IN [0C..font.min) DO font.width[c] ← font.width[40C] ENDLOOP;
	FOR c IN (MIN[font.max, 176C]..177C] DO font.width[c] ← font.width[40C] ENDLOOP;
	font.width[15C] ← font.width[40C];	-- CR width hack
	END;

SetFontAddresses: PROCEDURE [font: Font] = INLINE
	BEGIN
	strike: Strike = font.address;
	font.bitmap ← @strike.body.bitmap + (IF font.kerned THEN SIZE[BoundingBox] ELSE 0);
	font.xInSegment ←
		LOOPHOLE[font.bitmap, LONG POINTER ] + font.raster*font.height -
		LOOPHOLE[font.min, INTEGER];
	END;

fontChain: Font ← NIL;

EstablishFont: PUBLIC PROCEDURE [family: NameSymbolTable.Name, size: NodeStyle.Dist]
	RETURNS [Font] = BEGIN
	myString: REF TEXT;
	myFontHandle: Font;

	FOR myFontHandle ← fontChain, myFontHandle.next UNTIL myFontHandle=NIL DO
		IF family=myFontHandle.family AND size=myFontHandle.size THEN
		RETURN [myFontHandle];
		ENDLOOP;

	myString ← NEW[TEXT[40]];
	NameSymbolTable.FromName[family, myString];
	
	IF size#0.0 THEN LongString.AppendDecimal[LOOPHOLE[myString], Real.RoundC[size]];

	LongString.AppendString [LOOPHOLE[myString], ".strike"];
	myFontHandle ← LoadFont[LOOPHOLE[myString]];

	myFontHandle.family ← family;
	myFontHandle.size ← size;

	RETURN [myFontHandle];
	END;

LoadFont: PROCEDURE [name: LONG STRING] RETURNS [myFontHandle: Font] = BEGIN
	ENABLE Directory.Error => GOTO Punt;
	file: File.Capability ← Directory.Lookup[fileName: name];
	pages: File.PageCount ← File.GetSize[file];
	size: Space.PageCount ← IF pages<20 THEN Inline.LowHalf[pages] ELSE ERROR;
	space: Space.Handle ← Space.Create[size: size, parent: Space.virtualMemory];
	Space.Map[space: space, window: [file: file, base: 1]]; -- first page is 1!

	myFontHandle ← NEW[FontObject];

	myFontHandle.address ← Space.LongPointer[space];
	Initialize [myFontHandle];

	myFontHandle.next ← fontChain;
	fontChain ← myFontHandle;
	EXITS Punt => RETURN[defaultFont]
	END;

SetDefaultFont: PROCEDURE [font: TEditFont.Font] = INLINE {defaultFont ← font};

SetDefaultFont [LoadFont["TiogaDefault.strike"L] ];

IF defaultFont=NIL THEN	-- not on the disk
	DO Runtime.CallDebugger["Tioga needs 'TiogaDefault.strike' to start up!"L] ENDLOOP;

END.