DIRECTORY Ascii, Graphics, GraphicsBasic, CGPrivate, UFPressFontReader, UFCubicScan, PressFontFormat, Real, RealConvert, RealFns, Rope, UFFileManager, UnifiedFonts; UFPressFontReaderImpl: CEDAR MONITOR -- monitored because of devicePath IMPORTS Graphics, CGPrivate, PressFontFormat, Rope, RealConvert, RealFns, UFFileManager, UFCubicScan, UnifiedFonts EXPORTS UFPressFontReader = BEGIN OPEN UFPressFontReader, F: PressFontFormat; FONT: TYPE = UnifiedFonts.FONT; 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], drawCharProc: PROC [data: REF, context: Graphics.Context, char: CHAR] ]; 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: UnifiedFonts.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]; }; GetCharPath: PUBLIC PROCEDURE [fontKey: FontKey, char: CHAR] RETURNS [path: Graphics.Path] = { moveToProc: UFPressFontReader.MoveToProc = {Graphics.MoveTo[path, x, y, FALSE]}; lineToProc: UFPressFontReader.LineToProc = {Graphics.LineTo[path, x, y]}; curveToProc: UFPressFontReader.CurveToProc = {Graphics.CurveTo[path, x1, y1, x2, y2, x3, y3]}; drawAreaProc: UFPressFontReader.DrawAreaProc = {}; path _ Graphics.NewPath[]; GetCharOutline[fontKey, char, moveToProc, lineToProc, curveToProc, drawAreaProc]; }; 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]; }; DrawChar: PUBLIC PROCEDURE [context: Graphics.Context, fontKey: FontKey, char: CHAR] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; object: Object _ GetObject[file, fontKey]; mark: Graphics.Mark _ Graphics.Save[context]; charInfo: CharInfo _ GetCharInfo[fontKey, char]; xRef, yRef: REAL; [xRef, yRef] _ Graphics.GetCP[context]; Graphics.Translate[context, xRef, yRef]; [] _ Graphics.SetPaintMode[context, transparent]; object.procs.drawCharProc[object.data, context, char]; Graphics.Restore[context, mark]; Graphics.SetCP[context, xRef+charInfo.widthX, yRef+charInfo.widthY]; }; 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, drawCharProc: CharsDrawChar ]]; 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; }; }; 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 ]; }; DrawCharRaster: PUBLIC PROCEDURE [context: Graphics.Context, fontKey: FontKey, char: CHAR] = TRUSTED { file: FontFile _ UFFileManager.Open[fontKey.fileKey, OpenInitialization]; object: Object _ GetObject[file, fontKey]; rasterData: RasterData _ NARROW[object.data]; data: F.BoundingBox _ rasterData.dataDesc[char-rasterData.indexEntry.bc]; mark: Graphics.Mark _ Graphics.Save[context]; DrawCharRasterPrivate[context, rasterData, char]; Graphics.Restore[context, mark]; Graphics.SetCP[self: context, x: BcplFractionToMesaReal[data.xwidth] - data.BBox, y: BcplFractionToMesaReal[data.ywidth] - data.BBoy, rel: TRUE]; }; 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, drawCharProc: SplinesDrawChar ]]; 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]; }; }; devicePath: UFCubicScan.DevicePath _ UFCubicScan.Allocate[]; SplinesDrawChar: ENTRY PROCEDURE [data: REF, context: Graphics.Context, char: CHAR] = TRUSTED { splinesData: SplinesData _ NARROW[data]; ix: LONG POINTER TO splines F.RawIndex _ splinesData.indexEntry; Map: SAFE PROC [v: GraphicsBasic.Vec] RETURNS [t: GraphicsBasic.Vec] = TRUSTED { [t.x, t.y] _ Graphics.UserToWorld[context, v.x, v.y]; }; LineProc: SAFE PROC [xmin, xmax, y: INTEGER] = TRUSTED { x0, y0, x1, y1: REAL; [x0, y0] _ Graphics.WorldToUser[context, xmin, y]; [x1, y1] _ Graphics.WorldToUser[context, xmax, y+1]; Graphics.DrawBox[context, [x0, y0, x1, y1]]; }; devicePath.Reset[]; UFCubicScan.PushPath[devicePath, GetCharPath[splinesData.fontKey, char], Map]; Graphics.SetColor[context, Graphics.black]; UFCubicScan.ScanConvert[devicePath, LineProc]; }; 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, drawCharProc: WidthsDrawChar ]]; 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; }; }; WidthsDrawChar: PROCEDURE [data: REF, context: Graphics.Context, char: CHAR] = {}; PFDrawChar: PROCEDURE [font: FONT, char: CHAR, context: Graphics.Context] = { mark: Graphics.Mark _ Graphics.Save[context]; Graphics.Scale[context, font.actualTransformation.scale, font.actualTransformation.scale]; Graphics.Rotate[context, font.actualTransformation.rotation]; DrawChar[context, [font.graphicsKey, 0], char]; Graphics.Restore[context, mark]; }; PFBoundingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: UnifiedFonts.Box] = { r: REAL _ font.actualTransformation.rotation - Rotation[[font.graphicsKey, 0]]; cos: REAL _ RealFns.CosDeg[r]; sin: REAL _ RealFns.SinDeg[r]; B: PROC[vec: UnifiedFonts.Vec, box: UnifiedFonts.Box] RETURNS [UnifiedFonts.Box] = { x: REAL _ vec.x*cos - vec.y*sin; y: REAL _ vec.x*sin + vec.y*cos; RETURN [[ xmin: MIN[box.xmin, x], ymin: MIN[box.ymin, y], xmax: MAX[box.xmax, x], ymax: MAX[box.ymax, y] ]] }; s: REAL _ font.actualTransformation.scale; charInfo: CharInfo _ GetCharInfo[[font.graphicsKey, 0], char]; x0: REAL _ charInfo.minX*s; y0: REAL _ charInfo.minY*s; x1: REAL _ charInfo.maxX*s; y1: REAL _ charInfo.maxY*s; box _ B[[x0,y0],B[[x0,y1],B[[x1,y0],B[[x1,y1],[0,0,0,0]]]]]; }; PFWidthVector: PROCEDURE [font: FONT, char: CHAR] RETURNS [vec: UnifiedFonts.Vec] = { r: REAL _ font.actualTransformation.rotation - Rotation[[font.graphicsKey, 0]]; s: REAL _ font.actualTransformation.scale; charInfo: CharInfo _ GetCharInfo[[font.graphicsKey, 0], char]; vec _ [charInfo.widthX*s, charInfo.widthY*s]; IF r#0 THEN { sin: REAL _ RealFns.SinDeg[r]; cos: REAL _ RealFns.CosDeg[r]; x: REAL _ vec.x*cos - vec.y*sin; y: REAL _ vec.x*sin + vec.y*cos; vec _ [x,y]; }; }; 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]]; }; PFClass: REF UnifiedFonts.FontGraphicsClassRec_NEW[UnifiedFonts.FontGraphicsClassRec_[ drawCharProc: PFDrawChar, boundingBoxProc: PFBoundingBox, widthVectorProc: PFWidthVector, containsProc: PFContains ]]; UnifiedFonts.RegisterFontGraphicsClass[$Sd, PFObjectInit]; END.  UFPressFontReaderImpl.mesa Created January 17, 1983 Last edit by Michael Plass on March 2, 1983 1:25 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 initialise the FontVector that is recorded by the file manager. Raster format operations. Spline format operations. Routines for converting to bezier points. Widths format operations. Someday maybe we should synthesize something here. FontGraphicsClass interface Ê– "Cedar" style˜Jšœ™Jšœ™Jšœ3™3unitšÏk ˜ Jšœ˜Jšœ ˜ J˜J˜ Jšœ˜Jšœ ˜ Jšœ˜J˜J˜ J˜Jšœ˜Jšœ˜Jšœ ˜ —šœœœÏc"˜GJšœk˜rJšœ˜Jšœ˜Jšœ'˜+Kšœœœ˜Kšœœ˜Kšœ œ˜(Kšœ œœ˜%šœœœ˜Jšœœœ˜Jšœœ œœ˜,Jšœ˜JšœÒ™Ò—šœœœ˜J˜Jšœ˜ Jšœ˜J™‰—šœœœ˜Jšœ œœ˜J˜ Jšœ˜ Jšœ˜—Kšœœœ ˜šœ œœ˜Jš œœœœœ˜DJšœœœ#œ˜EJ˜—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š œ œœœœœ˜,Jšœ=œ<œœR˜ÙJšœ?œ˜EJšœ˜Jšœž˜žJšœ˜—š Ÿœœ œ5œœ˜fJšœI˜IJšœ*˜*Jšœœ˜-JšœœB˜IJšœ-˜-Jšœ1˜1Jšœ ˜ 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šœ˜J˜—š Ÿœ œœœœœ˜WJšœœ˜(Jš œœœœ œ#˜@Jšœœœ˜9Jšœœœœ+œœœ!˜Œšœ˜Jšœœ3˜:Jšœ*˜*Jšœ*˜*Jšœ&˜&Jšœ&˜&Jšœ(˜(Jšœ&˜&J˜—Jšœ˜—Kšœ<˜<š Ÿœœ œœ#œœ˜_Jšœœ˜(Jš œœœœ œ#˜@š Ÿœœœœœ˜PJšœ5˜5Jšœ˜—š Ÿœœœœœ˜8Jšœœ˜Jšœ2˜2Jšœ4˜4Jšœ,˜,Jšœ˜—Jšœ˜JšœN˜NJšœ+˜+Jšœ.˜.J˜—š Ÿœ œœ œœœ˜GJšœœ˜'J˜—š Ÿœ œœœœœ˜SJšœœœœ˜1J˜—šŸœœ œ˜"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˜—šœ)™)Iprocšœœœœ˜Lš Ÿœ œœ œœ˜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šœ˜J˜—š Ÿœ œœœœœ˜VJšœœ˜&Jš œœœœœ"˜>Jšœœœ˜8Jš œœœœœ!˜Lšœ˜Jšœœ)˜0Jš œœœ œœ ˜9Jš œœœœœ ˜PJš œœœœœ ˜PJšœœœœœœœœ"˜\Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ)˜)J˜—Jšœ˜—šŸœ œœ#œ˜RJ™2——šÏb™šŸ œ œœœ ˜MJšœ-˜-JšœZ˜ZJšœ=˜=Jšœ/˜/Jšœ ˜ Jšœ˜—š Ÿ œ œœœœ˜UJšœœH˜OJšœœ˜Jšœœ˜šœœ/œ˜TJšœœ˜ Jšœœ˜ šœ˜ Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ ˜J˜—Jšœ˜—Jšœœ#˜*Jšœ>˜>Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jš œœ œ œ œ˜˜>Jšœ-˜-šœœ˜ Jšœœ˜Jšœœ˜Jšœœ˜ Jšœœ˜ Jšœ ˜ J˜—Jšœ˜—š Ÿ œ œœœœœ˜DJšœ>˜>Jšœ+˜1Jšœ˜—šŸ œ œœ˜(Jšœ!˜!Jšœ2˜2Jšœ˜—šœ œ#œ$˜VJšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Kšœ:˜:—Kšœ˜——…—Wþt<