<<>> <> <> <> <> <<>> <> <<>> DIRECTORY Basics USING [Card16FromH, Int16FromH, HWORD], BasicTime USING [GMT], Char, CountedVM USING [Allocate, Handle], Imager USING [Context, MaskBitmap], ImagerFont USING [CorrectionType, Extents, nullXChar, XChar], ImagerMaskCache USING [CharMask, MakeCharMaskProc, RasterCharMaskFromSampleMap], ImagerSample USING [Clip, ObtainUnsafeDescriptor, ReleaseDescriptor, SampleMap, Trim], ImagerTransformation USING [Transformation], ImagerTypeface USING [CreatorFromFileExtension, RegisterCreator, Typeface, TypefaceClass, TypefaceClassRep, TypefaceFromFont, TypefaceRep], IO USING [Close, EndOfStream, STREAM, UnsafeGetBlock], Rope USING [ROPE], StrikeFontFormat USING [Body, BoundingBox, byteSizeBody, Header, nullWidthEntry, WidthEntry, WTable, XTable], Vector2 USING [VEC]; ImagerStrikeTypefaceImpl: CEDAR PROGRAM IMPORTS Basics, Char, CountedVM, Imager, ImagerMaskCache, ImagerSample, ImagerTypeface, IO ~ BEGIN ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; Typeface: TYPE ~ ImagerTypeface.Typeface; TypefaceClass: TYPE ~ ImagerTypeface.TypefaceClass; TypefaceClassRep: TYPE ~ ImagerTypeface.TypefaceClassRep; TypefaceRep: TYPE ~ ImagerTypeface.TypefaceRep; CorrectionType: TYPE ~ ImagerFont.CorrectionType; Extents: TYPE ~ ImagerFont.Extents; XChar: TYPE ~ ImagerFont.XChar; nullXChar: XChar ~ ImagerFont.nullXChar; Int16: TYPE ~ Basics.HWORD; Card16: TYPE ~ Basics.HWORD; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ min, max: BYTE ¬ 0, left, right, ascent, descent: INTEGER ¬ 0, -- font extents strike: LONG POINTER ¬ NIL, raster, lines: NAT ¬ 0, xtable: LONG POINTER TO StrikeFontFormat.XTable ¬ NIL, wtable: LONG POINTER TO StrikeFontFormat.WTable ¬ NIL, vm: CountedVM.Handle ¬ NIL ]; MalformedStrikeFont: ERROR ~ CODE; Assert: PROC [pred: BOOL] ~ { IF pred THEN NULL ELSE ERROR MalformedStrikeFont }; IOrd: PROC [h: Int16] RETURNS [INT16] ~ INLINE { RETURN [Basics.Int16FromH[h]] }; COrd: PROC [h: Card16] RETURNS [INT16] ~ INLINE { RETURN [Basics.Card16FromH[h]] }; MWords: PROC [hWordCount: NAT] RETURNS [NAT] ~ INLINE { <> RETURN [((hWordCount*16)+(BITS[WORD]-1))/BITS[WORD]] }; MUnits: PROC [hWordCount: NAT] RETURNS [NAT] ~ INLINE { <> RETURN [MWords[hWordCount]*UNITS[WORD]] }; ReadBytes: UNSAFE PROC [stream: IO.STREAM, nBytes: INT, base: LONG POINTER, vm: CountedVM.Handle] ~ UNCHECKED { IF vm # NIL THEN { <> unitOffset: NAT ¬ base-vm.pointer; spareBytes: NAT ¬ (vm.words*BYTES[WORD])-(unitOffset*BYTES[UNIT]+nBytes); }; IF IO.UnsafeGetBlock[stream, [base: base, count: nBytes]]=nBytes THEN NULL ELSE ERROR IO.EndOfStream[stream]; -- file too short }; StrikeCreate: PROC [stream: IO.STREAM] RETURNS [Typeface] ~ { OPEN StrikeFontFormat; data: Data ¬ NIL; header: Header; box: BoundingBox ¬ [[0, 0], [0, 0], [0, 0], [0, 0]]; body: Body; left, right, descent, ascent, lines: INTEGER ¬ 0; vm: CountedVM.Handle ¬ NIL; strikeWords, xtableWords, wtableWords: NAT ¬ 0; vmBytes: NAT ¬ 0; strike, xtable, wtable: LONG POINTER ¬ NIL; TRUSTED { ReadBytes[stream: stream, base: @header, nBytes: BYTES[Header], vm: NIL] }; Assert[header.oneBit=T AND header.index=F AND header.unused=0]; Assert[COrd[header.max] IN [COrd[header.min]..BYTE.LAST]]; IF header.kerned=T THEN TRUSTED { ReadBytes[stream: stream, base: @box, nBytes: BYTES[BoundingBox], vm: NIL] }; TRUSTED { ReadBytes[stream: stream, base: @body, nBytes: byteSizeBody, vm: NIL] }; descent ¬ IOrd[body.descent]; ascent ¬ IOrd[body.ascent]; Assert[(lines ¬ descent+ascent)>0]; IF header.kerned=T THEN { left ¬ -IOrd[box.fbbox]; right ¬ IOrd[box.fbbox]+IOrd[box.fbbdx]; Assert[IOrd[box.fbboy]=-descent AND IOrd[box.fbbdy]=lines AND IOrd[box.fbbdx]>0] } ELSE { left ¬ 0; right ¬ COrd[header.maxwidth] }; strikeWords ¬ COrd[body.raster]*lines; xtableWords ¬ COrd[header.max]-COrd[header.min]+3; Assert[INT[byteSizeBody/2 + strikeWords + xtableWords] <= COrd[body.length]]; IF header.kerned=T THEN wtableWords ¬ COrd[header.max]-COrd[header.min]+2; vmBytes ¬ (MWords[strikeWords]+MWords[xtableWords]+MWords[wtableWords])*BYTES[WORD]; vm ¬ CountedVM.Allocate[words: (vmBytes+BYTES[WORD]-1)/BYTES[WORD]]; IF NAT[vm.words]*BYTES[WORD] < vmBytes THEN ERROR -- not enough words ?? ELSE TRUSTED { -- read remainder of file: strike, xtable, and wtable base: LONG POINTER ~ vm.pointer; strike ¬ base; ReadBytes[stream: stream, base: strike, nBytes: strikeWords*BYTES[Basics.HWORD], vm: vm]; xtable ¬ strike+MUnits[strikeWords]; ReadBytes[stream: stream, base: xtable, nBytes: xtableWords*BYTES[Basics.HWORD], vm: vm]; IF wtableWords#0 THEN { wtable ¬ xtable+MUnits[xtableWords]; ReadBytes[stream: stream, base: wtable, nBytes: wtableWords*BYTES[Basics.HWORD], vm: vm]; }; }; IO.Close[stream]; data ¬ NEW[DataRep ¬ [min: COrd[header.min], max: COrd[header.max], left: left, right: right, ascent: IOrd[body.ascent], descent: IOrd[body.descent], strike: strike, raster: COrd[body.raster], lines: lines, xtable: LOOPHOLE[xtable], wtable: LOOPHOLE[wtable], vm: vm]]; RETURN[NEW[TypefaceRep ¬ [class: IF data.wtable=NIL THEN strikeClass ELSE ksClass, data: data]]]; }; Index: PROC [data: Data, char: XChar] RETURNS [NAT] ~ { IF Char.Set[char]=0 AND Char.Code[char] IN[data.min..data.max] THEN { index: NAT ~ Char.Code[char]-data.min; IF data.wtable=NIL THEN TRUSTED { x0: INTEGER ~ IOrd[data.xtable[index]]; x1: INTEGER ~ IOrd[data.xtable[index+1]]; IF x1>x0 THEN RETURN[index]; } ELSE TRUSTED { w: StrikeFontFormat.WidthEntry ~ data.wtable[index]; IF w#StrikeFontFormat.nullWidthEntry THEN RETURN[index]; }; }; RETURN[data.max-data.min+1]; }; StrikeContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; RETURN[Index[data, char]<=(data.max-data.min)]; }; StrikeNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ { data: Data ~ NARROW[self.data]; start: BYTE ¬ 0; IF char=nullXChar THEN NULL ELSE IF Char.Set[char]=0 AND Char.Code[char]x0 THEN RETURN[[leftExtent: 0, rightExtent: (x1-x0), descent: data.descent, ascent: data.ascent]]; } ELSE TRUSTED { w: StrikeFontFormat.WidthEntry ~ data.wtable[index]; IF w#StrikeFontFormat.nullWidthEntry THEN { x0: INTEGER ~ IOrd[data.xtable[index]]; x1: INTEGER ~ IOrd[data.xtable[index+1]]; left: INTEGER ~ data.left-w.offset; RETURN[[leftExtent: left, rightExtent: (x1-x0)-left, descent: data.descent, ascent: data.ascent]]; }; }; RETURN[[0, 0, 0, 0]]; }; StrikeFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; RETURN[[leftExtent: data.left, rightExtent: data.right, descent: data.descent, ascent: data.ascent]]; }; StrikeEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; index: NAT ~ Index[data, char]; IF data.wtable=NIL THEN TRUSTED { x0: INTEGER ~ IOrd[data.xtable[index]]; x1: INTEGER ~ IOrd[data.xtable[index+1]]; IF x1>x0 THEN RETURN[[x1-x0, 0]]; } ELSE TRUSTED { w: StrikeFontFormat.WidthEntry ~ data.wtable[index]; IF w#StrikeFontFormat.nullWidthEntry THEN RETURN[[x: w.width, y: 0]]; }; RETURN[[0, 0]]; }; spaceChar: XChar ~ Char.Make[set: 0, code: 40B]; StrikeAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { RETURN[char=spaceChar]; }; StrikeCorrection: PROC [self: Typeface, char: XChar] RETURNS [CorrectionType] ~ { RETURN[IF char=spaceChar THEN space ELSE mask]; }; StrikeKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { RETURN[[0, 0]]; }; StrikeNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; StrikeLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; StrikeNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; GetBitmap: PROC [self: Typeface, char: XChar] RETURNS [map: ImagerSample.SampleMap ¬ NIL] = TRUSTED { data: Data ~ NARROW[self.data]; index: NAT ~ Index[data, char]; x0: INTEGER ~ IOrd[data.xtable[index]]; x1: INTEGER ~ IOrd[data.xtable[index+1]]; IF x0<=x1 AND NAT[x1]<=NAT[data.raster*16] THEN NULL ELSE ERROR MalformedStrikeFont; -- xtable inconsistent IF data.wtable=NIL THEN { IF x1>x0 THEN { bits: ImagerSample.SampleMap ~ ImagerSample.ObtainUnsafeDescriptor[ size: [s: data.lines, f: x1-x0], bitsPerSample: 1, bitsPerLine: data.raster*16, base: [word: data.strike, bit: 0], ref: data, words: MWords[data.raster*data.lines], rawMin: [f: x0, s: 0], delta: [-data.ascent, 0] ]; map ¬ ImagerSample.Clip[bits, ImagerSample.Trim[bits]]; ImagerSample.ReleaseDescriptor[bits]; }; } ELSE { w: StrikeFontFormat.WidthEntry ~ data.wtable[index]; IF w#StrikeFontFormat.nullWidthEntry AND x1>x0 THEN { bits: ImagerSample.SampleMap ~ ImagerSample.ObtainUnsafeDescriptor[ size: [s: data.lines, f: x1-x0], bitsPerSample: 1, bitsPerLine: data.raster*16, base: [word: data.strike, bit: 0], ref: data, words: MWords[data.raster*data.lines], rawMin: [f: x0, s: 0], delta: [-data.ascent, w.offset-data.left] ]; map ¬ ImagerSample.Clip[bits, ImagerSample.Trim[bits]]; ImagerSample.ReleaseDescriptor[bits]; }; }; }; StrikeMask: PROC [self: Typeface, char: XChar, context: Imager.Context] ~ { bits: ImagerSample.SampleMap ~ GetBitmap[self, char]; IF bits # NIL THEN { Imager.MaskBitmap[context: context, bitmap: bits]; TRUSTED { ImagerSample.ReleaseDescriptor[bits] }; }; }; tolerance: REAL ¬ 0.01; -- allow up to 1% slop in mask transform for fast case. NearlyForm9: PROC [m: ImagerTransformation.Transformation] RETURNS [BOOL] ~ { RETURN [m.form = 9 OR (m.form = 2 AND MAX[ABS[m.b-(-1.0)], ABS[m.d-1.0]] <= tolerance)] }; StrikeMakeCharMask: ImagerMaskCache.MakeCharMaskProc ~ { <<[parameters: Parameters, font: Font, char: XChar] RETURNS [CharMask]>> IF font.charToClient.integerTrans AND font.charToClient.tx = 0 AND font.charToClient.ty = 0 AND NearlyForm9[font.charToClient] THEN { bits: ImagerSample.SampleMap ~ GetBitmap[ImagerTypeface.TypefaceFromFont[font], char]; IF bits # NIL THEN { charMask: ImagerMaskCache.CharMask ~ ImagerMaskCache.RasterCharMaskFromSampleMap[bits]; TRUSTED { ImagerSample.ReleaseDescriptor[bits] }; RETURN [charMask] }; }; RETURN [NIL] }; strikeClass: TypefaceClass ~ NEW[TypefaceClassRep ¬ [ type: $Strike, Contains: StrikeContains, NextChar: StrikeNextChar, Escapement: StrikeEscapement, Amplified: StrikeAmplified, Correction: StrikeCorrection, BoundingBox: StrikeBoundingBox, FontBoundingBox: StrikeFontBoundingBox, Ligature: StrikeLigature, NextLigature: StrikeNextLigature, Kern: StrikeKern, NextKern: StrikeNextKern, Mask: StrikeMask, MakeCharMask: StrikeMakeCharMask ]]; ksClass: TypefaceClass ~ NEW[TypefaceClassRep ¬ [ type: $KS, Contains: StrikeContains, NextChar: StrikeNextChar, Escapement: StrikeEscapement, Amplified: StrikeAmplified, Correction: StrikeCorrection, BoundingBox: StrikeBoundingBox, FontBoundingBox: StrikeFontBoundingBox, Ligature: StrikeLigature, NextLigature: StrikeNextLigature, Kern: StrikeKern, NextKern: StrikeNextKern, Mask: StrikeMask, MakeCharMask: StrikeMakeCharMask ]]; ImagerTypeface.RegisterCreator[ImagerTypeface.CreatorFromFileExtension["KS", StrikeCreate]]; ImagerTypeface.RegisterCreator[ImagerTypeface.CreatorFromFileExtension["strike", StrikeCreate]]; END.