DIRECTORY Atom, Basics, BasicTime, Checksum, Imager, ImagerFont, ImagerFontPrivate, ImagerTransformation, ImagerTypeface, PrincOps, SymTab, RefTab, RefText, Rope, Vector2
;
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;
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:
INT ←
INT.
LAST, charAction: XCharProc] ~ {
buf: REF TEXT ~ RefText.ObtainScratch[512];
state: {run, escape, escape2, extended, extended2} ← run;
set: BYTE ← 0;
index: INT ← start;
rem: INT ← MIN[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:
NAT ←
NAT.
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];
};
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;
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];
};
};
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]
};
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:
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] ~ {
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:
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]];
};