ImagerTypefaceACImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, March 7, 1986 4:14:12 pm PST
DIRECTORY
Basics USING [BYTE, bytesPerWord],
CountedVM USING [Allocate, Handle],
FS USING [OpenFile, StreamFromOpenFile],
Imager USING [ConcatT, Context, DoSave, MaskBits, RotateT],
ImagerFont USING [CorrectionType, Extents, nullXChar, XChar],
ImagerTransformation USING [ApplyPreRotate, Rectangle, Scale2, Transformation, TransformRectangle, TransformVec],
ImagerTypeface USING [Register, Typeface, TypefaceClass, TypefaceClassRep, TypefaceRep],
IO USING [Close, SetIndex, STREAM, UnsafeGetBlock],
PrePressFontFormat USING [CardFromBcpl, CharacterData, CharacterIndexEntry, CharDataArray, DirectoryArray, IntFromBcpl, IXHeader, missingCharacter, missingFilePos, NameIndexEntry, RasterDefn, RelFilePos],
Real USING [FScale],
Vector2 USING [VEC];
ImagerTypefaceACImpl: CEDAR PROGRAM
IMPORTS CountedVM, FS, Imager, ImagerTransformation, ImagerTypeface, IO, PrePressFontFormat, Real
~ BEGIN OPEN ImagerTypeface, ImagerFont;
BYTE: TYPE ~ [0..377B];
VEC: TYPE ~ Vector2.VEC;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
bc, ec: BYTE ← 0,
charData: LONG POINTER TO PrePressFontFormat.CharDataArray ← NIL,
directory: LONG POINTER TO PrePressFontFormat.DirectoryArray ← NIL,
pixelToChar: Transformation ← NIL,
vm: CountedVM.Handle ← NIL
];
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];
};
ACCreate: PROC [file: FS.OpenFile] RETURNS [Typeface] ~ {
data: Data ~ NEW[DataRep ← []];
stream: IO.STREAM ~ FS.StreamFromOpenFile[file];
ix: PrePressFontFormat.IXHeader;
ixSize: NAT ~ SIZE[PrePressFontFormat.IXHeader];
name: PrePressFontFormat.NameIndexEntry;
index: PrePressFontFormat.CharacterIndexEntry;
nameFound, indexFound: BOOLFALSE;
segmentIndex, segmentWords: INT ← 0;
charUnitsPerResolutionUnit: REAL ← 0;
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
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
data.bc ← index.bc; data.ec ← index.ec;
segmentIndex ← PrePressFontFormat.CardFromBcpl[index.segmentSA]; -- in words!
segmentWords ← PrePressFontFormat.CardFromBcpl[index.segmentLength];
data.vm ← CountedVM.Allocate[words: segmentWords];
SetWordIndex[stream, segmentIndex];
TRUSTED { -- read segment
base: LONG POINTER ~ data.vm.pointer;
ReadWords[stream, base, segmentWords];
data.charData ← base;
data.directory ← base+SIZE[PrePressFontFormat.CharDataArray[index.ec-index.bc+1]];
};
IO.Close[stream];
charUnitsPerResolutionUnit ← 25400.0/index.size; -- units of resolution are dots per 10 inches
data.pixelToChar ← ImagerTransformation.Scale2[[
charUnitsPerResolutionUnit/index.resolutionX,
charUnitsPerResolutionUnit/index.resolutionY
]];
IF index.rotation#0 THEN data.pixelToChar.ApplyPreRotate[-index.rotation/60.0];
RETURN[NEW[TypefaceRep ← [class: acClass, data: data]]];
};
ACContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {
data: Data ~ NARROW[self.data];
IF char.set=0 AND char.code IN[data.bc..data.ec] THEN {
index: NAT ~ char.code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
RETURN[cd.bbdy#PrePressFontFormat.missingCharacter];
};
RETURN[FALSE];
};
ACNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ {
data: Data ~ NARROW[self.data];
start: BYTE ← 0;
IF char=nullXChar THEN NULL
ELSE IF char.set=0 AND char.code<data.ec THEN start ← char.code+1
ELSE RETURN[nullXChar];
FOR code: BYTE IN[MAX[data.bc, start]..data.ec] DO
index: NAT ~ code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN RETURN[[set: 0, code: code]];
ENDLOOP;
RETURN[nullXChar];
};
ACEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ {
data: Data ~ NARROW[self.data];
IF char.set=0 AND char.code IN[data.bc..data.ec] THEN {
index: NAT ~ char.code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
wx: REAL ~ Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wx], -16];
wy: REAL ~ Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wy], -16];
RETURN[data.pixelToChar.TransformVec[[wx, wy]]];
};
};
RETURN[[0, 0]];
};
ACAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {
RETURN[char.set=0 AND char.code=40B];
};
ACCorrection: PROC [self: Typeface, char: XChar] RETURNS [CorrectionType] ~ {
data: Data ~ NARROW[self.data];
IF char.set=0 AND char.code IN[data.bc..data.ec] THEN {
IF char.code=40B THEN RETURN[space] ELSE RETURN[mask];
};
RETURN[none];
};
ACBoundingBox: PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ {
data: Data ~ NARROW[self.data];
IF char.set=0 AND char.code IN[data.bc..data.ec] THEN {
index: NAT ~ char.code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
r: ImagerTransformation.Rectangle ~ data.pixelToChar.TransformRectangle[
[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]];
};
ACFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ {
data: Data ~ NARROW[self.data];
count: NAT ← 0;
fxmin, fymin, fxmax, fymax: REAL ← 0;
FOR code: BYTE IN[data.bc..data.ec] DO
index: NAT ~ code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
r: ImagerTransformation.Rectangle ~ data.pixelToChar.TransformRectangle[
[x: cd.bbox, y: cd.bboy, w: cd.bbdx, h: cd.bbdy]];
xmin: REAL ~ r.x; xmax: REAL ~ r.x+r.w;
ymin: REAL ~ r.y; ymax: REAL ~ r.y+r.w;
IF count=0 THEN { fxmin ← xmin; fymin ← ymin; fxmax ← xmax; fymax ← ymax }
ELSE {
IF xmin<fxmin THEN fxmin ← xmin;
IF ymin<fymin THEN fymin ← ymin;
IF xmax>fxmax THEN fxmax ← xmax;
IF ymax>fymax THEN fymax ← ymax;
};
count ← count+1;
};
ENDLOOP;
RETURN[[leftExtent: -fxmin, rightExtent: fxmax, descent: -fymin, ascent: fymax]];
};
ACKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ {
RETURN[[0, 0]];
};
ACNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ {
RETURN[nullXChar];
};
ACLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ {
RETURN[nullXChar];
};
ACNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ {
RETURN[nullXChar];
};
ACMask: PROC [self: Typeface, char: XChar, context: Imager.Context] ~ {
data: Data ~ NARROW[self.data];
IF char.set=0 AND char.code IN[data.bc..data.ec] THEN {
index: NAT ~ char.code-data.bc;
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← data.charData[index] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN {
raster, lines: NAT;
base: LONG POINTERNIL;
TRUSTED {
defn: LONG POINTER TO PrePressFontFormat.RasterDefn ← NIL;
filePos: PrePressFontFormat.RelFilePos ~ data.directory[index];
offset: INT ~ PrePressFontFormat.IntFromBcpl[filePos];
IF filePos=PrePressFontFormat.missingFilePos THEN ERROR MalformedACFont;
defn ← LOOPHOLE[data.directory+offset];
[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.RotateT[context, 90];
Imager.ConcatT[context, data.pixelToChar];
Imager.MaskBits[context: context, base: base, wordsPerLine: raster,
sMin: 0, fMin: 0, sSize: cd.bbdx, fSize: cd.bbdy, tx: cd.bboy, ty: -cd.bbox];
};
Imager.DoSave[context, action];
};
};
};
};
acClass: TypefaceClass ~ NEW[TypefaceClassRep ← [
type: $AC,
Contains: ACContains,
NextChar: ACNextChar,
Escapement: ACEscapement,
Amplified: ACAmplified,
Correction: ACCorrection,
BoundingBox: ACBoundingBox,
FontBoundingBox: ACFontBoundingBox,
Ligature: ACLigature,
NextLigature: ACNextLigature,
Kern: ACKern,
NextKern: ACNextKern,
Mask: ACMask
]];
ImagerTypeface.Register["AC", ACCreate];
END.