<> <> <> <<>> 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 cxminsxmax THEN sxmax _ cxmax; IF cyminsymax 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.