UFPressFontReaderImpl.mesa
Created January 17, 1983
Last edit by Michael Plass on July 11, 1983 8:25 am
DIRECTORY
Ascii,
ImagerBasic,
UFPressFontReader,
ScanConverter,
PressFontFormat,
Real,
RealConvert,
Rope,
UFFileManager,
UnifiedFonts;
UFPressFontReaderImpl: CEDAR MONITOR -- monitored because of devicePath
IMPORTS PressFontFormat, Rope, RealConvert, UFFileManager, ScanConverter, UnifiedFonts
EXPORTS UFPressFontReader
= BEGIN
OPEN UFPressFontReader, F: PressFontFormat;
FONT: TYPE = UnifiedFonts.FONT;
Pair: TYPE = ImagerBasic.Pair;
Path: TYPE = ImagerBasic.Path;
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
];
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.
CodeNamePair: TYPE = RECORD [
code: [0..256),
name: ROPE
];
Strings in press font files are represented by one-byte codes; this list records the correspondence between codes and names for the file.
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 {
This gets called when the font file is first opened; its responsibility is to initialize the FontVector that is recorded by the file manager.
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: BOOLEANFALSE];
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];
};
CharPathData: TYPE = RECORD [fontKey: FontKey, char: CHAR];
GenerateCharPath: PROCEDURE [
path: Path,
move: PROC [Pair],
line: PROC [Pair],
curve: PROC [Pair, Pair, Pair]
] = {
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 = {};
data: REF CharPathData ← NARROW[path.data];
GetCharOutline[data.fontKey, data.char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
GetCharPath: PUBLIC PROCEDURE [fontKey: FontKey, char: CHAR] RETURNS [path: ImagerBasic.Path] = {
path ← NEW[ImagerBasic.PathRep];
path.generateProc ← GenerateCharPath;
path.data ← 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];
};
Raster format operations.
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;
};
};
CharsDrawChar: PROCEDURE [data: REF, context: Graphics.Context, char: CHAR] = TRUSTED {
rasterData: RasterData ← NARROW[data];
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
scalex: REALIF ix.size = 0 THEN 1.0 ELSE 25400.0/(INT[ix.resolutionx]*ix.size);
scaley: REALIF 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 CARDINALF.LongCardinalFromBcplLongPointer[rasterData.dirDesc[char-ix.bc]];
rawRasterPointer: LONG POINTERBASE[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.
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
};
Routines for converting to bezier points.
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�
b1←VecAdd[c0,VecDiv[c1,3]];
b2←VecAdd[b1,VecDiv[VecAdd[c1,c2],3]];
b3←VecAdd[VecAdd[VecAdd[c0,c1],c2],c3];
};
Widths format operations.
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: REALIF 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;
};
};
FontGraphicsClass interface
devicePath: ScanConverter.DevicePath ← ScanConverter.Allocate[];
PFMask: ENTRY PROC [font: FONT, transformation: UnifiedFonts.Transformation, char: CHAR, run: PROC [s, fMin: INTEGER, fSize: NAT]] = {
ENABLE UNWIND => NULL;
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
transformation.d * p.x + transformation.e * p.y + transformation.f,
transformation.a * p.x + transformation.b * p.y + transformation.c
]]};
moveToProc: UFPressFontReader.MoveToProc ~ {move[Xform[[x, y]]]};
lineToProc: UFPressFontReader.LineToProc ~ {line[Xform[[x, y]]]};
curveToProc: UFPressFontReader.CurveToProc ~ {curve[Xform[[x1, y1]], Xform[[x2, y2]], Xform[[x3, y3]]]};
drawAreaProc: UFPressFontReader.DrawAreaProc = {};
GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
devicePath.Reset;
devicePath.PushPath[GenPath];
devicePath.ScanConvert[proc: BoxFromScanConverter, parityFill: TRUE];
devicePath.Reset;
};
PFBoundingBox: PROCEDURE [font: FONT, char: CHAR] RETURNS [box: UnifiedFonts.Box] = {
m: ImagerBasic.Transformation ← font.actualTransformation;
B: PROC[p: Vec, box: UnifiedFonts.Box] RETURNS [UnifiedFonts.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]
]]
};
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: ImagerBasic.Transformation ← font.actualTransformation;
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]];
};
PFClass: REF UnifiedFonts.FontGraphicsClassRec←NEW[UnifiedFonts.FontGraphicsClassRec←[
maskProc: PFMask,
boundingBoxProc: PFBoundingBox,
widthVectorProc: PFWidthVector,
containsProc: PFContains
]];
UnifiedFonts.RegisterFontGraphicsClass[$Sd, PFObjectInit];
END.