<> <> <> DIRECTORY Font, Real USING [MinusZero, SqRt], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [FromRefText, ROPE], Tfm, UFFileManager USING [FontFile, GetData, InBounds, InitProc, Open, Pointer], UFTfmReader; UFTfmReaderImpl: CEDAR PROGRAM IMPORTS Font, Real, RefText, Rope, Tfm, UFFileManager EXPORTS UFTfmReader = BEGIN OPEN UFTfmReader; TwoToTheTenth: INT = 2*2*2*2*2 * 2*2*2*2*2; TwoToTheTwentieth: INT = TwoToTheTenth*TwoToTheTenth; RealTwoToTheTwentieth: REAL = TwoToTheTwentieth; FixedToReal: PROCEDURE [x: Tfm.Fixed] RETURNS [REAL] = {RETURN[Tfm.Long[x]/RealTwoToTheTwentieth]}; Ord: PROCEDURE [c: CHAR] RETURNS [[0..256)] = INLINE {RETURN[LOOPHOLE[c]]}; Data: TYPE = REF DataRec; DataRec: TYPE = RECORD [ characterCodingScheme: Rope.ROPE, family: Rope.ROPE, dirPtr: LONG POINTER TO Tfm.Directory, headerPtr: LONG POINTER TO Tfm.Header, fInfoDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.FInfoEntry, widthDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.Fixed, heightDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.Fixed, depthDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.Fixed, charIcDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.Fixed, ligKernDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.LigKernEntry, kernDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.Fixed, extenDesc: LONG DESCRIPTOR FOR ARRAY OF Tfm.ExtRecipe, paramPtr: LONG POINTER TO Tfm.Param ]; OpenInit: UFFileManager.InitProc = TRUSTED { base: LONG POINTER _ fontFile.Pointer[]; dir: LONG POINTER TO Tfm.Directory _ base; data: Data _ NEW[DataRec]; curPtr: LONG POINTER _ base; Next: UNSAFE PROC [size: NAT] RETURNS [p: LONG POINTER] = UNCHECKED {p _ curPtr; curPtr _ curPtr + size}; s: NAT = SIZE[Tfm.Word]; IF NOT fontFile.InBounds[dir, SIZE[Tfm.Directory]] THEN ERROR Font.BadFontFileFormat; IF NOT Tfm.ValidDirectory[dir^] THEN ERROR Font.BadFontFileFormat; IF NOT fontFile.InBounds[base, dir.lf] THEN ERROR Font.BadFontFileFormat; data.dirPtr _ Next[SIZE[Tfm.Directory]]; data.headerPtr _ Next[s*dir.lh]; data.fInfoDesc _ DESCRIPTOR[Next[s*(dir.ec-dir.bc+1)], s*(dir.ec-dir.bc+1)]; data.widthDesc _ DESCRIPTOR[Next[s*dir.nw], s*dir.nw]; data.heightDesc _ DESCRIPTOR[Next[s*dir.nh], s*dir.nh]; data.depthDesc _ DESCRIPTOR[Next[s*dir.nd], s*dir.nd]; data.charIcDesc _ DESCRIPTOR[Next[s*dir.ni], s*dir.ni]; data.ligKernDesc _ DESCRIPTOR[Next[s*dir.nl], s*dir.nl]; data.kernDesc _ DESCRIPTOR[Next[s*dir.nk], s*dir.nk]; data.extenDesc _ DESCRIPTOR[Next[s*dir.ne], s*dir.ne]; data.paramPtr _ Next[s*dir.np]; <> BEGIN temp: REF TEXT _ RefText.ObtainScratch[40]; temp.length _ Ord[data.headerPtr.characterCodingScheme[0]]; FOR i: NAT IN [0..temp.length) DO temp[i] _ data.headerPtr.characterCodingScheme[i+1]; ENDLOOP; data.characterCodingScheme _ Rope.FromRefText[temp]; temp.length _ Ord[data.headerPtr.externalFontIdentifier[0]]; FOR i: NAT IN [0..temp.length) DO temp[i] _ data.headerPtr.externalFontIdentifier[i+1]; ENDLOOP; data.family _ Rope.FromRefText[temp]; RefText.ReleaseScratch[temp]; END; RETURN[data]; }; CheckSum: PUBLIC PROCEDURE [key: Key] RETURNS [checkSum: Tfm.Word] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; checkSum _ data.headerPtr.checkSum; }; CharacterCodingScheme: PUBLIC PROCEDURE [key: Key] RETURNS [characterCodingScheme: Rope.ROPE] = { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; characterCodingScheme _ data.characterCodingScheme; }; Family: PUBLIC PROCEDURE [key: Key] RETURNS [family: Rope.ROPE] = { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; family _ data.family; }; DesignSize: PUBLIC PROCEDURE [key: Key] RETURNS [designSize: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; designSize _ FixedToReal[data.headerPtr.designSize]; }; Face: PUBLIC PROCEDURE [key: Key] RETURNS [face: [0..256)] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; face _ data.headerPtr.extraInfo.parcFaceByte; }; Range: PUBLIC PROCEDURE [key: Key] RETURNS [bc, ec: CHAR] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; bc _ '\000+data.dirPtr.bc; ec _ '\000+data.dirPtr.ec; }; NullFInfoEntry: Tfm.FInfoEntry = [0, 0, 0, 0, none[[]]]; Contains: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [BOOLEAN] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char]>= data.dirPtr.bc AND Ord[char] <= data.dirPtr.ec) THEN RETURN [FALSE]; RETURN[data.fInfoDesc[Ord[char]-data.dirPtr.bc] # NullFInfoEntry]; }; Width: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [width: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char] IN [data.dirPtr.bc..data.dirPtr.ec]) THEN RETURN [Real.MinusZero]; width _ FixedToReal[data.widthDesc[data.fInfoDesc[Ord[char]-data.dirPtr.bc].widthIndex]]; }; Height: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [height: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char] IN [data.dirPtr.bc..data.dirPtr.ec]) THEN RETURN [Real.MinusZero]; height _ FixedToReal[data.heightDesc[data.fInfoDesc[Ord[char]-data.dirPtr.bc].heightIndex]]; }; Depth: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [depth: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char] IN [data.dirPtr.bc..data.dirPtr.ec]) THEN RETURN [Real.MinusZero]; depth _ FixedToReal[data.depthDesc[data.fInfoDesc[Ord[char]-data.dirPtr.bc].depthIndex]]; }; ItalicCorrection: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [italicCorrection: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char] IN [data.dirPtr.bc..data.dirPtr.ec]) THEN RETURN [Real.MinusZero]; italicCorrection _ FixedToReal[data.charIcDesc[data.fInfoDesc[Ord[char]-data.dirPtr.bc].charIcIndex]]; }; LigKern: PUBLIC PROCEDURE [key: Key, char1, char2: CHAR] RETURNS [ligOrKern: Font.LigatureOrKern] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; IF NOT (Ord[char1] IN [data.dirPtr.bc..data.dirPtr.ec] AND Ord[char2] IN [data.dirPtr.bc..data.dirPtr.ec]) THEN RETURN [[neither[]]]; WITH data.fInfoDesc[Ord[char1]-data.dirPtr.bc] SELECT FROM ligFInfoEntry: Tfm.FInfoEntry[lig] => { FOR i: NAT _ ligFInfoEntry.ligKernStart, i+1 DO ligKernEntry: Tfm.LigKernEntry _ data.ligKernDesc[i]; IF ligKernEntry.nextChar = char2 THEN RETURN[ WITH ligKernEntry SELECT FROM ligEntry: Tfm.LigKernEntry[lig] => [ligature[ligatureCode: ligEntry.ligCode]], kernEntry: Tfm.LigKernEntry[kern] => [kern[kernAmount: FixedToReal[data.kernDesc[kernEntry.kernIndex]]]], ENDCASE => ERROR ]; IF ligKernEntry.stop THEN RETURN[[neither[]]]; ENDLOOP }; ENDCASE => RETURN[[neither[]]]; }; Slant: PUBLIC PROCEDURE [key: Key] RETURNS [numerator, denominator: INT] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; numerator _ Tfm.Long[data.paramPtr[slant].ValueTimesTwoToTheTwentieth]; denominator _ TwoToTheTwentieth; }; Parameter: PUBLIC PROCEDURE [key: Key, index: Tfm.ParamIndex] RETURNS [parameterValue: REAL] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key,OpenInit]; data: Data _ NARROW[file.GetData[]]; parameterValue _ IF LOOPHOLE[index, CARDINAL] > data.dirPtr.np THEN 0.0 ELSE FixedToReal[data.paramPtr[index]]; }; <> FONT: TYPE = Font.FONT; LigatureOrKern: TYPE = Font.LigatureOrKern; Box: TYPE = Font.Box; VecMag: PROC [x, y: REAL] RETURNS [REAL] = {RETURN[Real.SqRt[x*x + y*y]]}; TFMLigKern: PROCEDURE [font: FONT, char1, char2: CHAR] RETURNS [ligatureOrKern: LigatureOrKern] = { ligKern: LigatureOrKern _ LigKern[font.formattingKey, char1, char2]; WITH lg: ligKern SELECT FROM kern => { scale: REAL _ IF font.actualTransformation.b = 0 THEN font.actualTransformation.e ELSE VecMag[font.actualTransformation.b, font.actualTransformation.e]; RETURN[[kern[lg.kernAmount*scale]]] }; ENDCASE => RETURN[ligKern]; }; TFMFormattingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: Box] = { s: REAL _ IF font.actualTransformation.b = 0 THEN font.actualTransformation.e ELSE VecMag[font.actualTransformation.b, font.actualTransformation.e]; box.xmin _ 0; box.xmax _ Width[font.formattingKey, char]*s; box.ymin _ - Depth[font.formattingKey, char]*s; box.ymax _ Height[font.formattingKey, char]*s; }; TFMFormattingMetric: PROCEDURE [font: FONT, metric: ATOM, char: CHAR] RETURNS [REAL] = { RETURN[0.0]; }; TFMObjectInit: PROCEDURE [font: FONT] = { font.fontFormattingClass _ TFMClass; }; TFMClass: REF Font.FontFormattingClassRec _ NEW[Font.FontFormattingClassRec _ [ formattingBoxProc: TFMFormattingBox, ligKernProc: TFMLigKern, formattingMetricProc: TFMFormattingMetric ]]; Font.RegisterFontFormattingClass[$Tfm, TFMObjectInit]; END.