DIRECTORY
Checksum USING [ComputeChecksum],
II USING [ConcatT, Context, DoSaveAll, Rectangle],
IIFont USING [BYTE, CorrectionType, Extents, Font, FontRep, XChar, XCharProc, XStringProc],
IITransformation USING [Concat, Copy, Equal, PostScale, Scale, Transformation, TransformationRep, TransformRectangle, TransformVec],
IITypeface USING [FindTypeface, TypefaceRep],
PrincOps USING [zXOR],
Rope USING [ActionType, Map, ROPE],
Vector2 USING [Add, VEC];
~
BEGIN
BYTE: TYPE ~ IIFont.BYTE;
CorrectionType: TYPE ~ IIFont.CorrectionType;
Extents: TYPE ~ IIFont.Extents;
Font: TYPE ~ IIFont.Font;
FontRep: TYPE ~ IIFont.FontRep;
XChar: TYPE ~ IIFont.XChar;
XCharProc: TYPE ~ IIFont.XCharProc;
XStringProc: TYPE ~ IIFont.XStringProc;
Typeface: TYPE ~ REF TypefaceRep;
TypefaceRep:
PUBLIC
TYPE ~ IITypeface.TypefaceRep;
-- export to IITypeface
Rectangle: TYPE ~ II.Rectangle;
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;
};
VEC: TYPE ~ Vector2.VEC;
Transformation: TYPE ~ IITransformation.Transformation;
TransformationRep: TYPE ~ IITransformation.TransformationRep;
identityTransformation: Transformation ~ IITransformation.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 ~
RECORD [hash:
CARDINAL, 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 [typeface: Typeface, m: Transformation]
RETURNS [
CARDINAL] ~
TRUSTED
INLINE {
RETURN[Checksum.ComputeChecksum[cs: Munch[LOOPHOLE[typeface]],
nWords: SIZE[TransformationRep], p: LOOPHOLE[m]]];
};
MakeFont:
PUBLIC ENTRY
PROC [typeface: Typeface, m: Transformation]
RETURNS [Font] ~ {
N.B. Exported to IITypeface
ENABLE UNWIND => NULL;
hash: CARDINAL ~ Hash[typeface, m];
hashIndex: HashIndex ~ hash MOD hashTableSize;
head: NodeList ~ hashTable[hashIndex];
FOR each: NodeList ← head, each.rest
UNTIL each=
NIL
DO
node: Node ~ each.first;
IF node.hash = hash AND node.font.typeface = typeface AND IITransformation.Equal[node.font.charToClient, m] THEN RETURN[node.font] ELSE misses ← misses+1;
ENDLOOP;
{
-- not found, so make a new Font
font: Font ~ NEW[FontRep ← [charToClient: IITransformation.Copy[m], typeface: typeface]];
hashTable[hashIndex] ← CONS[[hash: hash, font: font], head];
entries ← entries+1;
IF head#NIL THEN collisions ← collisions+1;
RETURN[font];
};
};
RectangleFromExtents:
PROC [e: Extents]
RETURNS [Rectangle] ~
INLINE {
RETURN[[x: -e.leftExtent, y: -e.descent, w: e.leftExtent+e.rightExtent, h: e.descent+e.ascent]];
};
ExtentsFromRectangle:
PROC [r: Rectangle]
RETURNS [Extents] ~
INLINE {
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 ~ IITypeface.FindTypeface[name]; -- may raise II.Error
RETURN[MakeFont[typeface, identityTransformation]];
};
Modify:
PUBLIC
PROC [font: Font, m: Transformation]
RETURNS [Font] ~ {
RETURN[MakeFont[font.typeface, font.charToClient.Concat[m]]];
};
Scale:
PUBLIC
PROC [font: Font, s:
REAL]
RETURNS [Font] ~ {
RETURN[MakeFont[font.typeface, font.charToClient.PostScale[s]]];
};
FindScaled:
PUBLIC
PROC [name:
ROPE, s:
REAL]
RETURNS [Font] ~ {
RETURN[Scale[Find[name], s]];
};
Contains:
PUBLIC
PROC [font: Font, char: XChar]
RETURNS [
BOOL] ~ {
typeface: Typeface ~ font.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] ~ {
typeface: Typeface ~ font.typeface;
RETURN[typeface.class.NextChar[typeface, char]];
};
Escapement:
PUBLIC
PROC [font: Font, char: XChar]
RETURNS [
VEC] ~ {
typeface: Typeface ~ font.typeface;
escapement: VEC ~ typeface.class.Escapement[typeface, char];
RETURN[font.charToClient.TransformVec[escapement]];
};
Amplified:
PUBLIC
PROC [font: Font, char: XChar]
RETURNS [
BOOL] ~ {
typeface: Typeface ~ font.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] ~ {
typeface: Typeface ~ font.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] ~ {
typeface: Typeface ~ font.typeface;
charExtents: Extents ~ typeface.class.BoundingBox[typeface, char];
RETURN[TransformExtents[font.charToClient, charExtents]];
};
FontBoundingBox:
PUBLIC
PROC [font: Font]
RETURNS [Extents] ~ {
typeface: Typeface ~ font.typeface;
fontExtents: Extents ~ typeface.class.FontBoundingBox[typeface];
RETURN[TransformExtents[font.charToClient, fontExtents]];
};
Name:
PUBLIC
PROC [font: Font]
RETURNS [
ROPE] ~ {
typeface: Typeface ~ font.typeface;
RETURN [typeface.name];
};
Kern:
PUBLIC
PROC [font: Font, char, successor: XChar]
RETURNS [
VEC] ~ {
typeface: Typeface ~ font.typeface;
RETURN[typeface.class.Kern[typeface, char, successor]];
};
NextKern:
PUBLIC
PROC [font: Font, char, successor: XChar]
RETURNS [XChar] ~ {
typeface: Typeface ~ font.typeface;
RETURN[typeface.class.NextKern[typeface, char, successor]];
};
Ligature:
PUBLIC
PROC [font: Font, char, successor: XChar]
RETURNS [XChar] ~ {
typeface: Typeface ~ font.typeface;
RETURN[typeface.class.Ligature[typeface, char, successor]];
};
NextLigature:
PUBLIC
PROC [font: Font, char, successor: XChar]
RETURNS [XChar] ~ {
typeface: Typeface ~ font.typeface;
RETURN[typeface.class.NextLigature[typeface, char, successor]];
};
MaskChar:
PUBLIC
PROC [font: Font, char: XChar, context: II.Context] ~ {
N.B. Exported to IITypeface
typeface: Typeface ~ font.typeface;
action:
PROC ~ {
II.ConcatT[context, font.charToClient];
typeface.class.Mask[typeface, char, context];
};
II.DoSaveAll[context, action];
};
StringEscapement:
PUBLIC
PROC [font: Font, string: XStringProc]
RETURNS [
VEC] ~ {
sum: VEC ← [0, 0]; -- accumulated in character coordinates
typeface: Typeface ~ font.typeface;
charAction: XCharProc ~ {
escapement: VEC ~ typeface.class.Escapement[typeface, char];
sum ← Vector2.Add[sum, escapement];
};
string[charAction];
RETURN[font.charToClient.TransformVec[sum]];
};
RopeEscapement:
PUBLIC
PROC [font: Font, rope:
ROPE,
start:
INT ← 0, len:
INT ←
INT.
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:
NAT ←
NAT.
LAST]
RETURNS [
VEC] ~ {
string: XStringProc ~ { MapText[text, start, len, charAction] };
RETURN[StringEscapement[font, string]];
};
StringBoundingBox:
PUBLIC
PROC [font: Font, string: XStringProc]
RETURNS [Extents] ~ {
typeface: Typeface ~ font.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:
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]];
};