GGFontImpl.mesa
Copyright (C) 1987 by Xerox Corporation. All rights reserved.
Created by: Ken Pier, February 10, 1987 11:23:45 am PST
Last Edited by: Ken Pier, February 17, 1987 12:46:30 pm PST
DIRECTORY
Convert, FileNames, GGFont, GGParseIn, Imager, ImagerFont, ImagerTransformation, IO, Real, Rope;
GGFontImpl: CEDAR PROGRAM IMPORTS Convert, FileNames, GGParseIn, Imager, ImagerFont, ImagerTransformation, IO, Real, Rope
EXPORTS GGFont = BEGIN
ROPE: TYPE = Rope.ROPE;
FontData: TYPE = GGFont.FontData;
FontDataRec: TYPE = GGFont.FontDataRec;
ParseError:
PUBLIC
SIGNAL[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", "bold", "italic"];
DigitProc:
IO.BreakProc = {
SELECT char
FROM
IO.TAB, IO.CR, 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];
};
CreateFontData:
PUBLIC
PROC
RETURNS [data: FontData] ={
data ← NEW[FontDataRec ← []];
};
InitFontData:
PUBLIC
PROC [data: FontData]
RETURNS [newData: FontData] ={
newData ← data;
IF newData#NIL THEN newData^ ← []; -- fill in from definition defaults
};
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];
};
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.
ENABLE
IO.Error,
IO.EndOfStream, Convert.Error, GGParseIn.SyntaxError => {
ParseError["Font Parse Error"]; -- for now
CONTINUE;
};
DisallowedEnding:
PROC [s: Rope.
ROPE]
RETURNS [
BOOL] = {
tail: Rope.ROPE ← FileNames.Tail[s, '-];
FOR endRope:
LIST
OF
ROPE ← endList, endRope.rest
UNTIL endRope=
NIL
DO
IF Rope.Find[s, endRope.first, 0, FALSE]#-1 THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
newData ← IF data=NIL THEN CreateFontData[] ELSE data;
IF literalP
THEN {
newData.literal ← IO.GetTokenRope[inStream, IO.IDProc].token; -- "xerox/myFonts/FooBarFont"
newData.prefix ← FileNames.Directory[newData.literal]; -- "xerox/myFonts/"
newData.literalFSF ← FileNames.Tail[newData.literal, '/]; -- "FooBarFont"
}
ELSE {
lit: ROPE ← IO.GetTokenRope[inStream, IO.IDProc].token; -- "xerox/xc1-2-2/Modern-BI"
IF prefixP
THEN {
newData.prefix ← FileNames.Directory[lit]; -- "xerox/xc1-2-2/"
lit ← FileNames.Tail[lit, '/]; -- "Modern-BI"
};
IF familyP
THEN {
IF DisallowedEnding[lit] THEN ParseError["Literal name not allowed"];
newData.userFSF ← lit; -- "Modern-BI"
newData.family ← Before[lit, '-]; -- "Modern
};
IF faceP
THEN {
-- means figure out the faces from the userFSF
faceRope: ROPE;
faceS: IO.STREAM ← IO.RIS[newData.userFSF]; -- "Modern-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 newData.bold THEN EXIT;
newData.bold ← Rope.Find[faceRope, boldRope.first, 0, FALSE]#-1;
ENDLOOP;
FOR italicRope:
LIST
OF
ROPE ← italicList, italicRope.rest
UNTIL italicRope=
NIL
DO
IF newData.italic THEN EXIT;
newData.italic ← Rope.Find[faceRope, italicRope.first, 0, FALSE]#-1;
ENDLOOP;
newData.faceKnown ← TRUE;
};
newData.comfortable ← TRUE;
};
IF transformP THEN newData.transform ← GGParseIn.ReadFactoredTransformation[inStream];
IF transformP THEN newData.transform ← GGParseIn.ReadFactoredTransformationVEC[inStream];
IF scaleP THEN newData.scale ← Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..."
IF storedSizeP THEN newData.storedSize ← Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..."
IF designSizeP THEN newData.designSize ← Convert.RealFromRope[IO.GetTokenRope[inStream, IO.IDProc].token]; -- "12 or 20.123 or ..."
IF literalP THEN UserDataFromFontData[newData] ELSE LiteralDataFromFontData[newData];
};
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]
ENABLE
IO.Error,
IO.EndOfStream, Convert.Error, GGParseIn.SyntaxError => {
ParseError["Font Parse Error"]; -- for now
CONTINUE;
};
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[data.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/";
printPrefix: Rope.ROPE ← "xerox/xc1-2-2/";
screenPrefix: Rope.ROPE ← "xerox/tiogafonts/";
faceRope: Rope.ROPE;
SELECT
TRUE
FROM
Rope.Equal[data.prefix, pressPrefix,
FALSE]
AND data.faceKnown => {
faceRope ←
SELECT
TRUE
FROM
data.bold AND data.italic => "-bir",
data.bold => "-brr",
data.italic => "-mir",
ENDCASE => IF HasEndDigits[] THEN "" ELSE "-mrr";
};
Rope.Equal[data.prefix, printPrefix,
FALSE]
AND data.faceKnown => {
faceRope ←
SELECT
TRUE
FROM
data.bold AND data.italic => "-bold-italic",
data.bold => "-bold",
data.italic => "-italic",
ENDCASE => "";
};
Rope.Equal[data.prefix, screenPrefix,
FALSE] => {
-- derive storedSize from the font name
For TiogaFonts, data.family includes storedSize, like Tioga10
storedSize: REAL ← 1.5;
nameStream: IO.STREAM ← IO.RIS[data.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.Float[Real.Fix[storedSize]] # storedSize THEN ParseError["StoredSize must be an integer for screen fonts"];
data.storedSize ← storedSize; -- so things come out the right size
faceRope ←
SELECT
TRUE
FROM
data.bold AND data.italic => "BI",
data.bold => "B",
data.italic => "I",
ENDCASE => "";
};
ENDCASE => NULL;
data.literalFSF ← Rope.Concat[data.family, faceRope];
data.literal ← Rope.Concat[data.prefix, data.literalFSF];
};
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.
ENABLE
IO.Error,
IO.EndOfStream, Convert.Error, GGParseIn.SyntaxError => {
ParseError["Font Parse Error"]; -- for now
CONTINUE;
};
isBold, isItalic: BOOL ← FALSE;
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
data.family ← Before[data.literalFSF, '-]; -- Modern
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
isBold AND isItalic => "-BI",
isBold => "-B",
isItalic => "-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.ReadBlankAndWord[nameStream]; -- like BI or NIL
storedSize ← Convert.RealFromRope[storedSizeRope ! Convert.Error => CONTINUE;]; -- get any digit characters
IF Real.Float[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;
};
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];
So, what do we do here, Bunkie?? Well, 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, "xc1", 0,
FALSE]#-1
THEN {
dashIndex: INT ← Rope.Index[data.userFSF, 0, "-"]; -- is there a dash in the userFSF ??
altName: Rope.ROPE ← IF dashIndex=-1 THEN Rope.Cat["xerox/tiogafonts/", data.userFSF, "10"] ELSE Rope.Cat["xerox/tiogafonts/", Rope.Replace[data.userFSF, dashIndex, 1, "10"] ];
alternate ← ImagerFont.Scale[ImagerFont.Find[altName ! Imager.Error => {alternate ← font; CONTINUE;}; ], 0.10];
}
ELSE alternate ← font;
};
Head: PROC [s: Rope.ROPE, char: CHAR] RETURNS [Rope.ROPE] = {
Head returns the part of a rope before the last instance of char.
Head returns the entire rope if char is not found
pos: INT ← s.Length[] - 1;
IF pos < 0 THEN RETURN[NIL];
DO
IF s.Fetch[pos] = char THEN RETURN[s.Substr[0, pos]];
pos ← pos - 1;
IF pos < 0 THEN RETURN[s];
ENDLOOP;
};
Before:
PROC [s: Rope.
ROPE, char:
CHAR]
RETURNS [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
pos: INT ← 0;
len: INT ← s.Length[];
IF s=NIL THEN RETURN[NIL];
DO
IF s.Fetch[pos] = char THEN RETURN[s.Substr[0, pos]];
pos ← pos + 1;
IF pos = len THEN RETURN[s];
ENDLOOP;
};
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];
};
[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"
};
END.