ImagerFontImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, November 17, 1985 7:06:27 pm PST
DIRECTORY
Atom USING [GetPropFromList, PutPropOnList],
Imager USING [ConcatT, Context, DoSaveAll],
ImagerFont USING [BYTE, CorrectionType, Extents, Font, FontImplRep, FontRep, XChar, XCharProc, XStringProc],
ImagerFontPrivate USING [FontAtom, FontImpl, FontImplRep, MakeFontAtom, EscapementTable, EscapementTableRep],
ImagerTransformation USING [Concat, PostScale, Rectangle, Scale, Transform, Transformation, TransformRectangle, TransformVec],
ImagerTypeface USING [Find, Typeface],
PrincOps USING [zXOR],
Rope USING [ActionType, Map, ROPE],
Vector2 USING [Add, VEC];
ImagerFontImpl: CEDAR MONITOR
IMPORTS Atom, Imager, ImagerFontPrivate, ImagerTransformation, ImagerTypeface, Rope, Vector2
EXPORTS ImagerFont, ImagerFontPrivate
~ BEGIN OPEN ImagerFont, ImagerFontPrivate;
Typeface: TYPE ~ ImagerTypeface.Typeface;
ROPE: TYPE ~ Rope.ROPE;
MapRope: PUBLIC PROC[rope: ROPE,
start: INT ← 0, len: INTINT.LAST, charAction: XCharProc] ~ {
state: {run, escape, escape2, extended, extended2} ← run;
set: BYTE ← 0;
byteAction: PROC[c: CHAR] RETURNS[BOOLFALSE] ~ {
b: BYTE ~ ORD[c];
SELECT state FROM
run => IF b=255 THEN state ← escape ELSE { charAction[[set: set, code: b]] };
escape => IF b=255 THEN state ← escape2 ELSE { set ← b; state ← run };
escape2 => IF b=0 THEN state ← extended ELSE ERROR;
extended => IF b=255 THEN state ← escape ELSE { set ← b; state ← extended2 };
extended2 => { charAction[[set: set, code: b]]; state ← extended };
ENDCASE;
};
[] ← Rope.Map[base: rope, start: start, len: len, action: byteAction];
};
MapText: PUBLIC PROC[text: REF READONLY TEXT,
start: NAT ← 0, len: NATNAT.LAST, charAction: XCharProc] ~ {
rem: NAT ~ text.length-start; -- bounds check
state: {run, escape, escape2, extended, extended2} ← run;
set: BYTE ← 0;
FOR i: NAT IN[0..MIN[len, rem]) DO
b: BYTE ~ ORD[text[start+i]];
SELECT state FROM
run => IF b=255 THEN state ← escape ELSE { charAction[[set: set, code: b]] };
escape => IF b=255 THEN state ← escape2 ELSE { set ← b; state ← run };
escape2 => IF b=0 THEN state ← extended ELSE ERROR;
extended => IF b=255 THEN state ← escape ELSE { set ← b; state ← extended2 };
extended2 => { charAction[[set: set, code: b]]; state ← extended };
ENDCASE;
ENDLOOP;
};
FontImpl: TYPE ~ ImagerFontPrivate.FontImpl;
FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to ImagerFont
VEC: TYPE ~ Vector2.VEC;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
Transformation: TYPE ~ ImagerTransformation.Transformation;
identityTransformation: Transformation ~ ImagerTransformation.Scale[1];
hashTableSize: NAT ~ 53;
HashIndex: TYPE ~ [0..hashTableSize);
HashTable: TYPE ~ REF HashTableRep;
HashTableRep: TYPE ~ ARRAY HashIndex OF NodeList;
NodeList: TYPE ~ LIST OF Node;
Node: TYPE ~ REF NodeRep;
NodeRep: TYPE ~ RECORD[fontAtom: FontAtom, font: Font];
hashTable: HashTable ~ NEW[HashTableRep ← ALL[NIL]];
entries: INT ← 0;
collisions: INT ← 0;
misses: INT ← 0;
Munch: PROC[LONG CARDINAL] RETURNS[CARDINAL]
~ TRUSTED MACHINE CODE { PrincOps.zXOR };
Hash: PROC[f: FontAtom] RETURNS[CARDINAL] ~ INLINE { RETURN[Munch[LOOPHOLE[f]]] };
GetFont: ENTRY PROC[typeface: Typeface, m: Transformation] RETURNS [Font] ~ {
ENABLE UNWIND => NULL;
f: FontAtom ~ ImagerFontPrivate.MakeFontAtom[typeface, m];
hash: CARDINAL ~ Hash[f];
hashIndex: HashIndex ~ hash MOD hashTableSize;
head: NodeList ~ hashTable[hashIndex];
FOR list: NodeList ← head, list.rest UNTIL list=NIL DO
node: Node ~ list.first;
IF node.fontAtom=f THEN RETURN[node.font] ELSE misses ← misses+1;
ENDLOOP;
{ -- not found, so make a new Font
font: Font ~ CreateFont[typeface, m];
node: Node ~ NEW[NodeRep ← [fontAtom: f, font: font]];
hashTable[hashIndex] ← CONS[node, head];
entries ← entries+1;
IF head#NIL THEN collisions ← collisions+1;
RETURN[font];
};
};
CreateFont: PROC[typeface: Typeface, m: Transformation] RETURNS [Font] ~ {
impl: FontImpl ~ NEW[FontImplRep ← [typeface: typeface]];
font: Font ~ NEW[FontRep ← [name: typeface.name, charToClient: m, impl: impl]];
IF typeface.info#NIL THEN BuildTables[font];
RETURN[font];
};
BuildTables: PROC[font: Font] ~ {
m: Transformation ~ font.charToClient;
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
escapementX, escapementY: EscapementTable ← NIL;
impl.fontBoundingBox ← TransformExtents[m, typeface.class.FontBoundingBox[typeface]];
FOR code: BYTE IN BYTE DO
escapement: VEC ~ m.Transform[typeface.class.Escapement[typeface, [set: 0, code: code]]];
IF escapement.x#0 THEN {
IF escapementX=NIL THEN escapementX ← NEW[EscapementTableRep ← ALL[0]];
escapementX[code] ← escapement.x;
};
IF escapement.y#0 THEN {
IF escapementY=NIL THEN escapementY ← NEW[EscapementTableRep ← ALL[0]];
escapementY[code] ← escapement.y;
};
ENDLOOP;
impl.escapementX ← escapementX;
impl.escapementY ← escapementY;
};
RectangleFromExtents: PROC[e: Extents] RETURNS[Rectangle] ~ {
RETURN[[x: -e.leftExtent, y: -e.descent, w: e.leftExtent+e.rightExtent, h: e.descent+e.ascent]];
};
ExtentsFromRectangle: PROC[r: Rectangle] RETURNS[Extents] ~ {
RETURN[[leftExtent: -r.x, rightExtent: r.x+r.w, descent: -r.y, ascent: r.y+r.h]];
};
TransformExtents: PROC[m: Transformation, e: Extents] RETURNS[Extents] ~ {
RETURN[ExtentsFromRectangle[m.TransformRectangle[RectangleFromExtents[e]]]]
};
Find: PUBLIC PROC[name: ROPE] RETURNS[Font] ~ {
typeface: Typeface ~ ImagerTypeface.Find[name]; -- may raise Imager.Error
RETURN[GetFont[typeface, identityTransformation]];
};
Modify: PUBLIC PROC[font: Font, m: Transformation] RETURNS[Font] ~ {
impl: FontImpl ~ font.impl;
RETURN[GetFont[impl.typeface, font.charToClient.Concat[m]]];
};
Scale: PUBLIC PROC[font: Font, s: REAL] RETURNS[Font] ~ {
impl: FontImpl ~ font.impl;
RETURN[GetFont[impl.typeface, font.charToClient.PostScale[s]]];
};
Contains: PUBLIC PROC[font: Font, char: XChar] RETURNS[BOOL] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
IF char.set=0 AND typeface.info#NIL THEN RETURN[typeface.info[char.code].exists]
ELSE RETURN[typeface.class.Contains[typeface, char]];
};
NextChar: PUBLIC PROC[font: Font, char: XChar] RETURNS[next: XChar] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
RETURN[typeface.class.NextChar[typeface, char]];
};
Escapement: PUBLIC PROC[font: Font, char: XChar] RETURNS[VEC] ~ {
impl: FontImpl ~ font.impl;
IF char.set=0 AND impl.typeface.info#NIL THEN {
escapementX: EscapementTable ~ impl.escapementX;
escapementY: EscapementTable ~ impl.escapementY;
RETURN[[
x: IF escapementX=NIL THEN 0 ELSE escapementX[char.code],
y: IF escapementY=NIL THEN 0 ELSE escapementY[char.code]
]];
}
ELSE {
typeface: Typeface ~ impl.typeface;
escapement: VEC ~ typeface.class.Escapement[typeface, char];
RETURN[font.charToClient.TransformVec[escapement]];
};
};
Amplified: PUBLIC PROC[font: Font, char: XChar] RETURNS[BOOL] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
IF char.set=0 AND typeface.info#NIL THEN RETURN[typeface.info[char.code].amplified]
ELSE RETURN[typeface.class.Amplified[typeface, char]];
};
Correction: PUBLIC PROC[font: Font, char: XChar] RETURNS[CorrectionType] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
IF char.set=0 AND typeface.info#NIL THEN RETURN[typeface.info[char.code].correction]
ELSE RETURN[typeface.class.Correction[typeface, char]];
};
BoundingBox: PUBLIC PROC[font: Font, char: XChar] RETURNS[Extents] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
charExtents: Extents ~ typeface.class.BoundingBox[typeface, char];
RETURN[TransformExtents[font.charToClient, charExtents]];
};
FontBoundingBox: PUBLIC PROC[font: Font] RETURNS[Extents] ~ {
impl: FontImpl ~ font.impl;
RETURN[impl.fontBoundingBox];
};
Kern: PUBLIC PROC[font: Font, char, successor: XChar] RETURNS[VEC] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
RETURN[typeface.class.Kern[typeface, char, successor]];
};
NextKern: PUBLIC PROC[font: Font, char, successor: XChar] RETURNS[XChar] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
RETURN[typeface.class.NextKern[typeface, char, successor]];
};
Ligature: PUBLIC PROC[font: Font, char, successor: XChar] RETURNS[XChar] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
RETURN[typeface.class.Ligature[typeface, char, successor]];
};
NextLigature: PUBLIC PROC[font: Font, char, successor: XChar] RETURNS[XChar] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
RETURN[typeface.class.NextLigature[typeface, char, successor]];
};
MaskChar: PUBLIC PROC[font: Font, char: XChar, context: Imager.Context] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
action: PROC ~ {
Imager.ConcatT[context, font.charToClient];
typeface.class.Mask[typeface, char, context];
};
Imager.DoSaveAll[context, action];
};
PutProp: PUBLIC PROC[font: Font, key: REF, val: REF] ~ {
font.propList ← Atom.PutPropOnList[propList: font.propList, prop: key, val: val];
};
GetProp: PUBLIC PROC[font: Font, key: REF] RETURNS[value: REF] ~ {
RETURN[Atom.GetPropFromList[propList: font.propList, prop: key]];
};
StringEscapement: PUBLIC PROC [font: Font, string: XStringProc] RETURNS [VEC] ~ {
impl: FontImpl ~ font.impl;
escapementX: EscapementTable ~ impl.escapementX;
escapementY: EscapementTable ~ impl.escapementY;
sum: VEC ← [0, 0];
charAction: XCharProc ~ {
IF char.set=0 THEN {
IF escapementX#NIL THEN sum.x ← sum.x+escapementX[char.code];
IF escapementY#NIL THEN sum.y ← sum.y+escapementY[char.code];
}
ELSE {
typeface: Typeface ~ impl.typeface;
escapement: VEC ~ font.charToClient.TransformVec[typeface.class.Escapement[typeface, char]];
sum.x ← sum.x+escapement.x; sum.y ← sum.y+escapement.y;
};
};
string[charAction];
RETURN[sum];
};
RopeEscapement: PUBLIC PROC [font: Font, rope: ROPE,
start: INT ← 0, len: INTINT.LAST] RETURNS [VEC] ~ {
string: XStringProc ~ { MapRope[rope, start, len, charAction] };
RETURN[StringEscapement[font, string]];
};
TextEscapement: PUBLIC PROC [font: Font, text: REF READONLY TEXT,
start: NAT ← 0, len: NATNAT.LAST] RETURNS [VEC] ~ {
string: XStringProc ~ { MapText[text, start, len, charAction] };
RETURN[StringEscapement[font, string]];
};
StringBoundingBox: PUBLIC PROC [font: Font, string: XStringProc] RETURNS [Extents] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
sxmin, symin, sxmax, symax: REAL ← 0;
position: VEC ← [0, 0];
count: INT ← 0;
charAction: XCharProc ~ {
charExtents: Extents ~ typeface.class.BoundingBox[typeface, char];
charEscapement: VEC ~ typeface.class.Escapement[typeface, char];
cxmin: REAL ~ position.x-charExtents.leftExtent;
cxmax: REAL ~ position.x+charExtents.rightExtent;
cymin: REAL ~ position.y-charExtents.descent;
cymax: REAL ~ position.y+charExtents.ascent;
IF count=0 THEN {
sxmin ← cxmin;
sxmax ← cxmax;
symin ← cymin;
symax ← cymax;
}
ELSE {
IF cxmin<sxmin THEN sxmin ← cxmin;
IF cxmax>sxmax THEN sxmax ← cxmax;
IF cymin<symin THEN symin ← cymin;
IF cymax>symax THEN symax ← cymax;
};
position ← Vector2.Add[position, charEscapement];
count ← count+1;
};
string[charAction];
RETURN[TransformExtents[font.charToClient,
[leftExtent: -sxmin, rightExtent: sxmax, descent: -symin, ascent: symax]]];
};
RopeBoundingBox: PUBLIC PROC [font: Font, rope: ROPE,
start: INT ← 0, len: INTINT.LAST] RETURNS [Extents] ~ {
string: XStringProc ~ { MapRope[rope, start, len, charAction] };
RETURN[StringBoundingBox[font, string]];
};
TextBoundingBox: PUBLIC PROC [font: Font, text: REF READONLY TEXT,
start: NAT ← 0, len: NATNAT.LAST] RETURNS [Extents] ~ {
string: XStringProc ~ { MapText[text, start, len, charAction] };
RETURN[StringBoundingBox[font, string]];
};
END.