DIRECTORY Ascii USING [NUL], Basics USING [bitsPerWord], UFont USING [Box, FONT, FontGraphicsClassRec, Key, PutProp, RegisterFontGraphicsClass, Transformation], Imager USING [MakePixelArrayFromBits], ImagerBasic USING [PathMapType, PixelArray, Pair], ImagerMasks, ImagerScanConverter USING [ConvertToRuns, CreatePath, DevicePath, PathProc], ImagerTransform USING [Concat, Contents, Scale2, TransformationRec, Translate], UFPressFontFormat USING [bcplFraction, bcplLONGPOINTER, BcplREAL, BcplSplineCommand, BcplSplineData, BcplToMesaFraction, BcplToMesaLongCardinal, BcplWidthSegment, BoundingBox, CharBboxDesc, CharDirDesc, drawCurveCode, drawToCode, endSplineCode, FontPrivateRec, LongCardinalFromBcplLongPointer, missingCharValue, moveToCode, newObjectCode, RasterDimension, RasterPointer, RawIndex, SplineDataDesc, SplineDirDesc], Real USING [MinusZero], RealConvert USING [BcplToIeee], Rope USING [FromProc, ROPE], UFFileManager USING [FontFile, GetData, InBounds, InitProc, Open, Pointer], UFPressFontReader USING [CharInfo, CharShapeRepresentation, CurveToProc, DrawAreaProc, ErrorCode, FontKey, LineToProc, MoveToProc]; PressFontImpl: CEDAR PROGRAM IMPORTS Imager, ImagerMasks, ImagerTransform, UFont, ImagerScanConverter, UFPressFontFormat, RealConvert, Rope, UFFileManager EXPORTS UFPressFontReader ~ BEGIN OPEN UFPressFontReader, F: UFPressFontFormat; ROPE: TYPE = Rope.ROPE; FONT: TYPE = UFont.FONT; Pair: TYPE = ImagerBasic.Pair; zero: REAL = Real.MinusZero; FontFile: TYPE = UFFileManager.FontFile; FontVector: TYPE = REF FontVectorRec; FontVectorRec: TYPE = RECORD [ names: LIST OF CodeNamePair, fonts: SEQUENCE length: NAT OF PressFontRec ]; CodeNamePair: TYPE = RECORD [ code: [0..256), name: ROPE ]; PressFontRec: TYPE = RECORD [ rawIndexPtr: LONG POINTER, procs: Procs, data: REF ]; Procs: TYPE = REF ProcsRec; ProcsRec: TYPE = RECORD [ charInfoProc: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] ]; Object: TYPE = RECORD [procs: Procs, data: REF]; FontPrivateRec: TYPE = F.FontPrivateRec; Error: PUBLIC SIGNAL[errorCode: ErrorCode, fontFileWordOffset: INT] = CODE; OpenInitialization: UFFileManager.InitProc = TRUSTED { fontVector: FontVector; fontCount: NAT _ 0; countProc: VisitProc = TRUSTED { SELECT p.hdr.type FROM splines, chars, widths => { fontCount _ fontCount+1; }; ENDCASE; }; saveProc: VisitProc = TRUSTED { SELECT p.hdr.type FROM splines, chars, widths => { fontVector[fontCount].rawIndexPtr _ p; fontCount _ fontCount+1; }; name => { pp: LONG POINTER _ p; nameIndex: LONG POINTER TO name F.RawIndex _ pp; fontVector.names _ CONS[[nameIndex.code, ExtractName[nameIndex]], fontVector.names] }; ENDCASE; }; EnumerateIndex[fontFile, countProc]; fontVector _ NEW[FontVectorRec[fontCount]]; fontCount _ 0; EnumerateIndex[fontFile, saveProc]; RETURN[fontVector]; }; NameFromCode: PROC [names: LIST OF CodeNamePair, code: [0..256)] RETURNS [ROPE] = { FOR n: LIST OF CodeNamePair _ names, n.rest UNTIL n=NIL DO IF n.first.code = code THEN RETURN [n.first.name]; ENDLOOP; RETURN [NIL] }; ExtractName: PROC [p: LONG POINTER TO name F.RawIndex] RETURNS [rope: ROPE] = TRUSTED { i: NAT _ 1; len: [0..20) _ p.fontname[0]-Ascii.NUL; charProc: SAFE PROC RETURNS [char: CHAR] = TRUSTED { char _ p.fontname[i]; i _ i + 1; }; rope _ Rope.FromProc[len, charProc]; }; VisitProc: TYPE = PROC [openFile: FontFile, p: LONG POINTER TO F.RawIndex] RETURNS [quit: BOOLEAN _ FALSE]; EnumerateIndex: PROC [openFile: FontFile, visitProc: VisitProc] = TRUSTED { p: LONG POINTER TO F.RawIndex _ openFile.Pointer; offset: INT _ 0; DO p: LONG POINTER TO F.RawIndex _ openFile.Pointer[] + offset; IF NOT openFile.InBounds[p, p.hdr.length] THEN ERROR Error[fontIndexTooLong, offset]; IF visitProc[openFile, p] THEN EXIT; IF p.hdr.type = end THEN EXIT; offset _ offset + p.hdr.length; ENDLOOP }; NumberOfFontsInFile: PUBLIC PROC [fileKey: UFont.Key] RETURNS [NAT] = { file: FontFile _ UFFileManager.Open[fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; RETURN[data.length]; }; Family: PUBLIC PROC [fontKey: FontKey] RETURNS [family: ROPE] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; family _ NameFromCode[data.names, p.family]; }; Face: PUBLIC PROC [fontKey: FontKey] RETURNS [face: [0..256)] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; face _ p.face; }; Range: PUBLIC PROC [fontKey: FontKey] RETURNS [bc, ec: CHAR] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; bc _ p.bc; ec _ p.ec; }; Size: PUBLIC PROC [fontKey: FontKey] RETURNS [size: REAL] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; size _ p.size/100000.0; }; Rotation: PUBLIC PROC [fontKey: FontKey] RETURNS [rotation: REAL] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; rotation _ p.rotation/60.0; }; Representation: PUBLIC PROC [fontKey: FontKey] RETURNS [representation: CharShapeRepresentation] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; representation _ SELECT p.hdr.type FROM splines => outline, chars => raster, widths => widthsOnly, ENDCASE => ERROR; }; Resolution: PUBLIC PROC [fontKey: FontKey] RETURNS [xRes, yRes: REAL] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; p: LONG POINTER TO chars F.RawIndex _ data[fontKey.fontIndex].rawIndexPtr; IF p.hdr.type # chars THEN {xRes _ yRes _ zero} ELSE { xRes _ p.resolutionx/10.0; yRes _ p.resolutiony/10.0; } }; GetCharInfo: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [info: CharInfo] = TRUSTED { fontFile: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; object: Object _ GetObject[fontFile, fontKey]; info _ object.procs.charInfoProc[object.data, char]; }; CharPathData: TYPE = RECORD [fontKey: FontKey, char: CHAR]; GenerateCharPath: PROC [ data: REF, move: PROC [Pair], line: PROC [Pair], curve: PROC [Pair, Pair, Pair], conic: PROC [Pair, Pair, REAL] ] = { moveToProc: UFPressFontReader.MoveToProc = {move[[x, y]]}; lineToProc: UFPressFontReader.LineToProc = {line[[x, y]]}; curveToProc: UFPressFontReader.CurveToProc = {curve[[x1, y1], [x2, y2], [x3, y3]]}; drawAreaProc: UFPressFontReader.DrawAreaProc = {}; d: REF CharPathData _ NARROW[data]; GetCharOutline[d.fontKey, d.char, moveToProc, lineToProc, curveToProc, drawAreaProc]; }; GetCharPath: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [pathMap: ImagerBasic.PathMapType, pathData: REF] = { RETURN[pathMap: GenerateCharPath, pathData: NEW[CharPathData _ [fontKey, char]]]; }; GetObject: PROC [fontFile: FontFile, fontKey: FontKey] RETURNS [object: Object] = TRUSTED { fontVector: FontVector _ NARROW[fontFile.GetData[]]; charsIndexPtr: LONG POINTER TO chars F.RawIndex _ fontVector[fontKey.fontIndex].rawIndexPtr; IF fontVector[fontKey.fontIndex].data = NIL THEN { charsIndexPtr: LONG POINTER TO chars F.RawIndex _ fontVector[fontKey.fontIndex].rawIndexPtr; SELECT charsIndexPtr.hdr.type FROM chars => MakeCharsObject[fontFile, fontVector, fontKey]; splines => MakeSplinesObject[fontFile, fontVector, fontKey]; widths => MakeWidthsObject[fontFile, fontVector, fontKey]; ENDCASE => ERROR; }; object _ [fontVector[fontKey.fontIndex].procs, fontVector[fontKey.fontIndex].data]; }; RasterData: TYPE = REF RasterDataRec; RasterDataRec: TYPE = RECORD [ indexEntry: LONG POINTER TO chars F.RawIndex, dataDesc: F.CharBboxDesc, dirDesc: F.CharDirDesc, rasterStart, rasterEnd: LONG CARDINAL, base: LONG POINTER ]; MakeCharsObject: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED { rasterData: RasterData _ NEW[RasterDataRec]; ix: LONG POINTER TO chars F.RawIndex _ fontVector[fontKey.fontIndex].rawIndexPtr; base: LONG POINTER _ fontFile.Pointer[]; nChars: NAT _ ix.ec - ix.bc + 1; dataOffset, dirOffset, rasterOffset: LONG CARDINAL; IF ix.hdr.type # chars THEN ERROR Error[cantHappen, ix-base]; dataOffset _ F.LongCardinalFromBcplLongPointer[ix.startaddress]; dirOffset _ dataOffset + nChars*SIZE[F.BoundingBox]; rasterOffset _ dirOffset + nChars*SIZE[F.bcplLONGPOINTER]; IF rasterOffset - dataOffset > F.BcplToMesaLongCardinal[ix.length] THEN ERROR Error[dataSegmentTooLong, dataOffset]; rasterData.indexEntry _ ix; rasterData.rasterStart _ rasterOffset; rasterData.rasterEnd _ dataOffset + F.BcplToMesaLongCardinal[ix.length]; rasterData.dataDesc _ DESCRIPTOR[base+dataOffset, nChars]; rasterData.dirDesc _ DESCRIPTOR[base+dirOffset, nChars]; rasterData.base _ base; fontVector[fontKey.fontIndex].procs _ charsProcs; fontVector[fontKey.fontIndex].data _ rasterData; }; charsProcs: Procs _ NEW[ProcsRec _ [ charInfoProc: CharsCharInfo ]]; CharsCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED { rasterData: RasterData _ NARROW[data]; ix: LONG POINTER TO chars F.RawIndex _ rasterData.indexEntry; IF ix.hdr.type # chars THEN ERROR Error[cantHappen, 0]; IF (NOT char IN [ix.bc..ix.ec]) THEN RETURN[[zero,zero,zero,zero,zero,zero]] ELSE { data: F.BoundingBox _ rasterData.dataDesc[char-ix.bc]; xscale: REAL _ 25400.0/(INT[ix.resolutionx]*ix.size); yscale: REAL _ 25400.0/(INT[ix.resolutiony]*ix.size); IF data.BBdy = LAST[CARDINAL] THEN RETURN[[zero,zero,zero,zero,zero,zero]]; info.widthX _ BcplFractionToMesaReal[data.xwidth]*xscale; info.widthY _ BcplFractionToMesaReal[data.ywidth]*yscale; info.minX _ data.BBox*xscale; info.minY _ data.BBoy*yscale; info.maxX _ data.BBdx*xscale + info.minX; info.maxY _ data.BBdy*yscale + info.minY; }; }; GetCharRaster: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [pa: ImagerBasic.PixelArray] ~ TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; object: Object _ GetObject[file, fontKey]; rasterData: RasterData _ NARROW[object.data]; ix: LONG POINTER TO chars F.RawIndex _ rasterData.indexEntry; data: F.BoundingBox _ rasterData.dataDesc[char-ix.bc]; offset: LONG CARDINAL _ F.LongCardinalFromBcplLongPointer[rasterData.dirDesc[char-ix.bc]]; rawRasterPointer: LONG POINTER _ BASE[rasterData.dirDesc] + offset; r: F.RasterPointer _ rawRasterPointer; rawRasterPointer _ rawRasterPointer + SIZE[F.RasterDimension]; IF data.BBdy = LAST[CARDINAL] THEN RETURN[NIL]; IF offset = LAST[LONG CARDINAL] THEN RETURN[NIL]; IF (rawRasterPointer-rasterData.base) < rasterData.rasterStart OR ((rawRasterPointer-rasterData.base) >= rasterData.rasterEnd AND MIN[data.BBdy, data.BBdx] > 0) THEN ERROR Error[invalidPointerInFile, @(rasterData.dirDesc[char-ix.bc]) - rasterData.base]; pa _ Imager.MakePixelArrayFromBits[ bitPointer: rawRasterPointer, bitsPerLine: r.height*Basics.bitsPerWord, samplesPerLine: data.BBdy, numberOfLines: data.BBdx ]; pa.m _ ImagerTransform.Translate[data.BBox, data.BBoy]; }; SplinesData: TYPE = REF SplinesDataRec; SplinesDataRec: TYPE = RECORD [ indexEntry: LONG POINTER TO splines F.RawIndex, dataDesc: F.SplineDataDesc, dirDesc: F.SplineDirDesc, commandStart, commandEnd: LONG CARDINAL, fontKey: FontKey, base: LONG POINTER ]; MakeSplinesObject: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED { splinesData: SplinesData _ NEW[SplinesDataRec]; ix: LONG POINTER TO splines F.RawIndex _ fontVector[fontKey.fontIndex].rawIndexPtr; base: LONG POINTER _ fontFile.Pointer[]; nChars: NAT _ ix.ec - ix.bc + 1; dataOffset, dirOffset, commandOffset: LONG CARDINAL; IF ix.hdr.type # splines THEN ERROR Error[cantHappen, ix-base]; dataOffset _ F.LongCardinalFromBcplLongPointer[ix.startaddress]; dirOffset _ dataOffset + nChars*SIZE[F.BcplSplineData]; commandOffset _ dirOffset + nChars*SIZE[F.bcplLONGPOINTER]; IF commandOffset - dataOffset > F.BcplToMesaLongCardinal[ix.length] THEN ERROR Error[dataSegmentTooLong, dataOffset]; splinesData.indexEntry _ ix; splinesData.commandStart _ commandOffset; splinesData.commandEnd _ dataOffset + F.BcplToMesaLongCardinal[ix.length]; splinesData.dataDesc _ DESCRIPTOR[base+dataOffset, nChars]; splinesData.dirDesc _ DESCRIPTOR[base+dirOffset, nChars]; splinesData.base _ base; splinesData.fontKey _ fontKey; fontVector[fontKey.fontIndex].procs _ splinesProcs; fontVector[fontKey.fontIndex].data _ splinesData; }; splinesProcs: Procs _ NEW[ProcsRec _ [ charInfoProc: SplinesCharInfo ]]; SplinesCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED { splinesData: SplinesData _ NARROW[data]; ix: LONG POINTER TO splines F.RawIndex _ splinesData.indexEntry; IF ix.hdr.type # splines THEN ERROR Error[cantHappen, 0]; IF (NOT char IN [ix.bc..ix.ec]) OR splinesData.dataDesc[char-ix.bc].xwidth = F.missingCharValue THEN RETURN[[zero,zero,zero,zero,zero,zero]] ELSE { data: F.BcplSplineData _ splinesData.dataDesc[char-ix.bc]; info.widthX _ BcplToMesaReal[data.xwidth]; info.widthY _ BcplToMesaReal[data.ywidth]; info.minX _ BcplToMesaReal[data.bbox]; info.minY _ BcplToMesaReal[data.bboy]; info.maxX _ BcplToMesaReal[data.rightx]; info.maxY _ BcplToMesaReal[data.topy]; }; }; BcplToMesaReal: PROC [b: F.BcplREAL] RETURNS [r: REAL] = TRUSTED { r _ RealConvert.BcplToIeee[LOOPHOLE[b]] }; BcplFractionToMesaReal: PROC [f: F.bcplFraction] RETURNS [r: REAL] = TRUSTED { r _ F.BcplToMesaFraction[f]/(LAST[CARDINAL]+1.0); }; GetCharOutline: PUBLIC PROC [ fontKey: FontKey, char: CHAR, moveToProc: MoveToProc, lineToProc: LineToProc, curveToProc: CurveToProc, drawAreaProc: DrawAreaProc ] = TRUSTED { p: LONG POINTER; curX, curY: REAL _ 0.0; file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; object: Object _ GetObject[file, fontKey]; splinesData: SplinesData _ NARROW[object.data]; bc: CHAR _ splinesData.indexEntry.bc; IF splinesData.indexEntry.hdr.type # splines THEN ERROR Error[cantHappen, 0]; IF NOT char IN [bc..splinesData.indexEntry.ec] THEN RETURN; IF splinesData.dataDesc[char-bc].xwidth = F.missingCharValue THEN RETURN; p _ BASE[splinesData.dirDesc] + F.LongCardinalFromBcplLongPointer[splinesData.dirDesc[char-bc]]; DO splineCommand: LONG POINTER TO F.BcplSplineCommand _ p; IF (p-splinesData.base) < splinesData.commandStart OR (p-splinesData.base) >= splinesData.commandEnd THEN ERROR Error[invalidPointerInFile, @(splinesData.dirDesc[char-bc])-splinesData.base]; SELECT splineCommand.type FROM F.moveToCode => {cmd: LONG POINTER TO MoveTo F.BcplSplineCommand _ p; moveToProc[curX _ BcplToMesaReal[cmd.x], curY _ BcplToMesaReal[cmd.y]]; p _ p + SIZE[MoveTo F.BcplSplineCommand]; }; F.drawToCode => {cmd: LONG POINTER TO DrawTo F.BcplSplineCommand _ p; lineToProc[curX _ BcplToMesaReal[cmd.x], curY _ BcplToMesaReal[cmd.y]]; p _ p + SIZE[DrawTo F.BcplSplineCommand]; }; F.drawCurveCode => {cmd: LONG POINTER TO DrawCurve F.BcplSplineCommand _ p; b: Bezier _ CoeffsToBezier[[ [curX, curY], [BcplToMesaReal[cmd.x0], BcplToMesaReal[cmd.y0]], [BcplToMesaReal[cmd.x1], BcplToMesaReal[cmd.y1]], [BcplToMesaReal[cmd.x2], BcplToMesaReal[cmd.y2]] ]]; [curX, curY] _ b.b3; curveToProc[b.b1.x, b.b1.y, b.b2.x, b.b2.y, b.b3.x, b.b3.y]; p _ p + SIZE[DrawCurve F.BcplSplineCommand]; }; F.newObjectCode => { drawAreaProc[]; p _ p + SIZE[NewObject F.BcplSplineCommand]; }; F.endSplineCode => { drawAreaProc[]; EXIT }; ENDCASE => ERROR Error[invalidCodeInFile, p-splinesData.base]; ENDLOOP }; Vec: TYPE = RECORD[x, y: REAL]; VecAdd: PROC [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x+b.x,a.y+b.y]]}; VecSub: PROC [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x-b.x,a.y-b.y]]}; VecDiv: PROC [a: Vec, s: REAL] RETURNS [Vec] = INLINE {RETURN[[a.x/s,a.y/s]]}; Coeffs: TYPE = RECORD[c0,c1,c2,c3: Vec]; Bezier: TYPE = RECORD[b0,b1,b2,b3: Vec]; CoeffsToBezier: PROC [c: Coeffs] RETURNS [b: Bezier] = { OPEN b,c; b0_c0; b1_VecAdd[c0,VecDiv[c1,3]]; b2_VecAdd[b1,VecDiv[VecAdd[c1,c2],3]]; b3_VecAdd[VecAdd[VecAdd[c0,c1],c2],c3]; }; WidthsData: TYPE = REF WidthsDataRec; WidthsDataRec: TYPE = RECORD [ indexEntry: LONG POINTER TO widths F.RawIndex, widthSeg: LONG POINTER TO F.BcplWidthSegment, xWidthDesc: LONG DESCRIPTOR FOR ARRAY OF CARDINAL, yWidthDesc: LONG DESCRIPTOR FOR ARRAY OF CARDINAL ]; MakeWidthsObject: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED { widthsData: WidthsData _ NEW[WidthsDataRec]; ix: LONG POINTER TO widths F.RawIndex _ fontVector[fontKey.fontIndex].rawIndexPtr; base: LONG POINTER _ fontFile.Pointer[]; nChars: NAT _ ix.ec - ix.bc + 1; segOffset, xWidthOffset, yWidthOffset: LONG CARDINAL; nx, ny: NAT; IF ix.hdr.type # widths THEN ERROR Error[cantHappen, ix-base]; segOffset _ F.LongCardinalFromBcplLongPointer[ix.startaddress]; widthsData.indexEntry _ ix; widthsData.widthSeg _ base + segOffset; nx _ IF widthsData.widthSeg.XWidthFixed THEN 1 ELSE nChars; ny _ IF widthsData.widthSeg.YWidthFixed THEN 1 ELSE nChars; xWidthOffset _ segOffset + SIZE[F.BcplWidthSegment]; yWidthOffset _ xWidthOffset + nx; widthsData.xWidthDesc _ DESCRIPTOR[fontFile.Pointer[]+xWidthOffset, nx]; widthsData.yWidthDesc _ DESCRIPTOR[fontFile.Pointer[]+yWidthOffset, ny]; IF yWidthOffset + ny - segOffset # F.BcplToMesaLongCardinal[ix.length] THEN ERROR Error [consistencyCheck, ix-fontFile.Pointer[]]; fontVector[fontKey.fontIndex].procs _ widthsProcs; fontVector[fontKey.fontIndex].data _ widthsData; }; widthsProcs: Procs _ NEW[ProcsRec _ [ charInfoProc: WidthsCharInfo ]]; WidthsCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED { widthsData: WidthsData _ NARROW[data]; ix: LONG POINTER TO widths F.RawIndex _ widthsData.indexEntry; IF ix.hdr.type # widths THEN ERROR Error[cantHappen, 0]; IF (NOT char IN [ix.bc..ix.ec]) THEN RETURN[[zero,zero,zero,zero,zero,zero]] ELSE { data: F.BcplWidthSegment _ widthsData.widthSeg^; scale: REAL _ IF ix.size = 0 THEN 0.001 ELSE 1.0/ix.size; wx: INTEGER _ widthsData.xWidthDesc[IF data.XWidthFixed THEN 0 ELSE char-ix.bc]; wy: INTEGER _ widthsData.yWidthDesc[IF data.YWidthFixed THEN 0 ELSE char-ix.bc]; IF wx = FIRST[INTEGER] OR wy = FIRST[INTEGER] THEN RETURN[[zero,zero,zero,zero,zero,zero]]; info.widthX _ wx*scale; info.widthY _ wy*scale; info.minX _ data.FBBox*scale; info.minY _ data.FBBoy*scale; info.maxX _ data.FBBdx*scale + info.minX; info.maxY _ data.FBBdy*scale + info.minY; }; }; pfClass: Font.Class ~ NEW[Font.ClassRep _ [ Contains: PFContains, NextChar: PFNextChar, Width: PFWidth, Amplified: PFAmplified, Correction: PFCorrection, BoundingBox: PFBoundingBox, FontBoundingBox: PFFontBoundingBox, GetLigature: PFGetLigature, NextLigature: PFNextLigature, GetKern: PFGetKern, NextKern: PFNextKern, CharInfo: PFCharInfo, DrawChar: PFDrawChar ]]; PFContains: PROC[font: FONT, char: CharCode] RETURNS[BOOLEAN] = { data: Data ~ NARROW[font.data]; IF char IN[0..255] THEN { charInfo: CharInfo _ GetCharInfo[[data.graphicsKey, 0], VAL[char]]; RETURN[charInfo#[zero,zero,zero,zero,zero,zero]]; }; RETURN[FALSE]; }; PFWidth: PROC[font: FONT, char: CharCode] RETURNS[Pair] = { data: Data ~ NARROW[font.data]; IF char IN[0..255] THEN { charInfo: CharInfo ~ GetCharInfo[[data.graphicsKey, 0], VAL[char]]; p: Vec ~ [charInfo.widthX, charInfo.widthY]; RETURN[ImagerTransform.Transform[p, font.transformation]]; }; RETURN[[0, 0]]; }; PFDrawChar: PROC[font: FONT, char: CharCode, context: REF] ~ { data: Data ~ NARROW[font.data]; imager: Imager.Context ~ NARROW[context]; SELECT Representation[[data.graphicsKey, 0]] FROM outline => { GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc]; }; }; raster => { pa: ImagerBasic.PixelArray _ GetCharRaster[[font.graphicsKey, 0], char]; mask: ImagerMasks.Mask; bb: ImagerMasks.Mask; size, xRes, yRes: REAL; bitsPerEmX, bitsPerEmY: REAL; IF pa = NIL THEN RETURN; size _ Size[[font.graphicsKey, 0]]; -- in meters [xRes, yRes] _ Resolution[[font.graphicsKey, 0]]; -- in bits per inch bitsPerEmX _ xRes * size / 0.0254; bitsPerEmY _ yRes * size / 0.0254; mask _ ImagerMasks.FromPixelArray[pa, pa.m.Concat[ImagerTransform.Scale2[1/bitsPerEmX, 1/bitsPerEmY]].Concat[transformation]]; bb _ ImagerMasks.FromRectangle[ImagerMasks.BoundingBox[mask]]; ImagerMasks.GenerateRuns[mask: mask, clipper: bb, runProc: run]; }; ENDCASE => NULL; }; PFBoundingBox: PROC [font: FONT, char: CHAR] RETURNS [box: UFont.Box] = { m: ImagerTransform.TransformationRec _ font.actualTransformation.Contents; B: PROC[p: Vec, box: UFont.Box] RETURNS [UFont.Box] = { x: REAL _ m.a * p.x + m.b * p.y + m.c; y: REAL _ m.d * p.x + m.e * p.y + m.f; RETURN [[ xmin: MIN[box.xmin, x], ymin: MIN[box.ymin, y], xmax: MAX[box.xmax, x], ymax: MAX[box.ymax, y] ]] }; IF Representation[[font.graphicsKey, 0]] = outline THEN { moveToProc: UFPressFontReader.MoveToProc ~ { box _ B[[x, y], box] }; lineToProc: UFPressFontReader.LineToProc ~ { box _ B[[x, y], box] }; curveToProc: UFPressFontReader.CurveToProc ~ { box _ B[[x1, y1], B[[x2, y2], B[[x3, y3], box]]]; }; drawAreaProc: UFPressFontReader.DrawAreaProc = {}; box _ [xmin: 999999, ymin: 999999, xmax: -999999, ymax: -999999]; GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc]; IF box=[xmin: 999999, ymin: 999999, xmax: -999999, ymax: -999999] THEN box _ [0,0,0,0]; } ELSE { charInfo: CharInfo _ GetCharInfo[[font.graphicsKey, 0], char]; x0: REAL _ charInfo.minX; y0: REAL _ charInfo.minY; x1: REAL _ charInfo.maxX; y1: REAL _ charInfo.maxY; box _ B[[x0,y0],B[[x0,y1],B[[x1,y0],[x1,y1,x1,y1]]]]; }; }; PFObjectInit: PROC [font: FONT] = { font.fontGraphicsClass _ PFClass; [font.bc, font.ec] _ Range[[font.graphicsKey, 0]]; font.PutProp[$PressFont, $PressFont]; }; PFClass: REF UFont.FontGraphicsClassRec_NEW[UFont.FontGraphicsClassRec_[ maskProc: PFMask, boundingBoxProc: PFBoundingBox, widthVectorProc: PFWidthVector, containsProc: PFContains ]]; UFont.RegisterFontGraphicsClass[$Sd, PFObjectInit]; UFont.RegisterFontGraphicsClass[$Ac, PFObjectInit]; END. 4PressFontImpl.mesa Copyright c 1983, 1984, Xerox Corporation. All rights reserved. Created January 17, 1983 Last edit by Michael Plass on April 23, 1984 3:38:40 pm PST Doug Wyatt, August 7, 1984 1:09:37 pm PDT A press font file can contain many fonts; the FontVector records where each one starts. The names and raw index pointers are computed when the font file is opened, and the font objects are filled in as needed. Strings in press font files are represented by one-byte codes; this list records the correspondence between codes and names for the file. This gets called when the font file is first opened; its responsibility is to initialize the FontVector that is recorded by the file manager. Raster format operations. CharsDrawChar: PROC [data: REF, context: Graphics.Context, char: CHAR] = TRUSTED { rasterData: RasterData _ NARROW[data]; ix: LONG POINTER TO chars F.RawIndex _ rasterData.indexEntry; scalex: REAL _ IF ix.size = 0 THEN 1.0 ELSE 25400.0/(INT[ix.resolutionx]*ix.size); scaley: REAL _ IF ix.size = 0 THEN 1.0 ELSE 25400.0/(INT[ix.resolutiony]*ix.size); Graphics.Scale[context, scalex, scaley]; Graphics.Rotate[context, - ix.rotation/60.0]; DrawCharRasterPrivate[context, rasterData, char]; }; DrawCharRasterPrivate: PROC [context: Graphics.Context, rasterData: RasterData, char: CHAR] = TRUSTED { ix: LONG POINTER TO chars F.RawIndex _ rasterData.indexEntry; data: F.BoundingBox _ rasterData.dataDesc[char-ix.bc]; offset: LONG CARDINAL _ F.LongCardinalFromBcplLongPointer[rasterData.dirDesc[char-ix.bc]]; rawRasterPointer: LONG POINTER _ BASE[rasterData.dirDesc] + offset; r: F.RasterPointer _ rawRasterPointer; rawRasterPointer _ rawRasterPointer + SIZE[F.RasterDimension]; IF data.BBdy = LAST[CARDINAL] THEN RETURN; IF offset = LAST[LONG CARDINAL] THEN RETURN; IF (rawRasterPointer-rasterData.base) < rasterData.rasterStart OR (rawRasterPointer-rasterData.base) >= rasterData.rasterEnd THEN ERROR Error[invalidPointerInFile, @(rasterData.dirDesc[char-ix.bc]) - rasterData.base]; Graphics.SetCP[self: context, x: data.BBox, y: data.BBoy, rel: TRUE]; Graphics.Rotate[context, 90]; CGPrivate.DrawBits[ self: context, base: rawRasterPointer, raster: r.height, bitsPerPixel: 0, x: 0, y: 0, w: data.BBdy, h: data.BBdx, xorigin:0, yorigin: 0 ]; }; Spline format operations. Routines for converting to bezier points. Widths format operations. Font Class interface PFMask: ENTRY PROC [font: FONT, transformation: UFont.Transformation, char: CHAR, run: PROC [sMin, fMin: INTEGER, fSize: NAT]] = { ENABLE UNWIND => NULL; SELECT Representation[[font.graphicsKey, 0]] FROM outline => { GenPath: ImagerScanConverter.PathProc -- move: PROC [s, f: REAL], -- -- line: PROC [s, f: REAL], -- -- curve: PROC [s1, f1, s2, f2, s3, f3: REAL] -- = { m: ImagerTransform.TransformationRec ~ ImagerTransform.Contents[transformation]; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.a * p.x + m.b * p.y + m.c, m.d * p.x + m.e * p.y + m.f ]]}; moveToProc: UFPressFontReader.MoveToProc ~ { p: Pair = Xform[[x, y]]; move[p.x, p.y] }; lineToProc: UFPressFontReader.LineToProc ~ { p: Pair = Xform[[x, y]]; line[p.x, p.y] }; curveToProc: UFPressFontReader.CurveToProc ~ { p1: Pair = Xform[[x1, y1]]; p2: Pair = Xform[[x2, y2]]; p3: Pair = Xform[[x3, y3]]; curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y] }; drawAreaProc: UFPressFontReader.DrawAreaProc = {}; GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc]; }; devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: [-16000, -16000, 32000, 32000], scratch: devicePath]; devicePath.ConvertToRuns[runProc: run, clipBox: [-16000, -16000, 32000, 32000], parityFill: TRUE]; }; raster => { pa: ImagerBasic.PixelArray _ GetCharRaster[[font.graphicsKey, 0], char]; mask: ImagerMasks.Mask; bb: ImagerMasks.Mask; size, xRes, yRes: REAL; bitsPerEmX, bitsPerEmY: REAL; IF pa = NIL THEN RETURN; size _ Size[[font.graphicsKey, 0]]; -- in meters [xRes, yRes] _ Resolution[[font.graphicsKey, 0]]; -- in bits per inch bitsPerEmX _ xRes * size / 0.0254; bitsPerEmY _ yRes * size / 0.0254; mask _ ImagerMasks.FromPixelArray[pa, pa.m.Concat[ImagerTransform.Scale2[1/bitsPerEmX, 1/bitsPerEmY]].Concat[transformation]]; bb _ ImagerMasks.FromRectangle[ImagerMasks.BoundingBox[mask]]; ImagerMasks.GenerateRuns[mask: mask, clipper: bb, runProc: run]; }; ENDCASE => NULL; }; work harder to get a tighter bounding box Ê–– "Cedar" style˜šœ™Jšœ Ïmœ5™@Jšœ™Jšœ;™;Jšœ)™)J™—šÏk ˜ Jšœžœžœ˜Jšœžœ˜JšœžœžœQ˜gJšœžœ˜&Jšœ žœ!˜2J˜ Jšœžœ3˜LJšœžœ:˜OJšœžœ…˜œJšœžœ ˜Jšœ žœ˜Jšœžœ žœ˜Jšœžœ8˜KJšœžœl˜ƒJ˜—Jšœžœž˜Jšžœv˜}Jšžœ˜Jšœžœžœžœ˜5J˜Jšžœžœžœ˜Jšžœžœ žœ˜Jšœžœ˜Jšœžœ˜J˜Jšœ žœ˜(J˜Jšœ žœžœ˜%šœžœžœ˜Jšœžœžœ˜Jšœžœ žœžœ˜,Jšœ˜JšœÒ™ÒJ˜—šœžœžœ˜J˜Jšœž˜ Jšœ˜J™‰J˜—šœžœžœ˜Jšœ žœžœ˜J˜ Jšœž˜ Jšœ˜J˜—Jšœžœžœ ˜šœ žœžœ˜Jš œžœžœžœžœ˜CJ˜J˜—Jšœžœžœžœ˜0Jšœžœžœ˜(J˜Jš Ïnœžœžœ+žœžœ˜KJ˜šŸœžœ˜6Jšœ™Jšœ˜Jšœ žœ˜šœžœ˜ šžœ ž˜˜J˜J˜—Jšžœ˜—J˜—šœžœ˜šžœ ž˜˜Jšœ&˜&J˜J˜—šœ ˜ Jšœžœžœ˜Jš œ žœžœžœžœ˜0Jšœžœ<˜SJšœ˜—Jšžœ˜—J˜—Jšœ$˜$Jšœ žœ˜+Jšœ˜Jšœ#˜#Jšžœ ˜J˜J˜—š Ÿ œžœ žœžœžœžœ˜Sš žœžœžœžœžœž˜:Jšžœžœžœ˜2Jšžœ˜—Jšžœžœ˜ J˜J˜—š Ÿ œžœžœžœžœ˜6Jšžœžœžœ˜ Jšœžœ˜ Jšœ#žœ˜'š œ žœžœžœžœžœ˜4Jšœ#˜#—Jšœ$˜$J˜J˜—š Ÿ œžœžœžœžœžœ ˜JJšžœžœžœ˜ J˜—šŸœžœ.žœ˜KJš œžœžœžœžœ˜1Jšœžœ˜šž˜Jš œžœžœžœžœ(˜Jš žœ žœžœžœžœžœ˜/Jšžœ žœžœžœžœžœžœ˜1Jš žœ=žœ=žœžœžœžœR˜ýšœ#˜#Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ˜—Jšœ7˜7Jšœ˜J˜—š Ÿ œžœžœ#žœžœ™RJšœžœ™&Jš œžœžœžœžœ"™=Jš œžœžœ žœžœ žœ™RJš œžœžœ žœžœ žœ™RJšœ(™(Jšœ-™-Jšœ1™1J™J™—šŸœžœ;žœžœ™gJš œžœžœžœžœ"™=Jšœžœ/™6JšœžœžœžœA™ZJšœžœžœžœ™CJšœžœ"™&Jšœ&žœžœ™>Jš žœ žœžœžœžœ™*Jš žœ žœžœžœžœžœ™,Jšžœ=žœ<žœžœR™ÙJšœ?žœ™EJšœ™Jšœž™žJšœ™J™——šŸ™J˜Jšœ žœžœ˜'šœžœžœ˜Jš œ žœžœžœ žœ ˜/Jšœ žœ˜Jšœ žœ˜Jšœžœžœ˜(J˜Jšœžœž˜J˜J˜—šŸœžœBžœ˜bJšœžœ˜/Jš œžœžœžœ žœ6˜SJšœžœžœ˜(Jšœžœ˜ Jšœ&žœžœ˜4Jšžœžœžœ˜?Jšœ žœ2˜@Jšœ žœžœ˜7Jšœ#žœžœ˜;šžœžœ#ž˜HJšžœ'˜,—Jšœ˜Jšœ)˜)Jšœ&žœ#˜JJšœž œ˜;Jšœž œ˜9Jšœ˜Jšœ˜Jšœ3˜3Jšœ1˜1Jšœ˜J˜—šœžœ ˜&Jšœ˜J˜J˜—š Ÿœžœžœžœžœžœ˜RJšœžœ˜(Jš œžœžœžœ žœ#˜@Jšžœžœžœ˜9Jšžœžœžœžœ+žœžœžœ!˜Œšžœ˜Jšœžœ3˜:Jšœ*˜*Jšœ*˜*Jšœ&˜&Jšœ&˜&Jšœ(˜(Jšœ&˜&J˜—Jšœ˜J˜—š Ÿœžœžœ žœžœžœ˜BJšœžœ˜'J˜J˜—š Ÿœžœžœžœžœžœ˜NJšœžœžœžœ˜1J˜J˜—šŸœžœžœ˜Jšœ˜Jšœžœ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœžœ˜ Jšœžœžœ˜Jšœ žœ˜JšœI˜IJšœ*˜*Jšœžœ˜/Jšœžœ˜%Jšžœ+žœžœ˜MJš žœžœžœ!žœžœ˜;Jšžœ(žœžœžœ˜IJšœ žœ?˜`š žœžœžœžœžœ˜:Jšžœ0žœ/žœžœN˜¾šžœž˜š œžœžœžœžœ˜EJšœG˜GJšœžœžœ˜)Jšœ˜—š œžœžœžœžœ˜EJšœG˜GJšœžœžœ˜)Jšœ˜—š œžœžœžœ žœ˜Kšœ˜Jšœ ˜ Jšœ1˜1Jšœ1˜1Jšœ0˜0J˜—Jšœ˜Jšœ=˜=Jšœžœ žœ˜,Jšœ˜—šœ˜Jšœ˜Jšœžœ žœ˜,Jšœ˜—šœ˜Jšœ˜Jšž˜Jšœ˜—Jšžœžœ.˜>—Jšž˜—J˜J˜——šÏb)™)J˜Jšœžœžœžœ˜Jš Ÿœžœžœ žœžœ˜QJš Ÿœžœžœ žœžœ˜QJš Ÿœžœ žœžœ žœžœ˜NJšœžœžœ˜(Jšœžœžœ˜(šŸœžœ žœ˜8Jšžœ˜ J˜J˜J˜&J˜'J˜J˜——šŸ™J˜Jšœ žœžœ˜%šœžœžœ˜Jš œ žœžœžœžœ ˜.Jš œ žœžœžœžœ˜-Jš œ žœž œžœžœžœžœ˜2Jš œ žœž œžœžœžœž˜1J˜J˜—šŸœžœBžœ˜aJšœžœ˜,Jš œžœžœžœžœ6˜RJšœžœžœ˜(Jšœžœ˜ Jšœ'žœžœ˜5Jšœžœ˜ Jšžœžœžœ˜>Jšœ žœ2˜?Jšœ˜Jšœ'˜'Jšœžœ!žœžœ˜;Jšœžœ!žœžœ˜;Jšœžœžœ˜4Jšœ!˜!Jšœž œ&˜HJšœž œ&˜Hšžœ!žœ#ž˜KJšžœ1˜6—Jšœ2˜2Jšœ0˜0Jšœ˜J˜—šœžœ ˜%Jšœ˜J˜J˜—š Ÿœžœžœžœžœžœ˜QJšœžœ˜&Jš œžœžœžœžœ"˜>Jšžœžœžœ˜8Jš žœžœžœžœžœ!˜Lšžœ˜Jšœžœ)˜0Jš œžœžœ žœžœ ˜9Jš œžœžœžœžœ ˜PJš œžœžœžœžœ ˜PJšžœžœžœžœžœžœžœžœ"˜\Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ)˜)J˜—Jšœ˜J˜——š ™J˜šœžœ˜+Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ#˜#Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—J˜š Ÿ œžœžœžœžœ˜AJšœ žœ ˜šžœžœ žœ˜Jšœ8žœ˜CJšžœ+˜1J˜—Jšžœžœ˜Jšœ˜J˜—šŸœžœžœžœ ˜;Jšœ žœ ˜šžœžœ žœ˜Jšœ8žœ˜CJšœ,˜,Jšžœ4˜:J˜—Jšžœ ˜Jšœ˜J˜—šŸ œžœžœžœ˜>Jšœ žœ ˜Jšœžœ ˜)šžœ'ž˜1šœ ˜ šœ_˜_Jšœ˜—Jšœ˜—šœ ˜ JšœH˜HJšœ˜Jšœ˜Jšœžœ˜Jšœžœ˜Jšžœžœžœžœ˜Jšœ$Ïc ˜0Jšœ2¡˜EJšœ"˜"Jšœ"˜"Jšœ~˜~Jšœ>˜>Jšœ@˜@Jšœ˜—Jšžœžœ˜—Jšœ˜J˜—šŸœž œžœ.žœ™QJšœžœžœ žœ™0Jšžœžœžœ™šžœ'ž™1šœ ™ šŸœ™%Jšœ žœžœ™Jšœ žœžœ™Jšœ žœžœ™0Jšœ™JšœP™PšŸœžœ žœ žœ™0Jšœ™Jšœ™Jšœ™—šœ,™,Jšœ™Jšœ™—šœ,™,Jšœ™Jšœ™—šœ.™.Jšœ™Jšœ™Jšœ™Jšœ,™,—Jšœ2™2Jšœ_™_Jšœ™—Jšœ}™}Jšœ\žœ™bJšœ™—šœ ™ JšœH™HJšœ™Jšœ™Jšœžœ™Jšœžœ™Jšžœžœžœžœ™Jšœ$¡ ™0Jšœ2¡™EJšœ"™"Jšœ"™"Jšœ~™~Jšœ>™>Jšœ@™@Jšœ™—Jšžœžœ™—Jšœ™J˜—š Ÿ œžœžœžœžœ˜IJšœJ˜Jšžœžœžœ˜7Jšœžœ˜&Jšœžœ˜&šžœ˜ Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ ˜J˜—Jšœ˜—šžœ1žœ˜9J™)šœ,˜,Jšœžœ ˜Jšœ˜—šœ,˜,Jšœžœ ˜Jšœ˜—šœ.˜.Jšœžœ žœ žœ˜2Jšœ˜—Jšœ2˜2J˜AJšœ_˜_Jšžœ@žœ˜WJ˜—šžœ˜Jšœ>˜>Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ žœ žœ˜5J˜—Jšœ˜J˜—šŸ œžœžœ˜#Jšœ!˜!Jšœ2˜2Jšœ%˜%Jšœ˜J˜—šœ žœžœ˜HJšœ˜Jšœ˜Jšœ˜Jšœ˜J˜J˜—Jšœ3˜3Jšœ3˜3J˜—Jšžœ˜—…—WƆ