DIRECTORY Commander USING [CommandProc, Register], Convert USING [RopeFromInt], Font USING [BoundingBox, Box, Contains, Create, FONT, GetLigatureOrKern, LigatureOrKern, Name, WidthVector], Imager USING [black, ConcatT, Context, Create, CurveTo, LineTo, MakeFont, MakeGray, MaskFill, MaskVector, MoveTo, Reset, Scale, SetColor, SetFont, SetXY, ShowCharacters, Trajectory, TrajectoryRep, Transform, Translate, Pair, Transformation], ImagerBasic USING [PathMapType], ImagerBridge USING [SetViewFromGraphicsContext], ImagerTransform USING [Concat, Scale], IO USING [CharClass, EndOfStream, GetTokenRope, PutF, PutRope, refAny, RIS, STREAM], IPOutput USING [BeginMakeSimpleCO, BeginMaster, BeginPreamble, ConcatT, CorrectMask, CurveTo, EndMakeSimpleCO, EndMaster, EndPreamble, IGet, LineTo, MakeOutline, MakeVec, MaskFill, Master, MoveTo, PutIdentifier, PutInt, PutName, PutOp, PutReal, Scale, SetXRel, SetXYRel, Trans], Menus USING [ClickProc, CreateEntry, CreateMenu, InsertMenuEntry, Menu], Process USING [MsecToTicks, Pause], Real USING [RoundLI], RealFns USING [Power], Rope USING [Cat, Equal, Fetch, Find, Length, ROPE, Substr], UFPressFontReader USING [GetCharOutline, Representation], ViewerClasses USING [InitProc, PaintProc, ScrollProc, Viewer, ViewerClass, ViewerClassRec], ViewerOps USING [CreateViewer, DestroyViewer, PaintViewer, RegisterViewerClass]; IPFontMasterImpl: CEDAR MONITOR IMPORTS Commander, Font, ImagerTransform, IO, IPOutput, Real, Rope, UFPressFontReader, ViewerOps, ImagerBridge, Imager, Convert, Menus, RealFns, Process SHARES Imager -- for TrajectoryRep = BEGIN Master: TYPE ~ IPOutput.Master; ROPE: TYPE ~ Rope.ROPE; Pair: TYPE ~ Imager.Pair; MapTrajectory: ImagerBasic.PathMapType = { t: Imager.Trajectory = NARROW[data]; move[t.lp]; FOR x: Imager.Trajectory _ t, x.prev UNTIL x.prev=NIL DO p0: Pair = x.prev.lp; WITH x SELECT FROM x: REF Imager.TrajectoryRep[line] => line[p0]; x: REF Imager.TrajectoryRep[curve] => curve[x.p2, x.p1, p0]; x: REF Imager.TrajectoryRep[conic] => conic[x.p1, p0, x.r]; x: REF Imager.TrajectoryRep[move] => ERROR; ENDCASE => ERROR; ENDLOOP; }; ConstructSampleFontVector: PROC [master: Master] ~ { vecCounts: ARRAY [0..20) OF INT _ ALL[0]; vecTags: ARRAY [0..20) OF ATOM; vecTop: INTEGER _ -1; El: PROC ~ {vecCounts[vecTop] _ vecCounts[vecTop] + 1}; BeginVector: PROC [tag: ATOM] ~ { vecTop _ vecTop + 1; vecTags[vecTop] _ tag; vecCounts[vecTop] _ 0; }; EndVector: PROC [tag: ATOM] ~ { IF vecTags[vecTop] # tag THEN ERROR; master.MakeVec[vecCounts[vecTop]]; vecTags[vecTop] _ NIL; vecCounts[vecTop] _ 0; vecTop _ vecTop - 1; }; PutInfoForChar: PROC [c: INT] ~ { IF c = 32 THEN { BeginVector[$spaceMetrics]; El[]; master.PutIdentifier["widthX"]; El[]; master.PutReal[0.34]; El[]; master.PutIdentifier["widthY"]; El[]; master.PutInt[0]; El[]; master.PutIdentifier["amplified"]; El[]; master.PutInt[1]; EndVector[$spaceMetrics]; } ELSE { BeginVector[$charMetrics]; El[]; master.PutIdentifier["widthX"]; El[]; master.PutReal[0.24]; EndVector[$charMetrics]; } }; first: INT _ 32; last: INT _ 34; BeginVector[$font]; El[]; master.PutIdentifier["characterMetrics"]; El[]; BeginVector[$characterMetrics]; FOR c: INT IN [first..last] DO El[]; master.PutInt[c]; El[]; PutInfoForChar[c]; ENDLOOP; EndVector[$characterMetrics]; El[]; master.PutIdentifier["name"]; El[]; master.PutName["xerox/ascii/times"]; EndVector[$font]; }; SampleMetricMaster: PROC ~ { master: Master _ IPOutput.BeginMaster["Times.IPFont"]; master.BeginPreamble; ConstructSampleFontVector[master]; master.EndPreamble; master.EndMaster; }; Switches: TYPE ~ RECORD [ operators: BOOLEAN _ FALSE, correction: BOOLEAN _ FALSE, bbox: BOOLEAN _ FALSE, kerns: BOOLEAN _ FALSE, ligatures: BOOLEAN _ FALSE, integerCoding: BOOLEAN _ FALSE, viewer: BOOLEAN _ FALSE ]; BadSwitch: ERROR [offset: INT] ~ CODE; ParseSwitches: PROC [rope: ROPE] RETURNS [switches: Switches] ~ { FOR i: INT IN [1..rope.Length) DO SELECT rope.Fetch[i] FROM 'o, 'O => switches.operators _ TRUE; 'c, 'C => switches.correction _ TRUE; 'b, 'B => switches.bbox _ TRUE; 'k, 'K => switches.kerns _ TRUE; 'l, 'L => switches.ligatures _ TRUE; 'e, 'E => switches _ [TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE]; 'i, 'I => switches.integerCoding _ TRUE; 'v, 'V => switches.viewer _ TRUE; '} => IF i = rope.Length-1 THEN RETURN; ENDCASE => ERROR BadSwitch[i]; ENDLOOP; ERROR BadSwitch[rope.Length]; }; MustBeSplineFontToGetOperators: ERROR [name: ROPE] ~ CODE; fiducial: REAL _ 15840; lastInteger: REAL _ INTEGER.LAST; ConstructUnifiedFontVector: PROC [master: Master, font: Font.FONT, switches: Switches] ~ { vecCounts: ARRAY [0..20) OF INT _ ALL[0]; vecTags: ARRAY [0..20) OF ATOM; vecTop: INTEGER _ -1; viewer: ViewerClasses.Viewer _ IF switches.viewer THEN ViewerOps.CreateViewer[$FontMasterViewerClass, [name: font.Name, column: left, iconic: TRUE]] ELSE NIL; El: PROC ~ {vecCounts[vecTop] _ vecCounts[vecTop] + 1}; BeginVector: PROC [tag: ATOM] ~ { vecTop _ vecTop + 1; vecTags[vecTop] _ tag; vecCounts[vecTop] _ 0; }; EndVector: PROC [tag: ATOM] ~ { IF vecTags[vecTop] # tag THEN ERROR; master.MakeVec[vecCounts[vecTop]]; vecTags[vecTop] _ NIL; vecCounts[vecTop] _ 0; vecTop _ vecTop - 1; }; PutOperatorForChar: PROC [c: CHAR] ~ { width: Pair _ font.WidthVector[c]; nTrajectories: INT _ 0; Scale: PROC [r: REAL] RETURNS [s: REAL] ~ { IF NOT switches.integerCoding THEN s _ r ELSE { s _ fiducial*r; IF ABS[s] IN [1..lastInteger] THEN s _ Real.RoundLI[s]; }; }; outline: LIST OF Imager.Trajectory _ NIL; moveToProc: PROCEDURE [x, y: REAL] ~ { outline _ CONS[Imager.MoveTo[[Scale[x], Scale[y]]], outline]; nTrajectories _ nTrajectories + 1; }; lineToProc: PROCEDURE [x, y: REAL] ~ { outline.first _ outline.first.LineTo[[Scale[x], Scale[y]]]; }; curveToProc: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] ~ { outline.first _ outline.first.CurveTo[[Scale[x1], Scale[y1]], [Scale[x2], Scale[y2]], [Scale[x3], Scale[y3]]]; master.CurveTo[Scale[x1], Scale[y1], Scale[x2], Scale[y2], Scale[x3], Scale[y3]]; }; drawAreaProc: PROCEDURE ~ {}; IF UFPressFontReader.Representation[[font.graphicsKey, 0]] # outline THEN ERROR MustBeSplineFontToGetOperators[font.Name]; master.BeginMakeSimpleCO; master.Trans; IF switches.integerCoding THEN {master.Scale[1.0/fiducial]; master.ConcatT}; UFPressFontReader.GetCharOutline[[font.graphicsKey, 0], c, moveToProc, lineToProc, curveToProc, drawAreaProc]; IF viewer # NIL THEN { data: ViewerData _ NARROW[viewer.data]; IF viewer.destroyed THEN ERROR ABORTED; IF data # NIL THEN { data.outline _ outline; data.charCode _ ORD[c]; ViewerOps.PaintViewer[viewer, client]; WaitForNext[viewer]; }; }; IF nTrajectories > 0 THEN { FOR s: LIST OF Imager.Trajectory _ outline, s.rest UNTIL s = NIL DO move: PROC[p: Pair] ~ {master.MoveTo[p.x, p.y]}; line: PROC[p: Pair] ~ {master.LineTo[p.x, p.y]}; curve: PROC[p1, p2, p3: Pair] ~ {master.CurveTo[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]}; conic: PROC[p1, p2: Pair, r: REAL] ~ {ERROR}; MapTrajectory[s.first, move, line, curve, conic]; ENDLOOP; master.MakeOutline[nTrajectories]; master.MaskFill; }; IF switches.integerCoding THEN {master.Scale[fiducial]; master.ConcatT}; IF c = ' THEN { master.PutReal[width.x]; master.IGet[amplifySpace]; master.PutOp[mul]; IF width.y = 0 THEN master.PutOp[space] ELSE { master.PutReal[width.y]; master.IGet[amplifySpace]; master.PutOp[mul]; master.PutInt[2]; master.PutOp[copy]; master.PutOp[setxyrel]; master.PutOp[correctspace]; }; } ELSE { IF width.y = 0 THEN master.SetXRel[width.x] ELSE master.SetXYRel[width.x, width.y]; master.CorrectMask; }; master.EndMakeSimpleCO; }; PutInfoForChar: PROC [c: CHAR] ~ { width: Pair _ font.WidthVector[c]; areAny: BOOLEAN _ FALSE; BeginVector[$charMetrics]; IF width.x # 0 THEN { El[]; master.PutIdentifier["widthX"]; El[]; master.PutReal[width.x]; }; IF width.y # 0 THEN { El[]; master.PutIdentifier["widthY"]; El[]; master.PutReal[width.y]; }; IF c = ' THEN { El[]; master.PutIdentifier["amplified"]; El[]; master.PutInt[1]; }; IF switches.correction THEN {El[]; master.PutIdentifier["correction"]; El[]; master.PutInt[IF c = ' THEN 1 ELSE 2]}; IF c # ' AND switches.bbox THEN { box: Font.Box _ font.BoundingBox[c]; IF box.xmin # 0 THEN { El[]; master.PutIdentifier["leftExtent"]; El[]; master.PutReal[box.xmin]; }; IF box.xmax # 0 THEN { El[]; master.PutIdentifier["rightExtent"]; El[]; master.PutReal[box.xmax]; }; IF box.ymin # 0 THEN { El[]; master.PutIdentifier["descent"]; El[]; master.PutReal[box.ymin]; }; IF box.ymax # 0 THEN { El[]; master.PutIdentifier["ascent"]; El[]; master.PutReal[box.ymax]; }; }; areAny _ FALSE; IF switches.kerns THEN FOR c2: CHAR IN [font.bc..font.ec] DO IF font.Contains[c2] THEN { ligKern: Font.LigatureOrKern _ font.GetLigatureOrKern[c, c2]; WITH ligKern SELECT FROM k: Font.LigatureOrKern.kern => { IF NOT areAny THEN { El[]; master.PutIdentifier["kerns"]; El[]; BeginVector[$kerns]; areAny _ TRUE; }; El[]; BeginVector[$kern]; El[]; master.PutInt[ORD[c2]]; El[]; master.PutReal[k.kernAmount]; El[]; master.PutInt[0]; EndVector[$kern]; }; ENDCASE => NULL; }; ENDLOOP; IF areAny THEN EndVector[$kerns]; areAny _ FALSE; IF switches.ligatures THEN FOR c2: CHAR IN [font.bc..font.ec] DO IF font.Contains[c2] THEN { ligKern: Font.LigatureOrKern _ font.GetLigatureOrKern[c, c2]; WITH ligKern SELECT FROM lig: Font.LigatureOrKern.ligature => { IF NOT areAny THEN { El[]; master.PutIdentifier["ligatures"]; El[]; BeginVector[$ligatures]; areAny _ TRUE; }; El[]; BeginVector[$ligature]; El[]; master.PutInt[ORD[c2]]; El[]; master.PutInt[ORD[lig.ligatureCode]]; EndVector[$ligature]; }; ENDCASE => NULL; }; ENDLOOP; IF areAny THEN EndVector[$ligatures]; areAny _ FALSE; EndVector[$charMetrics]; }; BeginVector[$font]; IF switches.operators THEN { El[]; master.PutIdentifier["operators"]; El[]; BeginVector[$operators]; FOR c: CHAR IN [font.bc..font.ec] DO IF font.Contains[c] THEN { El[]; master.PutInt[ORD[c]]; El[]; PutOperatorForChar[c]; }; ENDLOOP; EndVector[$operators]; }; El[]; master.PutIdentifier["characterMetrics"]; El[]; BeginVector[$characterMetrics]; FOR c: CHAR IN [font.bc..font.ec] DO IF font.Contains[c] THEN { El[]; master.PutInt[ORD[c]]; El[]; PutInfoForChar[c]; }; ENDLOOP; EndVector[$characterMetrics]; El[]; master.PutIdentifier["name"]; El[]; master.PutName[font.Name]; EndVector[$font]; IF viewer # NIL AND NOT viewer.destroyed THEN ViewerOps.DestroyViewer[viewer]; }; GetToken: PROC [stream: IO.STREAM] RETURNS [token: ROPE _ NIL] = { token _ stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token; }; Break: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ OR char = '; THEN RETURN [break]; IF char = ' OR char = ' OR char = ', OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; FontMetricMasterCommand: Commander.CommandProc ~ { stream: IO.STREAM _ IO.RIS[cmd.commandLine]; outputName: ROPE _ GetToken[stream]; token: ROPE _ GetToken[stream]; globalSwitches: Switches; master: Master; IF NOT token.Equal["_"] THEN { cmd.out.PutRope["Specify output _ input, please\n"]; RETURN; }; master _ IPOutput.BeginMaster[outputName]; master.BeginPreamble; token _ GetToken[stream]; WHILE token # NIL DO switchesStart: INT _ token.Find["{"]; switches: Switches _ IF switchesStart >= 0 THEN ParseSwitches[token.Substr[start: switchesStart]] ELSE globalSwitches; IF switchesStart = 0 THEN globalSwitches _ switches ELSE { fontName: ROPE _ IF switchesStart >= 0 THEN token.Substr[len: switchesStart] ELSE token; font: Font.FONT _ Font.Create[fontName, ImagerTransform.Scale[1]]; cmd.out.PutRope[fontName]; cmd.out.PutF["%g", IO.refAny[NEW[Switches _ switches]]]; ConstructUnifiedFontVector[master, font, switches]; cmd.out.PutRope["\n"]; }; token _ GetToken[stream]; ENDLOOP; master.EndPreamble; master.EndMaster; cmd.out.PutRope[outputName]; cmd.out.PutRope[" written.\n"]; }; ViewerData: TYPE ~ REF ViewerDataRep; ViewerStatus: TYPE ~ {edited, filling, editing}; ViewerDataRep: TYPE ~ RECORD [ transformation: Imager.Transformation, outline: LIST OF Imager.Trajectory, charCode: INT, nextHit: BOOL _ FALSE, inputEnabled: BOOL _ FALSE ]; ViewerPaint: ViewerClasses.PaintProc ~ { imager: Imager.Context _ Imager.Create[$LFDisplay]; data: ViewerData _ NARROW [self.data]; origin: Pair; IF data = NIL THEN RETURN; ImagerBridge.SetViewFromGraphicsContext[imager, context]; origin _ Imager.Transform[data.transformation, [0, 0]]; Imager.SetColor[imager, Imager.MakeGray[0.5]]; Imager.MaskVector[imager, [origin.x - 0.01, origin.y], [origin.x + 0.01, origin.y]]; Imager.MaskVector[imager, [origin.x, origin.y - 0.01], [origin.x, origin.y + 0.01]]; Imager.ConcatT[imager, data.transformation]; Imager.SetColor[imager, Imager.black]; IF data.outline # NIL THEN Imager.MaskFill[imager, data.outline]; Imager.Reset[imager]; Imager.SetXY[imager, [0.005, 0.005]]; Imager.SetFont[imager, Imager.MakeFont["xerox/pressfonts/modern/mrr", 0.005]]; IF data.charCode >= 0 THEN Imager.ShowCharacters[imager, Convert.RopeFromInt[data.charCode].Cat[ " ", Convert.RopeFromInt[data.charCode, 8], " ", Convert.RopeFromInt[data.charCode, 16] ] ]; }; HScrollHit: Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: ViewerData _ NARROW[viewer.data]; IF data = NIL THEN RETURN; data.transformation _ data.transformation.Concat[Imager.Translate[IF mouseButton = red THEN -16*metersPerPoint ELSE 16*metersPerPoint, 0]]; ViewerOps.PaintViewer[viewer, client]; }; scaleAmt: REAL _ RealFns.Power[2, 0.25]; statusChange: CONDITION; NextHit: ENTRY Menus.ClickProc ~ { ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer _ NARROW[parent]; data: ViewerData _ NARROW[viewer.data]; done: BOOLEAN _ FALSE; IF data = NIL THEN RETURN; UNTIL data.inputEnabled DO WAIT statusChange ENDLOOP; data.inputEnabled _ FALSE; data.nextHit _ TRUE; BROADCAST statusChange; UNTIL viewer.destroyed OR done DO UNTIL data.inputEnabled DO WAIT statusChange ENDLOOP; SELECT mouseButton FROM red => done _ TRUE; yellow => {IF data.outline # NIL AND data.outline.rest # NIL THEN done _ TRUE}; blue => {Process.Pause[Process.MsecToTicks[500]]}; ENDCASE => ERROR; IF NOT done THEN { data.inputEnabled _ FALSE; data.nextHit _ TRUE; BROADCAST statusChange; }; ENDLOOP; }; WaitForNext: ENTRY PROC [viewer: ViewerClasses.Viewer] ~ { ENABLE UNWIND => NULL; data: ViewerData _ NARROW[viewer.data]; IF data.nextHit THEN {data.nextHit _ FALSE; RETURN}; data.inputEnabled _ TRUE; BROADCAST statusChange; UNTIL data.nextHit DO WAIT statusChange ENDLOOP; data.nextHit _ FALSE; }; SizeHit: ENTRY Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: ViewerData _ NARROW[viewer.data]; scale: REAL _ IF mouseButton = red THEN scaleAmt ELSE 1/scaleAmt; IF data = NIL THEN RETURN; UNTIL data.inputEnabled DO WAIT statusChange ENDLOOP; data.transformation _ Imager.Scale[scale].Concat[data.transformation]; ViewerOps.PaintViewer[viewer, client]; }; ReverseHit: ENTRY Menus.ClickProc ~ { viewer: ViewerClasses.Viewer _ NARROW[parent]; data: ViewerData _ NARROW[viewer.data]; refn: REF INT _ NARROW[clientData]; IF data = NIL THEN RETURN; UNTIL data.inputEnabled DO WAIT statusChange ENDLOOP; IF refn # NIL THEN { list: LIST OF Imager.Trajectory _ data.outline; n: INT _ 0; FOR t: LIST OF Imager.Trajectory _ list, t.rest UNTIL t=NIL DO n _ n + 1 ENDLOOP; IF refn^ >= n THEN RETURN; FOR i: INT IN [0..refn^) DO list _ list.rest ENDLOOP; list.first _ ReverseTrajectory[list.first]; ViewerOps.PaintViewer[viewer, client]; }; }; ReverseTrajectory: PROC [s: Imager.Trajectory] RETURNS [t: Imager.Trajectory] ~ { move: PROC[p: Pair] ~ {t _ Imager.MoveTo[p]}; line: PROC[p: Pair] ~ {t _ t.LineTo[p]}; curve: PROC[p1, p2, p3: Pair] ~ {t _ t.CurveTo[p1, p2, p3]}; conic: PROC[p1, p2: Pair, r: REAL] ~ {ERROR}; MapTrajectory[s, move, line, curve, conic]; }; ViewerInit: ViewerClasses.InitProc ~ { viewerData: ViewerData _ NEW[ViewerDataRep]; menu: Menus.Menu _ Menus.CreateMenu[1]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "Next", proc: NextHit]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "<<<-->>>", proc: HScrollHit]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "Size", proc: SizeHit]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r5", proc: ReverseHit, clientData: NEW[INT _ 5]]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r4", proc: ReverseHit, clientData: NEW[INT _ 4]]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r3", proc: ReverseHit, clientData: NEW[INT _ 3]]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r2", proc: ReverseHit, clientData: NEW[INT _ 2]]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r1", proc: ReverseHit, clientData: NEW[INT _ 1]]]; Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "r0", proc: ReverseHit, clientData: NEW[INT _ 0]]]; viewerData.transformation _ Imager.Scale[0.1].Concat[Imager.Translate[0.025, 0.025]]; self.data _ viewerData; self.menu _ menu; }; metersPerPoint: REAL _ 0.0254/72; ViewerScroll: ENTRY ViewerClasses.ScrollProc ~ { data: ViewerData _ NARROW [self.data]; IF data = NIL OR op = query OR op = thumb THEN RETURN [0, 100]; IF amount = 0 THEN RETURN [0, 100]; IF op = down THEN amount _ -amount; UNTIL data.inputEnabled DO WAIT statusChange ENDLOOP; data.transformation _ data.transformation.Concat[Imager.Translate[0, amount*metersPerPoint]]; ViewerOps.PaintViewer[self, client]; RETURN [0, 100]; }; fontMasterViewerClass: ViewerClasses.ViewerClass _ NEW [ViewerClasses.ViewerClassRec _ [ init: ViewerInit, paint: ViewerPaint, scroll: ViewerScroll, coordSys: bottom, icon: tool ]]; ViewerOps.RegisterViewerClass[$FontMasterViewerClass, fontMasterViewerClass]; Commander.Register["IPFontMaster", FontMetricMasterCommand, "Make an Interpress metric master from Imager fonts (output _ {} fontname1{} fontname2 . . .)\nO => operators\nC => correction\nB => bbox\nK => kerns\nL => ligatures\nE => all of the above\nI => integerCoding\nV => viewer\n"]; END. ˆIPFontMasterImpl.mesa Copyright (C) 1984, Xerox Corporation. All rights reserved. Michael Plass, August 20, 1984 4:53:38 pm PDT Κψ˜J™J™