StrikeFontImpl.mesa
Copyright © 1984, Xerox Corporation. All rights reserved.
Doug Wyatt, October 9, 1984 3:19:34 pm PDT
DIRECTORY
Basics USING [bytesPerWord],
CountedVM USING [Allocate, Handle, Pointer, Words],
Font USING [Char, Class, ClassRep, CorrectionType, Extents, FONT, FontRep, nullChar, OptionalReal],
FS USING [StreamOpen],
Imager,
ImagerTransformation,
IO USING [Close, STREAM, UnsafeGetBlock],
Rope USING [ROPE],
StrikeFontFormat USING [Body, BoundingBox, Header, nullWidthEntry, WidthEntry, WTable, XTable],
Vector2 USING [VEC];
StrikeFontImpl: CEDAR PROGRAM
IMPORTS CountedVM, FS, Imager, ImagerTransformation, IO
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Vector2.VEC;
Transformation: TYPE ~ ImagerTransformation.Transformation;
FONT: TYPE ~ Font.FONT;
FontRep: TYPE ~ Font.FontRep;
name: ROPE,
transformation: Transformation,
class: Class,
data: REF,
propList: REF
Char: TYPE ~ Font.Char; -- CARDINAL
nullChar: Char ~ Font.nullChar;
CorrectionType: TYPE ~ Font.CorrectionType; -- {none, space, mask};
Extents: TYPE ~ Font.Extents; -- RECORD[leftExtent, rightExtent, descent, ascent: REAL];
OptionalReal: TYPE ~ Font.OptionalReal; -- RECORD[exists: BOOLEAN, value: REAL];
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
file: StrikeFile,
pixelToClient: Transformation
];
StrikeFile: TYPE ~ REF StrikeFileRep;
StrikeFileRep: TYPE ~ RECORD[
min, max: CARDINAL,
foffset: INTEGER,
ascent, descent: INTEGER,
strike: LONG POINTER,
raster, lines: NAT,
xtable: LONG POINTER TO StrikeFontFormat.XTable,
wtable: LONG POINTER TO StrikeFontFormat.WTable,
pixelToChar: Transformation,
vm: CountedVM.Handle
];
MalformedStrikeFont: ERROR ~ CODE;
ReadWords: UNSAFE PROC[stream: IO.STREAM, base: LONG POINTER, words: INT] ~ UNCHECKED {
count: CARDINAL ~ words*Basics.bytesPerWord;
IF IO.UnsafeGetBlock[stream, [base: base, count: count]]=count THEN NULL
ELSE ERROR MalformedStrikeFont; -- file too short
};
Open: PROC[fileName: ROPE, size: NAT] RETURNS[StrikeFile] ~ {
stream: IO.STREAM ~ FS.StreamOpen[fileName];
header: StrikeFontFormat.Header;
box: StrikeFontFormat.BoundingBox ← [0, 0, 0, 0];
body: StrikeFontFormat.Body;
lines: NAT ← 0;
vm: CountedVM.Handle ← NIL;
strikeWords, xtableWords, wtableWords: NAT ← 0;
vmWords: INT ← 0;
strike, xtable, wtable: LONG POINTERNIL;
pixelToChar: Transformation ← NIL;
TRUSTED { -- read header
ReadWords[stream, @header, SIZE[StrikeFontFormat.Header]];
};
IF header.oneBit=T AND header.index=F AND header.unused=0
AND header.min<=header.max AND header.max<256 THEN NULL
ELSE ERROR MalformedStrikeFont; -- invalid header
IF header.kerned=T THEN TRUSTED { -- if kerned, read font bounding box
ReadWords[stream, @box, SIZE[StrikeFontFormat.BoundingBox]];
};
TRUSTED { -- read body prefix
ReadWords[stream, @body, SIZE[StrikeFontFormat.Body]];
};
IF (body.ascent+body.descent)>0 THEN lines ← body.ascent+body.descent
ELSE ERROR MalformedStrikeFont; -- nonpositive height
IF header.kerned=T THEN {
IF box.fbboy=-body.descent AND box.fbbdx>0 AND box.fbbdy=lines THEN NULL
ELSE ERROR MalformedStrikeFont; -- font box is inconsistent
};
strikeWords ← body.raster*lines;
xtableWords ← header.max-header.min+3;
IF body.length=CARDINAL[SIZE[StrikeFontFormat.Body]+strikeWords+xtableWords] THEN NULL
ELSE ERROR MalformedStrikeFont; -- inconsistent lengths
IF header.kerned=T THEN wtableWords ← header.max-header.min+2;
vmWords ← LONG[strikeWords+xtableWords]+wtableWords;
vm ← CountedVM.Allocate[words: vmWords];
TRUSTED { -- read remainder of file: strike, xtable, and wtable
base: LONG POINTER ~ CountedVM.Pointer[vm];
IF vmWords<=CountedVM.Words[vm] THEN ReadWords[stream, base, vmWords]
ELSE ERROR; -- CountedVM didn't allocate enough words!?
strike ← base;
xtable ← strike+strikeWords;
IF wtableWords#0 THEN wtable ← xtable+xtableWords;
};
IO.Close[stream];
pixelToChar ← ImagerTransformation.Rotate[-90];
pixelToChar.PreScale[1.0/size];
RETURN[NEW[StrikeFileRep ← [min: header.min, max: header.max,
foffset: box.fbbox, ascent: body.ascent, descent: body.descent,
strike: strike, raster: body.raster, lines: lines,
xtable: xtable, wtable: wtable, pixelToChar: pixelToChar, vm: vm]]];
};
Find: PROC[name: ROPE] RETURNS[FONT] ~ {
file: StrikeFile ~ Open[name, 10 -- **** fix this ***** --];
charToClient: Transformation ~ ImagerTransformation.Scale[1];
data: Data ~ NEW[DataRep ← [file: file, pixelToClient: file.pixelToChar]];
RETURN[NEW[Font.FontRep ← [class: class, data: data,
name: name, charToClient: charToClient, props: NIL]]];
};
Modify: PROC[font: FONT, m: Transformation] RETURNS[FONT] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
charToClient: Transformation ~ font.charToClient.Concat[m];
pixelToClient: Transformation ~ file.pixelToChar.Concat[charToClient];
newData: Data ~ NEW[DataRep ← [file: file, pixelToClient: pixelToClient]];
RETURN[NEW[FontRep ← [class: class, data: newData,
name: font.name, charToClient: charToClient, props: NIL]]];
};
Contains: PROC[font: FONT, char: Char] RETURNS[BOOL] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
w: StrikeFontFormat.WidthEntry ← StrikeFontFormat.nullWidthEntry;
IF char IN[file.min..file.max] THEN TRUSTED { w ← file.wtable[char-file.min] };
RETURN[w#StrikeFontFormat.nullWidthEntry];
};
NextChar: PROC[font: FONT, char: Char] RETURNS[next: Char] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
start: Char;
SELECT char FROM
=nullChar => start ← file.min;
<file.min => start ← file.min;
<file.max => start ← char+1;
ENDCASE => RETURN[nullChar];
FOR probe: Char IN[start..file.max] DO
w: StrikeFontFormat.WidthEntry;
TRUSTED { w ← file.wtable[probe-file.min] };
IF w#StrikeFontFormat.nullWidthEntry THEN RETURN[probe];
ENDLOOP;
RETURN[nullChar];
};
BoundingBox: PROC[font: FONT, char: Char] RETURNS[Extents] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
IF char IN[file.min..file.max] THEN {
index: NAT ~ char-file.min;
w: StrikeFontFormat.WidthEntry;
TRUSTED { w ← file.wtable[index] };
IF w#StrikeFontFormat.nullWidthEntry THEN {
f0, f1: CARDINAL;
r: ImagerTransformation.Rectangle;
TRUSTED { f0 ← file.xtable[index]; f1 ← file.xtable[index+1] };
r ← ImagerTransformation.TransformRectangle[data.pixelToClient,
[x: -file.ascent, y: file.foffset+w.offset, w: file.lines, h: f1-f0]];
RETURN[[leftExtent: -r.x, rightExtent: r.x+r.w, descent: -r.y, ascent: r.y+r.h]];
};
};
RETURN[[0, 0, 0, 0]];
};
Width: PROC[font: FONT, char: Char] RETURNS[VEC] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
IF char IN[file.min..file.max] THEN {
w: StrikeFontFormat.WidthEntry;
TRUSTED { w ← file.wtable[char-file.min] };
IF w#StrikeFontFormat.nullWidthEntry THEN {
RETURN[data.pixelToClient.TransformVec[[0, w.width]]];
};
};
RETURN[[0, 0]];
};
Amplified: PROC[font: FONT, char: Char] RETURNS[BOOL] ~ {
RETURN[char=40B];
};
Correction: PROC[font: FONT, char: Char] RETURNS[CorrectionType] ~ {
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
IF char IN[file.min..file.max] THEN {
IF char=40B THEN RETURN[space] ELSE RETURN[mask];
};
RETURN[none];
};
Kern: PROC[font: FONT, char, successor: Char] RETURNS[VEC] ~ {
RETURN[[0, 0]];
};
NextKern: PROC[font: FONT, char, successor: Char] RETURNS[Char] ~ {
RETURN[nullChar];
};
Ligature: PROC[font: FONT, char, successor: Char] RETURNS[Char] ~ {
RETURN[nullChar];
};
NextLigature: PROC[font: FONT, char, successor: Char] RETURNS[Char] ~ {
RETURN[nullChar];
};
CharInfo: PROC[font: FONT, char: Char, key: ATOM] RETURNS[OptionalReal] ~ {
RETURN[[FALSE, 0]];
};
MaskChar: PROC[font: FONT, char: Char, imager: REF] ~ {
context: Imager.Context ~ NARROW[imager];
data: Data ~ NARROW[font.data];
file: StrikeFile ~ data.file;
IF char IN[file.min..file.max] THEN {
index: CARDINAL ~ char-file.min;
w: StrikeFontFormat.WidthEntry;
TRUSTED { w ← file.wtable[index] };
IF w#StrikeFontFormat.nullWidthEntry THEN {
f0, f1: CARDINAL;
TRUSTED { f0 ← file.xtable[index]; f1 ← file.xtable[index+1] };
IF f0<=f1 AND f1<=CARDINAL[file.raster*16] THEN NULL
ELSE ERROR MalformedStrikeFont; -- xtable inconsistent
IF f1>f0 THEN {
action: PROC ~ {
Imager.ConcatT[context, data.pixelToClient];
Imager.MaskBits[context: context, base: file.strike, wordsPerLine: file.raster,
sMin: 0, fMin: f0, sSize: file.lines, fSize: f1-f0,
sOffset: -file.ascent, fOffset: file.foffset+w.offset];
};
Imager.DoSave[context, action];
};
};
};
};
class: Font.Class ~ NEW[Font.ClassRep ← [
Modify: Modify,
Contains: Contains,
NextChar: NextChar,
Width: Width,
Amplified: Amplified,
Correction: Correction,
BoundingBox: BoundingBox,
Ligature: Ligature,
NextLigature: NextLigature,
Kern: Kern,
NextKern: NextKern,
CharInfo: CharInfo,
MaskChar: MaskChar
]];
END.