-- CGTextImpl.mesa -- Last changed by Doug Wyatt, October 4, 1982 11:34 am DIRECTORY CGArea USING [Ref], CGClipper USING [Data, IsAllBoxes, Node, Ref], CGColor USING [GetStipple], CGContext USING [GenBox, Ref], CGDevice USING [Ref], CGFont USING [defaultFont, LoadFont, Ref, Rep, WidthArray], CGMatrix USING [Assign, Concat, GetTrans, Id, IsTrivial, Map, New, Ref, SetTrans, Translate], CGPrivate USING [Context], CGSource USING [Ref, Rep], CGStorage USING [pZone, qZone], Graphics USING [Warning], GraphicsOps USING [], GraphicsBasic USING [black, Box, Vec, white], ConvertUnsafe USING [AppendRope], Rope USING [Cat, Map, MaxLen, ROPE], BitBlt USING [AlignedBBTable, BBTableSpace, BITBLT, BitBltTablePtr], Inline USING [LongMult], Real USING [RoundI, RoundLI], SpecialReal USING [Small], StrikeFormat USING [nullWidthEntry, WidthEntry, WTable, XTable]; CGTextImpl: CEDAR PROGRAM IMPORTS CGClipper, CGColor, CGContext, CGFont, CGMatrix, CGStorage, Graphics, ConvertUnsafe, Rope, BitBlt, Inline, Real, SpecialReal EXPORTS CGContext, CGPrivate, Graphics, GraphicsBasic, GraphicsOps = { OPEN StrikeFormat, GraphicsBasic; FontObject: PUBLIC TYPE = CGFont.Rep; -- export to GraphicsBasic TextDataRep: PUBLIC TYPE = DataRep; -- export to CGContext Context: TYPE = CGPrivate.Context; ContextData: TYPE = CGContext.Ref; Data: TYPE = REF DataRep; DataRep: TYPE = RECORD [ font: CGFont.Ref, -- default font lastid: CGMatrix.Id, -- id of last matrix used to form map map: CGMatrix.Ref, -- font (or bitmap) to device matrix mask: CGSource.Ref -- source info for font ]; Text: TYPE = REF READONLY TEXT; dataZone: ZONE = CGStorage.qZone; textZone: ZONE = CGStorage.pZone; srcZone: ZONE = CGStorage.qZone; GetData: PROC[self: ContextData] RETURNS[Data] = INLINE { data: Data _ self.textdata; RETURN[IF data=NIL THEN MakeData[self] ELSE data] }; MakeData: PROC[self: ContextData] RETURNS[Data] = { data: Data _ dataZone.NEW[DataRep _ [font: NIL, lastid: 0, map: NIL, mask: NIL]]; data.font _ CGFont.defaultFont; data.map _ CGMatrix.New[]; data.mask _ srcZone.NEW[CGSource.Rep _ [type: array, mode: transparent, fat: FALSE, bps: 0, color: GraphicsBasic.black, xbase: NIL, xrast: 0, Get: NIL]]; self.textdata _ data; RETURN[data]; }; ActionProc: TYPE = PROC[CHAR] RETURNS[BOOL]; -- this is SAFE, and matches Rope.ActionType DrawChar: PUBLIC PROC[self: Context, char: CHARACTER, font: CGFont.Ref] = { CharMap: PROC[action: ActionProc] = { [] _ action[char] }; self.procs.DrawChars[self,font,CharMap]; }; DrawString: PUBLIC UNSAFE PROC[self: Context, string: LONG STRING, start: NAT _ 0, len: NAT _ LAST[NAT], font: CGFont.Ref] = UNCHECKED { StringMap: SAFE PROC[action: ActionProc] = CHECKED { FOR i: CARDINAL IN[start..start+len) DO c: CHARACTER; TRUSTED { c _ string[i] }; IF action[c] THEN EXIT ENDLOOP }; -- Be sure substring limits are in range IF string=NIL THEN RETURN; start _ MIN[start,string.length]; len _ MIN[len,string.length-start]; self.procs.DrawChars[self,font,StringMap]; }; DrawText: PUBLIC PROC[self: Context, text: Text, start: NAT _ 0, len: NAT _ LAST[NAT], font: CGFont.Ref] = { TextMap: PROC[action: ActionProc] = { FOR i: CARDINAL IN[start..start+len) DO IF action[text[i]] THEN EXIT ENDLOOP }; -- Be sure substring limits are in range IF text=NIL THEN RETURN; start _ MIN[start,text.length]; len _ MIN[len,text.length-start]; self.procs.DrawChars[self,font,TextMap]; }; DrawRope: PUBLIC PROC[self: Context, rope: Rope.ROPE, start: INT _ 0, len: INT _ Rope.MaxLen, font: CGFont.Ref] = { RopeMap: PROC[action: ActionProc] = { [] _ Rope.Map[rope,start,len,action] }; IF rope=NIL THEN RETURN; self.procs.DrawChars[self,font,RopeMap]; }; DrawTextFromProc: PUBLIC PROC[self: Context, map: PROC[ActionProc], font: CGFont.Ref] = { self.procs.DrawChars[self, font, map]; }; -- DrawChars must do the following for each character: -- Get character information from strike font: -- xl,xr,yt,yb bounding box of character in strike -- xo,yo position of character origin in strike -- xw,yw width vector for character -- Let T be a copy of the current transformation -- Translate T to the rounded current position -- Concatenate: M _ [1,0,0,-1,-xo,yo] * T -- Use M to transform the box [xl,yt,xr,yb] -- Clip and display the resulting shape -- Use M to transform the relative vector [xw,yw] -- Move the current position by this amount -- -- Fortunately, much of the above computation can be taken out of the loop. -- For all characters in a strike font, yt,yb,yo and yw(=0) are fixed -- Only the translation components of the matrices change -- Most of the time, M is a simple translation -- -- Easiest case: cp is in integer range, msd is a translation, clipper is a box DrawChars: PUBLIC PROC[self: Context, font: CGFont.Ref, map: PROC[ActionProc]] = TRUSTED { ctx: ContextData _ NARROW[self.data]; data: Data _ GetData[ctx]; -- information from strike font f: CGFont.Ref _ IF font=NIL THEN data.font ELSE font; kern: BOOLEAN = f.kerned; min: CHARACTER = f.min; max: CHARACTER = f.max; offset: INTEGER = f.ox; xtable: LONG POINTER TO XTable = f.xtable; wtable: LONG POINTER TO WTable = f.wtable; width: REF CGFont.WidthArray _ f.width; -- information from display context T: CGMatrix.Ref _ ctx.matrix; clipper: CGClipper.Ref _ ctx.clipper; cp: Vec _ ctx.cp; src: CGSource.Ref _ ctx.src; M: CGMatrix.Ref _ data.map; bxmin,bxmax,bymin,bymax: INTEGER _ 0; -- bounding box for string x: INTEGER _ 0; -- current x position wid: INTEGER _ 0; -- width of string len: INT _ 0; -- number of chars -- Determine the font-to-device transformation matrix -- Compute it if necessary, then cache it for the next time IF data.lastid#T.id THEN { CGMatrix.Assign[M,T]; IF ctx.yUp THEN CGMatrix.Concat[M,1,0,0,-1]; data.lastid _ T.id }; { -- Compute the bounding box for the string (and count the characters) BoxAction: SAFE PROC[char: CHARACTER] RETURNS[BOOLEAN] = TRUSTED { xl,xr,xmin,xmax: INTEGER; -- get character information from font IF kern THEN { i: NAT; w: WidthEntry _ nullWidthEntry; IF char IN[min..max] THEN w _ wtable[i _ char-min]; IF w=nullWidthEntry THEN w _ wtable[i _ max-min+1]; xl _ xtable[i]; xr _ xtable[i+1]; xmin _ x + (offset + w.offset) } ELSE { i: NAT _ IF char IN[min..max] THEN char-min ELSE max-min+1; xl _ xtable[i]; xr _ xtable[i+1]; xmin _ x }; xmax _ xmin + (xr - xl); IF len=0 THEN { bxmin _ xmin; bxmax _ xmax } -- update x part of bounding box ELSE { IF xminbxmax THEN bxmax _ xmax }; x _ x + width[char]; -- advance position len _ len + 1; RETURN[FALSE]; }; map[BoxAction]; wid _ x; IF len>0 THEN { bymin _ -(f.oy+f.dy); bymax _ bymin + f.dy }; }; -- Test for the easy (and, we hope, common) case IF ctx.haveRaster AND CGMatrix.IsTrivial[M] AND CGClipper.IsAllBoxes[clipper] AND (cp.y+MIN[bymin,0])>FIRST[INTEGER] AND (cp.y+MAX[0,bymax])FIRST[INTEGER] AND (cp.x+MAX[0,bxmax,x])cxmin AND bxmincymax THEN { ymax _ cymax }; dline _ dbase + Inline.LongMult[ymin,drast]; sline _ sbase + Inline.LongMult[yb,srast]; bb.height _ ymax - ymin; x _ cpx; { EasyDrawAction: SAFE PROC[char: CHARACTER] RETURNS[BOOLEAN] = TRUSTED { xl,xr,xmin,xmax: INTEGER; IF kern THEN { i: NAT; w: WidthEntry _ nullWidthEntry; IF char IN[min..max] THEN w _ wtable[i _ char-min]; IF w=nullWidthEntry THEN w _ wtable[i _ max-min+1]; xl _ xtable[i]; xr _ xtable[i+1]; xmin _ x + (offset + w.offset) } ELSE { i: NAT _ IF char IN[min..max] THEN char-min ELSE max-min+1; xl _ xtable[i]; xr _ xtable[i+1]; xmin _ x }; xmax _ xmin + (xr - xl); IF xmax>cxmin AND xmincxmax THEN { xmax _ cxmax }; bb.dst _ [word: dline + LOOPHOLE[xmin,WB].w, bit: LOOPHOLE[xmin,WB].b]; bb.src _ [word: sline + LOOPHOLE[xl,WB].w, bit: LOOPHOLE[xl,WB].b]; bb.width _ xmax-xmin; BitBlt.BITBLT[bb]; }; x _ x + width[char]; -- advance position RETURN[FALSE]; }; map[EasyDrawAction]; }; }; ENDLOOP; IF ctx.boxing THEN { -- *** fix up bounding box here *** }; cp.x _ cp.x + wid; } ELSE { -- have to do it the hard way device: CGDevice.Ref _ ctx.device; mask: CGSource.Ref _ data.mask; yo: REAL _ f.oy+f.dy; box: Box; mask.xbase _ f.bitmap; mask.xrast _ f.raster; mask.color _ src.color; mask.mode _ (IF src.mode=invert THEN invert ELSE transparent); box.ymin _ 0; box.ymax _ f.dy; -- Round cp to nearest device grid point IF SpecialReal.Small[cp.x] THEN cp.x _ Real.RoundLI[cp.x]; IF SpecialReal.Small[cp.y] THEN cp.y _ Real.RoundLI[cp.y]; CGMatrix.SetTrans[M,cp]; { HardDrawAction: SAFE PROC[char: CHARACTER] RETURNS[BOOLEAN] = TRUSTED { xl,xr,xo,xw: INTEGER; area: CGArea.Ref _ NIL; -- get character information from font IF kern THEN { i: NAT; w: WidthEntry _ nullWidthEntry; IF char IN[min..max] THEN w _ wtable[i _ char-min]; IF w=nullWidthEntry THEN w _ wtable[i _ max-min+1]; xl _ xtable[i]; xr _ xtable[i+1]; xo _ xl - (offset + w.offset) } ELSE { i: NAT _ IF char IN[min..max] THEN char-min ELSE max-min+1; xl _ xtable[i]; xr _ xtable[i+1]; xo _ xl }; xw _ width[char]; -- width from width table CGMatrix.Translate[M,-xo,-yo]; box.xmin _ xl; box.xmax _ xr; area _ CGContext.GenBox[ctx,box,M]; device.Show[device,area,mask,M]; -- move current position by character width CGMatrix.SetTrans[M,cp]; CGMatrix.Translate[M,xw,0]; cp _ CGMatrix.GetTrans[M]; RETURN[FALSE]; }; map[HardDrawAction]; }; IF ctx.boxing THEN { -- *** fix up bounding box here *** }; }; ctx.cp _ cp; -- update current position }; DefaultFont: PUBLIC PROC RETURNS[CGFont.Ref] = { RETURN[CGFont.defaultFont] }; -- for GraphicsOps MakeFont: PUBLIC PROC[name: Rope.ROPE] RETURNS[CGFont.Ref] = { IsDot: PROC[c: CHARACTER] RETURNS[BOOLEAN] = { RETURN[c='.] }; string: STRING _ [100]; font: CGFont.Ref; IF NOT Rope.Map[base: name, action: IsDot] THEN name _ Rope.Cat[name,".ks"]; TRUSTED { ConvertUnsafe.AppendRope[string,name]; font _ CGFont.LoadFont[string] }; IF font=NIL THEN { SIGNAL Graphics.Warning[fontNotFound]; font _ CGFont.defaultFont } ELSE font.fileName _ name; RETURN[font]; }; CharBox: PUBLIC PROC[font: CGFont.Ref, char: CHARACTER] RETURNS[xmin,ymin,xmax,ymax: REAL] = { kern: BOOLEAN = font.kerned; min: CHARACTER = font.min; max: CHARACTER = font.max; xtable: LONG POINTER TO XTable = font.xtable; wtable: LONG POINTER TO WTable = font.wtable; bymin: INTEGER _ font.oy; bymax: INTEGER _ bymin + font.dy; xl,xr,xo: INTEGER; IF kern THEN { i: NAT; w: WidthEntry _ nullWidthEntry; IF char IN[min..max] THEN { i _ char-min; TRUSTED {w _ wtable[i]} }; IF w=nullWidthEntry THEN { i _ max-min+1; TRUSTED {w _ wtable[i]} }; TRUSTED {xl _ xtable[i]; xr _ xtable[i+1]}; xo _ xl - (font.ox + w.offset) } ELSE { i: NAT _ IF char IN[min..max] THEN char-min ELSE max-min+1; TRUSTED {xl _ xtable[i]; xr _ xtable[i+1]}; xo _ xl }; RETURN[xmin: xl - xo, xmax: xr - xo, ymin: bymin, ymax: bymax]; }; TextBox: PUBLIC PROC[font: CGFont.Ref, text: Text, start: NAT _ 0, len: NAT _ LAST[NAT]] RETURNS[xmin,ymin,xmax,ymax: REAL] = { TextMap: PROC[action: ActionProc] = { FOR i: CARDINAL IN[start..start+len) DO IF action[text[i]] THEN EXIT ENDLOOP }; box: Box; -- Be sure substring limits are in range IF text=NIL THEN start _ len _ 0 ELSE { start _ MIN[start,text.length]; len _ MIN[len,text.length-start] }; box _ CharsBox[font,TextMap]; RETURN[xmin: box.xmin, ymin: box.ymin, xmax: box.xmax, ymax: box.ymax]; }; RopeBox: PUBLIC PROC[font: CGFont.Ref, rope: Rope.ROPE, start: INT _ 0, len: INT _ LAST[INT]] RETURNS[xmin,ymin,xmax,ymax: REAL] = { RopeMap: PROC[action: ActionProc] = { [] _ Rope.Map[rope,start,len,action] }; box: Box; box _ CharsBox[font,RopeMap]; RETURN[xmin: box.xmin, ymin: box.ymin, xmax: box.xmax, ymax: box.ymax]; }; CharsBox: PROC[font: CGFont.Ref, map: PROC[ActionProc]] RETURNS[Box] = { kern: BOOLEAN = font.kerned; min: CHARACTER = font.min; max: CHARACTER = font.max; offset: INTEGER = font.ox; xtable: LONG POINTER TO XTable = font.xtable; wtable: LONG POINTER TO WTable = font.wtable; width: REF CGFont.WidthArray _ font.width; cpx: INTEGER _ 0; bxmin,bxmax: INTEGER _ 0; bymin: INTEGER _ font.oy; bymax: INTEGER _ bymin + font.dy; first: BOOLEAN _ TRUE; { Action: PROC[char: CHARACTER] RETURNS[BOOLEAN] = { xl,xr,xo,xw,tx,cxmin,cxmax: INTEGER; -- get character information from font IF kern THEN { i: NAT; w: WidthEntry _ nullWidthEntry; IF char IN[min..max] THEN { i _ char-min; TRUSTED {w _ wtable[i]} }; IF w=nullWidthEntry THEN { i _ max-min+1; TRUSTED {w _ wtable[i]} }; TRUSTED {xl _ xtable[i]; xr _ xtable[i+1]}; xo _ xl - (offset + w.offset) } ELSE { i: NAT _ IF char IN[min..max] THEN char-min ELSE max-min+1; TRUSTED {xl _ xtable[i]; xr _ xtable[i+1]}; xo _ xl }; xw _ width[char]; -- width from width table tx _ cpx - xo; -- x translation cxmin _ xl + tx; cxmax _ xr + tx; -- x bounds -- update x components of bounding box IF first THEN { bxmin _ cxmin; bxmax _ cxmax; first _ FALSE } ELSE { IF cxminbxmax THEN bxmax _ cxmax }; -- move current position by character width cpx _ cpx + xw; RETURN[FALSE]; }; map[Action]; }; IF first THEN RETURN[[0,0,0,0]] ELSE RETURN[[xmin: bxmin, xmax: bxmax, ymin: bymin, ymax: bymax]]; }; CharWidth: PUBLIC PROC[font: CGFont.Ref, char: CHARACTER] RETURNS[xw,yw: REAL] = { RETURN[font.width[char],0]; }; TextWidth: PUBLIC PROC[font: CGFont.Ref, text: Text, start: NAT _ 0, len: NAT _ LAST[NAT]] RETURNS[xw,yw: REAL] = { TextMap: PROC[action: ActionProc] = { FOR i: CARDINAL IN[start..start+len) DO IF action[text[i]] THEN EXIT ENDLOOP }; w: Vec; -- Be sure substring limits are in range IF text=NIL THEN start _ len _ 0 ELSE { start _ MIN[start,text.length]; len _ MIN[len,text.length-start] }; w _ CharsWidth[font,TextMap]; RETURN[xw: w.x, yw: w.y]; }; RopeWidth: PUBLIC PROC[font: CGFont.Ref, rope: Rope.ROPE, start: INT _ 0, len: INT _ LAST[INT]] RETURNS[xw,yw: REAL] = { RopeMap: PROC[action: ActionProc] = { [] _ Rope.Map[rope,start,len,action] }; w: Vec; w _ CharsWidth[font,RopeMap]; RETURN[xw: w.x, yw: w.y]; }; CharsWidth: PROC[font: CGFont.Ref, map: PROC[ActionProc]] RETURNS[Vec] = { width: REF CGFont.WidthArray _ font.width; w: INTEGER _ 0; Action: PROC[char: CHARACTER] RETURNS[BOOLEAN] = { w _ w + width[char]; RETURN[FALSE] }; map[Action]; RETURN[[w,0]]; }; FontBox: PUBLIC PROC[font: CGFont.Ref] RETURNS[xmin,ymin,xmax,ymax: REAL] = { RETURN[xmin: font.ox, xmax: font.ox+font.dx, ymin: font.oy, ymax: font.oy+font.dy]; }; SetDefaultFont: PUBLIC PROC[self: Context, font: CGFont.Ref] = { ctx: ContextData _ NARROW[self.data]; data: Data _ GetData[ctx]; data.font _ font; }; GetDefaultFont: PUBLIC PROC[self: Context] RETURNS[CGFont.Ref] = { ctx: ContextData _ NARROW[self.data]; data: Data _ GetData[ctx]; RETURN[data.font]; }; }. Κ ‘– "Mesa" style˜Iprocšͺ ΟcKœΟk œ žœžœ*žœžœžœžœ;žœ`žœžœžœžœžœžœ+žœžœžœ žœ žœžœžœ"žœžœ<žœžœžœ€žœ@žœ+žœžœœ žœžœ œ žœ#žœžœžœžœžœœ&œ%œœ žœžœžœžœ žœžœžœΟnœžœžœ žœ#žœžœžœžœžœ Ÿœžœžœ#žœžœžœžœYžœ:žœ-žœžœžœŸ œžœžœžœžœžœ-œŸœžœžœž œŸœžœ_Ÿ œžœžœžœžœžœ žœ žœžœžœž œŸ œžœžœžœžœžœžœžœž œžœžœ žœžœžœ)œžœžœžœžœ žœžœNŸœžœžœ%žœ žœžœžœŸœžœžœžœžœžœžœžœžœžœ)œžœžœžœžœ žœžœJŸœžœžœžœ žœ žœ(ŸœžœCžœžœžœžœ3ŸœžœžœžœUΫœPœŸ œžœžœ)žœžœžœ, œžœžœžœ žœžœž œž œžœžœžœžœžœžœžœžœ $œžœ{žœ6žœœžœœžœœžœœ6œ<œžœ žœžœžœžœžœ žœžœžœFœŸ œžœžœž œžœžœžœžœ'œžœžœžœ(žœžœ žœ!žœžœižœžœžœžœ žœ žœažœžœ !œžœžœ žœžœ žœ*œžœžœ)žœžœ91œžœžœžœžœ!žœžœ žœžœžœžœ žœžœžœžœ žœžœžœžœ žœžœžœ žœžœJžœžœžœžœžœžœ|ž œ žœžœžœžœžœžœžœžœ‘žœžœ+žœžœ žœžœ žœœžœžœžœžœ4žœ(žœ(žœ(žœ!žœžœžœ(œžœ žœ žœ žœžœžœžœžœžœžœ žœ žœ,žœ žœΏŸœžœžœž œžœžœžœžœ žœžœžœ.žœžœ žœ'žœžœužœžœžœžœ žœ žœsžœ žœ žœžœžœžœž œžœ*žœ žœ7žœ žœ7žœžœ žœžœ*žœžœ žœžœ;žœ4œ žœžœOžœžœ žœ $œ(žœœSžœ{žœžœžœ6)œžœžœ žœžœ2žœŸœžœžœž œžœžœžœžœžœ 'œžœžœžœ*žœžœ žœ#žœžœlžœžœžœžœ žœ žœYœžœUžœ&žœ ,œžœžœ%žœ žœžœ0žœ žœ $œœŸ œžœžœžœžœœŸœžœžœ žœžœŸœžœž œžœžœžœžœ žœžœ%žœ žœNžœžœžœžœ?žœžœŸœžœžœž œžœžœžœž œž œžœžœžœ!žœžœžœ žœžœ žœžœžœžœ&žœžœ žœžœžœžœžœžœHžœžœžœžœ žœ žœžœ2žœ@Ÿœžœžœ(žœ žœžœžœžœžœŸœžœžœžœžœžœžœžœžœžœ)œžœžœžœžœ žœžœ=žœHŸœžœžœžœ žœ žœžœžœžœžœŸœžœožœHŸœžœžœžœžœž œž œžœžœžœžœ!žœžœžœ žœ(žœžœžœžœžœžœ Ÿœžœž œžœžœ&žœ'œžœžœžœ(žœžœ žœžœžœžœžœžœIžœžœžœžœ žœ žœžœFœœ& œ'œžœžœ)žœžœžœ žœžœ žœ,œžœžœ žœžœžœžœžœ>Ÿ œžœžœž œžœžœžœŸ œžœžœ(žœ žœžœžœžœžœŸœžœžœžœžœžœžœžœžœžœ)œžœžœžœžœ žœžœ=žœŸ œžœžœžœ žœ žœžœžœžœžœŸœžœmžœŸ œžœžœžœžœ&žœŸœžœž œžœžœžœžœžœŸœžœžœžœžœžœXŸœžœžœ;žœDŸœžœžœžœ&žœ,žœ˜š‰—…—DœO3