<> <> <> <> <<>> 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 <> 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; <> identityTransformation: Transformation ~ ImagerTransformation.Scale[1]; nullXChar: XChar ~ ImagerFont.nullXChar; <> 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]; }; <> 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] }; <> 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]; }; }; <> 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; <> 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]; }; <> GetMasks: PROC [uName: UName] RETURNS [Masks] ~ { cache: Cache ~ FunctionCache.GlobalCache[]; masks: Masks _ NIL; compare: FunctionCache.CompareProc ~ {good _ argument = 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]; }; <> 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] }; <> 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]]]] }; <> 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 cxminsxmax THEN sxmax _ cxmax; IF cyminsymax 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]]; }; <<>> END.