DIRECTORY Ascii USING [NUL], Basics USING [bitsPerWord], Font 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]; UFPressFontReaderImpl: CEDAR MONITOR -- monitored because of devicePath IMPORTS Imager, ImagerMasks, ImagerTransform, Font, ImagerScanConverter, UFPressFontFormat, RealConvert, Rope, UFFileManager EXPORTS UFPressFontReader = BEGIN OPEN UFPressFontReader, F: UFPressFontFormat; ROPE: TYPE = Rope.ROPE; FONT: TYPE = Font.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: PROCEDURE [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: PROCEDURE [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 = PROCEDURE [openFile: FontFile, p: LONG POINTER TO F.RawIndex] RETURNS [quit: BOOLEAN _ FALSE]; EnumerateIndex: PROCEDURE [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 PROCEDURE [fileKey: Font.Key] RETURNS [NAT] = { file: FontFile _ UFFileManager.Open[fileKey, OpenInitialization]; data: FontVector _ NARROW[file.GetData[]]; RETURN[data.length]; }; Family: PUBLIC PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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: PROCEDURE [ 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 PROCEDURE [fontKey: FontKey, char: CHAR] RETURNS [pathMap: ImagerBasic.PathMapType, pathData: REF] = { RETURN[pathMap: GenerateCharPath, pathData: NEW[CharPathData _ [fontKey, char]]]; }; GetObject: PROCEDURE [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: PROCEDURE [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: PROCEDURE [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 PROCEDURE [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: PROCEDURE [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: PROCEDURE [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: PROCEDURE [b: F.BcplREAL] RETURNS [r: REAL] = TRUSTED { r _ RealConvert.BcplToIeee[LOOPHOLE[b]] }; BcplFractionToMesaReal: PROCEDURE [f: F.bcplFraction] RETURNS [r: REAL] = TRUSTED { r _ F.BcplToMesaFraction[f]/(LAST[CARDINAL]+1.0); }; GetCharOutline: PUBLIC PROCEDURE [ 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: PROCEDURE [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x+b.x,a.y+b.y]]}; VecSub: PROCEDURE [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x-b.x,a.y-b.y]]}; VecDiv: PROCEDURE [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: PROCEDURE [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: PROCEDURE [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: PROCEDURE [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; }; }; devicePath: ImagerScanConverter.DevicePath _ NIL; PFMask: ENTRY PROC [font: FONT, transformation: Font.Transformation, char: CHAR, run: PROC [sMin, fMin: INTEGER, fSize: NAT]] = { ENABLE UNWIND => NULL; SELECT Representation[[font.graphicsKey, 0]] FROM outline => { GenPath: ImagerScanConverter.PathProc = { 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; }; PFBoundingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: Font.Box] = { m: ImagerTransform.TransformationRec _ font.actualTransformation.Contents; B: PROC[p: Vec, box: Font.Box] RETURNS [Font.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]]]]; }; }; PFWidthVector: PROCEDURE [font: FONT, char: CHAR] RETURNS [Pair] = { m: ImagerTransform.TransformationRec _ font.actualTransformation.Contents; charInfo: CharInfo _ GetCharInfo[[font.graphicsKey, 0], char]; p: Vec _ [charInfo.widthX, charInfo.widthY]; RETURN [[ x: m.a * p.x + m.b * p.y + m.c, y: m.d * p.x + m.e * p.y + m.f ]]; }; PFContains: PROCEDURE [font: FONT, char: CHAR] RETURNS [BOOLEAN] = { charInfo: CharInfo _ GetCharInfo[[font.graphicsKey, 0], char]; RETURN[charInfo#[zero,zero,zero,zero,zero,zero]]; }; PFObjectInit: PROCEDURE [font: FONT] = { font.fontGraphicsClass _ PFClass; [font.bc, font.ec] _ Range[[font.graphicsKey, 0]]; font.PutProp[$PressFont, $PressFont]; }; PFClass: REF Font.FontGraphicsClassRec_NEW[Font.FontGraphicsClassRec_[ maskProc: PFMask, boundingBoxProc: PFBoundingBox, widthVectorProc: PFWidthVector, containsProc: PFContains ]]; Font.RegisterFontGraphicsClass[$Sd, PFObjectInit]; Font.RegisterFontGraphicsClass[$Ac, PFObjectInit]; END. ¾UFPressFontReaderImpl.mesa Created January 17, 1983 Last edit by Michael Plass on April 23, 1984 3:38:40 pm PST Last edit by Doug Wyatt on October 3, 1983 2:42 pm 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: PROCEDURE [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: PROCEDURE [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. FontGraphicsClass interface move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] work harder to get a tighter bounding box ÊY– "Cedar" style˜Jšœ™Jšœ™Jšœ;™;Jšœ2™2šÏk ˜ Jšœœœ˜Jšœœ˜JšœœœQ˜fJšœœ˜&Jšœ œ!˜2J˜ Jšœœ3˜LJšœœ:˜OJšœœ…˜œJšœœ ˜Jšœ œ˜Jšœœ œ˜Jšœœ8˜KJšœœl˜ƒJ˜—unitšœœœÏc"˜GJšœu˜|Jšœ˜Jšœ˜Jšœ)˜-Kšœœœ˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœ œ˜(Kšœ œœ˜%šœœœ˜Jšœœœ˜Jšœœ œœ˜,Jšœ˜JšœÒ™Ò—šœœœ˜J˜Jšœ˜ Jšœ˜J™‰—šœœœ˜Jšœ œœ˜J˜ Jšœ˜ Jšœ˜—Kšœœœ ˜šœ œœ˜Jš œœœœœ˜CJ˜—Kšœœœœ˜0Kšœœœ˜(Kš Ïnœœœ+œœ˜KšŸœœ˜6Jšœ™Jšœ˜Jšœ œ˜šœœ˜ šœ ˜˜J˜J˜—Jšœ˜—J˜—šœœ˜šœ ˜˜Jšœ&˜&J˜J˜—šœ ˜ Jšœœœ˜Jš œ œœœœ˜0Jšœœ<˜SJšœ˜—Jšœ˜—J˜—Jšœ$˜$Jšœ œ˜+Jšœ˜Jšœ#˜#Jšœ ˜J˜—š Ÿ œ œ œœœœ˜Xš œœœœœ˜:Jšœœœ˜2Jšœ˜—Jšœœ˜ J˜—š Ÿ œ œœœœ˜;Jšœœœ˜ Jšœœ˜ Jšœ#œ˜'š œ œœœœœ˜4Jšœ#˜#—Jšœ$˜$J˜—š Ÿ œœ œœœœ ˜OJšœœœ˜ —šŸœ œ.œ˜PJš œœœœœ˜1Jšœœ˜š˜Jš œœœœœ(˜Jš œ œœœœœ˜/Jšœ œœœœœœ˜1Jš œ=œ=œœœœR˜ýšœ#˜#Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ˜—Jšœ7˜7Jšœ˜—š Ÿ œ œœ#œœ™WJšœœ™&Jš œœœœœ"™=Jš œœœ œœ œ™RJš œœœ œœ œ™RJšœ(™(Jšœ-™-Jšœ1™1J™—šŸœ œ;œœ™lJš œœœœœ"™=Jšœœ/™6JšœœœœA™ZJšœœœœ™CJšœœ"™&Jšœ&œœ™>Jš œ œœœœ™*Jš œ œœœœœ™,Jšœ=œ<œœR™ÙJšœ?œ™EJšœ™Jšœž™žJšœ™——šŸ™Kšœ œœ˜'šœœœ˜Jš œ œœœ œ ˜/Jšœ œ˜Jšœ œ˜Jšœœœ˜(J˜Jšœœ˜J˜—šŸœ œBœ˜gJšœœ˜/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˜—š Ÿœ œœœœœ˜WJšœœ˜(Jš œœœœ œ#˜@Jšœœœ˜9Jšœœœœ+œœœ!˜Œšœ˜Jšœœ3˜:Jšœ*˜*Jšœ*˜*Jšœ&˜&Jšœ&˜&Jšœ(˜(Jšœ&˜&J˜—Jšœ˜—š Ÿœ œœ œœœ˜GJšœœ˜'J˜—š Ÿœ œœœœœ˜SJšœœœœ˜1J˜—šŸœœ œ˜"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˜——šÏb)™)Kšœœœœ˜Iprocš Ÿœ œœ œœ˜VLš Ÿœ œœ œœ˜VLš Ÿœ œ œœ œœ˜SLšœœœ˜(Lšœœœ˜(šŸœ œ œ˜=Jšœ˜ J˜J˜J˜&J˜'J˜——šŸ™Kšœ œœ˜%šœœœ˜Jš œ œœœœ ˜.Jš œ œœœœ˜-Jš œ œ œœœœœ˜2Jš œ œ œœœœ˜1J˜—šŸœ œBœ˜fJšœœ˜,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˜—š Ÿœ œœœœœ˜VJšœœ˜&Jš œœœœœ"˜>Jšœœœ˜8Jš œœœœœ!˜Lšœ˜Jšœœ)˜0Jš œœœ œœ ˜9Jš œœœœœ ˜PJš œœœœœ ˜PJšœœœœœœœœ"˜\Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ)˜)J˜—Jšœ˜——š ™Kšœ-œ˜1šŸœ œœ-œ˜PJšœœœ œ˜0Jšœœœ˜šœ'˜1šœ ˜ šŸœ˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜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šœ˜—š Ÿ œ œœœœ˜MJšœJ˜Jšœœœ˜5Jšœœ˜&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šœ˜—š Ÿ œ œœœœ ˜DJšœJ˜JJšœ>˜>Jšœ,˜,šœ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—š Ÿ œ œœœœœ˜DJšœ>˜>Jšœ+˜1Jšœ˜—šŸ œ œœ˜(Jšœ!˜!Jšœ2˜2Jšœ%˜%Jšœ˜—šœ œœ˜FJšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Kšœ2˜2Kšœ2˜2—Kšœ˜——…—ZH_