<<>> <> <> <> <<>> DIRECTORY Atom USING [MakeAtom], Basics USING [LongNumber], Char, Imager USING [ConcatT, Context, Error, MaskRectangle], ImagerBackdoor USING [GetTransformation], ImagerBox USING [Extents], ImagerFDTypeface USING [CharInfo, CharInfoRun, CharInfoRunIndex, CharInfoRunRep, CharInfoRunSeq, CharInfoRunSeqRep, CharRuns, CharRunsRep, CharSet, Data, DataRep, OverflowCharInfoSeq, OverflowCharInfoSeqRep, password, RealSeq, RealSeqRep, TransformationSeq, TransformationSeqRep, TypefaceCell, TypefaceCellRep, TypefaceCellSeq, TypefaceCellSeqRep, TypefaceIndex, VerboseCharInfo, VerboseCharInfoRep], ImagerError USING [Error, ErrorDesc], ImagerFont USING [CorrectionType, Font, nullXChar, XChar], ImagerMaskCache USING [CharMask, Parameters], ImagerSys USING [StreamFileName], ImagerTransformation USING [Concat, Copy, Create, Destroy, Equal, Factor, FactoredTransformation, Invert, Scale, Transformation], ImagerTypeface USING [CreatorFromFileExtension, FindTypeface, MakeFont, RegisterCreator, Typeface, TypefaceClass, TypefaceClassRep, TypefaceFromFont, TypefaceRep, Version, VersionRep], IO USING [Close, EndOfStream, GetByte, GetIndex, GetRope, PutFR, STREAM], Prop USING [PropList], Real USING [FScale], RefTab USING [Create, EqualProc, HashProc, Update, UpdateAction], Rope USING [ROPE], RuntimeError USING [BoundsFault], Vector2 USING [VEC]; ImagerFDTypefaceImpl: CEDAR MONITOR IMPORTS Atom, Char, Imager, ImagerBackdoor, ImagerError, ImagerSys, ImagerTransformation, ImagerTypeface, IO, Real, RefTab, RuntimeError EXPORTS ImagerFDTypeface ~ BEGIN OPEN ImagerFDTypeface; ROPE: TYPE = Rope.ROPE; VEC: TYPE = Vector2.VEC; Transformation: TYPE = ImagerTransformation.Transformation; TransformationIndex: TYPE = NAT; Typeface: TYPE = ImagerTypeface.Typeface; XChar: TYPE = ImagerFont.XChar; nullXChar: XChar = ImagerFont.nullXChar; Extents: TYPE = ImagerBox.Extents; <> FDCreate: PROC [stream: IO.STREAM] RETURNS [Typeface] ~ { data: Data ~ FromStream[stream ! FormatError => { name: ROPE ~ ImagerSys.StreamFileName[stream]; ERROR Imager.Error[[$malformedFont, IO.PutFR["Font file %g is malformed (byte index = %g)", [rope[name]], [integer[byteIndex]]], LIST[[$name, name]]]] }]; IO.Close[stream]; RETURN[NEW[ImagerTypeface.TypefaceRep ¬ [class: fdClass, data: data]]]; }; GetRun: PROC [data: Data, char: XChar] RETURNS [REF CharRunsRep] = { set: CARD ~ Char.Set[char]; IF set > BYTE.LAST THEN RETURN [NIL]; FOR this: CharRuns ¬ data.setTable[set], this.link UNTIL this = NIL DO IF Char.Code[char] < this.bc THEN RETURN [NIL]; IF Char.Code[char] <= this.ec THEN RETURN [this]; ENDLOOP; RETURN [NIL] }; FDContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; RETURN [GetRun[data, char] # NIL]; }; FDNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ { data: Data ~ NARROW[self.data]; bump: CARDINAL = IF char = ImagerFont.nullXChar THEN 0 ELSE ORD[char]+1; next ¬ Char.Make[MIN[bump/256, 255], bump MOD 256]; DO FOR this: CharRuns ¬ data.setTable[Char.Set[next]], this.link UNTIL this = NIL DO IF Char.Code[next] < this.bc THEN {next ¬ Char.Make[Char.Set[next], this.bc]; RETURN}; IF Char.Code[next] <= this.ec THEN RETURN; ENDLOOP; IF Char.Set[next] = BYTE.LAST THEN RETURN [ImagerFont.nullXChar]; next ¬ Char.Make[Char.Set[next]+1, 0]; ENDLOOP; }; FetchCharInfo: PROC [data: Data, run: REF CharRunsRep, char: XChar] RETURNS [CharInfo] = { charInfoRun: CharInfoRun = FetchCharInfoRun[data, run]; RETURN [charInfoRun[MIN[Char.Code[char]-run.bc, charInfoRun.size-1]]]; }; FetchCharInfoRun: PROC [data: Data, run: REF CharRunsRep] RETURNS [CharInfoRun] = { RETURN [data.charInfoRunSeq[run[0].charInfoRunIndex]] }; FDEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { WITH FetchCharInfo[data, run, char] SELECT FROM info: CharInfo.short => { RETURN [[ x: data.commonEscapements[info.escapementX], y: data.commonEscapements[info.escapementY]]] }; info: CharInfo.verbose => { RETURN [data.overflowCharInfoSeq[info.overflowIndex].escapement] }; ENDCASE => ERROR; }; IF char # substituteChar THEN RETURN [FDEscapement[self, substituteChar]]; RETURN [[0.5, 0]]; }; FDAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { charInfoRun: CharInfoRun = FetchCharInfoRun[data, run]; RETURN [charInfoRun.bits.amplified] }; RETURN [FALSE]; }; FDCorrection: PROC [self: Typeface, char: XChar] RETURNS [ImagerFont.CorrectionType] ~ { data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { charInfoRun: CharInfoRun = FetchCharInfoRun[data, run]; RETURN [charInfoRun.bits.correction] }; RETURN [mask]; }; substituteChar: XChar ~ Char.Make[set: 360B, code: 312B]; FDBoundingBox: PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { WITH FetchCharInfo[data, run, char] SELECT FROM info: CharInfo.short => { RETURN [[ leftExtent: data.commonLeftExtents[info.leftExtent], rightExtent: data.commonRightExtents[info.rightExtent], descent: data.commonDescents[info.descent], ascent: data.commonAscents[info.ascent] ]] }; info: CharInfo.verbose => { RETURN [data.overflowCharInfoSeq[info.overflowIndex].extents] }; ENDCASE => ERROR; }; IF char # substituteChar THEN RETURN [FDBoundingBox[self, substituteChar]]; RETURN[[leftExtent: -0.05, rightExtent: 0.45, descent: 0, ascent: 0.6]]; }; FDFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; RETURN [data.fontExtents]; }; FDKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; RETURN [[0, 0]]; }; FDNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { data: Data ~ NARROW[self.data]; RETURN [nullXChar]; }; FDLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { data: Data ~ NARROW[self.data]; RETURN [nullXChar]; }; FDNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { data: Data ~ NARROW[self.data]; RETURN [nullXChar]; }; RawHash: UNSAFE PROC [p: LONG POINTER TO CARDINAL, bytes: INT] RETURNS [hash: CARDINAL ¬ 31415] = UNCHECKED { IF p # NIL THEN WHILE (bytes ¬ bytes - BYTES[WORD]) >= 0 DO hash ¬ hash + hash*128 + p­; p ¬ p + SIZE[WORD]; ENDLOOP; }; TransformationHash: RefTab.HashProc = TRUSTED { <<[key: RefTab.Key] RETURNS [CARDINAL]>> m: Transformation = NARROW[key]; IF m = NIL THEN RETURN [13] ELSE { a: ARRAY [0..6) OF REAL ¬ [ABS[m.a], ABS[m.b], ABS[m.c], ABS[m.d], ABS[m.e], ABS[m.f]]; p: LONG POINTER TO ARRAY [0..6) OF REAL = @a; hash: CARDINAL = RawHash[LOOPHOLE[p], BYTES[ARRAY [0..6) OF REAL]]; RETURN [hash] }; }; TransformationEqual: RefTab.EqualProc = { <<-- [key1, key2: Key] RETURNS [BOOL]>> a1: Transformation = NARROW[key1]; a2: Transformation = NARROW[key2]; RETURN [(a1=NIL AND a2=NIL) OR ImagerTransformation.Equal[a1, a2]] }; minScale: REAL ¬ 0.9875; maxScale: REAL ¬ 1.0125; TransformationIndexFromTransformation: PROC [data: Data, t: Transformation] RETURNS [index: TransformationIndex ¬ 0] ~ { Update: RefTab.UpdateAction = { <<[found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]>> IF found THEN { index ¬ NARROW[val, REF INT]­; ImagerTransformation.Destroy[t]; -- This was our copy, so it is OK to destroy it. } ELSE { closeMatch: BOOL ¬ FALSE; deviceToClient: Transformation ¬ ImagerTransformation.Invert[m: t]; FOR pass: NAT IN [0..1] UNTIL closeMatch DO Right: PROC [angle: REAL] RETURNS [BOOL] ~ { IF pass = 0 THEN RETURN [angle = 0.0]; WHILE angle < 0 DO angle ¬ angle + 90.0 ENDLOOP; WHILE angle > 45.0 DO angle ¬ angle - 90.0 ENDLOOP; RETURN [ABS[angle] <= 1.666667E-2] }; FOR i: NAT IN [0..data.charToDeviceTransformations.size) UNTIL closeMatch DO charToDevice: Transformation = data.charToDeviceTransformations[i]; IF charToDevice # NIL THEN { composite: Transformation ¬ ImagerTransformation.Concat[charToDevice, deviceToClient]; f: ImagerTransformation.FactoredTransformation ~ ImagerTransformation.Factor[composite]; ImagerTransformation.Destroy[composite]; composite ¬ NIL; IF Right[f.r1] AND Right[f.r2] AND ABS[f.s.x] IN [minScale..maxScale] AND ABS[f.s.y] IN [minScale..maxScale] THEN { closeMatch ¬ TRUE; index ¬ i; }; }; ENDLOOP; ENDLOOP; ImagerTransformation.Destroy[deviceToClient]; deviceToClient ¬ NIL; new ¬ NEW[INT ¬ index]; op ¬ store; -- Transformation t will be in the table, so DON'T DESTROY IT! }; }; t ¬ ImagerTransformation.Copy[t]; -- so we know who owns this storage RefTab.Update[x: data.transformationCache, key: t, action: Update]; }; SelectTransformationIndex: PROC [data: Data, context: Imager.Context] RETURNS [index: TransformationIndex ¬ 0] ~ { t: Transformation ¬ ImagerBackdoor.GetTransformation[context: context, from: client, to: device ! Imager.Error => GOTO Out]; index ¬ TransformationIndexFromTransformation[data: data, t: t]; ImagerTransformation.Destroy[t]; EXITS Out => NULL; }; identity: Transformation = ImagerTransformation.Scale[1]; InstantiateTypeface: PROC [typefaceCell: TypefaceCell] RETURNS [typeface: Typeface ¬ NIL]~ { Fetch: ENTRY PROC RETURNS [Typeface] ~ INLINE {RETURN [typefaceCell.typeface]}; Store: ENTRY PROC ~ INLINE { IF typefaceCell.typeface = NIL THEN typefaceCell.typeface ¬ typeface ELSE typeface ¬ typefaceCell.typeface }; IF typefaceCell = NIL THEN ERROR; -- can't happen; don't break with monitor held! typeface ¬ Fetch[]; IF typeface = NIL THEN { errorDesc: ImagerError.ErrorDesc ¬ [ok, NIL]; version: ImagerTypeface.Version = NEW[ImagerTypeface.VersionRep ¬ [createDate: typefaceCell.createDate, type: typefaceCell.type]]; typeface ¬ ImagerTypeface.FindTypeface[name: typefaceCell.fontName, substitution: $noSubstitute, versionHint: version ! ImagerError.Error => {errorDesc ¬ error; CONTINUE}]; -- try for exact version first. IF typeface = NIL THEN { <> typeface ¬ ImagerTypeface.FindTypeface[name: typefaceCell.fontName, substitution: $noSubstitute]; -- error if the constituent font is not there. }; Store[]; }; }; FDMask: PROC [self: Typeface, char: XChar, context: Imager.Context] ~ { data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { ti: TransformationIndex ¬ SelectTransformationIndex[data, context]; typefaceCell: TypefaceCell = data.typefaceCellSeq[run[ti].typefaceIndex]; charInfoRun: CharInfoRun = data.charInfoRunSeq[run[ti].charInfoRunIndex]; placement: Transformation = data.placementTransformations[charInfoRun.placementCode]; code: CARD = ORD[char] + charInfoRun.xcCodeDelta; typeface: Typeface ~ InstantiateTypeface[typefaceCell]; IF typeface = NIL THEN { Imager.MaskRectangle[context, [0.05, 0, 0.4, 0.6]]; RETURN }; IF NOT ImagerTransformation.Equal[placement, identity] THEN { Imager.ConcatT[context, placement]; }; typeface.class.Mask[typeface, VAL[code], context]; RETURN; }; IF char # substituteChar THEN FDMask[self, substituteChar, context] ELSE Imager.MaskRectangle[context, [0.05, 0, 0.4, 0.6]]; }; FDMakeCharMask: PROC [parameters: ImagerMaskCache.Parameters, font: ImagerFont.Font, char: XChar] RETURNS [ImagerMaskCache.CharMask ¬ NIL] ~ { self: Typeface ~ ImagerTypeface.TypefaceFromFont[font]; data: Data ~ NARROW[self.data]; run: REF CharRunsRep = GetRun[data, char]; IF run # NIL THEN { ti: TransformationIndex = TransformationIndexFromTransformation[data, font.charToClient]; typefaceCell: TypefaceCell = data.typefaceCellSeq[run[ti].typefaceIndex]; typeface: Typeface ~ InstantiateTypeface[typefaceCell]; IF typeface # NIL AND typeface.class.MakeCharMask # NIL THEN { charInfoRun: CharInfoRun = data.charInfoRunSeq[run[ti].charInfoRunIndex]; placement: Transformation = data.placementTransformations[charInfoRun.placementCode]; code: CARD = ORD[char] + charInfoRun.xcCodeDelta; charToClient: Transformation ¬ ImagerTransformation.Concat[placement, font.charToClient]; innerFont: ImagerFont.Font ~ ImagerTypeface.MakeFont[typeface, charToClient]; ImagerTransformation.Destroy[charToClient]; charToClient ¬ NIL; RETURN[typeface.class.MakeCharMask[parameters, innerFont, VAL[code]]]; }; }; }; fdClass: ImagerTypeface.TypefaceClass ~ NEW[ImagerTypeface.TypefaceClassRep ¬ [ type: $FD, Contains: FDContains, NextChar: FDNextChar, Escapement: FDEscapement, Amplified: FDAmplified, Correction: FDCorrection, BoundingBox: FDBoundingBox, FontBoundingBox: FDFontBoundingBox, Ligature: FDLigature, NextLigature: FDNextLigature, Kern: FDKern, NextKern: FDNextKern, Mask: FDMask, MakeCharMask: FDMakeCharMask ]]; <> FormatError: PUBLIC ERROR [diagnosis: ATOM, byteIndex: INT] = CODE; FromStream: PUBLIC PROC [stream: IO.STREAM] RETURNS [Data] = { data: Data = NEW[DataRep]; Inner: PROC = { nSets: NAT ¬ 0; IF GetCARD32[stream] # password THEN FormatError[$badFileTypeCode, 0]; data.fontExtents ¬ GetExtents[stream]; data.commonLeftExtents ¬ GetRealSeq[stream]; data.commonRightExtents ¬ GetRealSeq[stream]; data.commonDescents ¬ GetRealSeq[stream]; data.commonAscents ¬ GetRealSeq[stream]; data.commonEscapements ¬ GetRealSeq[stream]; data.charToDeviceTransformations ¬ GetTransformationSeq[stream]; data.typefaceCellSeq ¬ GetTypefaceCellSeq[stream]; data.placementTransformations ¬ GetTransformationSeq[stream]; data.charInfoRunSeq ¬ GetCharInfoRunSeq[stream, data.charToDeviceTransformations.size]; data.overflowCharInfoSeq ¬ GetOverflowCharInfoSeq[stream]; data.propList ¬ GetPropList[stream]; nSets ¬ GetCount[stream]; FOR i: NAT IN [0..nSets) DO c: CharSet = GetCount[stream]; data.setTable[c] ¬ GetCharRuns[stream, data.charToDeviceTransformations.size]; ENDLOOP; <> }; data.transformationCache ¬ RefTab.Create[equal: TransformationEqual, hash: TransformationHash]; Inner[ ! IO.EndOfStream => FormatError[$unexpectedEndOfFile, IO.GetIndex[stream]]; RuntimeError.BoundsFault => FormatError[$rangeError, IO.GetIndex[stream]]; ]; RETURN [data] }; GetExtents: PROC [stream: IO.STREAM] RETURNS [ImagerBox.Extents] = { extents: ImagerBox.Extents; extents.leftExtent ¬ GetREAL[stream]; extents.rightExtent ¬ GetREAL[stream]; extents.descent ¬ GetREAL[stream]; extents.ascent ¬ GetREAL[stream]; RETURN [extents] }; GetRealSeq: PROC [stream: IO.STREAM] RETURNS [s: RealSeq] = { size: NAT = GetCount[stream]; s ¬ NEW[RealSeqRep[size]]; FOR i: NAT IN [0..s.size) DO s[i] ¬ GetREAL[stream]; ENDLOOP; }; GetTransformationSeq: PROC [stream: IO.STREAM] RETURNS [s: TransformationSeq] = { size: NAT = GetCount[stream]; s ¬ NEW[TransformationSeqRep[size]]; FOR i: NAT IN [0..s.size) DO s[i] ¬ GetTransformation[stream]; ENDLOOP; }; GetTransformation: PROC [stream: IO.STREAM] RETURNS [Transformation] = { count : NAT = GetCount[stream]; IF count = 0 THEN RETURN [NIL] ELSE { check: [6..6] = count; t: ARRAY [0..6) OF REAL; FOR i: NAT IN [0..6) DO t[i] ¬ GetREAL[stream]; ENDLOOP; RETURN [ImagerTransformation.Create[a: t[0], b: t[1], c: t[2], d: t[3], e: t[4], f: t[5]]] }; }; GetTypefaceCellSeq: PROC [stream: IO.STREAM] RETURNS [s: TypefaceCellSeq] = { size: NAT = GetCount[stream]; s ¬ NEW[TypefaceCellSeqRep[size]]; FOR i: NAT IN [0..s.size) DO s[i] ¬ GetTypefaceCell[stream]; ENDLOOP; }; GetCharInfoRunSeq: PROC [stream: IO.STREAM, nt: NAT] RETURNS [s: CharInfoRunSeq] = { size: NAT = GetCount[stream]; s ¬ NEW[CharInfoRunSeqRep[size]]; FOR i: NAT IN [0..s.size) DO s[i] ¬ GetCharInfoRun[stream, nt]; ENDLOOP; }; GetTypefaceCell: PROC [stream: IO.STREAM] RETURNS [TypefaceCell] = { s: TypefaceCell ¬ NEW[TypefaceCellRep]; s.createDate ¬ LOOPHOLE[GetCARD32[stream]]; s.fontName ¬ GetIdentifier[stream]; s.type ¬ Atom.MakeAtom[GetIdentifier[stream]]; RETURN [s] }; GetOverflowCharInfoSeq: PROC [stream: IO.STREAM] RETURNS [s: OverflowCharInfoSeq] = { size: NAT = GetCount[stream]; s ¬ NEW[OverflowCharInfoSeqRep[size]]; FOR i: NAT IN [0..s.size) DO s[i] ¬ GetVerboseCharInfo[stream]; ENDLOOP; }; GetVerboseCharInfo: PROC [stream: IO.STREAM] RETURNS [VerboseCharInfo] = { s: VerboseCharInfo ¬ NEW[VerboseCharInfoRep]; s.extents ¬ GetExtents[stream]; s.escapement ¬ GetVEC[stream]; s.propList ¬ GetPropList[stream]; RETURN [s] }; GetVEC: PROC [stream: IO.STREAM] RETURNS [v: VEC] = { v.x ¬ GetREAL[stream]; v.y ¬ GetREAL[stream]; }; GetPropList: PROC [stream: IO.STREAM] RETURNS [Prop.PropList] = { props: Prop.PropList ¬ NIL; count: NAT = GetCount[stream]; IF count # 0 THEN ERROR; -- unimplemented RETURN [props] }; GetCharInfoRun: PROC [stream: IO.STREAM, nt: NAT] RETURNS [CharInfoRun] = { xcCodeDelta: INT = GetInteger[stream]; bits: BYTE = GetBYTE[stream]; placementCode: CARD = GetCount[stream]; size: NAT = GetCount[stream]; charInfoRun: CharInfoRun = NEW[CharInfoRunRep[size]]; charInfoRun.xcCodeDelta ¬ xcCodeDelta; charInfoRun.bits.amplified ¬ VAL[bits MOD 2]; charInfoRun.bits.correction ¬ VAL[bits/2]; charInfoRun.placementCode ¬ placementCode; FOR i: NAT IN [0..size) DO byte0: BYTE = GetBYTE[stream]; IF byte0 = 255 THEN { info: CharInfo.verbose = [verbose[overflowIndex: GetCount[stream]]]; TRUSTED {charInfoRun[i] ¬ info}; } ELSE { info: CharInfo.short ¬ [short[0, 0, 0, 0, 0, 0]]; info.leftExtent ¬ byte0; info.rightExtent ¬ GetBYTE[stream]; info.descent ¬ GetBYTE[stream]; info.ascent ¬ GetBYTE[stream]; info.escapementX ¬ GetBYTE[stream]; info.escapementY ¬ GetBYTE[stream]; TRUSTED {charInfoRun[i] ¬ info}; }; ENDLOOP; RETURN [charInfoRun] }; GetCharRuns: PROC [stream: IO.STREAM, nt: NAT] RETURNS [charRuns: CharRuns ¬ NIL] = { last: CharRuns ¬ NIL; count: NAT = GetCount[stream]; nextOKbc: NAT ¬ 0; FOR i: NAT IN [0..count) DO bc: BYTE = GetBYTE[stream]; ec: BYTE = GetBYTE[stream]; new: CharRuns = NEW[CharRunsRep[nt]]; IF ec < bc OR bc < nextOKbc THEN FormatError[$charsOutOfOrder, IO.GetIndex[stream]]; new.bc ¬ bc; new.ec ¬ ec; nextOKbc ¬ NAT[ec]+1; FOR i: NAT IN [0..nt) DO typefaceIndex: TypefaceIndex ¬ GetCount[stream]; charInfoRunIndex: CharInfoRunIndex ¬ GetCount[stream]; new[i] ¬ [typefaceIndex: typefaceIndex, charInfoRunIndex: charInfoRunIndex]; ENDLOOP; IF last = NIL THEN charRuns ¬ last ¬ new ELSE last ¬ last.link ¬ new; ENDLOOP; RETURN [charRuns]; }; <> GetCARD32: PROC [stream: IO.STREAM] RETURNS [CARD32] = { <> ln: Basics.LongNumber ¬ [lc[0]]; ln.hh ¬ IO.GetByte[stream]; ln.hl ¬ IO.GetByte[stream]; ln.lh ¬ IO.GetByte[stream]; ln.ll ¬ IO.GetByte[stream]; RETURN [ln.lc] }; GetBYTE: PROC [stream: IO.STREAM] RETURNS [BYTE] = { RETURN [IO.GetByte[stream]]; }; GetCount: PROC [stream: IO.STREAM] RETURNS [count: CARD ¬ 0] = { <> <> <> <> b: BYTE ¬ IO.GetByte[stream]; IF b < 128 THEN RETURN [b]; WHILE b >= 128 DO count ¬ count * 128 + (b-128); b ¬ IO.GetByte[stream]; ENDLOOP; count ¬ count * 128 + b; }; GetIdentifier: PROC [stream: IO.STREAM] RETURNS [ROPE] = { size: INT = GetCount[stream]; rope: ROPE = IO.GetRope[self: stream, len: size, demand: TRUE]; RETURN [rope] }; GetInteger: PROC [stream: IO.STREAM] RETURNS [INT] = { <> <> <<(i.e., uses the low-order bit to indicate the sign)>> card: CARD ¬ GetCount[stream]; IF card MOD 2 = 1 THEN RETURN [-INT[(card+1)/2]] ELSE RETURN [INT[card/2]]; }; GetREAL: PROC [stream: IO.STREAM] RETURNS [REAL] = { <> exponent, significand: INT; significand ¬ GetInteger[stream]; exponent ¬ GetInteger[stream]; RETURN [Real.FScale[significand, exponent]]; }; <> ImagerTypeface.RegisterCreator[creator: ImagerTypeface.CreatorFromFileExtension["FD", FDCreate], before: TRUE]; END.