GGFont.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Pier April 21, 1987 11:58:34 am PDT
Bier, March 19, 1992 11:01 am PST
DIRECTORY
GGModelTypes, ImagerFont, ImagerTransformation, IO, Rope;
GGFont: CEDAR DEFINITIONS = BEGIN
FontData: TYPE = REF FontDataRec;
FontDataRec: TYPE = GGModelTypes.FontDataRec;
FONTS (How They Work in Gargoyle)
Introduction
Each text string should have a single font associated with it. That font may be either a print font or a screen font. For our purposes here, a font is a collection of shapes, defined in some font coordinate system, together with a transformation to apply to a string of those shapes when the string is rendered.
There are two kinds of people in the font world: users and wizards. Users are accustomed to thinking simply about fonts; i.e. fonts have a family name like Helvetica or Gacha, a size like 12 points, and a face like bold or boldItalic. They don't care much about PressFonts, TiogaFonts, XCFonts, etc. Wizards know full names of fonts, like "xerox/xc1-2-2/Modern-bold-italic" or "xerox/pressfonts/cmr55", and the fact that Helvetica10.ks is a bitmap font at size ten pixels but Helvetica-mrr is a resolution independent Helvetica font which needs more information, like a scaling, to be meaningfully seen.
In the Gargoyle world, text strings are like any other graphical object and can be transformed by an arbitrary affine transformation. So, fonts don't just have scale factors any more; they have affine transformations associated with them. All this gives us some font parameters to define and deal with:
literal: the wizards full name for a font, like "xerox/xc1-2-2/Modern-bold-italic".
storedSize: the REAL in points at which the font is actually stored in the font file, which can be theoretically different from the size at which it was designed to be displayed, that is, its designSize. For example, one could imagine a font which had to be encoded as a bitmap but was designed to look good on a billboard. The storedSize might be 20, to keep the bitmaps a reasonable size, but the designSize might be 4000.
designSize: a REAL in points which is the size for which the font was originally intended for display. Most fonts don't have design sizes, with the exception of bitmap fonts, which do. For example, Tioga10.ks was designed to be shown at 10 points, but Helvetica-mrr has no design size. A designSize value of 1.0 means "no special design size".
transform: the six point affine transformation, or its factored form, applied to the font in order to render it properly.
Font literal names almost always consist of two parts:
prefix: the leading substring of a literal name; the official "font prefix" from the Xerox Font Interchange Standard. For example, "xerox/pressfonts/", "xerox/tiogafonts/", "xerox/xc1-2-2/" are common prefixes. Case is insignificant.
familySizeFace: the trailing substring of a literal name; can have any format you like, but often encodes the family name, size, and face of a font. In particular, fonts with names like xerox/tiogafonts/Tioga10BI are telling you that the family is Tioga, the storedSize and the designSize are both 10, and the face is boldItalic. Unfortunately, you can have anything you want in a familySizeFace; Gargoyle will make an attempt to understand it if possible and to not throw up even if it can't understand it.
For the convenience of users, Gargoyle knows about a simplified familySizeFace called the user familySizeFace. If a user knows that the face terms Bold or Italic make sense with a particular kind of prefix and family combination, the user can use the user familySizeFace when specifying the font. For example, font family Modern comes in bold and italic, so the user can type Modern-B, Modern-I, Modern-BI instead of wondering what the actual naming convention for Modern is. For Tioga fonts, the user familySizeFace includes the stored size in the name, as in Tioga10 or Hippo12.
comfortable: a BOOLEAN that Gargoyle associates with a font if it believes it is possible to parse its familySizeFace into meaningful sub-components such as size and face. If Gargoyle is not comfortable with a font literal it will still pass it along to the Cedar font machinery in hopes that the font machinery can deal with it.
So, we invent a data structure to hold all of this:
FontDataRec: TYPE = RECORD [
literal: Rope.ROPE, -- "xerox/xc1-2-2/Modern-bold-italic"
prefix: Rope.ROPE,-- "xerox/xc1-2-2/"
literalFSF: Rope.ROPE, -- literal familySizeFace: "Modern-bold-italic"
userFSF: Rope.ROPE, -- user familySizeFace: "Modern-BI"
family: Rope.ROPE, -- family: "Modern"
transform: ImagerTransformation.Transformation,
scale: REAL ← 1.0,
storedSize: REAL ← 1.0,
designSize: REAL ← 1.0,
comfortable: BOOLFALSE,
bold, italic, faceKnown: BOOLFALSE,
substituteOK: BOOLFALSE,
substituted: BOOLFALSE
];
FontData REFS will be the lingua franca and coin of the realm for fonts inside of Gargoyle. There must be ways to create them and to fill them in from several sources. The currently identified sources are:
1. Literal User Format: a string provided by a wizard which contains what the FontData needs in literal form. This includes a full path name, a transformation, a storedSize, and a designSize. Literal user format produces a FontData with comfortable set to FALSE. This format is designed to take any arbitrary font and transformation that a user can invent.
2. Detailed User Format: a string provided by a user with a prefix, userFSF, and transformation. Gargoyle derives the designSize and storedSize from the prefix and userFSF. Detailed user format produces a FontData with comfortable set to TRUE. This format is designed to deal with detailed but relatively normal font data.
3. Short User Format: a string provided by the user with ONLY a userFSF and a scale factor. This is for use by vanilla users who want to continue to think about fonts in the old simple way. The prefix for this format is indirectly provided by the user to Gargoyle depending on what operation a user invokes when providing a short form. Short user format produces a FontData with comfortable set to TRUE.
4. GGFile Format: this format is what is written/read in GG files. There are two versions of it, depending on whether the FontData is comfortable or not (see File Format, below). That fact is recorded in a BOOLEAN (comfortable: BOOLEAN) at the beginning of the file format entry; the BOOLEAN is followed by a literal format string if the BOOLEAN is F and prefix/userFSF if the BOOLEAN is T. The TextFilein code will treat the data as either Literal User Format (comfortable=FALSE) or Detailed User Format (comfortable=TRUE).
5. Interpress file format: reading an interpress file and deriving one of our internal formats is tricky. We intend to manufacture a Literal User Format or Detailed User Format from the context state variables, depending on whether or not we recognize the font as being comfortable. If not, designSize=1.0 and storedSize=1.0 because we can't tell anything about the fonts that come in from Interpress masters.
File Format
Ignoring user interface for the moment, here is what we will store in .gargoyle files:
comfortable literal        Transformation  storedSize designSize
T  xerox/PressFonts/Helvetica-BI   M = [a, b, c, d, e, f] --    --
T  xerox/TiogaFonts/Tioga10-BI   M = [a, b, c, d, e, f] --    --
T  xerox/xc1-2-2/Modern-BI    M = [a, b, c, d, e, f] --    --
F  xerox/newFont/NewCondensed  M = [a, b, c, d, e, f] 1.0    1.0
F  xerox/PressFonts/CMR55    M = [a, b, c, d, e, f] 5.0    5.0
The shapes of the font are assumed to be 1 unit large in font master coordinates. If the named FontPrefix/FontFamily does not obey this property, the font will be scaled to be a unit font before the Transformation is applied. Hence, if M = Scale[12], the font will appear as 12 screen dots high. Our units are 1/72.0 of an inch. Tioga uses 1/72.27 inches. Life is hard.
The shapes of the font are assumed to be 1 unit large in font master coordinates; this is often not true. However, the font will be scaled to be a unit font before the Transformation is applied. Gargoyle will have to know about fonts, but can hide this knowledge from the user. For instance: CMR10 is a 1 unit font, which must be scaled by 10 to look good. Tioga10, however, is stored at 10 units high in font master coordinates. Gargoyle will scale it by 0.1 to get a unit font, but M will usually be Scale[10] so all will be well.
We store both kinds of fonts in .gargoyle files using the same format with storedSize=designSize = 1.0 for resolution independent fonts. This is a unique value which signals the fileIn code that it is a resolution independent font. It's size is one unit, and the appearance scaling is in the transformation.
Setting Fonts
Here's where we have to be flexible. We'll have several kinds of SetFont commands, some which are convenient for everyday use and some which let you be very specific.
SetPressFont. The user selects a <FontFamily-FontFace> <scalar> pair such as "Helvetica-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/PressFonts/ and lets transformation M = Scale[<scalar>]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about).
SetPrintFont. The user selects a <FontFamily-FontFace> <scalar> pair such as "Modern-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/XC1-2-2/ and lets transformation M = Scale[<scalar>]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about).
SetScreenFont. The user selects a <FontFamily-FontFace> <scalar> pair such as "Tioga10 20" or "CMR 12". Gargoyle derives a storedSize from the <FontFamily-FontFace>. Gargoyle assumes FontPrefix = Xerox/TiogaFonts/ and lets transformation M = Scale[<scalar>]. The font is made with ImagerFont.Scale[font, 1] if the font is CMR, with ImagerFont.Scale[font, 1.0/<storedSize>], if the font is Helvetica, TimesRoman, Tioga. If the font is one of the traditional TiogaFonts (e.g. Helvetica, TimesRoman, Tioga, ...) the font machinery will attempt to find the corresponding strike font. For example, SetScreenFont TimesRoman-BI 9 will find the font in file ///fonts/xerox/tiogafonts/TimesRoman9BI.ks. If the user requests a TiogaFont not in the Tioga font set (often an unusual size requested), Gargoyle will fail to change the font. At any rate, Gargoyle will have to keep track of which fonts are in which camp.
SetFontDetailed. The user selects a <FontPrefix> <FontFamily-FontFace> <Transformation> triple. Gargoyle assumes the designSize and the storedSize can be derived from the <FontPrefix> and <FontFamily-FontFace>; if not, then designSize and storedSize are defaulted to 1.0. The user must provide a factored transformation. For example:
xerox/pressfonts/gacha-bi [r1: REAL, s: VEC, r2: REAL, t: VEC]
xerox/xc1-2-2/classic-b [r1: REAL, s: VEC, r2: REAL, t: VEC]
xerox/tiogafonts/oldEnglish12-b [r1: REAL, s: VEC, r2: REAL, t: VEC]
SetFontLiteral. The user selects a <FontPrefix> <FontFamily-FontFace> <Transformation> <storedSize> <design size> quintuple. Gargoyle accepts this as literal information and makes no attempt at understanding the semantics of the data. For example:
xerox/pressfonts/gacha-bir [r1: REAL, s: VEC, r2: REAL, t: VEC] 1.0 1.0
xerox/myfonts/myFavorite55 [r1: REAL, s: VEC, r2: REAL, t: VEC] 5.0 1.0
xerox/tiogafonts/oldEnglish12B [r1: REAL, s: VEC, r2: REAL, t: VEC] 12.0 12.0
Note: For screen fonts, we may run into trouble when <scalar>*(1.0/<scalar>) isn't quite 1.0. We can either round within epsilon for screen fonts, or we can keep around an unscaled font when we know we should be hitting the fast case.
Asking About Fonts
Internally, all fonts carry around values for <FontPrefix> <FontFamily-FontFace> <Transformation> <storedSize> <designSize>. There are two formats in which the user can request font information: Show and ShowLiteral. Show produces output which is suitable in format as input to SetFontDetailed; ShowLiteral produces output which is suitable in format as input to SetFontLiteral. For example:
xerox/pressfonts/helvetica-BI [r1: REAL, s: VEC, r2: REAL, t: VEC] -- Show
xerox/pressfonts/helvetica-bir [r1: REAL, s: VEC, r2: REAL, t: VEC] 1.0 1.0 -- ShowLiteral
xerox/xc1-2-2/modern-BI [r1: REAL, s: VEC, r2: REAL, t: VEC]  -- Show
xerox/xc1-2-2/modern-bold-italic [r1: REAL, s: VEC, r2: REAL, t: VEC] 1.0 1.0 -- ShowLiteral
An Error
ParseError: ERROR [explanation: Rope.ROPE];
The Font Cache
FlushFontCache: PROC;
Removes all fonts from Gargoyle's current font cache. This must be done if the font paths change or new fonts become available.
Creating a FontData
CreateFontData: PROC RETURNS [data: FontData];
CopyFontData: PROC [data: FontData, oldCopy: FontData ← NIL] RETURNS [newCopy: FontData];
If oldCopy#NIL, returns oldCopy with copied data inside ELSE creates a new copy.
Filling a FontData with values—default values, from textual descriptions, from a nameless font, from an alternate font.
InitFontData: PROC [data: FontData] RETURNS [newData: FontData];
Sets the fields of data to their definition defaults. (See GGModelTypes.FontData). data = newData (Why does this routine bother to return a value? Bier, September 10, 1987)
ParseFontData: PROC [data: FontData ← NIL, inStream: IO.STREAM, literalP, prefixP, familyP, faceP, transformP, scaleP, storedSizeP, designSizeP: BOOLFALSE] 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. Fills in as many of the newData fields as possible.
FontDataFromNamelessFont: PROC [font: ImagerFont.Font] RETURNS [newData: FontData];
Builds a FontData for a font defined in an interpress master that has no name.
AlternateFont: PROC [data: FontData, font: ImagerFont.Font, op: ATOM] RETURNS [alternate: ImagerFont.Font];
Takes an existing font and fontData and returns an alternate. Typical op is $visible.
Completing a FontData from some of its parts.
LiteralDataFromFontData: 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.
UserDataFromFontData: 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.
Describing a Font Textually
FontAsDetailedRope: PROC [font: FontData] RETURNS [detailed: Rope.ROPE];
FontPutDetailedStream: PROC [font: FontData, stream: IO.STREAM];
FontAsLiteralRope: PROC [font: FontData] RETURNS [literal: Rope.ROPE];
FontPutLiteralStream: PROC [font: FontData, stream: IO.STREAM];
For Compatibility
OldParseFontData: PROC [inStream: IO.STREAM, prefixP, familyP, faceP, transformP, sizeP: BOOLFALSE] RETURNS [fail: BOOL, prefix, family, face: Rope.ROPE, transform: ImagerTransformation.Transformation, size: REAL ← 0.0];
This proc needed to parse old style font data appearing in older GG files.
END.