<> <> <> <> <> <<>> DIRECTORY CodeTimer, Commander, CommanderOps, Convert, Feedback, FileNames, GGFont, GGModelTypes, GGParseIn, GGParseOut, Imager, ImagerFont, ImagerSys, ImagerTransformation, IO, PFS, Real, Rope, SymTab; GGFontImpl: CEDAR PROGRAM IMPORTS CodeTimer, Commander, CommanderOps, Convert, Feedback, FileNames, GGParseIn, GGParseOut, Imager, ImagerFont, ImagerSys, ImagerTransformation, IO, PFS, Real, Rope, SymTab EXPORTS GGFont = BEGIN Font: TYPE = ImagerFont.Font; FontData: TYPE = GGModelTypes.FontData; FontDataRec: TYPE = GGModelTypes.FontDataRec; ROPE: TYPE = Rope.ROPE; ParseError: PUBLIC ERROR[explanation: ROPE] = CODE; boldList: LIST OF ROPE _ LIST["-B", "-BI", "-IB"]; italicList: LIST OF ROPE _ LIST["-I", "-BI", "-IB"]; <> <> fontTable, alternateFontTable: SymTab.Ref; -- created by Init FlushFontCache: PUBLIC PROC = { SymTab.Erase[fontTable]; SymTab.Erase[alternateFontTable]; }; GGFontCache: Commander.CommandProc = { argv: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd]; IF argv.argc <= 1 THEN GOTO Failed ELSE { IF Rope.Equal[argv[1], "on", FALSE] THEN fontCacheOn _ TRUE ELSE IF Rope.Equal[argv[1], "off", FALSE] THEN fontCacheOn _ FALSE ELSE GOTO Failed; }; EXITS Failed => cmd.err.PutF["usage: GGFontCache enables or disables Gargoyle's font cache\n"]; }; <> AlternateFont: PUBLIC PROC [data: FontData, font: ImagerFont.Font, op: ATOM] RETURNS [alternate: ImagerFont.Font] = { <> <> IF font=NIL THEN RETURN[NIL]; IF op#$visible THEN RETURN[font]; <> IF Rope.Find[data.prefix, "pressfont", 0, FALSE]#-1 OR Rope.Find[data.prefix, "xc", 0, FALSE]#-1 THEN { epsilon: REAL = 0.01; ft: ImagerTransformation.FactoredTransformation; CodeTimer.StartInt[$AlternateFontHard, $Gargoyle]; ft _ ImagerTransformation.Factor[data.transform]; IF ABS[ABS[ft.s.x] - ABS[ft.s.y]] < epsilon THEN { alternate _ NearestTiogaFont[data.userFSF, ABS[ft.s.x], font]; } ELSE alternate _ font; CodeTimer.StopInt[$AlternateFontHard, $Gargoyle]; } ELSE alternate _ font; }; FontEntry: TYPE = REF FontEntryObj; FontEntryObj: TYPE = RECORD [ font: Font, scaledList: LIST OF ScaledFontEntry ]; fontCacheOn: BOOL _ TRUE; FindScaledFont: PROC [name: ROPE, scale: CARD] RETURNS [scaledFont: Font _ NIL] = { found: BOOL _ FALSE; val: REF; s: REAL; font: Font; IF scale = 0 THEN scale _ 1; s _ scale; IF fontCacheOn THEN { fE: FontEntry; [found, val] _ SymTab.Fetch[fontTable, name]; IF found THEN fE _ NARROW[val] ELSE { font _ ImagerFont.Find[name, noSubstitute ! PFS.Error, Imager.Error, Imager.Warning, ImagerSys.FileError => {font _ NIL; CONTINUE}]; fE _ NEW[FontEntryObj _ [font: font, scaledList: NIL]]; [] _ SymTab.Store[fontTable, name, fE]; }; IF fE.font = NIL THEN RETURN[NIL]; FOR list: LIST OF ScaledFontEntry _ fE.scaledList, list.rest UNTIL list = NIL DO IF scale = list.first.scale THEN RETURN[list.first.font]; ENDLOOP; scaledFont _ ImagerFont.Scale[fE.font, 1.0/s]; fE.scaledList _ CONS[[scale: scale, font: scaledFont], fE.scaledList]; } ELSE { font _ ImagerFont.Find[name, noSubstitute ! PFS.Error, Imager.Error, Imager.Warning, ImagerSys.FileError => {font _ NIL; CONTINUE}]; IF font = NIL THEN RETURN[NIL]; scaledFont _ ImagerFont.Scale[font, 1.0/s]; }; }; ScaledFontRef: TYPE = REF ScaledFontEntry; ScaledFontEntry: TYPE = RECORD [ scale: CARD, font: Font ]; NearestTiogaFont: PROC [userFSF: ROPE, scaleReal: REAL, default: ImagerFont.Font] RETURNS [alternate: ImagerFont.Font] = { fE: ScaledFontRef; TryName: PROC [name: ROPE, scale: CARD] RETURNS [alt: ImagerFont.Font _ NIL] = { scaleRope: ROPE; dashIndex: INT _ Rope.Find[name, "-"]; IF scale = 0 THEN RETURN; scaleRope _ Convert.RopeFromCard[from: scale, base: 10, showRadix: FALSE]; altName _ IF dashIndex=-1 THEN Rope.Cat["xerox/tiogafonts/", name, scaleRope] ELSE Rope.Cat["xerox/tiogafonts/", Rope.Replace[name, dashIndex, Rope.MaxLen, scaleRope]]; alt _ FindScaledFont[altName, scale]; }; scale: CARD; altName: ROPE; scale _ Real.Round[scaleReal]; IF fontCacheOn THEN { found: BOOL _ FALSE; val: REF; fontName: ROPE _ Rope.Concat[userFSF, Convert.RopeFromCard[scale]]; [found, val] _ SymTab.Fetch[alternateFontTable, fontName]; IF found THEN { fE _ NARROW[val]; RETURN[fE.font]; -- this is the fast case } ELSE { fE _ NEW[ScaledFontEntry _ [scale: scale, font: NIL]]; -- fill in font when we GOTO Found; [] _ SymTab.Store[alternateFontTable, fontName, fE]; }; }; BEGIN alternate _ TryName[userFSF, scale]; IF alternate # NIL THEN GOTO Found; alternate _ TryName[userFSF, scale+1]; IF alternate # NIL THEN GOTO Found; IF scale > 0 THEN { alternate _ TryName[userFSF, scale-1]; IF alternate # NIL THEN GOTO Found; }; SELECT scale FROM IN [4..8] => alternate _ TryName[userFSF, 10]; IN [9..10] => alternate _ TryName[userFSF, 12]; IN [14..15] => alternate _ TryName[userFSF, 12]; 16 => alternate _ TryName[userFSF, 18]; 20 => alternate _ TryName[userFSF, 18]; ENDCASE; IF alternate # NIL THEN GOTO Found; IF scale >= 18 THEN { alternate _ default; GOTO Found; }; alternate _ TryName["Helvetica", scale]; IF alternate # NIL THEN GOTO Found; alternate _ TryName["Helvetica", scale+1]; IF alternate # NIL THEN GOTO Found; IF scale > 0 THEN { alternate _ TryName["Helvetica", scale-1]; IF alternate # NIL THEN GOTO Found; }; IF scale = 4 THEN alternate _ TryName["Helvetica", 6] ELSE IF scale = 20 THEN alternate _ TryName["Helvetica", 18]; IF alternate # NIL THEN GOTO Found; IF scale IN [2..17] THEN { Feedback.PutFByName[$Gargoyle, oneLiner, $Error, "Couldn't find alternate font for %g at size %g", [rope[userFSF]], [integer[scale]]]; }; alternate _ default; GOTO Found; EXITS Found => IF fontCacheOn THEN fE.font _ alternate; END; }; <> CreateFontData: PUBLIC PROC RETURNS [data: FontData] = { data _ NEW[FontDataRec _ []]; data.substituteOK _ TRUE; -- bad hack here. Avoids a bogus font }; CopyFontData: PUBLIC PROC [data: FontData, oldCopy: FontData _ NIL] RETURNS [newCopy: FontData] = { newCopy _ IF oldCopy#NIL THEN oldCopy ELSE CreateFontData[]; IF data#NIL THEN newCopy^ _ data^; IF newCopy.transform#NIL THEN newCopy.transform _ ImagerTransformation.Copy[newCopy.transform]; }; <<>> <> <<>> InitFontData: PUBLIC PROC [data: FontData] RETURNS [newData: FontData] = { newData _ data; IF newData#NIL THEN newData^ _ []; -- fill in from definition defaults }; <<>> <> ParseFontData: PUBLIC PROC [data: FontData _ NIL, inStream: IO.STREAM, literalP, prefixP, familyP, faceP, transformP, scaleP, storedSizeP, designSizeP: BOOL _ FALSE] RETURNS [newData: FontData] = { <> <> Inner: PROC = { DisallowedEnding: PROC [s: Rope.ROPE] RETURNS [BOOL] = { <> <> <> <> RETURN[FALSE]; -- who knows what an ending contains these days. <> }; IF literalP THEN { scratchData.literal _ IO.GetTokenRope[inStream, IO.IDProc].token; -- "xerox/myFonts/FooBarFont" scratchData.prefix _ FileNames.Directory[scratchData.literal]; -- "xerox/myFonts/" scratchData.literalFSF _ FileNames.Tail[scratchData.literal, '/]; -- "FooBarFont" } ELSE { lit: ROPE _ IO.GetTokenRope[inStream, IO.IDProc].token; -- "xerox/xc1-2-2/Modern-BI" IF prefixP THEN { scratchData.prefix _ FileNames.Directory[lit]; -- "xerox/xc1-2-2/" lit _ FileNames.Tail[lit, '/]; -- "Modern-BI" }; IF familyP THEN { IF DisallowedEnding[lit] THEN ParseError["Literal font name not allowed here"]; scratchData.userFSF _ lit; -- "Modern-BI" scratchData.family _ Before[lit, '-]; -- "Modern }; IF faceP THEN { -- means figure out the faces from the userFSF faceRope: ROPE; <> faceS: IO.STREAM _ IO.RIS[After[scratchData.userFSF, '-]]; -- "-BI" <<[] _ IO.GetTokenRope[faceS, IO.TokenProc]; -- read and discard family name "Modern">> faceRope _ IO.GetTokenRope[faceS, IO.IDProc ! IO.EndOfStream => CONTINUE;].token; -- -BI or SP in other cases FOR boldRope: LIST OF ROPE _ boldList, boldRope.rest UNTIL boldRope=NIL DO IF scratchData.bold THEN EXIT; scratchData.bold _ Rope.Find[faceRope, boldRope.first, 0, FALSE]#-1; ENDLOOP; FOR italicRope: LIST OF ROPE _ italicList, italicRope.rest UNTIL italicRope=NIL DO IF scratchData.italic THEN EXIT; scratchData.italic _ Rope.Find[faceRope, italicRope.first, 0, FALSE]#-1; ENDLOOP; scratchData.faceKnown _ TRUE; }; scratchData.comfortable _ TRUE; }; <> IF transformP THEN scratchData.transform _ GGParseIn.ReadFactoredTransformationVEC[inStream]; IF scaleP THEN scratchData.scale _ Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..." IF storedSizeP THEN scratchData.storedSize _ Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..." IF designSizeP THEN scratchData.designSize _ Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..." <> <> }; -- Inner errorRope: Rope.ROPE; scratchData: FontData _ CreateFontData[]; BEGIN newData _ IF data=NIL THEN CreateFontData[] ELSE data; -- prepare newData scratchData^ _ newData^; -- operate on scratch copy in case failure happens while parsing Inner[ ! IO.Error => {errorRope _ "FontName IO Error"; GOTO Error}; IO.EndOfStream => {errorRope _ "FontName EndOfStream"; GOTO Error}; Convert.Error => {errorRope _ "FontName Convert Error"; GOTO Error}; GGParseIn.SyntaxError => {errorRope _ "FontName SyntaxError"; GOTO Error}; ]; IF literalP THEN UserDataFromFontData[scratchData] ELSE LiteralDataFromFontData[scratchData]; <> newData^ _ scratchData^; EXITS Error => ParseError[Rope.Concat[errorRope, ": See GGFontSampler.tioga for syntax"]]; END; }; FontDataFromNamelessFont: PUBLIC PROC [font: ImagerFont.Font] RETURNS [newData: FontData] = { newFont: ImagerFont.Font _ NEW[ImagerFont.FontRep _ [ charToClient: ImagerTransformation.Scale[1.0], typeface: font.typeface]]; newData _ CreateFontData[]; newData.storedSize _ 1.0; newData.namelessFont _ newFont; }; <<>> <> DigitProc: IO.BreakProc = { SELECT char FROM IO.LF, IO.CR, IO.TAB, IO.SP => RETURN [break]; '0, '1, '2, '3, '4, '5, '6, '7, '8, '9 => RETURN [break]; ENDCASE => RETURN [other]; }; NonDigitProc: IO.BreakProc = { SELECT char FROM '0, '1, '2, '3, '4, '5, '6, '7, '8, '9 => RETURN [other]; ENDCASE => RETURN [break]; }; <> LiteralDataFromFontData: PUBLIC PROC [data: FontData] = { <> <> <> <> <<>> << xerox/xc1-2-2/ -B -bold>> << xerox/xc1-2-2/ -I -italic>> << xerox/xc1-2-2/ -BI -bold-italic>> << xerox/xc1-2-2/ -IB -bold-italic>> << xerox/xc1-2-2/ none none>> <<>> << xerox/pressfonts/ -B -brr>> << xerox/pressfonts/ -I -mir>> << xerox/pressfonts/ -BI -bir>> << xerox/pressfonts/ -IB -bir>> << xerox/pressfonts/ none -mrr (unless CMR font)>> <<>> << xerox/tiogafonts/ -B Fix[size]B>> << xerox/tiogafonts/ -I Fix[size]I>> << xerox/tiogafonts/ -BI Fix[size]BI>> << xerox/tiogafonts/ -IB Fix[size]BI>> << xerox/tiogafonts/ none Fix[size]>> <<>> Inner: PROC = { HasEndDigits: PROC RETURNS [yep: BOOL _ FALSE] = { <> ENABLE IO.Error, IO.EndOfStream, Convert.Error => { yep _ FALSE; CONTINUE; }; nameStream: IO.STREAM _ IO.RIS[scratchData.family]; -- Helvetica or cmbbi66 [] _ IO.GetTokenRope[nameStream, DigitProc]; -- toss the leading alpha characters yep _ Convert.IntFromRope[IO.GetTokenRope[nameStream, NonDigitProc].token]>0; -- get any digit characters. Assume font names indicate positive values. }; pressPrefix: Rope.ROPE _ "xerox/pressfonts/"; fisPrefix: Rope.ROPE _ "xerox/xc1-3-3/fis/"; printPrefix: Rope.ROPE _ "xerox/xc1-2-2/"; screenPrefix: Rope.ROPE _ "xerox/tiogafonts/"; faceRope: Rope.ROPE; SELECT TRUE FROM (Rope.Equal[scratchData.prefix, pressPrefix, FALSE] OR Rope.Equal[scratchData.prefix, fisPrefix, FALSE]) AND scratchData.faceKnown => { <> faceRope _ SELECT TRUE FROM scratchData.bold AND scratchData.italic => "-bir", scratchData.bold => "-brr", scratchData.italic => "-mir", ENDCASE => IF HasEndDigits[] THEN "" ELSE "-mrr"; }; Rope.Equal[scratchData.prefix, printPrefix, FALSE] AND scratchData.faceKnown => { faceRope _ SELECT TRUE FROM scratchData.bold AND scratchData.italic => "-bold-italic", scratchData.bold => "-bold", scratchData.italic => "-italic", ENDCASE => ""; }; Rope.Equal[scratchData.prefix, screenPrefix, FALSE] => { -- derive storedSize from the font name <> storedSize: REAL _ 1.5; nameStream: IO.STREAM _ IO.RIS[scratchData.userFSF]; -- Tioga10-BI or TERMINAL [] _ IO.GetTokenRope[nameStream, DigitProc]; -- get the leading alpha characters storedSize _ Convert.RealFromRope[IO.GetTokenRope[nameStream, NonDigitProc].token]; -- get any digit characters IF REAL[Real.Fix[storedSize]] # storedSize THEN ParseError["StoredSize must be an integer for screen fonts"]; scratchData.storedSize _ storedSize; -- so things come out the right size faceRope _ SELECT TRUE FROM scratchData.bold AND scratchData.italic => "BI", scratchData.bold => "B", scratchData.italic => "I", ENDCASE => ""; }; ENDCASE => NULL; scratchData.literalFSF _ Rope.Concat[scratchData.family, IF HasMultiple[scratchData.family, '-] THEN NIL ELSE faceRope]; scratchData.literal _ Rope.Concat[scratchData.prefix, scratchData.literalFSF]; }; scratchData: FontData _ CreateFontData[]; errorRope: Rope.ROPE; BEGIN IF data=NIL THEN ParseError["NIL Font Data cannot be parsed"]; scratchData^ _ data^; -- operate on scratch copy in case failure happens while parsing Inner[ ! IO.Error => {errorRope _ "FontName IO Error"; GOTO Error}; IO.EndOfStream => {errorRope _ "FontName EndOfStream"; GOTO Error}; Convert.Error => {errorRope _ "FontName Convert Error"; GOTO Error}; GGParseIn.SyntaxError => {errorRope _ "FontName SyntaxError"; GOTO Error}; ]; <> data^ _ scratchData^; EXITS Error => ParseError[Rope.Concat[errorRope, ": (example: Helvetica-BI 18 for SetPressFont)"]]; END; }; UserDataFromFontData: PUBLIC PROC [data: FontData] = { <> <> Inner: PROC = { userFace, literalFace: Rope.ROPE; pressPrefix: Rope.ROPE _ "xerox/pressfonts/"; printPrefix: Rope.ROPE _ "xerox/xc1-2-2/"; screenPrefix: Rope.ROPE _ "xerox/tiogafonts/"; cmrFamily: Rope.ROPE _ "CMR"; dashBold: Rope.ROPE _ "-bold"; dashItalic: Rope.ROPE _ "-italic"; IF data.literal=NIL OR data.literalFSF=NIL THEN ERROR; data.prefix _ FileNames.Directory[data.literal]; -- xerox/... SELECT TRUE FROM Rope.Equal[data.prefix, pressPrefix, FALSE] => { <> data.family _ Before[data.literalFSF, '-]; -- Helvetica or CMR literalFace _ FileNames.Tail[data.literalFSF, '-]; -- bir or NIL data.bold _ Rope.Equal[literalFace, "bir", FALSE] OR Rope.Equal[literalFace, "brr", FALSE]; data.italic _ Rope.Equal[literalFace, "bir", FALSE] OR Rope.Equal[literalFace, "mir", FALSE]; data.faceKnown _ TRUE; data.comfortable _ TRUE; userFace _ SELECT TRUE FROM Rope.Equal[literalFace, "bir", FALSE] => "-BI", Rope.Equal[literalFace, "brr", FALSE] => "-B", Rope.Equal[literalFace, "mir", FALSE] => "-I", Rope.Equal[literalFace, "mrr", FALSE] => "", ENDCASE => ""; data.userFSF _ Rope.Concat[data.family, userFace]; }; Rope.Equal[data.prefix, printPrefix, FALSE] => { <> <> data.family _ Before[data.literalFSF, '-]; -- Modern or urw-itcgarramond-bold-ps data.bold _ Rope.Find[data.literalFSF, dashBold, 0, FALSE]#-1; -- has -bold data.italic _ Rope.Find[data.literalFSF, dashItalic, 0, FALSE]#-1; -- has -italic data.faceKnown _ TRUE; data.comfortable _ TRUE; userFace _ SELECT TRUE FROM data.bold AND data.italic => "-BI", data.bold => "-B", data.italic => "-I", ENDCASE => ""; data.userFSF _ Rope.Concat[data.family, userFace]; <> }; Rope.Equal[data.prefix, screenPrefix, FALSE] => { --Tioga10BI or TERMINAL alphaRope, faceRope, storedSizeRope: Rope.ROPE; storedSize: REAL _ 1.5; --, non integer default used below nameStream: IO.STREAM _ IO.RIS[data.literalFSF]; -- Tioga10BI or TERMINAL alphaRope _ IO.GetTokenRope[nameStream, DigitProc].token; -- get the leading alpha characters storedSizeRope _ IO.GetTokenRope[nameStream, NonDigitProc ! IO.EndOfStream, IO.Error => CONTINUE;].token; -- get any digit characters faceRope _ GGParseIn.ReadWWord[nameStream]; -- like BI or NIL storedSize _ Convert.RealFromRope[storedSizeRope ! Convert.Error => CONTINUE;]; -- get any digit characters IF REAL[Real.Fix[storedSize]] # storedSize THEN ParseError["StoredSize must be an integer for screen fonts"]; data.family _ Rope.Concat[alphaRope, storedSizeRope]; -- concat alpha and numeric parts data.scale _ data.storedSize _ storedSize; -- so things come out the right size. Note that this overides the user input during SetFontLiteral. The rule we use is that if you use a TiogaFont with a size in the name, like Tioga10, we derive the storedSize and scale from the name. IF faceRope#NIL THEN faceRope _ Rope.Concat["-", faceRope]; data.userFSF _ Rope.Concat[data.family, faceRope]; data.faceKnown _ TRUE; data.comfortable _ TRUE; FOR boldRope: LIST OF ROPE _ boldList, boldRope.rest UNTIL boldRope=NIL DO IF data.bold THEN EXIT; data.bold _ Rope.Find[faceRope, boldRope.first, 0, FALSE]#-1; ENDLOOP; FOR italicRope: LIST OF ROPE _ italicList, italicRope.rest UNTIL italicRope=NIL DO IF data.italic THEN EXIT; data.italic _ Rope.Find[faceRope, italicRope.first, 0, FALSE]#-1; ENDLOOP; }; ENDCASE => NULL; }; errorRope: Rope.ROPE; BEGIN Inner[ ! IO.Error => {errorRope _ "IO.Error during font parse"; GOTO Error}; IO.EndOfStream => {errorRope _ "IO.EndOfStream during font parse"; GOTO Error}; Convert.Error => {errorRope _ "Convert.Error during font parse"; GOTO Error}; GGParseIn.SyntaxError => {errorRope _ "GGParseIn.SyntaxError during font parse"; GOTO Error}; ]; EXITS Error => ParseError[errorRope]; END; }; HasMultiple: PROC [s: Rope.ROPE, char: CHAR] RETURNS [BOOL] = { <> IF s=NIL THEN RETURN[FALSE] ELSE { found: BOOL _ FALSE; count: INT _ 0; pos: INT _ 0; len: INT _ s.Length[]; DO IF s.Fetch[pos] = char THEN IF found THEN RETURN[TRUE] ELSE found _ TRUE; pos _ pos + 1; IF pos = len THEN RETURN[FALSE]; ENDLOOP; }; }; Before: PROC [s: Rope.ROPE, char: CHAR] RETURNS [r: Rope.ROPE] = { <> <> IF s=NIL THEN RETURN[NIL] ELSE { pos: INT _ 0; len: INT _ s.Length[]; found: BOOL _ FALSE; DO <> IF s.Fetch[pos] = char THEN { -- found an instance of char in s IF found THEN RETURN[s] ELSE { found _ TRUE; r _ s.Substr[0, pos]; }; }; pos _ pos + 1; IF pos = len THEN RETURN[IF found THEN r ELSE s]; ENDLOOP; }; }; After: PROC [s: Rope.ROPE, char: CHAR] RETURNS [r: Rope.ROPE] = { <> <> IF s=NIL THEN RETURN[NIL] ELSE { pos: INT _ 0; len: INT _ s.Length[]; found: BOOL _ FALSE; DO IF s.Fetch[pos] = char THEN { -- found an instance of char in s IF found THEN RETURN[s] ELSE { found _ TRUE; r _ s.Substr[start: pos, len: Rope.MaxLen]; }; }; pos _ pos + 1; IF pos = len THEN RETURN[IF found THEN r ELSE s]; ENDLOOP; }; }; <<>> <> <<>> FontAsDetailedRope: PUBLIC PROC [font: FontData] RETURNS [detailed: Rope.ROPE] = { }; FontPutDetailedStream: PUBLIC PROC [font: FontData, stream: IO.STREAM] = { }; FontAsLiteralRope: PUBLIC PROC [font: FontData] RETURNS [literal: Rope.ROPE] = { stream: IO.STREAM; stream _ IO.ROS[]; FontPutLiteralStream[font, stream]; literal _ IO.RopeFromROS[stream]; }; FontPutLiteralStream: PUBLIC PROC [font: FontData, stream: IO.STREAM] = { transformStream: IO.STREAM _ IO.ROS[]; transformRope: Rope.ROPE; GGParseOut.WriteFactoredTransformationVEC[transformStream, font.transform]; transformRope _ IO.RopeFromROS[transformStream]; IO.PutF[stream, "%g %g %g %g %g", [rope[font.literal]], [rope[transformRope]], [real[font.storedSize]], [real[font.designSize]], [rope[IF font.substituted THEN "SUBSTITUTED" ELSE ""]] ]; }; <<>> <> OldParseFontData: PUBLIC PROC [inStream: IO.STREAM, prefixP, familyP, faceP, transformP, sizeP: BOOL _ FALSE] RETURNS [fail: BOOL, prefix, family, face: Rope.ROPE, transform: ImagerTransformation.Transformation, size: REAL _ 0.0] = { ENABLE IO.Error, IO.EndOfStream, Convert.Error, GGParseIn.SyntaxError => { fail _ TRUE; CONTINUE; }; ReadWord: PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = { <> <> <