VFontsImpl.mesa;
Last Edited by McGregor, October 4, 1982 11:16 am
Last Edited by: Maxwell, January 3, 1983 1:07 pm
Last Edited by: Paul Rovner, August 10, 1983 4:00 pm
DIRECTORY
FS USING [OpenFile, Open, GetInfo, Error, Read],
Graphics USING [FontRef],
GraphicsOps USING [UnsafeNewFont],
IO USING [PutR, card],
Rope USING [Cat, Equal, Fetch, FromChar, ROPE, Size],
DebuggerSwap USING [CallDebugger],
UserProfile USING [Number, Token],
VFonts,
VM USING [Interval, Allocate, AddressForPageNumber];
VFontsImpl: CEDAR PROGRAM
IMPORTS FS, IO, GraphicsOps, Rope, DebuggerSwap, UserProfile, VFonts, VM
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, IO.PutR[IO.card[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, IO.PutR[IO.card[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 {
file: FS.OpenFile;
pages: INT;
space: VM.Interval;
IF Rope.Fetch[fileName] # '/ THEN fileName ← Rope.Cat[fontDir, fileName];
file ← FS.Open[fileName ! FS.Error => GOTO Punt];
pages ← FS.GetInfo[file].pages;
IF pages >= 20 THEN ERROR;
space ← VM.Allocate[count: pages];
font ← NEW[FontObject];
font.address ← VM.AddressForPageNumber[space.page];
FS.Read[file: file, from: 0, nPages: pages, to: font.address];
Initialize [font];
EXITS Punt => RETURN[NIL];
};
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 ! FS.Error => CHECKED {err ← TRUE; CONTINUE}];
END;
IF err THEN BEGIN-- try IVY
err ← FALSE;
defaultFont ← EstablishFont["/Ivy/Tioga/StrikeFonts/Tioga", 10, FALSE
! FS.Error => CHECKED {err ← TRUE; CONTINUE}];
END;
IF err THEN DO TRUSTED {DebuggerSwap.CallDebugger["Couldn't get any fonts!"L]}; ENDLOOP;
defaultGFont ← defaultFont.gFont;
END;
fontDir: Rope.ROPE = "/Indigo/Tioga/StrikeFonts/";
CreateDefaultFont[];
END.