VFontsImpl.mesa;
Last Edited by McGregor, October 4, 1982 11:16 am
Last Edited by: Maxwell, January 3, 1983 1:07 pm
DIRECTORY
CIFS USING [dontCheck, Error, GetFC, OpenButDontHandleError, read],
Convert USING [ValueToRope],
File USING [Capability, GetSize, PageCount],
Graphics USING [FontRef],
GraphicsOps USING [UnsafeNewFont],
Rope USING [Cat, Equal, Fetch, FromChar, ROPE, Size],
Runtime USING [CallDebugger],
Space USING [Create, Handle, LongPointer, Map, PageCount, virtualMemory],
UserProfile USING [Number, Token],
VFonts;
VFontsImpl: CEDAR PROGRAM
IMPORTS CIFS, Convert, File, GraphicsOps, Rope, Runtime, Space, UserProfile, VFonts
EXPORTS VFonts
SHARES VFonts = BEGIN OPEN VFonts;
defaultFont: PUBLIC VFonts.Font ← NIL;
defaultGFont: PUBLIC Graphics.FontRef ← NIL;
Error: PUBLIC ERROR [code: VFonts.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: BOOL,
indexed: BOOL,
fixed: BOOL,
kerned: BOOL,
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 PROC [string: Rope.ROPE, font: VFonts.Font ← defaultFont]
RETURNS [width: [0..LAST[INTEGER]]] = BEGIN
width ← 0;
FOR i: INT IN [0..Rope.Size[string]) DO
width ← width + VFonts.CharWidth[Rope.Fetch[string, i], font]
ENDLOOP;
END;
Initialize: PROC [font: VFonts.Font] =
TRUSTED 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 NOT IN [0C..177C] THEN ERROR Error[illegalFormat];
IF font.max NOT IN [0C..177C] THEN ERROR Error[illegalFormat];
font.ascent ← strike.body.ascent;
font.height ← strike.body.ascent + strike.body.descent;
font.raster ← strike.body.raster;
font.maxWidth ← strike.header.maxwidth;
SetFontAddresses[font];
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[font.min] ENDLOOP;
FOR c IN (MIN[font.max, 176C]..177C] DO font.width[c] ← font.width[font.min] ENDLOOP;
font.width[15C] ← font.width[40C]; -- CR width hack
END;
SetFontAddresses: PROC [font: Font] = TRUSTED 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 PROC [family: Rope.ROPE, size: CARDINAL, bold: BOOLFALSE,
italic: BOOLFALSE, defaultOnFailure: BOOLTRUE]
RETURNS [font: Font] = BEGIN
IF size#10 AND Rope.Equal[family, "Tioga", FALSE] THEN family ← "TimesRoman";
horrible hack to conpensate for lack of other size Tioga fonts
FOR font ← fontChain, font.next UNTIL font=NIL DO
IF size=font.size AND bold=font.bold AND
italic=font.italic AND Rope.Equal[family, font.family, FALSE]
THEN RETURN [font];
ENDLOOP;
RETURN[NewFont[family, size, bold, italic, defaultOnFailure]];
END;
NewFont: PROC [family: Rope.ROPE, size: CARDINAL, bold, italic, defaultOnFailure: BOOL]
RETURNS [font: Font] = BEGIN
trialName: Rope.ROPE;
IF bold OR italic THEN BEGIN
trialName ← Rope.Cat[trialName, family];
IF size#0 THEN trialName ← Rope.Cat[trialName, Convert.ValueToRope[[unsigned[size]]]];
IF bold THEN trialName ← Rope.Cat[trialName, Rope.FromChar['B]];
IF italic THEN trialName ← Rope.Cat[trialName, Rope.FromChar['I]];
trialName ← Rope.Cat[trialName, ".strike"];
IF (font ← LoadFont[trialName]) = NIL THEN BEGIN-- try to match size&family
FOR f: Font ← fontChain, f.next UNTIL f=NIL DO
IF size=f.size AND ~f.bold AND ~f.italic AND Rope.Equal[family, f.family, FALSE]
THEN {
font ← CopyFont[f];
IF bold THEN font.synthBold ← TRUE;
IF italic THEN font.synthItalic ← TRUE;
EXIT};
ENDLOOP;
END;
IF font=NIL THEN-- lose, give up
{IF ~defaultOnFailure THEN Error[fontNotFound] ELSE trialName ← NIL}
ELSE IF font.gFont=NIL THEN TRUSTED {
font.gFont ← LOOPHOLE[GraphicsOps.UnsafeNewFont[font.address]]};
END;
IF font=NIL THEN BEGIN-- no luck yet
trialName ← Rope.Cat[trialName, family];
IF size#0 THEN trialName ← Rope.Cat[trialName, Convert.ValueToRope[[unsigned[size]]]];
trialName ← Rope.Cat[trialName, ".strike"];
IF (font ← LoadFont[trialName])=NIL THEN-- lose
{IF ~defaultOnFailure THEN Error[fontNotFound]
ELSE font ← CopyFont[defaultFont]}
ELSE TRUSTED {font.gFont ← LOOPHOLE[GraphicsOps.UnsafeNewFont[font.address]]};
IF bold THEN font.synthBold ← TRUE;
IF italic THEN font.synthItalic ← TRUE;
END;
font.family ← family;
font.size ← size;
font.bold ← bold;
font.italic ← italic;
font.next ← fontChain;
fontChain ← font;
IF font.size=10 AND ~font.bold AND ~font.italic AND Rope.Equal[font.family, "Tioga", FALSE]
THEN font.ascent ← 9; -- horrible patch to compensate for bad baseline
END;
LoadFont: PROC [fileName: Rope.ROPE] RETURNS [font: Font] = TRUSTED BEGIN
file: File.Capability;
pages: File.PageCount;
size: Space.PageCount;
space: Space.Handle;
IF Rope.Fetch[fileName] # '/ THEN fileName ← Rope.Cat[fontDir, fileName];
file ← CIFS.GetFC[CIFS.OpenButDontHandleError[fileName, CIFS.read+CIFS.dontCheck
! CIFS.Error => CHECKED {GOTO Punt}]];
pages ← File.GetSize[file];
size ← IF pages<20 THEN pages ELSE ERROR;
space ← Space.Create[size: size, parent: Space.virtualMemory];
Space.Map[space: space, window: [file: file, base: 1]]; -- first page is 1!
font ← NEW[FontObject];
font.address ← Space.LongPointer[space];
Initialize [font];
EXITS Punt => {RETURN[NIL]}
END;
CopyFont: PROC [font: Font] RETURNS [Font] = INLINE {RETURN[NEW[FontObject ← font^]]};
CreateDefaultFont: PROC = BEGIN
err: BOOLFALSE;
defaultFamily: Rope.ROPE ← UserProfile.Token["DefaultFontFamily", "Tioga"];
defaultSize: CARDINAL ← UserProfile.Number["DefaultFontSize", 10];
defaultFont ← EstablishFont[defaultFamily, defaultSize, FALSE
! ANY => {err ← TRUE; CONTINUE}];
IF err AND (defaultSize#10 OR ~Rope.Equal["Tioga", defaultFamily, FALSE]) THEN BEGIN
substitute Tioga10.strike for user's failed choice
err ← FALSE;
defaultFont ← EstablishFont["Tioga", 10, FALSE ! CIFS.Error => CHECKED {err ← TRUE; CONTINUE}];
END;
IF err THEN BEGIN-- try IVY
err ← FALSE;
defaultFont ← EstablishFont["/Ivy/Tioga/StrikeFonts/Tioga", 10, FALSE
! CIFS.Error => CHECKED {err ← TRUE; CONTINUE}];
END;
IF err THEN DO TRUSTED {Runtime.CallDebugger["Couldn't get any fonts!"L]}; ENDLOOP;
defaultGFont ← defaultFont.gFont;
END;
fontDir: Rope.ROPE = "/Indigo/Tioga/StrikeFonts/";
CreateDefaultFont[];
END.