<> <> <> DIRECTORY Ascii, Environment, Font, Inline, ImagerBasic USING [IntRectangle, Pair, Transformation], ImagerTransform USING [Concat, Create, InverseTransform, InverseTransformVec, Invert, TransformIntRectangle], Real, Rope, Scaled, StrikeFormat, UFFileManager, UFStrikeReader; UFStrikeReaderImpl: CEDAR PROGRAM IMPORTS Inline, ImagerTransform, Real, Scaled, UFFileManager, Font EXPORTS UFStrikeReader = BEGIN OPEN UFStrikeReader, Tran: ImagerTransform; FONT: TYPE = Font.FONT; Transformation: TYPE = ImagerBasic.Transformation; StrikeFont: TYPE = REF StrikeFontRep; StrikeFontRep: TYPE = RECORD [ kerned: BOOLEAN, -- plain (FALSE) or kerned (TRUE) strike format height: [0..77777B], -- font height min, max: CHARACTER, -- range of defined characters width: REF WidthArray, -- a width for every character code dx, dy: INTEGER, -- font bounding box dimensions ox, oy: INTEGER, -- font bounding box offsets xtable: LONG POINTER TO StrikeFormat.XTable, -- bitmap positions wtable: LONG POINTER TO StrikeFormat.WTable, -- widths and kerns (if kerned=TRUE) bitmap: LONG POINTER TO ARRAY [0..0) OF WORD, -- the strike itself raster: CARDINAL, -- words per raster line in the strike address: LONG POINTER, -- virtual address of font fileName: Rope.ROPE _ NIL, -- name of font file, if available other: REF ANY _ NIL -- escape hatch ]; WidthArray: TYPE = PACKED ARRAY CHARACTER OF [0..377B]; OpenInitialization: UFFileManager.InitProc = TRUSTED { <> address: LONG POINTER _ fontFile.Pointer; hdr: LONG POINTER TO StrikeFormat.Header _ address; format: StrikeFormat.Format _ hdr.format; self: StrikeFont _ NEW[StrikeFontRep]; dx, dy, ox, oy: INTEGER _ 0; body: LONG POINTER TO StrikeFormat.Body _ NIL; raster, height: CARDINAL; bitmap: LONG POINTER _ NIL; kerned: BOOLEAN _ FALSE; IF format.oneBit=F THEN ERROR Font.BadFontFileFormat; IF format.index=T THEN ERROR Font.BadFontFileFormat; kerned _ (format.kerned=T); IF kerned THEN { ks: LONG POINTER TO StrikeFormat.KernedStrike _ address; body _ @ks.body; [fbbdx: dx, fbbdy: dy, fbbox: ox, fbboy: oy] _ ks.box } ELSE { ps: LONG POINTER TO StrikeFormat.PlainStrike _ address; body _ @ps.body; dx _ hdr.maxwidth; dy _ body.ascent+body.descent; ox _ 0; oy _ -body.descent }; height _ body.ascent+body.descent; bitmap _ LOOPHOLE[body + SIZE[StrikeFormat.Body]]; raster _ body.raster; self^ _ [kerned: kerned, height: height, min: hdr.min, max: hdr.max, width: NIL, dx: dx, dy: dy, ox: ox, oy: oy, xtable: NIL, wtable: NIL, bitmap: bitmap, raster: raster, address: address]; self.xtable _ LOOPHOLE[bitmap+raster*height]; IF kerned THEN self.wtable _ LOOPHOLE[body+body.length]; Initialize[self]; RETURN[self]; }; Initialize: UNSAFE PROC[font: StrikeFont] = UNCHECKED { min: CHARACTER _ font.min; max: CHARACTER _ font.max; width: REF WidthArray _ NEW[WidthArray]; IF font.kerned THEN { wtable: LONG POINTER TO StrikeFormat.WTable _ font.wtable; dummy: [0..377B] _ wtable[max-min+1].width; width^ _ ALL[dummy]; FOR c: CHARACTER IN[min..max] DO i: NAT _ c - min; entry: StrikeFormat.WidthEntry _ wtable[i]; IF entry#StrikeFormat.nullWidthEntry THEN width[c] _ entry.width; ENDLOOP; } ELSE { xtable: LONG POINTER TO StrikeFormat.XTable _ font.xtable; space: [0..377B] _ 0; FOR c: CHARACTER IN[min..max+1] DO i: NAT _ c - min; width[c] _ xtable[i+1] - xtable[i]; ENDLOOP; space _ width[Ascii.SP]; FOR c: CHARACTER IN [0C..min) DO width[c] _ space ENDLOOP; FOR c: CHARACTER IN (max+1..177C] DO width[c] _ space ENDLOOP; width[Ascii.TAB] _ 8*space; width[Ascii.CR] _ space; }; font.width _ width; }; IntBB: PROCEDURE [font: StrikeFont, char: CHAR] RETURNS [xmin, ymin, xmax, ymax: INTEGER] = TRUSTED { xl,xr,xo: INTEGER; i: NAT _ IF char IN [font.min..font.max] THEN char-font.min ELSE font.max-font.min+1; IF font.kerned THEN { w: StrikeFormat.WidthEntry _ font.wtable[i]; IF w=StrikeFormat.nullWidthEntry THEN i _ font.max-font.min+1; xl _ font.xtable[i]; xr _ font.xtable[i+1]; xo _ xl - (font.ox + w.offset); } ELSE {xo _ xl _ font.xtable[i]; xr _ font.xtable[i+1]}; xmin _ xl - xo; xmax _ xr - xo; ymin _ font.oy; ymax _ font.oy + font.dy }; BoundingBox: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [box: Font.Box] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key, OpenInitialization]; font: StrikeFont _ NARROW[file.GetData]; xmin, ymin, xmax, ymax: INTEGER; [xmin, ymin, xmax, ymax] _ IntBB[font, char]; box _ [xmin: xmin, xmax: xmax, ymin: font.oy, ymax: font.oy + font.dy]; }; WidthVector: PUBLIC PROCEDURE [key: Key, char: CHAR] RETURNS [Font.Pair] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[key, OpenInitialization]; font: StrikeFont _ NARROW[file.GetData]; RETURN [[font.width[char], 0]] }; <> ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { <> i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; SKMask: PROCEDURE [font: FONT, transformation: Transformation, char: CHAR, run: PROC [sMin, fMin: INTEGER, fSize: NAT]] = TRUSTED { file: UFFileManager.FontFile _ UFFileManager.Open[font.graphicsKey, OpenInitialization]; strikeFont: StrikeFont _ NARROW[file.GetData]; xLeft, xRight, xOrigin: INTEGER; c: NAT _ IF char IN [strikeFont.min..strikeFont.max] THEN char-strikeFont.min ELSE strikeFont.max-strikeFont.min+1; IF strikeFont.kerned THEN { w: StrikeFormat.WidthEntry _ strikeFont.wtable[c]; IF w=StrikeFormat.nullWidthEntry THEN c _ strikeFont.max-strikeFont.min+1; xLeft _ strikeFont.xtable[c]; xRight _ strikeFont.xtable[c+1]; xOrigin _ xLeft - (strikeFont.ox + w.offset); } ELSE {xOrigin _ xLeft _ strikeFont.xtable[c]; xRight _ strikeFont.xtable[c+1]}; { iSize: NAT _ strikeFont.dy; Bit: PROC [i, j: INTEGER] RETURNS [bit: [0..1]] ~ TRUSTED { j _ j + xLeft; IF i<0 OR i>=iSize OR j=xRight THEN bit _ 0 ELSE { jQuotient: NAT _ Inline.BITSHIFT[j, -Environment.logBitsPerWord]; jRem: NAT _ Inline.BITAND[j, Environment.bitsPerWord-1]; wordOffset: LONG CARDINAL _ Inline.LongMult[i, strikeFont.raster] + jQuotient; word: CARDINAL _ strikeFont.bitmap[wordOffset]; shiftedWord: CARDINAL _ Inline.BITSHIFT[word, jRem-(Environment.bitsPerWord-1)]; temp1, temp2: CARDINAL _ 0; temp1 _ Inline.BITAND[shiftedWord, 1]; temp2 _ temp1; bit _ temp1; }; }; baselineToTop: REAL _ strikeFont.oy + strikeFont.dy; ijToxy: Transformation _ Tran.Concat[Tran.Create[0, 1, 0, -1, 0, baselineToTop], Tran.Invert[font.actualTransformation]]; ijTosf: Transformation _ Tran.Concat[ijToxy, transformation]; bbox: ImagerBasic.IntRectangle _ Tran.TransformIntRectangle[[0, 0, iSize, xRight-xLeft], ijTosf]; sMin: INTEGER _ bbox.x; fMin: INTEGER _ bbox.y; sSize: NAT _ bbox.w; fSize: NAT _ bbox.h; nextPixel: ImagerBasic.Pair _ Tran.InverseTransformVec[[0, 1], ijTosf]; nextLine: ImagerBasic.Pair _ Tran.InverseTransformVec[[1, 0], ijTosf]; start: ImagerBasic.Pair _ Tran.InverseTransform[[0.5+sMin, 0.5+fMin], ijTosf]; iStart: Scaled.Value _ ScaledFromReal[start.x]; jStart: Scaled.Value _ ScaledFromReal[start.y]; iDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.x]; jDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.y]; iDeltaLine: Scaled.Value _ ScaledFromReal[nextLine.x]; jDeltaLine: Scaled.Value _ ScaledFromReal[nextLine.y]; FOR s: INTEGER IN [sMin..sMin+sSize) DO i: Scaled.Value _ iStart; j: Scaled.Value _ jStart; f: INTEGER _ fMin; WHILE ffStart THEN run[s, fStart, f-fStart]; ENDLOOP; iStart _ iStart.PLUS[iDeltaLine]; jStart _ jStart.PLUS[jDeltaLine]; ENDLOOP; }; }; SKBoundingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: Font.Box] = { box _ BoundingBox[font.graphicsKey, char]; }; SKWidthVector: PROCEDURE [font: FONT, char: CHAR] RETURNS [Font.Pair] = { RETURN [WidthVector[font.graphicsKey, char]]; }; SKContains: PROCEDURE [font: FONT, char: CHAR] RETURNS [BOOLEAN] = { RETURN[WidthVector[font.graphicsKey, char]#[0,0]]; }; SKGraphicsObjectInit: PROCEDURE [font: FONT] = { file: UFFileManager.FontFile _ UFFileManager.Open[font.graphicsKey, OpenInitialization]; strikeFont: StrikeFont _ NARROW[file.GetData]; font.fontGraphicsClass _ SKGraphicsClass; font.bc _ strikeFont.min; font.ec _ strikeFont.max; }; SKGraphicsClass: REF Font.FontGraphicsClassRec_NEW[Font.FontGraphicsClassRec_[ maskProc: SKMask, boundingBoxProc: SKBoundingBox, widthVectorProc: SKWidthVector, containsProc: SKContains ]]; <> LigatureOrKern: TYPE = Font.LigatureOrKern; Box: TYPE = Font.Box; SKLigKern: PROCEDURE [font: FONT, char1, char2: CHAR] RETURNS [ligatureOrKern: LigatureOrKern] = {RETURN[[neither[]]]}; SKFormattingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: Box] = { box _ BoundingBox[font.graphicsKey, char]; box.xmin _ 0; box.xmax _ WidthVector[font.graphicsKey, char].x; }; SKFormattingMetric: PROCEDURE [font: FONT, metric: ATOM, char: CHAR] RETURNS [REAL] = { RETURN[0.0]; }; SKFormattingObjectInit: PROCEDURE [font: FONT] = { font.fontFormattingClass _ SKFormattingClass; }; SKFormattingClass: REF Font.FontFormattingClassRec _ NEW[Font.FontFormattingClassRec_[ formattingBoxProc: SKFormattingBox, ligKernProc: SKLigKern, formattingMetricProc: SKFormattingMetric ]]; Font.RegisterFontGraphicsClass[$Strike, SKGraphicsObjectInit]; Font.RegisterFontFormattingClass[$Strike, SKFormattingObjectInit]; END.