GGFontImpl.mesa
Copyright Ó 1987, 1989, 1992 by Xerox Corporation. All rights reserved.
Bier, July 11, 1993 7:56 pm PDT
Pier, July 22, 1993 6:29 pm PDT
Doug Wyatt, December 19, 1989 12:04:59 pm PST
DIRECTORY
CodeTimer, Commander, CommanderOps, Convert, FileNames, GGFont, GGModelTypes, GGParseIn, GGParseOut, Imager, ImagerFont, ImagerSys, ImagerTransformation, IO, PFS, Real, Rope, SimpleFeedback, SymTab;
GGFontImpl:
CEDAR
PROGRAM
IMPORTS CodeTimer, Commander, CommanderOps, Convert, FileNames, GGParseIn, GGParseOut, Imager, ImagerFont, ImagerSys, ImagerTransformation, IO, PFS, Real, Rope, SimpleFeedback, 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"];
italicList: LIST OF ROPE ¬ LIST["-I", "-BI"];
endList: LIST OF ROPE ¬ LIST["mrr", "mir", "brr", "bir", "mor", "bor", "bold", "italic"];
The Font Cache
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 cmd.out.PutF1["GGFontCache is %g\n", [rope[IF fontCacheOn THEN "on" ELSE "off"]] ]
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.PutRope["usage: GGFontCache <on | off> enables or disables Gargoyle's font cache\n"];
};
Computing Alternate (Usually Bitmap) Fonts
AlternateFont:
PUBLIC
PROC [data: FontData, font: ImagerFont.Font, op:
ATOM]
RETURNS [alternate: ImagerFont.Font] = {
Takes an existing font and fontData and returns an alternate. Only implemented op is $visible.
Returns font if no alternate is possible. Remember that the alternate font has to work with the transformation in data.transform.
IF font=NIL THEN RETURN[NIL];
IF op#$visible THEN RETURN[font];
If we have a PressFont or an XC (Print) font, with a suitable data.userFSF, we concoct a TiogaFont name and see if we can Find it. Otherwise, we punt.
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.Concat["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 {
SimpleFeedback.PutFL[$Gargoyle, oneLiner, $Error, "Couldn't find alternate font for %g at size %g", LIST[[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];
};
Filling a FontData with values—default values, from textual descriptions, from a nameless font, from an alternate font.
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] = {
! ParseError[explanation] if something goes wrong.
Takes a FontData (if NIL, will create a new one), a stream, and BOOLEANS which denote what tokens to parse out of the stream and put in the newData. If literalP is TRUE, then prefixP, familyP, faceP are ignored and the parser only attempts to find a literal string and parses it no further. IF it is not literal, then if familyP then a userFSF string is parsed, and if faceP then the parser tries to extract face information from the userFSF. Fills in as many of the newData fields as possible.
Inner:
PROC
RETURNS [error: Rope.
ROPE] = {
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 {
scratchData.userFSF ¬ lit; -- "Modern-BI"
scratchData.family ¬ Before[lit, '-]; -- "Modern
IF scratchData.family=NIL OR scratchData.family.Length[]=0 THEN RETURN["FontName SyntaxError: no font family"];
};
IF faceP
THEN {
-- means figure out the faces from the userFSF
faceRope: ROPE;
faceS: IO.STREAM ← IO.RIS[scratchData.userFSF]; -- "Modern-BI"
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.ReadFactoredTransformation[inStream];
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
scratchData: FontData ← CreateFontData[];
errorRope: Rope.ROPE ← NIL;
BEGIN
newData ← IF data=NIL THEN CreateFontData[] ELSE data; -- prepare newData
scratchData^ ← newData^; -- operate on scratch copy in case failure happens while parsing
errorRope ← 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 errorRope#NIL THEN GOTO Error;
IF literalP THEN UserDataFromFontData[scratchData] ELSE LiteralDataFromFontData[scratchData];
if you get here, you successfully parsed. Return good data.
newData^ ← scratchData^;
EXITS
Error => ParseError[Rope.Concat[errorRope, ": See GGFontSampler.tioga for syntax"]];
END;
};
FontDataFromFont:
PUBLIC
PROC [font: ImagerFont.Font]
RETURNS [newData: FontData] = {
newData ← CreateFontData[];
newData.literal ¬ ImagerFont.Name[font]; -- "xerox/myFonts/FooBarFont"
newData.prefix ¬ FileNames.Directory[newData.literal]; -- "xerox/myFonts/"
newData.literalFSF ¬ FileNames.Tail[newData.literal, '/]; -- "FooBarFont"
newData.transform ¬ font.charToClient;
newData.storedSize ¬ 1.0;
newData.designSize ¬ 1.0;
UserDataFromFontData[newData];
};
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;
};
scratchData: FontData ← CreateFontData[]; BAD IDEA
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];
};
Completing a FontData from some of its parts.
LiteralDataFromFontData:
PUBLIC
PROC [data: FontData] = {
! ParseError[explanation] if something goes wrong.
Constructs a literal font name from the (non NIL) values in the FontData and fills in the literal field in FontData.
This routine has the hairy specific knowledge of the formats of font names.
So far: prefix face fontName suffix
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] = {
For PressFonts Only; digits on the end indicate a "stored size" but ImagerFont produces a unit font with no face characters like -mrr; e.g. cmr60, cmbbi66, ...
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 => {
Note: this won't work for italic fonts named -bor, but both -bir and -bor extensions are found in the xerox/xc1-3-3/fis directory, so we would actually have to look at the files to disambiguate. Yuk.
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
For TiogaFonts, scratchData.family includes storedSize, like Tioga10
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};
];
if you get here, you successfully parsed. Return good data.
data^ ← scratchData^;
EXITS
Error => ParseError[Rope.Concat[errorRope, ": (example: Helvetica-BI 18 for SetPressFont)"]];
END;
};
UserDataFromFontData:
PUBLIC
PROC [data: FontData] = {
! ParseError[explanation] if something goes wrong.
Parses the literal font string in FontData, filling in the other values in FontData as possible.
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] => {
xerox/pressfonts/Helvetica-bir or xerox/pressfonts/CMR63
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] => {
xerox/xc1-2-2/Modern-bold-italic
xerox/xc1-2-2/urw-itcgaramond-bold-italic-ps
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];
may look garbaged because print font names have become complex
};
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.ReadWord[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] = {
HasMultiple returns TRUE if more than one instance of char is found in s
IF s=NIL THEN RETURN[FALSE]
ELSE {
found: BOOL ← FALSE;
count: INT ← 0;
pos: INT ← 0;
len: INT ← s.Length[];
IF len<2 THEN RETURN[FALSE];
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] = {
Before returns the part of a rope before the first instance of char.
Before returns the entire rope if char is not found OR if multiple instances of char are found
IF s=NIL THEN RETURN[NIL]
ELSE {
pos: INT ← 0;
len: INT ← s.Length[];
found: BOOL ← FALSE;
DO
IF s.Fetch[pos] = char THEN RETURN[s.Substr[0, pos]];
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] = {
After returns the part of a rope including and after the first instance of char.
After returns the entire rope if char is not found OR if multiple instances of char are found
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;
};
};
Describing a Font Textually
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.PutFL[stream, "%g %g %g %g %g", LIST[[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] = {
Used to read in a rope which is data.
WordBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.TAB => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
', => RETURN [break];
'] => RETURN [break];
') => RETURN [break];
ENDCASE => RETURN [other];
};
WordBreakProc:
SAFE
PROC [char:
CHAR]
RETURNS [
IO.CharClass] =
CHECKED {
SELECT char
FROM
IO.LF, IO.CR, IO.TAB, IO.SP, ',, '], ') => RETURN [break];
ENDCASE => RETURN [other];
};
[word,
----] ←
IO.GetTokenRope[f, WordBreakProc
!IO.EndOfStream => {word ← NIL; CONTINUE}];
};
nameStream: IO.STREAM;
fail ← FALSE;
IF prefixP THEN prefix ← IO.GetTokenRope[inStream, IO.IDProc].token; -- "xerox/myfonts/"
nameStream ← IO.RIS[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "fontOne-BI"
IF familyP THEN family ← IO.GetTokenRope[nameStream, IO.TokenProc].token; -- "fontOne"
IF faceP THEN face ← ReadWord[nameStream]; -- "-BI" (or SP)
IF transformP THEN transform ← GGParseIn.ReadFactoredTransformation[inStream];
IF sizeP THEN size ← Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12"
};
Init:
PROC = {
fontTable ← SymTab.Create[case: FALSE]; -- cache results of ImagerFont.Find
alternateFontTable ← SymTab.Create[case: FALSE]; -- cache results of the alternate font mapping
Commander.Register["GGFontCache", GGFontCache, "GGFontCache <on | off> enables or disables Gargoyle's font cache"];
};
Init[];
END.