ImagerFontImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, November 17, 1985 7:06:27 pm PST
Michael Plass, February 25, 1986 1:40:00 pm PST
DIRECTORY Atom, Basics, BasicTime, Checksum, Imager, ImagerFont, ImagerFontPrivate, ImagerTransformation, ImagerTypeface, PrincOps, SymTab, RefTab, RefText, Rope, Vector2
;
ImagerFontImpl: CEDAR MONITOR
IMPORTS Basics, Checksum, Imager, ImagerTransformation, ImagerTypeface, RefTab, RefText, Rope, SymTab, Vector2
EXPORTS ImagerFont
~ BEGIN
Types
BYTE: TYPE ~ Basics.BYTE;
CorrectionType: TYPE ~ ImagerFont.CorrectionType;
EscapementTable: TYPE ~ REF EscapementTableRep;
EscapementTableRep: TYPE ~ ARRAY Basics.BYTE OF REAL;
Extents: TYPE ~ ImagerFont.Extents;
Font: TYPE ~ ImagerFont.Font;
UName: TYPE ~ ImagerFont.UName;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
ROPE: TYPE ~ Rope.ROPE;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Typeface: TYPE ~ ImagerTypeface.Typeface;
VEC: TYPE ~ Vector2.VEC;
XChar: TYPE ~ ImagerFont.XChar;
XCharProc: TYPE ~ ImagerFont.XCharProc;
XStringProc: TYPE ~ ImagerFont.XStringProc;
Constants
identityTransformation: Transformation ~ ImagerTransformation.Scale[1];
nullXChar: XChar ~ ImagerFont.nullXChar;
XChar Mapping
StringState: TYPE ~ {run, escape, escape2, extended, extended2} ← run;
MapTextPart: PROC [text: REF READONLY TEXT, start: NAT, len: NAT, charAction: XCharProc, set: BYTE, state: StringState] RETURNS [newSet: BYTE, newState: StringState] ~ {
end: NAT ~ Basics.BoundsCheck[start+len, text.maxLength];
FOR i: NAT IN [start..end) DO
b: BYTE ~ ORD[text[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;
RETURN [newSet: set, newState: state]
};
MapRope: PUBLIC PROC [rope: ROPE, start: INT ← 0, len: INTINT.LAST, charAction: XCharProc] ~ {
buf: REF TEXT ~ RefText.ObtainScratch[512];
state: {run, escape, escape2, extended, extended2} ← run;
set: BYTE ← 0;
index: INT ← start;
rem: INTMIN[Basics.NonNegative[Rope.InlineSize[rope]-start], len];
WHILE rem > 0 DO
amt: NAT ~ MIN[rem, buf.maxLength];
buf.length ← 0;
[] ← Rope.AppendChars[buffer: buf, rope: rope, start: index, len: amt];
[set, state] ← MapTextPart[text: buf, start: 0, len: amt, charAction: charAction, set: set, state: state];
index ← index + amt;
rem ← rem - amt;
ENDLOOP;
RefText.ReleaseScratch[buf];
};
MapText: PUBLIC PROC [text: REF READONLY TEXT, start: NAT ← 0, len: NATNAT.LAST, charAction: XCharProc] ~ {
rem: NAT ~ text.length-start; -- bounds check
[] ← MapTextPart[text: text, start: start, len: MIN[rem, len], charAction: charAction, set: 0, state: run];
};
UName Hash Table
uNameTable: SymTab.Ref ~ SymTab.Create[mod: 37, case: FALSE];
races: INT ← 0;
GetIdentifer: PROC [name: ROPE] RETURNS [UName] ~ {
found: BOOL; val: REF;
[found, val] ← SymTab.Fetch[uNameTable, name];
IF found THEN WITH val SELECT FROM
uName: UName => RETURN [uName];
ENDCASE => ERROR -- val is wrong type or NIL
ELSE RETURN [NIL]
};
nextUNameCode: INT ← 0;
InsertUName: ENTRY PROC [name: ROPE, metricVersion, maskVersion: BasicTime.GMT] RETURNS [UName] ~ {
ENABLE UNWIND => NULL;
uName: UName ← NEW[ImagerFont.UNameRep ← [
name: name,
metricVersion: metricVersion,
maskVersion: maskVersion,
code: nextUNameCode
]];
IF SymTab.Insert[x: uNameTable, key: name, val: uName] THEN {
nextUNameCode ← nextUNameCode + 1;
}
ELSE {
races ← races+1;
uName ← GetIdentifer[name];
};
RETURN [uName]
};
RopeFromRef: PROC [x: REF] RETURNS [ROPE] ~ {
WITH x SELECT FROM
text: REF TEXT => RETURN[Rope.FromRefText[text]];
text: REF READONLY TEXT => RETURN[Rope.FromRefText[text]];
ENDCASE => RETURN[Rope.Flatten[NARROW[x]]];
};
MakeUName: PUBLIC PROC [name: ROPE] RETURNS [UName] ~ {
uName: UName ← GetIdentifer[name];
IF uName = NIL THEN {
rope: ROPE ~ RopeFromRef[name];
metricV, maskV: BasicTime.GMT;
[metricV, maskV] ← ImagerTypeface.LookupVersions[rope];
uName ← InsertUName[name: name, metricVersion: metricV, maskVersion: maskV];
};
RETURN [uName]
};
Font Hash Table
hashTableSize: NAT ~ 53;
HashIndex: TYPE ~ [0..hashTableSize);
HashTable: TYPE ~ REF HashTableRep;
HashTableRep: TYPE ~ ARRAY HashIndex OF LIST OF 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 [uName: UName, m: Transformation] RETURNS [CARDINAL] ~ TRUSTED INLINE {
RETURN[Checksum.ComputeChecksum[cs: Munch[LOOPHOLE[uName]],
nWords: SIZE[ImagerTransformation.TransformationRep], p: LOOPHOLE[m]]];
};
GetFont: PUBLIC ENTRY PROC [uName: UName, m: Transformation, mIsImmutable: BOOL] RETURNS [Font] ~ {
ENABLE UNWIND => NULL;
hash: CARDINAL ~ Hash[uName, m];
hashIndex: HashIndex ~ hash MOD hashTableSize;
head: LIST OF Font ~ hashTable[hashIndex];
FOR list: LIST OF Font ← head, list.rest UNTIL list=NIL DO
font: Font ~ list.first;
IF font.uName=uName AND ImagerTransformation.Equal[font.charToClient, m] THEN RETURN [font] ELSE misses ← misses+1;
ENDLOOP;
{ -- not found, so make a new Font
font: Font ~ NEW[ImagerFont.FontRep ← [uName: uName, charToClient: IF mIsImmutable THEN m ELSE ImagerTransformation.Copy[m]]];
hashTable[hashIndex] ← CONS[font, head];
entries ← entries+1;
IF head#NIL THEN collisions ← collisions+1;
RETURN[font];
};
};
Metrics Table
metricsArraySize: NAT ~ 100;
metricsArray: REF ARRAY [0..metricsArraySize) OF Metrics ~ NEW[ARRAY [0..metricsArraySize) OF Metrics ← ALL[NIL]];
metricsTable: RefTab.Ref ← RefTab.Create[];
GetMetricsFast: PROC [uName: UName] RETURNS [t: Metrics ← NIL] ~ INLINE {
IF uName.code < metricsArraySize THEN t ← metricsArray[uName.code];
IF t = NIL THEN t ← GetMetrics[uName];
};
GetMetrics: PROC [uName: UName] RETURNS [Metrics] ~ {
warn: BOOL;
THIS NEEDS LOCKING
metrics: Metrics ← NARROW[RefTab.Fetch[x: metricsTable, key: uName].val];
IF metrics = NIL THEN {
metrics ← ImagerMetrics.Find[uName];
IF metrics # NIL THEN {
[] ← RefTab.Insert[x: metricsTable, key: uName, val: metrics];
IF uName.code < metricsArraySize THEN metricsArray[uName.code] ← metrics;
};
};
RETURN [metrics];
};
Masks Table
GetMasks: PROC [uName: UName] RETURNS [Masks] ~ {
cache: Cache ~ FunctionCache.GlobalCache[];
masks: Masks ← NIL;
compare: FunctionCache.CompareProc ~ {goodargument = uName};
val: REF; ok: BOOL;
[val, ok] ← FunctionCache.Lookup[cache, compare, $ImagerFontMasks];
IF ok THEN masks ← NARROW[val]
ELSE {
metrics: Metrics ~ GetMetricsFast[uName];
masks ← ImagerTypeface.NewMasks[uName.name, uName.maskVersion, metrics];
FunctionCache.Insert[x: cache, argument: uName, value: masks, size: ???, clientID: $ImagerFontMasks];
};
RETURN [masks];
};
Fonts
Find: PUBLIC PROC [name: ROPE] RETURNS [Font] ~ {
uName: UName ~ MakeUName[name];
new: Font ~ GetFont[uName, identityTransformation, TRUE];
RETURN [new]
};
Modify: PUBLIC PROC [font: Font, m: Transformation] RETURNS [Font] ~ {
charToClient: Transformation ~ ImagerTransformation.Concat[font.charToClient, m];
new: Font ~ GetFont[font.uName, charToClient, TRUE];
RETURN [new]
};
Scale: PUBLIC PROC [font: Font, s: REAL] RETURNS [Font] ~ {
charToClient: Transformation ~ ImagerTransformation.PostScale[font.charToClient, s];
new: Font ~ GetFont[font.uName, charToClient, TRUE];
RETURN [new]
};
FindScaled: PUBLIC PROC [name: ROPE, s: REAL] RETURNS [Font] ~ {
uName: UName ~ MakeUName[name];
charToClient: Transformation ~ ImagerTransformation.Scale[s];
new: Font ~ GetFont[uName, charToClient, TRUE];
RETURN [new]
};
Extents
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]]]]
};
Metrics
Contains: PUBLIC PROC [font: Font, char: XChar] RETURNS [BOOL] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN [metrics.class.Contains[metrics, char]]
};
NextChar: PUBLIC PROC [font: Font, char: XChar] RETURNS [next: XChar] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.NextChar[metrics, char]];
};
Escapement: PUBLIC PROC [font: Font, char: XChar] RETURNS [VEC] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
escapement: VEC ~ metrics.class.Escapement[metrics, char];
RETURN[ImagerTransformation.TransformVec[font.charToClient, escapement]];
};
Amplified: PUBLIC PROC [font: Font, char: XChar] RETURNS [BOOL] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.Amplified[metrics, char]];
};
Correction: PUBLIC PROC[font: Font, char: XChar] RETURNS[CorrectionType] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.Correction[metrics, char]];
};
BoundingBox: PUBLIC PROC[font: Font, char: XChar] RETURNS[Extents] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
charExtents: Extents ~ metrics.class.BoundingBox[metrics, char];
RETURN[TransformExtents[font.charToClient, charExtents]];
};
FontBoundingBox: PUBLIC PROC [font: Font] RETURNS [Extents] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
extents: Extents ~ metrics.class.FontBoundingBox[metrics];
RETURN[TransformExtents[font.charToClient, extents]];
};
Kern: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS[VEC] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.Kern[metrics, char, successor]];
};
NextKern: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS[XChar] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.NextKern[metrics, char, successor]];
};
Ligature: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS[XChar] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.Ligature[metrics, char, successor]];
};
NextLigature: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS[XChar] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
RETURN[metrics.class.NextLigature[metrics, char, successor]];
};
MaskChar: PUBLIC PROC [font: Font, char: XChar, context: Imager.Context] ~ {
masks: Masks ~ GetMasks[font.uName];
action: PROC ~ {
Imager.ConcatT[context, font.charToClient];
masks.class.Mask[masks, char, context];
};
Imager.DoSaveAll[context, action];
};
StringEscapement: PUBLIC PROC [font: Font, string: XStringProc] RETURNS [VEC] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
sum: VEC ← [0, 0];
charAction: XCharProc ~ {
escapement: VEC ~ metrics.class.Escapement[metrics, char];
sum.x ← sum.x+escapement.x; sum.y ← sum.y+escapement.y;
};
string[charAction];
RETURN[ImagerTransformation.TransformVec[font.charToClient, 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] ~ {
metrics: Metrics ~ GetMetricsFast[font.uName];
sxmin, symin, sxmax, symax: REAL ← 0;
position: VEC ← [0, 0];
count: INT ← 0;
charAction: XCharProc ~ {
charExtents: Extents ~ metrics.class.BoundingBox[metrics, char];
charEscapement: VEC ~ metrics.class.Escapement[metrics, 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.