DIRECTORY Char, Imager USING [Context, MaskBitmap, ScaleT], ImagerFont USING [CorrectionType, Extents, nullXChar, XChar], ImagerSample USING [Clear, Fill, ObtainScratchMap, ReleaseScratchMap, SampleMap], ImagerTypeface USING [CreatorFromFileExtension, RegisterCreator, Typeface, TypefaceClass, TypefaceClassRep, TypefaceRep], IO USING [EndOf, EndOfStream, Error, GetBlock, GetChar, GetID, GetIndex, GetLength, int, SetIndex, STREAM, TIS, Value], RefText USING [New], Rope USING [ROPE], Vector2 USING [VEC]; ImagerGFTypefaceImpl: CEDAR MONITOR LOCKS NARROW[self.data, Data] USING self: ImagerTypeface.Typeface IMPORTS Char, Imager, ImagerSample, ImagerTypeface, IO, RefText ~ BEGIN FormatError: ERROR [explanation: Rope.ROPE, v1, v2: IO.Value ¬ [null[]]] = CODE; XChar: TYPE ~ ImagerFont.XChar; CorrectionType: TYPE ~ ImagerFont.CorrectionType; Extents: TYPE ~ ImagerFont.Extents; SampleMap: TYPE ~ ImagerSample.SampleMap; Typeface: TYPE ~ ImagerTypeface.Typeface; TypefaceClass: TYPE ~ ImagerTypeface.TypefaceClass; TypefaceClassRep: TYPE ~ ImagerTypeface.TypefaceClassRep; TypefaceRep: TYPE ~ ImagerTypeface.TypefaceRep; VEC: TYPE ~ Vector2.VEC; nullXChar: XChar ~ ImagerFont.nullXChar; Data: TYPE ~ REF DataRep; -- Private data for instances of this Typeface class DataRep: TYPE ~ MONITORED RECORD [ stream: IO.STREAM, -- SHARED open stream on the GF file. Users of this must be ENTRYs charPtrs: ARRAY BYTE OF INT ¬ ALL[-1], -- Character positions in GF file, -1 if not present widths: ARRAY BYTE OF REAL ¬ ALL[0.0], -- Character widths in design-size units fontBB: Extents, -- Bounding box of the whole font (not precise), in design-size units bitsPerEm: REAL -- Number of pixels in a single design-size unit ]; Ord: PROC [ch: CHAR] RETURNS [BYTE] = INLINE { RETURN[ch - 0C]; }; GetByte: PROC [gfStream: IO.STREAM] RETURNS [BYTE] = INLINE { IF gfStream.EndOf[] THEN RETURN[0] ELSE RETURN[Ord[gfStream.GetChar[]]]; }; GetTwoBytes: PROC [gfStream: IO.STREAM] RETURNS [WORD] = { a: BYTE ¬ GetByte[gfStream]; RETURN[a * 256 + GetByte[gfStream]]; }; GetThreeBytes: PROC [gfStream: IO.STREAM] RETURNS [INT] = { a: INT ¬ GetByte[gfStream]; b: BYTE ¬ GetByte[gfStream]; RETURN[(a * 256 + b) * 256 + GetByte[gfStream]]; }; SignedQuad: PROC [gfStream: IO.STREAM] RETURNS [INT] = { a: INT ¬ GetByte[gfStream]; b: BYTE ¬ GetByte[gfStream]; c: BYTE ¬ GetByte[gfStream]; IF a < 128 THEN RETURN[((a * 256 + b) * 256 + c) * 256 + GetByte[gfStream]] ELSE RETURN[(((a - 256) * 256 + b) * 256 + c) * 256 + GetByte[gfStream]]; }; paint0: BYTE = 0; -- Beginning of the paint commands paint1: BYTE = 64; -- Move right a given # of columns, then black f white boc: BYTE = 67; -- Beginning of a character boc1: BYTE = 68; -- Abbreviated boc eoc: BYTE = 69; -- End of a character skip0: BYTE = 70; -- Skip no blank rows skip1: BYTE = 71; -- Skip over blank rows newRow0: BYTE = 74; -- Move down one row and then right xxx1: BYTE = 239; -- For special strings yyy: BYTE = 243; -- For numspecial numbers nop: BYTE = 244; -- No operation charLoc: BYTE = 245; -- Character locators in the postamble pre: BYTE = 247; -- Preamble post: BYTE = 248; -- Postamble beginning postpost: BYTE = 249; -- Postamble ending IDByte: BYTE = 131; -- Identifies the kind of GF files described here fillerByte: BYTE = 223; -- Padding bytes at very end of file StartOp: PROC [gfStream: IO.STREAM] RETURNS [op: BYTE -- Current opcode --, par: INT -- First parameter to current opcode --] = { op ¬ GetByte[gfStream]; SELECT op FROM IN [paint0..paint1) => par ¬ op - paint0; IN [newRow0..newRow0+164] => par ¬ op - newRow0; boc1, paint1, skip1, charLoc, charLoc + 1, xxx1 => par ¬ GetByte[gfStream]; paint1 + 1, skip1 + 1, xxx1 + 1 => par ¬ GetTwoBytes[gfStream]; paint1 + 2, skip1 + 2, xxx1 + 2 => par ¬ GetThreeBytes[gfStream]; boc, xxx1 + 3, yyy => par ¬ SignedQuad[gfStream]; ENDCASE => par ¬ 0; }; LoadGF: PROC [gfStream: IO.STREAM] RETURNS [font: Data] = { ReadPreamble: PROC = { byte: BYTE; IF (byte ¬ GetByte[gfStream]) # pre THEN ERROR FormatError["First byte is %g, not start of preamble.", IO.int[byte]]; IF (byte ¬ GetByte[gfStream]) # IDByte THEN ERROR FormatError["Identification byte is %g, should be %g.", IO.int[byte], IO.int[IDByte]]; }; FindPostamble: PROC ~ { byte: BYTE; FOR index: INT ¬ gfStream.GetLength[] - 1, index - 1 DO gfStream.SetIndex[index]; SELECT (byte ¬ GetByte[gfStream]) FROM fillerByte => NULL; IDByte => { gfStream.SetIndex[index - 4]; EXIT; }; ENDCASE => ERROR FormatError["Strange byte (%g) at end of file.", IO.int[byte]]; ENDLOOP; gfStream.SetIndex[SignedQuad[gfStream]]; IF (byte ¬ GetByte[gfStream]) # post THEN ERROR FormatError["Postamble byte isn't that, it's %g.", IO.int[byte]]; }; ReadPostamble: PROC = { op: BYTE; -- Current opcode par: INT; -- First parameter to that opcode designSize: INT; -- Design size in points * 2­20 hppp: INT; -- Horizontal pixels per point * 2­16. [] ¬ SignedQuad[gfStream]; -- Skip back pointer designSize ¬ SignedQuad[gfStream]; [] ¬ SignedQuad[gfStream]; -- Skip checksum hppp ¬ SignedQuad[gfStream]; [] ¬ SignedQuad[gfStream]; -- Skip vppp font.bitsPerEm ¬ (designSize / (65536.0 * 16.0)) * (hppp / 65536.0); font.fontBB.leftExtent ¬ - SignedQuad[gfStream] / font.bitsPerEm; -- min m font.fontBB.rightExtent ¬ SignedQuad[gfStream] / font.bitsPerEm; -- max m font.fontBB.descent ¬ - SignedQuad[gfStream] / font.bitsPerEm; -- min n font.fontBB.ascent ¬ SignedQuad[gfStream] / font.bitsPerEm; -- max n DO -- Read the charLoc commands for the character widths and positions [op: op, par: par] ¬ StartOp[gfStream]; SELECT op FROM charLoc => { -- Character locator [] ¬ SignedQuad[gfStream]; -- Skip escapement ``dx'' [] ¬ SignedQuad[gfStream]; -- Skip escapement ``dy'' font.widths[par] ¬ SignedQuad[gfStream] / (65536.0 * 16.0); -- TFM width font.charPtrs[par] ¬ SignedQuad[gfStream]; -- File pointer }; charLoc + 1 => { -- Abbreviated charLoc [] ¬ GetByte[gfStream]; -- Skip escapement abbreviation ``dm'' font.widths[par] ¬ SignedQuad[gfStream] / (65536.0 * 16.0); -- TFM width font.charPtrs[par] ¬ SignedQuad[gfStream]; -- File pointer }; nop => NULL; postpost => EXIT; ENDCASE => ERROR FormatError["Funny opcode (%g) in postamble at pos. %g.", IO.int[op], IO.int[gfStream.GetIndex[]]]; ENDLOOP; }; font ¬ NEW[DataRep]; font.stream ¬ gfStream; ReadPreamble[]; FindPostamble[]; ReadPostamble[]; }; GetBOC: INTERNAL PROC [gfStream: IO.STREAM] RETURNS [code: BYTE, mMin, mMax, nMin, nMax: INT] = { op: BYTE; par: INT; DO -- skip the nop's and specials in front of the character [op: op, par: par] ¬ StartOp[gfStream]; SELECT op FROM boc => { code ¬ par MOD 256; [] ¬ SignedQuad[gfStream]; -- Skip back pointer mMin ¬ SignedQuad[gfStream]; mMax ¬ SignedQuad[gfStream]; nMin ¬ SignedQuad[gfStream]; nMax ¬ SignedQuad[gfStream]; EXIT; }; boc1 => { mDel, nDel : INT; -- Deltas for abbreviating [mn]Min code ¬ par; mDel ¬ GetByte[gfStream]; mMax ¬ GetByte[gfStream]; nDel ¬ GetByte[gfStream]; nMax ¬ GetByte[gfStream]; mMin ¬ mMax - mDel; nMin ¬ nMax - nDel; EXIT; }; nop, yyy => NULL; IN [xxx1..xxx1+3] => ProcessSpecial[gfStream, par]; ENDCASE => { ERROR FormatError["Byte %g is not BOC (%g)", IO.int[gfStream.GetIndex[] - 1], IO.int[op]]; }; ENDLOOP; }; ProcessSpecial: INTERNAL PROC [gfStream: IO.STREAM, len: NAT] = { text: REF TEXT ¬ RefText.New[nChars: len]; specStream: IO.STREAM; operation: Rope.ROPE; IF gfStream.GetBlock[block: text, count: len] # len THEN ERROR FormatError["File not long enough to hold stated length of a SPECIAL text; length=%g", IO.int[len]]; specStream ¬ IO.TIS[text]; operation ¬ specStream.GetID[ ! IO.EndOfStream, IO.Error => GOTO GiveUp ]; SELECT TRUE FROM ENDCASE => NULL; EXITS GiveUp => NULL; }; GetRaster: INTERNAL PROC [data: Data, code: BYTE] RETURNS [pm: SampleMap] ~ { mMin, mMax, nMin, nMax : INT; -- Bounding box m, n: INT; -- GF abstract machine registers paintSwitch: BOOLEAN; -- TRUE means black op: BYTE; par: INT; data.stream.SetIndex[data.charPtrs[code]]; [mMin: mMin, mMax: mMax, nMin: nMin, nMax: nMax] ¬ GetBOC[data.stream]; pm ¬ ImagerSample.ObtainScratchMap[box: [[-nMax, mMin], [1-nMin, mMax+1]]]; ImagerSample.Clear[pm]; m ¬ mMin; -- Initialise GF abstract machine state n ¬ nMax; paintSwitch ¬ FALSE; DO [op: op, par: par] ¬ StartOp[data.stream]; SELECT op FROM IN [paint0..paint1 + 2] => { -- Paint par pixels IF paintSwitch AND par > 0 THEN ImagerSample.Fill[map: pm, box: [[-n, m], [1-n, par+m]], value: 1]; m ¬ m + par; paintSwitch ¬ NOT paintSwitch; }; IN [newRow0..newRow0+164] => { -- Move on to the next row m ¬ mMin + par; n ¬ n - 1; paintSwitch ¬ TRUE; }; IN [skip0..skip1 + 2] => { -- Skip some empty rows n ¬ n - (par + 1); m ¬ mMin; paintSwitch ¬ FALSE; }; nop => NULL; -- Do nothing pre => -- Bad preamble command ERROR FormatError["Preamble command within a character at pos. %g.", IO.int[data.stream.GetIndex[] - 1]]; post, postpost => -- Bad postamble command ERROR FormatError["Postamble command within a character at pos. %g.", IO.int[data.stream.GetIndex[] - 1]]; charLoc, charLoc + 1 => -- Bad character locator ERROR FormatError["charLoc command within a character at pos. %g.", IO.int[data.stream.GetIndex[] - 1]]; boc => -- Bad beginning of character ERROR FormatError["BOC command within a character at pos. %g.", IO.int[data.stream.GetIndex[] - 1]]; eoc => -- End of character EXIT; IN [xxx1..xxx1 + 3] => -- special string (process it) ProcessSpecial[data.stream, par]; yyy => -- numspecial number (ignore it) NULL; ENDCASE => -- Unknown opcode ERROR FormatError["Undefined Opcode %g at pos. %g.", IO.int[op], IO.int[data.stream.GetIndex[] - 1]]; ENDLOOP; }; GFCreate: PROC [stream: IO.STREAM] RETURNS [Typeface] ~ { data: Data ~ LoadGF[stream]; RETURN[NEW[TypefaceRep ¬ [class: gfClass, data: data]]]; }; GFContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; RETURN [Char.Set[char] = 0 AND data.charPtrs[Char.Code[char]] # -1]; }; GFNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ { data: Data ~ NARROW[self.data]; start: BYTE; IF char=nullXChar THEN start ¬ 0 ELSE IF Char.Set[char] = 0 AND Char.Code[char] < LAST[BYTE] THEN start ¬ Char.Code[char] + 1 ELSE RETURN[nullXChar]; FOR code: BYTE IN [start..LAST[BYTE]] DO IF data.charPtrs[code] # -1 THEN RETURN[Char.Make[set: 0, code: code]]; ENDLOOP; RETURN[nullXChar]; }; GFEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; IF Char.Set[char] = 0 THEN RETURN [[data.widths[Char.Code[char]], 0]] ELSE RETURN [[0, 0]] }; GFAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { RETURN [FALSE]; }; GFCorrection: PROC [self: Typeface, char: XChar] RETURNS [CorrectionType] ~ { RETURN [mask]; }; GFBoundingBox: ENTRY PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; IF Char.Set[char] = 0 AND data.charPtrs[Char.Code[char]] # -1 THEN { mMin, mMax, nMin, nMax: INT; data.stream.SetIndex[data.charPtrs[Char.Code[char]]]; [mMin: mMin, mMax: mMax, nMin: nMin, nMax: nMax] ¬ GetBOC[data.stream]; RETURN [[ leftExtent: - mMin / data.bitsPerEm, rightExtent: mMax / data.bitsPerEm, descent: - nMin / data.bitsPerEm, ascent: nMax / data.bitsPerEm ]]; } ELSE RETURN [[0, 0, 0, 0]]; }; GFFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; RETURN [data.fontBB]; }; GFKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { RETURN [[0, 0]]; }; GFNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; GFLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; GFNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; GFMask: ENTRY PROC [self: Typeface, char: XChar, context: Imager.Context] ~ { data: Data ~ NARROW[self.data]; IF Char.Set[char] = 0 AND data.charPtrs[Char.Code[char]] # -1 THEN { pm: SampleMap ¬ GetRaster[data, Char.Code[char]]; Imager.ScaleT[context, 1.0/data.bitsPerEm]; Imager.MaskBitmap[context: context, bitmap: pm]; ImagerSample.ReleaseScratchMap[pm]; pm ¬ NIL; }; }; gfClass: TypefaceClass ~ NEW[TypefaceClassRep ¬ [ type: $GF, Contains: GFContains, NextChar: GFNextChar, Escapement: GFEscapement, Amplified: GFAmplified, Correction: GFCorrection, BoundingBox: GFBoundingBox, FontBoundingBox: GFFontBoundingBox, Ligature: GFLigature, NextLigature: GFNextLigature, Kern: GFKern, NextKern: GFNextKern, Mask: GFMask ]]; ImagerTypeface.RegisterCreator[ImagerTypeface.CreatorFromFileExtension["GF", GFCreate]]; END. ξ ImagerGFTypefaceImpl.mesa Copyright Σ 1985, 1986, 1987, 1988, 1991 by Xerox Corporation. All rights reserved. Michael Plass, October 10, 1991 1:39 pm PDT Pavel, February 24, 1986 3:55:13 pm PST Symbolic names for some GF opcodes and quantities: Beginning of LoadGF An XXXn opcode has just been read. The next len characters of input are the text of the special command. This code is a boilerplate for future use; we currently ignore all specials. Rope.Equal[operation, "foo"] => {}; Κͺ•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ΟeœI™TKšœ+™+Kšœ'™'K™—šΟk ˜ K˜Kšœžœ˜+Kšœ žœ-˜=Kšœ žœ?˜QKšœžœe˜yKšžœžœ[žœžœ ˜wKšœžœ˜Kšœžœžœ˜šœžœžœ˜K˜——KšΠlnœžœž˜#Kšžœžœžœ˜AKšžœ-žœ ˜?šœž˜K˜Kš Οn œžœžœ žœžœ˜PK˜Kšœžœ˜Kšœžœ˜1Kšœ žœ˜#Kšœ žœ˜)Kšœ žœ˜)Kšœžœ ˜3Kšœžœ#˜9Kšœ žœ˜/Kšžœžœ žœ˜K˜K˜(K˜Kšœžœžœ Οc4˜Nšœ žœž œžœ˜"Kšœžœžœ‘C˜VKš œ žœžœžœžœžœ‘4˜[Kš œžœžœžœžœžœ‘(˜OKšœ‘E˜WKšœ žœ‘0˜AK˜—K˜Kš œžœžœžœžœžœžœ ˜BK˜š œžœ žœžœžœžœžœ˜=Kšžœž˜Kšžœ˜ Kšž˜Kšžœ˜ K˜—K˜š   œžœ žœžœžœžœ˜:Kšœžœ˜Kšžœ˜$K˜—K˜š   œžœ žœžœžœžœ˜;Kšœžœ˜Kšœžœ˜Kšžœ*˜0K˜—K˜š   œžœ žœžœžœžœ˜8Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœ ž˜Kšžœ5˜;Kšž˜Kšžœ>˜DK˜—K˜Kšœ2™2Kšœžœ‘"˜4Kšœžœ‘6˜IKšœžœ‘˜+Kšœžœ‘˜#Kšœžœ‘˜%Kšœžœ‘˜'Kšœžœ‘˜)Kšœ žœ‘#˜7Kšœžœ‘˜(Kšœžœ‘˜*Kšœžœ‘˜ Kšœ žœ‘&˜;Kšœžœ‘ ˜Kšœžœ‘˜(Kšœ žœ‘˜)K˜Kš œžœ‘1˜EKšœ žœ‘$˜‘˜Fšžœ‘C˜FK˜'šžœž˜šœ ‘˜!Kšœ‘˜4Kšœ‘˜4Kšœ<‘ ˜IKšœ+‘˜:K˜—šœ‘˜'Kšœ‘&˜?Kšœ<‘ ˜IKšœ+‘˜:K˜—Kšœžœ˜ Kšœ žœ˜Kšžœ˜ —šžœ:˜?Kšžœ žœ˜)—Kšžœ˜—K˜—Kšœ™Kšœžœ ˜K˜K˜K˜K˜K˜—K˜š œžœžœ žœžœžœžœžœ˜aKšœžœ˜ Kšœžœ˜ šžœ‘8˜;K˜'šžœž˜˜Kšœ žœ˜Kšœ‘˜0K˜K˜K˜K˜Kšžœ˜K˜—˜ Kšœ žœ‘"˜4K˜K˜ K˜K˜K˜K˜K˜K˜Kšžœ˜K˜—Kšœ žœ˜Kšžœ˜K˜šžœ˜ šžœ'˜,Kšžœžœ ˜-—K˜——Kšžœ˜—K˜—K˜š  œžœžœ žœžœžœ˜AKšœ·™·Kšœžœžœ˜*Kšœ žœžœ˜Kšœžœ˜Kšžœ2ž˜8KšžœXžœ ˜jKšœ žœžœ˜˜Kšœžœžœ ˜Kšžœ˜ K˜—šžœžœž˜Kšœ#™#Kšžœžœ˜—Kšž˜Kšœ žœ˜K˜—K˜š   œžœžœžœžœ˜MKšœžœ‘˜-Kšœžœ‘ ˜-Kšœ žœ‘˜*Kšœžœ˜ Kšœžœ˜ K˜*K˜GK˜KK˜Kšœ ‘'˜1K˜ Kšœžœ˜šž˜K˜*šžœž˜šžœ‘˜0Kšžœžœ ž˜#K˜CK˜ Kšœžœ ˜K˜—šžœ‘˜9K˜K˜ Kšœžœ˜K˜—šžœ‘˜2K˜K˜ Kšœžœ˜K˜—Kšœžœ‘ ˜šœ ‘˜ Kšžœ@žœ"˜i—šœ‘˜+KšžœAžœ"˜j—šœ‘˜1Kšžœ?žœ"˜h—šœ ‘˜&Kšžœ;žœ"˜d—šœ ‘˜Kšžœ˜—šžœ‘˜6K˜!—šœ ‘ ˜)Kšžœ˜—šžœ‘˜šžœ/˜4Kšžœ žœ"˜0———Kšžœ˜—K˜—K˜K˜š œžœ ž œžœ˜9K˜Kšžœžœ.˜8K˜—K˜š  œžœžœžœ˜AKšœ žœ ˜Kšžœžœ'˜FK˜—K˜š  œžœžœ˜HKšœ žœ ˜Kšœžœ˜ šžœ˜Kšžœ ˜Kš žœžœžœžœžœžœ˜\Kšž œ ˜—š žœžœžœ žœžœž˜(Kšžœž œ ˜GKšžœ˜—Kšžœ ˜K˜—K˜š  œžœžœžœ˜BKšœ žœ ˜šžœ˜Kšžœžœ$˜/Kšžœžœ ˜—K˜—K˜š  œžœžœžœ˜BKšžœžœ˜K˜—K˜š  œžœžœ˜MKšžœ˜K˜—K˜š  œžœžœžœ˜MKšœ žœ ˜šžœžœ%˜?šžœ˜Kšœžœ˜K˜5K˜Gšžœ˜ K˜$K˜#K˜!K˜K˜—K˜—Kšžœžœ˜—K˜—K˜š œžœžœ˜>Kšœ žœ ˜Kšžœ˜K˜—K˜š œžœ*žœžœ˜GKšžœ ˜K˜—K˜š  œžœ*žœ ˜MKšžœ ˜K˜—K˜š  œžœ*žœ ˜MKšžœ ˜K˜—K˜š œžœ*žœ ˜QKšžœ ˜K˜—K˜š œžœžœ;˜MKšœ žœ ˜šžœžœ&žœ˜FK˜1K˜+K˜0Kšœ)žœ˜-K˜—K˜—K˜šœžœ˜1K˜ Kš œ ˜Kš œ ˜Kš  œ˜Kš  œ˜Kš  œ˜Kš  œ˜Kš œ˜#Kš œ ˜Kš  œ˜Kš œ ˜ Kš œ ˜Kš œ˜ K˜—K˜KšœX˜XK˜—Kšžœ˜—…—1NAζ