<> <> <> <<>> DIRECTORY Basics USING [bytesPerWord], CountedVM USING [Allocate, Handle, Pointer], ImagerFont USING [Char, Class, ClassRep, CorrectionType, Extents, Font, FontRep, nullChar, OptionalReal, Register], FS USING [StreamOpen], Imager USING [ConcatT, Context, DoSave, MaskFillParity], ImagerPath USING [PathProc], ImagerTransformation USING [Concat, Rectangle, Scale, Transformation, TransformRectangle, TransformVec], IO USING [Close, SetIndex, STREAM, UnsafeGetBlock], PrePressFontFormat USING [BCPLREAL, CardFromDouble, DirectoryArray, IXHeader, missingFilePos, missingSpline, NameIndexEntry, SplineCode, SplineCommand, SplineCoords, SplineData, SplineDataArray, StdIndexEntry], RealConvert USING [BcplToIeee], Rope USING [FromProc, ROPE], Vector2 USING [Add, Div, VEC]; ImagerFontSDImpl: CEDAR PROGRAM IMPORTS CountedVM, ImagerFont, FS, Imager, ImagerTransformation, IO, PrePressFontFormat, RealConvert, Rope, Vector2 ~ BEGIN BYTE: TYPE ~ [0..377B]; ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; Transformation: TYPE ~ ImagerTransformation.Transformation; Font: TYPE ~ ImagerFont.Font; FontRep: TYPE ~ ImagerFont.FontRep; <> <> <> <> <> Char: TYPE ~ ImagerFont.Char; -- CARDINAL nullChar: Char ~ ImagerFont.nullChar; CorrectionType: TYPE ~ ImagerFont.CorrectionType; -- {none, space, mask}; Extents: TYPE ~ ImagerFont.Extents; -- RECORD[leftExtent, rightExtent, descent, ascent: REAL]; OptionalReal: TYPE ~ ImagerFont.OptionalReal; -- RECORD[exists: BOOLEAN, value: REAL]; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD[ file: SDFile ]; SDFile: TYPE ~ REF SDFileRep; SDFileRep: TYPE ~ RECORD[ bc, ec: BYTE, splineData: LONG POINTER TO PrePressFontFormat.SplineDataArray, directory: LONG POINTER TO PrePressFontFormat.DirectoryArray, vm: CountedVM.Handle ]; MalformedSDFont: ERROR ~ CODE; RealFromBcpl: PROC[x: PrePressFontFormat.BCPLREAL] RETURNS[REAL] ~ INLINE { RETURN[RealConvert.BcplToIeee[LOOPHOLE[x]]]; }; ReadWords: UNSAFE PROC[stream: IO.STREAM, base: LONG POINTER, words: INT] ~ UNCHECKED { count: INT ~ words*Basics.bytesPerWord; IF IO.UnsafeGetBlock[stream, [base: base, count: count]]=count THEN NULL ELSE ERROR MalformedSDFont; -- file too short }; SetWordIndex: PROC[stream: IO.STREAM, wordIndex: INT] ~ { index: CARDINAL ~ wordIndex*Basics.bytesPerWord; IO.SetIndex[stream, index]; }; Open: PROC[fileName: ROPE] RETURNS[SDFile] ~ { stream: IO.STREAM ~ FS.StreamOpen[fileName]; ix: PrePressFontFormat.IXHeader; ixSize: NAT ~ SIZE[PrePressFontFormat.IXHeader]; name: PrePressFontFormat.NameIndexEntry; index: PrePressFontFormat.StdIndexEntry; nameFound, indexFound: BOOL _ FALSE; family: ROPE _ NIL; segmentIndex, segmentWords: INT _ 0; vm: CountedVM.Handle _ NIL; splineData, directory: LONG POINTER _ NIL; DO -- read the index part TRUSTED { ReadWords[stream, @ix, SIZE[PrePressFontFormat.IXHeader]] }; SELECT ix.type FROM end => EXIT; name => { IF nameFound THEN ERROR MalformedSDFont; -- more than one name entry IF (ix.length-ixSize)=SIZE[PrePressFontFormat.NameIndexEntry] THEN TRUSTED { ReadWords[stream, @name, SIZE[PrePressFontFormat.NameIndexEntry]] } ELSE ERROR MalformedSDFont; -- wrong ix.length { -- convert name to rope i: NAT _ 0; p: PROC RETURNS[CHAR] ~ { RETURN[VAL[name.chars[i _ i+1]]] }; family _ Rope.FromProc[len: name.chars[0], p: p]; }; nameFound _ TRUE; }; spline => { IF indexFound THEN ERROR MalformedSDFont; -- more than one char index entry IF (ix.length-ixSize)=SIZE[PrePressFontFormat.StdIndexEntry] THEN TRUSTED { ReadWords[stream, @index, SIZE[PrePressFontFormat.StdIndexEntry]] } ELSE ERROR MalformedSDFont; -- wrong ix.length indexFound _ TRUE; }; ENDCASE => ERROR MalformedSDFont; -- unexpected ix type ENDLOOP; IF nameFound AND indexFound AND name.code=index.family THEN NULL ELSE ERROR MalformedSDFont; -- index part has wrong form IF index.bc>index.ec THEN ERROR MalformedSDFont; -- bc exceeds ec segmentIndex _ PrePressFontFormat.CardFromDouble[index.segmentSA]; -- in words! segmentWords _ PrePressFontFormat.CardFromDouble[index.segmentLength]; vm _ CountedVM.Allocate[words: segmentWords]; SetWordIndex[stream, segmentIndex]; TRUSTED { -- read segment base: LONG POINTER ~ CountedVM.Pointer[vm]; ReadWords[stream, base, segmentWords]; splineData _ base; directory _ splineData+SIZE[PrePressFontFormat.SplineDataArray[index.ec-index.bc+1]]; }; IO.Close[stream]; RETURN[NEW[SDFileRep _ [bc: index.bc, ec: index.ec, splineData: splineData, directory: directory, vm: vm]]]; }; Find: PROC[name: ROPE] RETURNS[Font] ~ { file: SDFile ~ Open[name]; charToClient: Transformation ~ ImagerTransformation.Scale[1]; data: Data ~ NEW[DataRep _ [file: file]]; RETURN[NEW[ImagerFont.FontRep _ [class: class, data: data, name: name, charToClient: charToClient, props: NIL]]]; }; Modify: PROC[font: Font, m: Transformation] RETURNS[Font] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; charToClient: Transformation ~ font.charToClient.Concat[m]; newData: Data ~ NEW[DataRep _ [file: file]]; RETURN[NEW[FontRep _ [class: class, data: newData, name: font.name, charToClient: charToClient, props: NIL]]]; }; Contains: PROC[font: Font, char: Char] RETURNS[BOOL] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; IF char IN[file.bc..file.ec] THEN { sd: PrePressFontFormat.SplineData; TRUSTED { sd _ file.splineData[char-file.bc] }; RETURN[sd.wx#PrePressFontFormat.missingSpline]; }; RETURN[FALSE]; }; NextChar: PROC[font: Font, char: Char] RETURNS[next: Char] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; start: Char; SELECT char FROM =nullChar => start _ file.bc; start _ file.bc; start _ char+1; ENDCASE => RETURN[nullChar]; FOR probe: Char IN[start..file.ec] DO sd: PrePressFontFormat.SplineData; TRUSTED { sd _ file.splineData[probe-file.bc] }; IF sd.wx#PrePressFontFormat.missingSpline THEN RETURN[probe]; ENDLOOP; RETURN[nullChar]; }; BoundingBox: PROC[font: Font, char: Char] RETURNS[Extents] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; IF char IN[file.bc..file.ec] THEN { sd: PrePressFontFormat.SplineData; TRUSTED { sd _ file.splineData[char-file.bc] }; IF sd.wx#PrePressFontFormat.missingSpline THEN { xmin: REAL ~ RealFromBcpl[sd.xmin]; ymin: REAL ~ RealFromBcpl[sd.ymin]; xmax: REAL ~ RealFromBcpl[sd.xmax]; ymax: REAL ~ RealFromBcpl[sd.ymax]; r: ImagerTransformation.Rectangle ~ ImagerTransformation.TransformRectangle[ font.charToClient, [x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]]; RETURN[[leftExtent: -r.x, rightExtent: r.x+r.w, descent: -r.y, ascent: r.y+r.h]]; }; }; RETURN[[0, 0, 0, 0]]; }; Width: PROC[font: Font, char: Char] RETURNS[VEC] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; IF char IN[file.bc..file.ec] THEN { sd: PrePressFontFormat.SplineData; TRUSTED { sd _ file.splineData[char-file.bc] }; IF sd.wx#PrePressFontFormat.missingSpline THEN { wx: REAL ~ RealFromBcpl[sd.wx]; wy: REAL ~ RealFromBcpl[sd.wy]; RETURN[font.charToClient.TransformVec[[wx, wy]]]; }; }; RETURN[[0, 0]]; }; Amplified: PROC[font: Font, char: Char] RETURNS[BOOL] ~ { RETURN[char=40B]; }; Correction: PROC[font: Font, char: Char] RETURNS[CorrectionType] ~ { data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; IF char IN[file.bc..file.ec] THEN { IF char=40B THEN RETURN[space] ELSE RETURN[mask]; }; RETURN[none]; }; Kern: PROC[font: Font, char, successor: Char] RETURNS[VEC] ~ { RETURN[[0, 0]]; }; NextKern: PROC[font: Font, char, successor: Char] RETURNS[Char] ~ { RETURN[nullChar]; }; Ligature: PROC[font: Font, char, successor: Char] RETURNS[Char] ~ { RETURN[nullChar]; }; NextLigature: PROC[font: Font, char, successor: Char] RETURNS[Char] ~ { RETURN[nullChar]; }; CharInfo: PROC[font: Font, char: Char, key: ATOM] RETURNS[OptionalReal] ~ { RETURN[[FALSE, 0]]; }; missingOffset: LONG CARDINAL ~ PrePressFontFormat.CardFromDouble[PrePressFontFormat.missingFilePos]; MaskChar: PROC[font: Font, char: Char, imager: REF] ~ { context: Imager.Context ~ NARROW[imager]; data: Data ~ NARROW[font.data]; file: SDFile ~ data.file; IF char IN[file.bc..file.ec] THEN { sd: PrePressFontFormat.SplineData; TRUSTED { sd _ file.splineData[char-file.bc] }; IF sd.wx#PrePressFontFormat.missingSpline THEN { base: LONG POINTER _ NIL; SDPath: ImagerPath.PathProc ~ { next: LONG POINTER _ base; lp: VEC _ [0, 0]; GetCode: PROC RETURNS[PrePressFontFormat.SplineCode] ~ TRUSTED { p: LONG POINTER TO PrePressFontFormat.SplineCommand ~ next; next _ next+SIZE[PrePressFontFormat.SplineCommand]; RETURN[p.code]; }; GetCoords: PROC RETURNS[VEC] ~ TRUSTED { p: LONG POINTER TO PrePressFontFormat.SplineCoords ~ next; next _ next+SIZE[PrePressFontFormat.SplineCoords]; RETURN[[RealFromBcpl[p.x], RealFromBcpl[p.y]]]; }; DO SELECT GetCode[] FROM $moveTo => { p: VEC ~ GetCoords[]; moveTo[p]; lp _ p; }; $drawTo => { p: VEC ~ GetCoords[]; lineTo[p]; lp _ p; }; $drawCurve => { c1: VEC ~ GetCoords[]; c2: VEC ~ GetCoords[]; c3: VEC ~ GetCoords[]; b1: VEC ~ lp.Add[c1.Div[3]]; b2: VEC ~ b1.Add[c1.Add[c2].Div[3]]; b3: VEC ~ lp.Add[c1].Add[c2].Add[c3]; curveTo[b1, b2, b3]; lp _ b3; }; $endDefinition => EXIT; $newObject => ERROR MalformedSDFont; -- not implemented ENDCASE => ERROR MalformedSDFont; -- undefined ENDLOOP; }; action: PROC ~ { Imager.ConcatT[context, font.charToClient]; Imager.MaskFillParity[context, SDPath]; }; TRUSTED { fp: LONG CARDINAL ~ PrePressFontFormat.CardFromDouble[file.directory[char-file.bc]]; IF fp#missingOffset THEN base _ LOOPHOLE[file.directory+fp] ELSE ERROR MalformedSDFont; }; Imager.DoSave[context, action]; }; }; }; class: ImagerFont.Class ~ NEW[ImagerFont.ClassRep _ [ Modify: Modify, Contains: Contains, NextChar: NextChar, Width: Width, Amplified: Amplified, Correction: Correction, BoundingBox: BoundingBox, Ligature: Ligature, NextLigature: NextLigature, Kern: Kern, NextKern: NextKern, CharInfo: CharInfo, MaskChar: MaskChar ]]; ImagerFont.Register["Xerox/SD", Find]; END.