<> <> <> 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.