DIRECTORY BitmapEdit, Commander, FileIO, Graphics, ImagerPixelMaps, Interminal, IO, Menus, MessageWindow, Process, RasterFontWriter, Real, Rope, TIPUser, ViewerClasses, ViewerOps, ViewerTools ; FontEditImpl: CEDAR PROGRAM IMPORTS BitmapEdit, Commander, FileIO, Graphics, ImagerPixelMaps, IO, Menus, MessageWindow, Process, RasterFontWriter, Real, Rope, TIPUser, ViewerOps, ViewerTools SHARES ViewerOps ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ fileName: ROPE, font: RasterFontWriter.InternalFont, refreshing: BOOLEAN _ FALSE, hasAChar: BOOLEAN _ FALSE, curCharCode: CHAR, savedCharRep: RasterFontWriter.InternalCharRep, bitmapViewer: ViewerClasses.Viewer, textBaseline: INTEGER _ 4, textDepth: INTEGER _ 2, text: ROPE, paintProcess: PROCESS _ NIL ]; defaultText: ROPE _ "the quick brown fox jumps over lazy dogs; THE QUICK BROWN FOX JUMPS OVER LAZY DOGS."; allChars: ROPE _ AllChars[]; AllChars: PROC RETURNS [ROPE] ~ { text: REF TEXT _ NEW[TEXT[256]]; FOR i: NAT IN [0..256) DO text[i] _ '\000 + i; ENDLOOP; text.length _ 256; RETURN [Rope.FromRefText[text]] }; DrawBitmapChar: PROC [context: Graphics.Context, char: RasterFontWriter.InternalCharRep, xMinGarbage: INT] RETURNS [xMaxGood: INT] ~ TRUSTED { mark: Graphics.Mark _ Graphics.Save[context]; xMaxGood _ MAX[Real.RoundLI[Graphics.GetCP[context, TRUE].x]+char.pixels.fSize+char.pixels.fMin+char.pixels.fOrigin, xMinGarbage]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, [xMinGarbage, -9999, xMaxGood, 9999]]; Graphics.SetColor[context, Graphics.black]; [] _ Graphics.SetPaintMode[context, transparent]; context.procs.DrawBits[context, char.pixels.refRep.pointer, char.pixels.refRep.rast, 0, char.pixels.fMin, char.pixels.sMin, char.pixels.fSize, char.pixels.sSize, -char.pixels.fOrigin, -char.pixels.sOrigin]; Graphics.SetCP[context, char.fWidth, char.sWidth, TRUE]; Graphics.Restore[context, mark]; }; DrawText: PROC [viewer: ViewerClasses.Viewer, context: Graphics.Context] ~ { data: Data _ NARROW[viewer.data]; mark: Graphics.Mark _ Graphics.Save[context]; xMinGarbage: INT _ 0; Action: PROC [c: CHAR] RETURNS [quit: BOOLEAN _ FALSE] ~ { IF xMinGarbage > viewer.cw THEN RETURN [TRUE]; xMinGarbage _ DrawBitmapChar[context, data.font.charRep[c], xMinGarbage]; }; IF data.font = NIL THEN RETURN; StoreCurrent[viewer]; Graphics.SetCP[context, 4, viewer.ch-data.textBaseline]; Graphics.ClipBox[context, [0, viewer.ch-data.textBaseline-data.textDepth, viewer.cw, viewer.ch]]; [] _ data.text.Map[action: Action]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, [xMinGarbage, -9999, 9999, 9999]]; Graphics.Restore[context, mark]; }; FindSelectedChar: PROC [viewer: ViewerClasses.Viewer, where: Interminal.MousePosition] RETURNS [ index: INT ] ~ { data: Data _ NARROW[viewer.data]; fMouse: INT _ where.mouseX; cpf: INT _ 4; Action: PROC [c: CHAR] RETURNS [quit: BOOLEAN _ FALSE] ~ { charRep: RasterFontWriter.InternalCharRep _ data.font.charRep[c]; window: ImagerPixelMaps.DeviceRectangle _ charRep.pixels.Window; IF fMouse-cpf IN [window.fMin..window.fMin+window.fSize) THEN RETURN [TRUE]; cpf _ cpf + charRep.fWidth; index _ index + 1; }; index _ 0; IF NOT data.text.Map[action: Action] THEN index _ -1; }; RefreshProcess: PROC [viewer: ViewerClasses.Viewer] ~ { data: Data _ NARROW[viewer.data]; WHILE data.refreshing AND NOT viewer.destroyed DO IF data.bitmapViewer.newVersion THEN { ViewerOps.PaintViewer[viewer, client, FALSE, $Text]; ViewerOps.SetNewVersion[viewer]; }; Process.Pause[Process.MsecToTicks[300]]; ENDLOOP; }; GetSelection: PROC RETURNS [selectionContents: ROPE] ~ { selectedViewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; IF selectedViewer = NIL OR selectedViewer.class.get = NIL THEN RETURN [NIL]; selectionContents _ NARROW[selectedViewer.class.get[selectedViewer, $SelChars]]; }; Create: PUBLIC PROC [fontFileName: ROPE] RETURNS [viewer: ViewerClasses.Viewer] ~ { menu: Menus.Menu _ Menus.CreateMenu[]; data: Data _ NEW[DataRep]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Get", proc: GetButton, documentation: "Get a raster font" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Store", proc: StoreButton, documentation: "Store a raster font" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Save", proc: SaveButton, documentation: "Save the raster font" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Char", proc: CharButton, documentation: "Edit a new character" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Next/Prev", proc: NextPrevButton, documentation: "Move to the next or previous char" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Text", proc: TextButton, documentation: "See what some text looks like" ] ]; viewer _ ViewerOps.CreateViewer[$FontEdit, [menu: menu, name: "Font Editor", data: data], FALSE]; data.bitmapViewer _ BitmapEdit.CreateBitmapViewer[4, 4, [border: FALSE, parent: viewer, wh: 20, ww: 20]]; Get[viewer, fontFileName]; IF data.font = NIL THEN data.font _ RasterFontWriter.Create[[-5, 1, 6, 4], 6]; data.text _ defaultText; ViewerOps.PaintViewer[viewer, all]; }; Get: PROC [viewer: ViewerClasses.Viewer, fontFileName: ROPE] ~ { data: Data _ NARROW[viewer.data]; data.font _ RasterFontWriter.Load[fontFileName ! RasterFontWriter.FormatError => GOTO BadFormat; FileIO.OpenFailed => GOTO NotFound ]; RasterFontWriter.Trim[data.font]; data.fileName _ fontFileName; viewer.name _ Rope.Concat["Font: ", fontFileName]; SetChar[viewer, 'A]; EXITS NotFound => { MessageWindow.Append["File not found: ", TRUE]; MessageWindow.Append[fontFileName, FALSE]; MessageWindow.Blink[]; }; BadFormat => { MessageWindow.Append["Not a legal font file: ", TRUE]; MessageWindow.Append[fontFileName, FALSE]; MessageWindow.Blink[]; }; }; Save: ViewerClasses.SaveProc ~ { data: Data _ NARROW[self.data]; firstChar: CHAR _ data.fileName.Fetch[0]; fontFileType: FontFileType _ FontFileNameType[data.fileName]; IF firstChar = '[ OR firstChar = '/ THEN { MessageWindow.Append["Cannot save remote file: ", TRUE]; MessageWindow.Append[data.fileName, FALSE]; MessageWindow.Blink[]; RETURN; }; StoreCurrent[self]; RasterFontWriter.Trim[data.font]; SELECT fontFileType FROM ks => RasterFontWriter.WriteKernedStrike[data.font, data.fileName]; strike => { FOR c: CHAR IN CHAR DO charRep: RasterFontWriter.InternalCharRep _ data.font.charRep[c]; IF charRep # data.font.defaultChar THEN { box: ImagerPixelMaps.DeviceRectangle _ charRep.pixels.Window; IF box.fMin < 0 THEN { charRep.pixels _ charRep.pixels.ShiftMap[0, -box.fMin]; box _ charRep.pixels.Window; }; IF box.fMin < 0 THEN ERROR; charRep.fWidth _ MAX[charRep.fWidth, box.fSize]; data.font.charRep[c] _ charRep; }; ENDLOOP; SetChar[self, data.curCharCode]; RasterFontWriter.WriteStrike[data.font, data.fileName]; }; ENDCASE => { IF fontFileType = ac THEN MessageWindow.Append[".ac format not supported: ", TRUE] ELSE MessageWindow.Append["Cannot figure out what file format to use for ", TRUE]; MessageWindow.Append[data.fileName, FALSE]; MessageWindow.Blink[]; }; }; GetButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: Data _ NARROW[viewer.data]; rope: ROPE _ GetSelection[]; IF rope.Length = 0 THEN { MessageWindow.Append["Please select a font file name.", TRUE]; MessageWindow.Blink[]; } ELSE Get[viewer, rope]; }; FontFileType: TYPE ~ {invalid, unknown, ac, strike, ks}; FontFileNameType: PROC [rope: ROPE] RETURNS [FontFileType] ~ { index: INT _ 0; dotLoc: INT _ -1; extension: ROPE; InvalidChar: PROC [c: CHAR] RETURNS [BOOLEAN] ~ { index _ index + 1; IF c IN ['a..'z] THEN RETURN [FALSE]; IF c IN ['A..'Z] THEN RETURN [FALSE]; IF c IN ['0..'9] THEN RETURN [FALSE]; IF c='. THEN dotLoc _ index-1; IF c='. OR c='- OR c='$ THEN RETURN [FALSE]; RETURN [TRUE]; }; IF rope.Map[action: InvalidChar] THEN RETURN [invalid]; IF index > 256 THEN RETURN [invalid]; IF dotLoc = -1 THEN RETURN [unknown]; extension _ rope.Substr[dotLoc]; SELECT TRUE FROM extension.Equal[".ac", FALSE] => RETURN [ac]; extension.Equal[".strike", FALSE] => RETURN [strike]; extension.Equal[".ks", FALSE] => RETURN [ks]; ENDCASE => RETURN [unknown]; }; StoreButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: Data _ NARROW[viewer.data]; rope: ROPE _ GetSelection[]; fontFileType: FontFileType _ FontFileNameType[rope]; IF fontFileType = invalid THEN { MessageWindow.Append["Please select a valid font file name.", TRUE]; MessageWindow.Blink[]; } ELSE { data.fileName _ IF fontFileType = unknown THEN rope.Concat[".ks"] ELSE rope; viewer.name _ Rope.Concat["Font: ", data.fileName]; ViewerOps.SaveViewer[viewer]; }; }; SaveButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; ViewerOps.SaveViewer[viewer]; }; CharButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: Data _ NARROW[viewer.data]; rope: ROPE _ GetSelection[]; IF rope.Length = 0 THEN { MessageWindow.Append["Please select a character.", TRUE]; MessageWindow.Blink[]; } ELSE { SetChar[viewer, rope.Fetch[0]]; }; }; NextPrevButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: Data _ NARROW[viewer.data]; newChar: CHAR _ data.curCharCode; IF mouseButton = blue THEN { IF newChar = FIRST[CHAR] THEN newChar _ LAST[CHAR] ELSE newChar _ newChar-1 }; IF mouseButton = red THEN { IF data.curCharCode = LAST[CHAR] THEN newChar _ FIRST[CHAR] ELSE newChar _ newChar+1 }; SetChar[viewer, newChar]; }; TextButton: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: Data _ NARROW[viewer.data]; IF mouseButton = red THEN data.text _ GetSelection[]; IF mouseButton = yellow THEN data.text _ defaultText; IF mouseButton = blue THEN data.text _ allChars.Substr[MAX[data.curCharCode-'\000-2, 0]]; ViewerOps.PaintViewer[viewer, client, FALSE, $Text]; }; StoreCurrent: PROC [viewer: ViewerClasses.Viewer] ~ { data: Data _ NARROW[viewer.data]; IF data.hasAChar AND data.bitmapViewer.newVersion THEN { charRep: RasterFontWriter.InternalCharRep; [sWidth: charRep.sWidth, fWidth: charRep.fWidth, pixelMap: charRep.pixels] _ BitmapEdit.GetBitmap[data.bitmapViewer]; charRep.pixels _ charRep.pixels.Trim[0]; data.font.charRep[data.curCharCode] _ charRep; }; data.bitmapViewer.newVersion _ FALSE; }; CharEncode: PROC [char: CHAR] RETURNS [rope: ROPE] ~ { text: REF TEXT _ NEW[TEXT[7]]; charVal: NAT _ char - '\000; text[0] _ text[1] _ text[2] _ text[3] _ ' ; IF char IN [' ..'~] THEN { text[1] _ ''; text[2] _ char; }; text[4] _ '0 + charVal/64; text[5] _ '0 + charVal/8 MOD 8; text[6] _ '0 + charVal MOD 8; text.length _ 7; rope _ Rope.FromRefText[text]; }; SetChar: PROC [viewer: ViewerClasses.Viewer, char: CHAR] ~ { data: Data _ NARROW[viewer.data]; window: ImagerPixelMaps.DeviceRectangle _ data.font.charRep[char].pixels.Window; sMax: INTEGER _ window.sMin+window.sSize; fMax: INTEGER _ window.fMin+window.fSize; pixelMap: ImagerPixelMaps.PixelMap; data.refreshing _ FALSE; IF data.paintProcess # NIL THEN TRUSTED {[] _ JOIN data.paintProcess}; data.paintProcess _ NIL; StoreCurrent[viewer]; window.sMin _ MIN[window.sMin, 0, data.font.charRep[char].sWidth, 4-data.textBaseline]-1; window.fMin _ MIN[window.fMin, 0, data.font.charRep[char].fWidth]-1; sMax _ MAX[sMax, 0, data.font.charRep[char].sWidth, data.textDepth-2]+1; fMax _ MAX[fMax, 0, data.font.charRep[char].fWidth]+1; window.sSize _ sMax - window.sMin; window.fSize _ fMax - window.fMin; data.curCharCode _ char; data.hasAChar _ TRUE; data.savedCharRep _ data.font.charRep[char]; pixelMap _ ImagerPixelMaps.Create[0, window]; pixelMap.Transfer[data.savedCharRep.pixels]; BitmapEdit.SetBitmap[data.bitmapViewer, pixelMap, data.savedCharRep.sWidth, data.savedCharRep.fWidth]; viewer.name _ Rope.Cat["Font: ", data.fileName, CharEncode[data.curCharCode]]; ViewerOps.PaintViewer[viewer, all]; data.refreshing _ TRUE; data.paintProcess _ FORK RefreshProcess[viewer]; }; FontEditPaintProc: ViewerClasses.PaintProc ~ { data: Data _ NARROW[self.data]; IF whatChanged = NIL THEN { data.textBaseline _ 4; data.textDepth _ 2; IF data.font # NIL THEN { bc, ec: CHAR; sMin, fMin, sMax, fMax: INTEGER; maxWidth, totalWidth, fSizeStrike: CARDINAL; [bc, ec, sMin, fMin, sMax, fMax, maxWidth, totalWidth, fSizeStrike] _ data.font.ComputeFontMetrics; data.textBaseline _ 4-sMin; data.textDepth _ 2+sMax; }; data.bitmapViewer.wx _ data.bitmapViewer.cx _ 0; data.bitmapViewer.wy _ data.bitmapViewer.cy _ 0; data.bitmapViewer.ww _ data.bitmapViewer.cw _ self.cw; data.bitmapViewer.wh _ data.bitmapViewer.ch _ self.ch-(data.textBaseline+data.textDepth); ViewerOps.ResetPaintCache[self, FALSE]; }; IF whatChanged = NIL OR whatChanged = $Text THEN { DrawText[self, context]; }; }; FontEditNotifyProc: ViewerClasses.NotifyProc = { IF ISTYPE[input.first, TIPUser.TIPScreenCoords] THEN { data: Data _ NARROW[self.data]; mousePlace: TIPUser.TIPScreenCoords _ NARROW[input.first]; SELECT input.rest.first FROM $SelectChar => { index: INT _ FindSelectedChar[self, mousePlace^]; IF index >= 0 THEN { char: CHAR _ data.text.Fetch[index]; IF char # data.curCharCode THEN { SetChar[self, char]; }; }; }; ENDCASE => NULL; }; }; Break: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ THEN RETURN [break]; IF char = ' OR char = ' OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; FontEditCommand: Commander.CommandProc ~ { stream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: ROPE _ stream.GetToken[Break]; IF name.Length = 0 THEN cmd.out.PutRope["Please supply a font file name (.ks or .strike)"] ELSE { IF name.Fetch[0] = '[ OR name.Fetch[0] = '/ THEN cmd.out.PutRope["(will not be able to save remote file)"]; [] _ Create[name]; }; }; fontEditClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: FontEditPaintProc, notify: FontEditNotifyProc, save: Save, tipTable: TIPUser.InstantiateNewTIPTable["FontEdit.TIP"] ]]; ViewerOps.RegisterViewerClass[$FontEdit, fontEditClass]; Commander.Register["FontEdit", FontEditCommand, "Edit the named strike or kerned-strike font."]; END. DFontEditImpl.mesa Michael Plass, September 14, 1983 11:27 am Κ‚˜J™J™*J˜šΟk ˜ Jšœ ˜ J˜ Jšœ˜Jšœ ˜ J˜J˜ J˜J˜Jšœ˜Jšœ˜Jšœ˜J˜J˜J˜Jšœ˜Jšœ ˜ J˜ J˜—J˜šœœ˜Jšœ›˜’Jšœ ˜Jšœ˜—J˜Jšœœœ˜J˜Jšœœœ ˜šœ œœ˜Jšœ œ˜Jšœ$˜$Jšœ œœ˜Jšœ œœ˜Jšœ œ˜Jšœ/˜/Jšœ#˜#Jšœœ˜Jšœ œ˜Jšœœ˜ Jšœœ˜Jšœ˜—Jšœ œY˜jJšœ œ˜šΟnœœœœ˜!Jš œœœœœ˜ šœœœ ˜J˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜—š žœœRœœ œœ˜ŽJšœ-˜-Jšœ œ&œJ˜‚Jšœ+˜+Jšœ@˜@Jšœ+˜+Jšœ1˜1JšœΞ˜ΞJšœ2œ˜8Jšœ ˜ Jšœ˜—šžœœ>˜LJšœ œ˜!Jšœ-˜-Jšœ œ˜š žœœœœœœ˜:Jšœœœœ˜.JšœI˜IJšœ˜—Jšœ œœœ˜Jšœ˜Jšœ8˜8Jšœa˜aJšœ#˜#Jšœ+˜+Jšœ<˜Jšœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœœ&˜8šžœœœœ˜>Jšœœ˜Jšœœ˜Jšœ œ˜š ž œœœœœ˜1Jšœ˜Jš œœ œœœ˜%Jš œœ œœœ˜%Jš œœ œœœ˜%Jšœœ˜Jš œœœœœœ˜,Jšœœ˜Jšœ˜—Jšœœœ ˜7Jšœ œœ ˜%Jšœ œœ ˜%Jšœ ˜ šœœ˜Jšœœœ˜-Jšœœœ ˜5Jšœœœ˜-Jšœœ ˜—Jšœ˜—šž œ˜ Jšœœ ˜.Jšœ œ˜!Jšœœ˜Jšœ4˜4šœœ˜ Jšœ>œ˜DJšœ˜Jšœ˜—šœ˜Jšœœœœ˜LJšœ3˜3Jšœ˜Jšœ˜—Jšœ˜—šž œ˜Jšœœ ˜.Jšœ˜Jšœ˜—šž œ˜Jšœœ ˜.Jšœ œ˜!Jšœœ˜šœœ˜Jšœ3œ˜9Jšœ˜Jšœ˜—šœ˜Jšœ˜Jšœ˜—Jšœ˜—šžœ˜#Jšœœ ˜.Jšœ œ˜!Jšœ œ˜!šœœ˜Jšœ œœœœœœ˜KJšœ˜—šœœ˜Jšœœœœ œœœ˜TJšœ˜—Jšœ˜Jšœ˜—šž œ˜Jšœœ ˜.Jšœ œ˜!Jšœœ˜5Jšœœ˜5Jšœœœ˜YJšœ&œ ˜4Jšœ˜—šž œœ#˜5Jšœ œ˜!šœœœ˜8Jšœ*˜*Jšœu˜uJšœ(˜(Jšœ.˜.Jšœ˜—Jšœœ˜%Jšœ˜—š ž œœœœœ˜6Jš œœœœœ˜Jšœ œ˜Jšœ+˜+šœœ œ˜Jšœ ˜ Jšœ˜Jšœ˜—Jšœ˜Jšœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜—šžœœ&œ˜˜HJšœœ,˜6Jšœ"˜"Jšœ"˜"Jšœ˜Jšœœ˜Jšœ,˜,Jšœ-˜-Jšœ,˜,Jšœf˜fJšœN˜NJšœ#˜#Jšœœ˜Jšœœ˜0Jšœ˜—šžœ˜.Jšœ œ ˜šœœœ˜Jšœ˜Jšœ˜šœ œœ˜Jšœœ˜ Jšœœ˜ Jšœ#œ˜,Jšœc˜cJšœ˜Jšœ˜J˜—J˜0J˜0J˜6JšœY˜YJšœ œ˜'Jšœ˜—šœœœœ˜2Jšœ˜Jšœ˜—Jšœ˜—unitšžœ˜0šœœ'œ˜6Jšœ œ ˜Jšœ&œ˜:šœ˜šœ˜Jšœœ'˜1šœ œ˜Jšœœ˜$šœœ˜!Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—Jšœœ˜—J˜—J˜—– "cedar" styleš žœœœœœ˜3Jšœ œœ ˜!Jšœ œ œ œ œ œœ˜UJšœ ˜Jšœ˜—šžœ˜*Jš œœœœœ˜,Jšœœ˜$JšœœC˜Zšœ˜Jšœœœ;˜kJšœ˜Jšœ˜—Jšœ˜—šœ+œ!˜OJšœ˜Jšœ˜J˜ Jšœ8˜8J˜—Jšœ8˜8J˜`Jšœ˜J˜—…—78Gώ