ImagerFontImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, May 27, 1985 1:49:59 pm PDT
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, WidthTable, WidthTableRep],
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]];
BuildTables[font];
RETURN[font];
};
BuildTables: PROC[font: Font] ~ {
m: Transformation ~ font.charToClient;
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
widthX, widthY: WidthTable ← NIL;
impl.fontBoundingBox ← TransformExtents[m, typeface.class.FontBoundingBox[typeface]];
FOR code: BYTE IN BYTE DO
width: VEC ~ m.Transform[typeface.class.Width[typeface, [set: 0, code: code]]];
IF width.x#0 THEN {
IF widthX=NIL THEN widthX ← NEW[WidthTableRep ← ALL[0]];
widthX[code] ← width.x;
};
IF width.y#0 THEN {
IF widthY=NIL THEN widthY ← NEW[WidthTableRep ← ALL[0]];
widthY[code] ← width.y;
};
ENDLOOP;
impl.widthX ← widthX;
impl.widthY ← widthY;
};
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 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]];
};
Width: PUBLIC PROC[font: Font, char: XChar] RETURNS[VEC] ~ {
impl: FontImpl ~ font.impl;
IF char.set=0 THEN {
widthX: WidthTable ~ impl.widthX;
widthY: WidthTable ~ impl.widthY;
RETURN[[
x: IF widthX=NIL THEN 0 ELSE widthX[char.code],
y: IF widthY=NIL THEN 0 ELSE widthY[char.code]
]];
}
ELSE {
typeface: Typeface ~ impl.typeface;
width: VEC ~ typeface.class.Width[typeface, char];
RETURN[font.charToClient.TransformVec[width]];
};
};
Amplified: PUBLIC PROC[font: Font, char: XChar] RETURNS[BOOL] ~ {
impl: FontImpl ~ font.impl;
typeface: Typeface ~ impl.typeface;
IF char.set=0 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 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]];
};
StringWidth: PUBLIC PROC [font: Font, string: XStringProc] RETURNS [VEC] ~ {
impl: FontImpl ~ font.impl;
widthX: WidthTable ~ impl.widthX;
widthY: WidthTable ~ impl.widthY;
sum: VEC ← [0, 0];
charAction: XCharProc ~ {
IF char.set=0 THEN {
IF widthX#NIL THEN sum.x ← sum.x+widthX[char.code];
IF widthY#NIL THEN sum.y ← sum.y+widthY[char.code];
}
ELSE {
typeface: Typeface ~ impl.typeface;
width: VEC ~ font.charToClient.TransformVec[typeface.class.Width[typeface, char]];
sum.x ← sum.x+width.x; sum.y ← sum.y+width.y;
};
};
string[charAction];
RETURN[sum];
};
RopeWidth: PUBLIC PROC [font: Font, rope: ROPE,
start: INT ← 0, len: INTINT.LAST] RETURNS [VEC] ~ {
string: XStringProc ~ { MapRope[rope, start, len, charAction] };
RETURN[StringWidth[font, string]];
};
TextWidth: PUBLIC PROC [font: Font, text: REF READONLY TEXT,
start: NAT ← 0, len: NATNAT.LAST] RETURNS [VEC] ~ {
string: XStringProc ~ { MapText[text, start, len, charAction] };
RETURN[StringWidth[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];
charWidth: VEC ~ typeface.class.Width[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, charWidth];
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.