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:
BOOL ←
FALSE,
italic: BOOL ← FALSE, defaultOnFailure: BOOL ← TRUE]
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: BOOL ← FALSE;
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.