-- CGFontImpl.mesa
-- Last edit by Doug Wyatt, August 30, 1982 5:16 pm
-- Last edit by Paul Rovner, July 20, 1983 1:47 pm

DIRECTORY
Ascii USING [CR, SP, TAB],
CGFont USING [ErrorType, Ref, Rep, WidthArray],
CGStorage USING [pZone, qZone],
ConvertUnsafe USING [ToRope],
KSFonts USING [Gacha10],
StrikeFormat USING [Body, Format, Header, KernedStrike, nullWidthEntry,
PlainStrike, WidthEntry, WTable, XTable],
PrincOps USING [GlobalFrameHandle],
PrincOpsUtils USING [Codebase],
FS USING [OpenFile, Open, GetInfo, Read, Error],
VM USING [Interval, Allocate, AddressForPageNumber];

CGFontImpl: CEDAR PROGRAM
IMPORTS CGStorage, ConvertUnsafe, KSFonts, FS, PrincOpsUtils, VM
EXPORTS CGFont = {
OPEN StrikeFormat, CGFont;

fontsZone: ZONE = CGStorage.pZone;
repZone: ZONE = CGStorage.qZone;
widthZone: ZONE = CGStorage.qZone;
nameZone: ZONE = CGStorage.pZone;

defaultFont: PUBLIC Ref ← NIL;

FontArray: TYPE = RECORD[SEQUENCE space: NAT OF Ref];

fonts: REF FontArray ← NIL;
nFonts: NAT ← 0;

Error: PUBLIC ERROR[type: ErrorType] = CODE;

-- Procedures

StringWidth: PUBLIC UNSAFE PROC[string: LONG STRING, font: Ref] RETURNS[NAT] = UNCHECKED {
width: REF WidthArray ← font.width;
w: NAT ← 0;
FOR i: NAT IN[0..string.length) DO w ← w + width[string[i]] ENDLOOP;
RETURN[w];
};

DefaultFont: PUBLIC PROC RETURNS[Ref] = { RETURN[defaultFont] };

LoadFont: PUBLIC UNSAFE PROC[name: LONG STRING] RETURNS[Ref] = UNCHECKED {
address: LONG POINTERNIL;
self: Ref ← NIL;
address ← GetFile[name];
IF address=NIL THEN RETURN[NIL];
self ← New[address];
Register[self];
RETURN[self];
};

Register: PROC[font: Ref] = {
space: NATIF fonts=NIL THEN 0 ELSE fonts.space;
i: NAT ← nFonts;
nFonts ← i + 1;
IF nFonts>space THEN {
old: REF FontArray ← fonts;
fonts ← fontsZone.NEW[FontArray[space+MAX[4,space/2]]];
FOR j: NAT IN[0..i) DO fonts[j] ← old[j] ENDLOOP;
};
fonts[i] ← font;
};

New: PUBLIC UNSAFE PROC[address: LONG POINTER] RETURNS[Ref] = UNCHECKED {
hdr: LONG POINTER TO Header = address;
format: Format ← hdr.format;
dx,dy,ox,oy: INTEGER ← 0;
body: LONG POINTER TO Body ← NIL;
raster,height: CARDINAL;
bitmap: LONG POINTERNIL;
kerned: BOOLEANFALSE;
self: Ref ← NIL;
IF format.oneBit=F THEN ERROR Error[illegalFormat];
IF format.index=T THEN ERROR Error[illegalFormat];
kerned ← (format.kerned=T);
IF kerned THEN {
ks: LONG POINTER TO KernedStrike ← address; body ← @ks.body;
[fbbdx: dx, fbbdy: dy, fbbox: ox, fbboy: oy] ← ks.box }
ELSE {
ps: LONG POINTER TO PlainStrike ← address; body ← @ps.body;
dx ← hdr.maxwidth; dy ← body.ascent+body.descent;
ox ← 0; oy ← -body.descent };
height ← body.ascent+body.descent;
bitmap ← LOOPHOLE[body + SIZE[Body]];
raster ← body.raster;
self ← repZone.NEW[Rep ← [kerned: kerned, height: height,
min: hdr.min, max: hdr.max, width: NIL, dx: dx, dy: dy, ox: ox, oy: oy,
xtable: NIL, wtable: NIL, bitmap: bitmap, raster: raster, address: address]];
self.xtable ← LOOPHOLE[bitmap+raster*height];
IF kerned THEN self.wtable ← LOOPHOLE[body+body.length];
Initialize[self];
RETURN[self];
};

Initialize: UNSAFE PROC[font: Ref] = UNCHECKED {
min: CHARACTER ← font.min;
max: CHARACTER ← font.max;
width: REF WidthArray ← widthZone.NEW[WidthArray];
IF font.kerned THEN {
wtable: LONG POINTER TO WTable ← font.wtable;
dummy: [0..377B] ← wtable[max-min+1].width;
width^ ← ALL[dummy];
FOR c: CHARACTER IN[min..max] DO
i: NAT ← c - min; entry: WidthEntry ← wtable[i];
IF entry#nullWidthEntry THEN width[c] ← entry.width;
ENDLOOP;
}
ELSE {
xtable: LONG POINTER TO XTable ← font.xtable;
space: [0..377B] ← 0;
FOR c: CHARACTER IN[min..max+1] DO
i: NAT ← c - min; width[c] ← xtable[i+1] - xtable[i];
ENDLOOP;
space ← width[Ascii.SP];
FOR c: CHARACTER IN [0C..min) DO width[c] ← space ENDLOOP;
FOR c: CHARACTER IN (max+1..177C] DO width[c] ← space ENDLOOP;
width[Ascii.TAB] ← 8*space; -- tab width hack
width[Ascii.CR] ← space; -- CR width hack
};
font.width ← width;
};

GetFile: UNSAFE PROC[name: LONG STRING] RETURNS[lp: LONG POINTER ← NIL] =
UNCHECKED {
file: FS.OpenFile ← FS.Open[name: ConvertUnsafe.ToRope[name] ! FS.Error => GOTO Punt];
pages: INTFS.GetInfo[file].pages;
space: VM.Interval;
IF pages>=30 THEN ERROR Error[fileTooLarge];
space ← VM.Allocate[count: pages];
lp ← VM.AddressForPageNumber[space.page];
FS.Read[file: file, from: 0, nPages: pages, to: lp]; -- first page is 0!
EXITS Punt => RETURN[NIL]
};

TRUSTED {defaultFont ← New[PrincOpsUtils.Codebase[LOOPHOLE[KSFonts.Gacha10, PrincOps.GlobalFrameHandle]]]};

}.