ACFontImpl.mesa
Copyright © 1984, Xerox Corporation. All rights reserved.
Doug Wyatt, October 9, 1984 9:39:32 am PDT
DIRECTORY
Basics USING [bytesPerWord],
CountedVM USING [Allocate, Handle, Pointer],
Font USING [Char, Class, ClassRep, CorrectionType, Extents, FONT, FontRep, nullChar, OptionalReal, Register],
FS USING [StreamOpen],
Imager USING [Class, ConcatT, Context, DoSave, MaskBits],
ImagerTransformation USING [Concat, PreRotate, Rectangle, Scale, Scale2, Transformation, TransformRectangle, TransformVec],
IO USING [Close, SetIndex, STREAM, UnsafeGetBlock],
PrePressFontFormat USING [CardFromDouble, CharacterData, CharacterIndexEntry, CharDataArray, DirectoryArray, FractionFromDouble, IXHeader, missingCharacter, missingFilePos, NameIndexEntry, RasterDefn],
Rope USING [FromProc, ROPE],
Scaled USING [Float, Value],
Vector2 USING [VEC];
ACFontImpl: CEDAR PROGRAM
IMPORTS CountedVM, Font, FS, Imager, ImagerTransformation, IO, PrePressFontFormat, Rope, Scaled
~ BEGIN
BYTE: TYPE ~ [0..377B];
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: ACFile,
pixelToClient: Transformation
];
ACFile: TYPE ~ REF ACFileRep;
ACFileRep: TYPE ~ RECORD[
bc, ec: BYTE,
charData: LONG POINTER TO PrePressFontFormat.CharDataArray,
directory: LONG POINTER TO PrePressFontFormat.DirectoryArray,
pixelToChar: Transformation,
vm: CountedVM.Handle
];
MalformedACFont: ERROR ~ CODE;
ReadWords: UNSAFE PROC[stream: IO.STREAM, base: LONG POINTER, words: INT] ~ UNCHECKED {
count: INT ~ words*Basics.bytesPerWord;
IF IO.UnsafeGetBlock[stream, [base: base, count: count]]=count THEN NULL
ELSE ERROR MalformedACFont; -- file too short
};
SetWordIndex: PROC[stream: IO.STREAM, wordIndex: INT] ~ {
index: CARDINAL ~ wordIndex*Basics.bytesPerWord;
IO.SetIndex[stream, index];
};
Open: PROC[fileName: ROPE] RETURNS[ACFile] ~ {
stream: IO.STREAM ~ FS.StreamOpen[fileName];
ix: PrePressFontFormat.IXHeader;
ixSize: NAT ~ SIZE[PrePressFontFormat.IXHeader];
name: PrePressFontFormat.NameIndexEntry;
index: PrePressFontFormat.CharacterIndexEntry;
nameFound, indexFound: BOOLFALSE;
family: ROPENIL;
segmentIndex, segmentWords: INT ← 0;
vm: CountedVM.Handle ← NIL;
charData, directory: LONG POINTERNIL;
perRes: REAL ← 0; -- char coordinate units per resolution unit
pixelToChar: Transformation ← NIL;
DO -- read the index part
TRUSTED { ReadWords[stream, @ix, SIZE[PrePressFontFormat.IXHeader]] };
SELECT ix.type FROM
end => EXIT;
name => {
IF nameFound THEN ERROR MalformedACFont; -- more than one name entry
IF (ix.length-ixSize)=SIZE[PrePressFontFormat.NameIndexEntry] THEN TRUSTED {
ReadWords[stream, @name, SIZE[PrePressFontFormat.NameIndexEntry]] }
ELSE ERROR MalformedACFont; -- wrong ix.length
{ -- convert name to rope
i: NAT ← 0; p: PROC RETURNS[CHAR] ~ { RETURN[VAL[name.chars[i ← i+1]]] };
family ← Rope.FromProc[len: name.chars[0], p: p];
};
nameFound ← TRUE;
};
character => {
IF indexFound THEN ERROR MalformedACFont; -- more than one char index entry
IF (ix.length-ixSize)=SIZE[PrePressFontFormat.CharacterIndexEntry] THEN TRUSTED {
ReadWords[stream, @index, SIZE[PrePressFontFormat.CharacterIndexEntry]] }
ELSE ERROR MalformedACFont; -- wrong ix.length
indexFound ← TRUE;
};
ENDCASE => ERROR MalformedACFont; -- unexpected ix type
ENDLOOP;
IF nameFound AND indexFound AND name.code=index.family THEN NULL
ELSE ERROR MalformedACFont; -- index part has wrong form
IF index.bc>index.ec THEN ERROR MalformedACFont; -- bc exceeds ec
segmentIndex ← PrePressFontFormat.CardFromDouble[index.segmentSA]; -- in words!
segmentWords ← PrePressFontFormat.CardFromDouble[index.segmentLength];
vm ← CountedVM.Allocate[words: segmentWords];
SetWordIndex[stream, segmentIndex];
TRUSTED { -- read segment
base: LONG POINTER ~ CountedVM.Pointer[vm];
ReadWords[stream, base, segmentWords];
charData ← base;
directory ← charData+SIZE[PrePressFontFormat.CharDataArray[index.ec-index.bc+1]];
};
IO.Close[stream];
perRes ← 25400.0/index.size; -- units of resolution are dots per 10 inches
pixelToChar ← ImagerTransformation.Scale2[perRes/index.resolutionX, perRes/index.resolutionY];
IF index.rotation#0 THEN pixelToChar.PreRotate[-index.rotation/60.0];
RETURN[NEW[ACFileRep ← [bc: index.bc, ec: index.ec,
charData: charData, directory: directory, pixelToChar: pixelToChar, vm: vm]]];
};
Find: PROC[name: ROPE] RETURNS[FONT] ~ {
file: ACFile ~ Open[name];
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: ACFile ~ 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: ACFile ~ data.file;
IF char IN[file.bc..file.ec] THEN {
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← file.charData[char-file.bc] };
RETURN[cd.bbdy#PrePressFontFormat.missingCharacter];
};
RETURN[FALSE];
};
NextChar: PROC[font: FONT, char: Char] RETURNS[next: Char] ~ {
data: Data ~ NARROW[font.data];
file: ACFile ~ data.file;
start: Char;
SELECT char FROM
=nullChar => start ← file.bc;
<file.bc => start ← file.bc;
<file.ec => start ← char+1;
ENDCASE => RETURN[nullChar];
FOR probe: Char IN[start..file.ec] DO
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← file.charData[probe-file.bc] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN RETURN[probe];
ENDLOOP;
RETURN[nullChar];
};
BoundingBox: PROC[font: FONT, char: Char] RETURNS[Extents] ~ {
data: Data ~ NARROW[font.data];
file: ACFile ~ data.file;
IF char IN[file.bc..file.ec] THEN {
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← file.charData[char-file.bc] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
r: ImagerTransformation.Rectangle ~ ImagerTransformation.TransformRectangle[
data.pixelToClient, [x: cd.bbox, y: cd.bboy, w: cd.bbdx, h: cd.bbdy]];
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: ACFile ~ data.file;
IF char IN[file.bc..file.ec] THEN {
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← file.charData[char-file.bc] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
wx: Scaled.Value ~ PrePressFontFormat.FractionFromDouble[cd.wx];
wy: Scaled.Value ~ PrePressFontFormat.FractionFromDouble[cd.wy];
RETURN[data.pixelToClient.TransformVec[[Scaled.Float[wx], Scaled.Float[wy]]]];
};
};
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: ACFile ~ data.file;
IF char IN[file.bc..file.ec] 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]];
};
missingOffset: LONG CARDINAL ~
PrePressFontFormat.CardFromDouble[PrePressFontFormat.missingFilePos];
MaskChar: PROC[font: FONT, char: Char, imager: REF] ~ {
context: Imager.Context ~ NARROW[imager];
data: Data ~ NARROW[font.data];
file: ACFile ~ data.file;
IF char IN[file.bc..file.ec] THEN {
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← file.charData[char-file.bc] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
raster, lines: NAT;
base: LONG POINTERNIL;
TRUSTED {
defn: LONG POINTER TO PrePressFontFormat.RasterDefn ← NIL;
fp: LONG CARDINAL ~ PrePressFontFormat.CardFromDouble[file.directory[char-file.bc]];
IF fp#missingOffset THEN defn ← LOOPHOLE[file.directory+fp]
ELSE ERROR MalformedACFont;
[raster: raster, lines: lines] ← defn^;
base ← defn+SIZE[PrePressFontFormat.RasterDefn];
};
IF lines=cd.bbdx AND raster=(cd.bbdy+15)/16 THEN NULL
ELSE ERROR MalformedACFont; -- raster and bounding box inconsistent
IF cd.bbdx>0 AND cd.bbdy>0 THEN {
action: PROC ~ {
Imager.ConcatT[context, data.pixelToClient];
Imager.MaskBits[context: context, base: base, wordsPerLine: raster,
sMin: 0, fMin: 0, sSize: cd.bbdx, fSize: cd.bbdy, sOffset: cd.bbox, fOffset: cd.bboy];
};
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
]];
Font.Register["Xerox/AC", Find];
END.