DIRECTORY PrintFontsPrivate, Cubic USING [Bezier, Coeffs, CoeffsToBezier], Quadratic USING [Roots, RealRoots], Basics USING [LowHalf], Rope USING [ROPE, Fetch, Equal], IO USING [STREAM, Backup, GetTokenRope, GetInt, GetReal, GetTime, rope,char, Close, PutFR, EndOfStream], FS USING [StreamOpen], Font USING [Key], BasicTime USING [GMT], UFFileManager USING[KeyOf], UFPressFontReader USING[Size, Range, Family, Face, Resolution, GetCharInfo, GetCharRaster, CharInfo, NumberOfFontsInFile, GetCharPath], ImagerBasic USING [PixelArray, PathMapType, Pair]; PrintFontsPrivateImpl: CEDAR PROGRAM IMPORTS UFFileManager, UFPressFontReader, IO, Rope, FS, Cubic, Basics, Quadratic EXPORTS PrintFontsPrivate = BEGIN OPEN PrintFontsPrivate; FontNotFound: PUBLIC SIGNAL=CODE; STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; Pair: TYPE = ImagerBasic.Pair; LoadAC: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [font: PrivateFont] ~ { fileKey: Font.Key _ UFFileManager.KeyOf[fileName]; sizeInMeters, bitsPerEmQuad: REAL; IF UFPressFontReader.NumberOfFontsInFile[fileKey] = 0 THEN SIGNAL FontNotFound; sizeInMeters _ UFPressFontReader.Size[[fileKey, 0]]; font _ NEW [PrivateFontRep]; font.type _ raster; [font.bc, font.ec] _ UFPressFontReader.Range[[fileKey, 0]]; font.family _ UFPressFontReader.Family[[fileKey, 0]]; font.face _ UFPressFontReader.Face[[fileKey, 0]]; bitsPerEmQuad _ sizeInMeters*UFPressFontReader.Resolution[[fileKey, 0]].xRes/0.0254; FOR char: CHAR IN [font.bc..font.ec] DO info: UFPressFontReader.CharInfo _ UFPressFontReader.GetCharInfo[[fileKey, 0], char]; pixelArray: ImagerBasic.PixelArray _ UFPressFontReader.GetCharRaster[[fileKey, 0], char]; font.charRep[char] _ [ width: (info.maxX-info.minX)*bitsPerEmQuad, pixels: pixelArray ]; IF font.charRep[char].width > font.maxW THEN font.maxW _ font.charRep[char].width; IF info.maxY > font.ascent THEN font.ascent _ info.maxY; IF ABS[info.minY] > font.descent THEN font.descent _ ABS[info.minY]; ENDLOOP; font.ascent _ font.ascent*bitsPerEmQuad; font.descent _ font.descent*bitsPerEmQuad; }; LoadSD: PUBLIC PROC [fileName: Rope.ROPE, pointSize: REAL] RETURNS [font: PrivateFont] ~ { fileKey: Font.Key _ UFFileManager.KeyOf[fileName]; IF UFPressFontReader.NumberOfFontsInFile[fileKey] = 0 THEN SIGNAL FontNotFound; font _ NEW [PrivateFontRep]; [font.bc, font.ec] _ UFPressFontReader.Range[[fileKey, 0]]; font.type _ outline; font.pointSize _ pointSize; font.family _ UFPressFontReader.Family[[fileKey, 0]]; font.face _ UFPressFontReader.Face[[fileKey, 0]]; FOR char: CHAR IN [font.bc..font.ec] DO info: UFPressFontReader.CharInfo _ UFPressFontReader.GetCharInfo[[fileKey, 0], char]; pathMap: ImagerBasic.PathMapType; pathData: REF; [pathMap, pathData]_ UFPressFontReader.GetCharPath[[fileKey, 0], char]; font.charRep[char] _ [ width: (info.maxX-info.minX)*pointSize, outline: NEW[PathTypeRec _ [pathMap, pathData]] ]; IF font.charRep[char].width > font.maxW THEN font.maxW _ font.charRep[char].width; IF info.maxY > font.ascent THEN font.ascent _ info.maxY; IF ABS[info.minY] > font.descent THEN font.descent _ ABS[info.minY]; ENDLOOP; font.ascent _ font.ascent*pointSize; font.descent _ font.descent*pointSize; }; LoadSF: PUBLIC PROC [fileName: Rope.ROPE, pointSize: REAL] RETURNS [font: PrivateFont] ~ { stream: IO.STREAM _ FS.StreamOpen[fileName]; first: BOOL _ TRUE; font _ NEW[PrivateFontRep]; font.type _ outline; font.pointSize _ pointSize; font.bc _ VAL[255]; --set with max/min in GetSFChar font.ec _ VAL[0]; font.descent _ font.ascent _ 0; --set with max/min in GetSFChar DO GetSFChar[font, stream, first, pointSize! Stop => EXIT]; first _ FALSE; ENDLOOP; IO.Close[stream]; }; CloseP: SIGNAL = CODE; Stop: SIGNAL = CODE; GetOpenP: PROC[stream: IO.STREAM] = { r: ROPE _ GetToken[stream ! IO.EndOfStream => SIGNAL Stop]; char: CHAR _ Rope.Fetch[r,0]; IF char='( THEN RETURN; IF char= ') THEN {IO.Backup[stream, char]; SIGNAL CloseP} ELSE IF Rope.Equal["STOP", r] THEN SIGNAL Stop ELSE ERROR; }; GetCloseP: PROC[stream: IO.STREAM] = { char: CHAR _ Rope.Fetch[GetToken[stream],0]; IF ') # char THEN ERROR; }; GetToken: PROC[stream: IO.STREAM] RETURNS[ROPE] = { r: ROPE; [r,] _ IO.GetTokenRope[stream]; RETURN[r]}; GetSFChar: PROC[font: PrivateFont, stream: STREAM, first: BOOL, pointSize: REAL] = { wx, wy: REAL; fx, fy: REAL; minX, minY, maxX, maxY: REAL; version: INT; char: CHAR; time: BasicTime.GMT; face0, face1, face2, token, family: ROPE; pathData: REF; GetOpenP[stream]; DO --get character pieces GetOpenP[stream ! CloseP => {GetCloseP[stream]; EXIT}]; --CloseP does a PutBack token _ GetToken[stream]; --now looking for a key word item SELECT TRUE FROM Rope.Equal[token, "FAMILY", FALSE] => family _ GetToken[stream]; Rope.Equal[token, "CHARACTER", FALSE] => char _ VAL[Basics.LowHalf[IO.GetInt[stream]]]; Rope.Equal[token, "FACE", FALSE] => { face0 _ GetToken[stream]; face1 _ GetToken[stream]; face2 _ GetToken[stream]; }; Rope.Equal[token, "WIDTH", FALSE] => { wx _ IO.GetReal[stream]; wy _ IO.GetReal[stream]; }; Rope.Equal[token, "FIDUCIAL", FALSE] => { fx _ IO.GetReal[stream]; fy _ IO.GetReal[stream]; }; Rope.Equal[token, "VERSION", FALSE] => { version _ IO.GetInt[stream]; time _ IO.GetTime[stream] }; Rope.Equal[token, "MADE-FROM", FALSE] => NULL; Rope.Equal[token, "SPLINES", FALSE] => --get spline path and a bounding box [pathData, minX, maxX, minY, maxY] _ GetSFSplines[stream]; ENDCASE; UNTIL Rope.Fetch[GetToken[stream],0] =') DO ENDLOOP; --end the item ENDLOOP; ScalePathData[pathData, 1/fx, 1/fy]; minX _ pointSize*minX/fx; minY _ pointSize*minY/fy; maxX _ pointSize*maxX/fx; maxY _ pointSize*maxY/fy; font.charRep[char] _ [ width: (maxX-minX), outline: NEW[PathTypeRec _ [PathMap, pathData]] ]; IF first THEN font.family _ IO.PutFR["%g-%g%g%g", IO.rope[family], IO.char[Rope.Fetch[face0,0]],IO.char[Rope.Fetch[face1,0]],IO.char[Rope.Fetch[face2,0]]]; IF font.charRep[char].width > font.maxW THEN font.maxW _ font.charRep[char].width; IF charfont.ec THEN font.ec _ char; IF maxY > font.ascent THEN font.ascent _ maxY; IF ABS[minY] > font.descent THEN font.descent _ ABS[minY]; }; PairSeq: TYPE = REF PairSeqRec; PairSeqRec: TYPE = RECORD[element: SEQUENCE length:NAT OF Pair]; BezierSeq: TYPE = REF BezierSeqRec; BezierSeqRec: TYPE = RECORD[element: SEQUENCE length: NAT OF Cubic.Bezier]; GetSFSplines: PROC[stream: IO.STREAM] RETURNS[pathData: LIST OF BezierSeq, minX, maxX, minY, maxY: REAL] = { minX _ minY _ 10E6; maxX _ maxY _ -10E6; DO bezierList: LIST OF BezierSeq; closedContour: BezierSeq; GetOpenP[stream! CloseP => EXIT]; --open closed curve DO --get splines knots, derivatives: PairSeq; bezier: BezierSeq; minBX, maxBX, minBY, maxBY: REAL; nKnots: INT; token: ROPE; GetOpenP[stream ! CloseP => EXIT]; --open spline nKnots _ IO.GetInt[stream]; knots _ NEW[PairSeqRec[nKnots]]; GetOpenP[stream]; --open knot list FOR i: INT IN [0..nKnots) DO --knot sequence GetOpenP[stream]; knots[i].x _ IO.GetReal[stream]; knots[i].y _ IO.GetReal[stream]; GetCloseP[stream]; ENDLOOP; GetCloseP[stream]; --close knot list token _ GetToken[stream]; --weight list. I assume this is always NIL IF NOT Rope.Equal[token, "NIL", FALSE] THEN ERROR; derivatives _ NEW[PairSeqRec[3*(nKnots-1)]]; GetOpenP[stream]; -- open derivative list FOR i: INT IN [0..nKnots-1) DO --derivatives sequence GetOpenP[stream]; derivatives[3*i].x _ IO.GetReal[stream]; --first derivatives[3*i].y _ IO.GetReal[stream]; derivatives[3*i+1].x _ IO.GetReal[stream]; --second derivatives[3*i+1].y _ IO.GetReal[stream]; derivatives[3*i+2].x _ IO.GetReal[stream]; --third derivatives[3*i+2].y _ IO.GetReal[stream]; GetCloseP[stream]; ENDLOOP; GetCloseP[stream]; --close derivative list token _ GetToken[stream]; --solution method, but we don't need it GetCloseP[stream]; --close spline [bezier, minBX, maxBX, minBY, maxBY] _ KnotsToBezier[knots, derivatives]; bezierList _ CONS[bezier, bezierList]; minX _ MIN[minX, minBX]; minY _ MIN[minY, minBY]; maxX _ MAX[maxX, maxBX]; maxY _ MAX[maxY, maxBY]; ENDLOOP; IF bezierList.rest=NIL THEN closedContour _ bezierList.first ELSE { --copy the elements into one long sequence nCubics: NAT _ 0; index: INT _ 0; rList: LIST OF BezierSeq; FOR l: LIST OF BezierSeq _ bezierList, l.rest UNTIL l=NIL DO rList _ CONS[l.first, rList]; --reverse the order nCubics _ nCubics+l.first.length; ENDLOOP; closedContour _ NEW[BezierSeqRec[nCubics]]; FOR l: LIST OF BezierSeq _ rList, l.rest UNTIL l=NIL DO FOR i: INT IN [0..l.first.length) DO closedContour[index]_ l.first[i]; index _ index+1; ENDLOOP; ENDLOOP; }; pathData _ CONS[closedContour, pathData]; GetCloseP[stream]; --close closed curve ENDLOOP; IF pathData=NIL THEN minX _ maxX _ minY _ maxY _ 0; RETURN[pathData, minX, maxX, minY, maxY]; }; KnotsToBezier: PROC [knots, derivatives: PairSeq] RETURNS [bezier: BezierSeq, minX, maxX, minY, maxY: REAL] = { getMinMax: PROC [a, b, c, d: REAL] RETURNS [min, max: REAL] = { min _ MIN[d, (a+b+c+d)]; max _ MAX[d, (a+b+c+d)]; IF c*(3*a+2*b+c) < 0 THEN { --sign of second derivative changes in [0..1] roots: Quadratic.Roots _ Quadratic.RealRoots[3*a, 2*b, c]; IF roots.nRoots=0 THEN ERROR; IF roots.r1 IN [0..1] THEN { x: REAL _ (((a*roots.r1+b)*roots.r1+c)*roots.r1+d); min _ MIN[min, x]; max _ MAX[max, x]; }; IF roots.nRoots=2 AND roots.r2 IN [0..1] THEN { x: REAL _ (((a*roots.r2+b)*roots.r2+c)*roots.r2+d); min _ MIN[min, x]; max _ MAX[max, x]; }; }; }; nCubics: INT _ knots.length-1; minX _ minY _ 10E+10; maxX _ maxY _ -10E+10; bezier _ NEW[BezierSeqRec[nCubics]]; FOR i: INT IN [0..nCubics) DO --knot sequence cubic: Cubic.Coeffs; min, max: REAL; cubic.c0.x _ knots[i].x; cubic.c0.y _ knots[i].y; cubic.c1.x _ derivatives[3*i].x; cubic.c1.y _ derivatives[3*i].y; cubic.c2.x _ derivatives[3*i+1].x/2; cubic.c2.y _ derivatives[3*i+1].y/2; cubic.c3.x _ derivatives[3*i+2].x/6; cubic.c3.y _ derivatives[3*i+2].y/6; bezier[i] _ Cubic.CoeffsToBezier[cubic]; [min, max] _ getMinMax[cubic.c3.x, cubic.c2.x, cubic.c1.x, cubic.c0.x]; minX _ MIN[min, minX]; maxX _ MAX[max, maxX]; [min, max] _ getMinMax[cubic.c3.y, cubic.c2.y, cubic.c1.y, cubic.c0.y]; minY _ MIN[min, minY]; maxY _ MAX[max, maxY]; IF debug THEN { IF minX/15840 NOT IN [-range..range] OR minY/15840 NOT IN [-range..range] OR maxX/15840 NOT IN [-range..range] OR maxY/15840 NOT IN [-range..range] THEN SIGNAL Debug[minX/15840, maxX/15840, minY/15840, maxY/15840]}; ENDLOOP; }; Debug: SIGNAL[minX, maxX, minY, maxY: REAL] = CODE; range: REAL _ 200; debug: BOOLEAN _ FALSE; ScalePathData: PUBLIC PROC[data: REF, sx, sy: REAL] = { bezierList: LIST OF BezierSeq _ NARROW[data]; FOR l: LIST OF BezierSeq _ bezierList, l.rest UNTIL l=NIL DO FOR i: INT IN [0..l.first.length) DO l.first[i].b0.x _ l.first[i].b0.x*sx; l.first[i].b0.y _ l.first[i].b0.y*sy; l.first[i].b1.x _ l.first[i].b1.x*sx; l.first[i].b1.y _ l.first[i].b1.y*sy; l.first[i].b2.x _ l.first[i].b2.x*sx; l.first[i].b2.y _ l.first[i].b2.y*sy; l.first[i].b3.x _ l.first[i].b3.x*sx; l.first[i].b3.y _ l.first[i].b3.y*sy; ENDLOOP; ENDLOOP; }; PathMap: ImagerBasic.PathMapType = { bezierList: LIST OF BezierSeq _ NARROW[data]; FOR l: LIST OF BezierSeq _ bezierList, l.rest UNTIL l=NIL DO move[[l.first[0].b0.x, l.first[0].b0.y]]; FOR i: INT IN [0..l.first.length) DO curve[[l.first[i].b1.x, l.first[i].b1.y], [l.first[i].b2.x, l.first[i].b2.y], [l.first[i].b3.x, l.first[i].b3.y]]; ENDLOOP; ENDLOOP; }; END. jPrintFontsPrivateImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Written by: Maureen Stone April 6, 1985 3:48:30 pm PST Last Changed: Maureen Stone May 28, 1985 9:28:30 am PDT Compute sizes relative to pointSize. PointSize is in device pixels Fill-in charRep from variables. max, min and splines are relative to the fiducial Get PathData and geometric bounding box Convert the knots and derivatives to a BezierSequence. Also computes bounding box for this sequence of cubics Add cubics to list. Compute global max and min now we have a LIST of Bezier sequences. Convert this to one sequence There's a max/min inside this piece. Find the root of the derivative Make knots and derivatives into a sequence of Bezier curves data: REF, move: PROC[p: Pair], line: PROC[p: Pair], curve: PROC[p1, p2, p3: Pair], conic: PROC[p1, p2: Pair, r: REAL] Κ ϊ˜šœ™Icodešœ Οmœ1™——Jšžœ˜—J˜—Jšœžœžœžœ˜3Jšœžœ˜Jšœžœžœ˜šŸ œž œžœ žœ˜7Jšœ žœžœ žœ˜-š žœžœžœ žœžœž˜<šžœžœžœž˜$JšœK˜KJšœK˜KJšœK˜KJšœK˜KJšžœ˜—Jšžœ˜—J˜J˜—˜$Jšœžœ™ Jšœžœ ™Jšœžœ ™Jšœžœ™Jšœžœžœ™"Jšœ žœžœ žœ˜-š žœžœžœ žœžœž˜