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:
INT ←
INT.
LAST, charAction: XCharProc] ~ {
state: {run, escape, escape2, extended, extended2} ← run;
set: BYTE ← 0;
byteAction:
PROC[c:
CHAR]
RETURNS[
BOOL ←
FALSE] ~ {
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:
NAT ←
NAT.
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:
INT ←
INT.
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:
NAT ←
NAT.
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:
INT ←
INT.
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:
NAT ←
NAT.
LAST]
RETURNS [Extents] ~ {
string: XStringProc ~ { MapText[text, start, len, charAction] };
RETURN[StringBoundingBox[font, string]];
};
END.