PressFontImpl.mesa
Copyright © 1983, 1984, Xerox Corporation. All rights reserved.
Created January 17, 1983
Last edit by Michael Plass on April 23, 1984 3:38:40 pm PST
Doug Wyatt, August 7, 1984 1:09:37 pm PDT
DIRECTORY
Ascii USING [NUL],
Basics USING [bitsPerWord],
UFont USING [Box, FONT, FontGraphicsClassRec, Key, PutProp, RegisterFontGraphicsClass, Transformation],
Imager USING [MakePixelArrayFromBits],
ImagerBasic USING [PathMapType, PixelArray, Pair],
ImagerMasks,
ImagerScanConverter USING [ConvertToRuns, CreatePath, DevicePath, PathProc],
ImagerTransform USING [Concat, Contents, Scale2, TransformationRec, Translate],
UFPressFontFormat USING [bcplFraction, bcplLONGPOINTER, BcplREAL, BcplSplineCommand, BcplSplineData, BcplToMesaFraction, BcplToMesaLongCardinal, BcplWidthSegment, BoundingBox, CharBboxDesc, CharDirDesc, drawCurveCode, drawToCode, endSplineCode, FontPrivateRec, LongCardinalFromBcplLongPointer, missingCharValue, moveToCode, newObjectCode, RasterDimension, RasterPointer, RawIndex, SplineDataDesc, SplineDirDesc],
Real USING [MinusZero],
RealConvert USING [BcplToIeee],
Rope USING [FromProc, ROPE],
UFFileManager USING [FontFile, GetData, InBounds, InitProc, Open, Pointer],
UFPressFontReader USING [CharInfo, CharShapeRepresentation, CurveToProc, DrawAreaProc, ErrorCode, FontKey, LineToProc, MoveToProc];
PressFontImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerMasks, ImagerTransform, UFont, ImagerScanConverter, UFPressFontFormat, RealConvert, Rope, UFFileManager
EXPORTS UFPressFontReader
~ BEGIN OPEN UFPressFontReader, F: UFPressFontFormat;
ROPE: TYPE = Rope.ROPE;
FONT: TYPE = UFont.FONT;
Pair: TYPE = ImagerBasic.Pair;
zero: REAL = Real.MinusZero;
FontFile: TYPE = UFFileManager.FontFile;
FontVector: TYPE = REF FontVectorRec;
FontVectorRec: TYPE = RECORD [
names: LIST OF CodeNamePair,
fonts: SEQUENCE length: NAT OF PressFontRec
];
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: PROC [names: LIST OF CodeNamePair, code: [0..256)] RETURNS [ROPE] = {
FOR n: LIST OF CodeNamePair ← names, n.rest UNTIL n=NIL DO
IF n.first.code = code THEN RETURN [n.first.name];
ENDLOOP;
RETURN [NIL]
};
ExtractName: PROC [p: LONG POINTER TO name F.RawIndex]
RETURNS [rope: ROPE] = TRUSTED {
i: NAT ← 1;
len: [0..20) ← p.fontname[0]-Ascii.NUL;
charProc: SAFE PROC RETURNS [char: CHAR] = TRUSTED {
char ← p.fontname[i];
i ← i + 1;
};
rope ← Rope.FromProc[len, charProc];
};
VisitProc: TYPE = PROC [openFile: FontFile, p: LONG POINTER TO F.RawIndex]
RETURNS [quit: BOOLEANFALSE];
EnumerateIndex: PROC [openFile: FontFile, visitProc: VisitProc] = TRUSTED {
p: LONG POINTER TO F.RawIndex ← openFile.Pointer;
offset: INT ← 0;
DO
p: LONG POINTER TO F.RawIndex ← openFile.Pointer[] + offset;
IF NOT openFile.InBounds[p, p.hdr.length] THEN ERROR Error[fontIndexTooLong, offset];
IF visitProc[openFile, p] THEN EXIT;
IF p.hdr.type = end THEN EXIT;
offset ← offset + p.hdr.length;
ENDLOOP
};
NumberOfFontsInFile: PUBLIC PROC [fileKey: UFont.Key] RETURNS [NAT] = {
file: FontFile ← UFFileManager.Open[fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
RETURN[data.length];
};
Family: PUBLIC PROC [fontKey: FontKey] RETURNS [family: ROPE] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
family ← NameFromCode[data.names, p.family];
};
Face: PUBLIC PROC [fontKey: FontKey] RETURNS [face: [0..256)] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
face ← p.face;
};
Range: PUBLIC PROC [fontKey: FontKey] RETURNS [bc, ec: CHAR] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
bc ← p.bc;
ec ← p.ec;
};
Size: PUBLIC PROC [fontKey: FontKey] RETURNS [size: REAL] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
size ← p.size/100000.0;
};
Rotation: PUBLIC PROC [fontKey: FontKey] RETURNS [rotation: REAL] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
rotation ← p.rotation/60.0;
};
Representation: PUBLIC PROC [fontKey: FontKey] RETURNS [representation: CharShapeRepresentation] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
representation ← SELECT p.hdr.type FROM
splines => outline,
chars => raster,
widths => widthsOnly,
ENDCASE => ERROR;
};
Resolution: PUBLIC PROC [fontKey: FontKey] RETURNS [xRes, yRes: REAL] = TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
IF p.hdr.type # chars THEN {xRes ← yRes ← zero}
ELSE {
xRes ← p.resolutionx/10.0;
yRes ← p.resolutiony/10.0;
}
};
GetCharInfo: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [info: CharInfo] = TRUSTED {
fontFile: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
object: Object ← GetObject[fontFile, fontKey];
info ← object.procs.charInfoProc[object.data, char];
};
CharPathData: TYPE = RECORD [fontKey: FontKey, char: CHAR];
GenerateCharPath: PROC [
data: REF,
move: PROC [Pair],
line: PROC [Pair],
curve: PROC [Pair, Pair, Pair],
conic: PROC [Pair, Pair, REAL]
] = {
moveToProc: UFPressFontReader.MoveToProc = {move[[x, y]]};
lineToProc: UFPressFontReader.LineToProc = {line[[x, y]]};
curveToProc: UFPressFontReader.CurveToProc = {curve[[x1, y1], [x2, y2], [x3, y3]]};
drawAreaProc: UFPressFontReader.DrawAreaProc = {};
d: REF CharPathData ← NARROW[data];
GetCharOutline[d.fontKey, d.char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
GetCharPath: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [pathMap: ImagerBasic.PathMapType, pathData: REF] = {
RETURN[pathMap: GenerateCharPath, pathData: NEW[CharPathData ← [fontKey, char]]];
};
GetObject: PROC [fontFile: FontFile, fontKey: FontKey] RETURNS [object: Object] = TRUSTED {
fontVector: FontVector ← NARROW[fontFile.GetData[]];
charsIndexPtr: LONG POINTER TO chars F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
IF fontVector[fontKey.fontIndex].data = NIL THEN {
charsIndexPtr: LONG POINTER TO chars F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
SELECT charsIndexPtr.hdr.type FROM
chars => MakeCharsObject[fontFile, fontVector, fontKey];
splines => MakeSplinesObject[fontFile, fontVector, fontKey];
widths => MakeWidthsObject[fontFile, fontVector, fontKey];
ENDCASE => ERROR;
};
object ← [fontVector[fontKey.fontIndex].procs, fontVector[fontKey.fontIndex].data];
};
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: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED {
rasterData: RasterData ← NEW[RasterDataRec];
ix: LONG POINTER TO chars F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
base: LONG POINTER ← fontFile.Pointer[];
nChars: NAT ← ix.ec - ix.bc + 1;
dataOffset, dirOffset, rasterOffset: LONG CARDINAL;
IF ix.hdr.type # chars THEN ERROR Error[cantHappen, ix-base];
dataOffset ← F.LongCardinalFromBcplLongPointer[ix.startaddress];
dirOffset ← dataOffset + nChars*SIZE[F.BoundingBox];
rasterOffset ← dirOffset + nChars*SIZE[F.bcplLONGPOINTER];
IF rasterOffset - dataOffset > F.BcplToMesaLongCardinal[ix.length] THEN
ERROR Error[dataSegmentTooLong, dataOffset];
rasterData.indexEntry ← ix;
rasterData.rasterStart ← rasterOffset;
rasterData.rasterEnd ← dataOffset + F.BcplToMesaLongCardinal[ix.length];
rasterData.dataDesc ← DESCRIPTOR[base+dataOffset, nChars];
rasterData.dirDesc ← DESCRIPTOR[base+dirOffset, nChars];
rasterData.base ← base;
fontVector[fontKey.fontIndex].procs ← charsProcs;
fontVector[fontKey.fontIndex].data ← rasterData;
};
charsProcs: Procs ← NEW[ProcsRec ← [
charInfoProc: CharsCharInfo
]];
CharsCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED {
rasterData: RasterData ← NARROW[data];
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
IF ix.hdr.type # chars THEN ERROR Error[cantHappen, 0];
IF (NOT char IN [ix.bc..ix.ec]) THEN RETURN[[zero,zero,zero,zero,zero,zero]]
ELSE {
data: F.BoundingBox ← rasterData.dataDesc[char-ix.bc];
xscale: REAL ← 25400.0/(INT[ix.resolutionx]*ix.size);
yscale: REAL ← 25400.0/(INT[ix.resolutiony]*ix.size);
IF data.BBdy = LAST[CARDINAL] THEN RETURN[[zero,zero,zero,zero,zero,zero]];
info.widthX ← BcplFractionToMesaReal[data.xwidth]*xscale;
info.widthY ← BcplFractionToMesaReal[data.ywidth]*yscale;
info.minX ← data.BBox*xscale;
info.minY ← data.BBoy*yscale;
info.maxX ← data.BBdx*xscale + info.minX;
info.maxY ← data.BBdy*yscale + info.minY;
};
};
GetCharRaster: PUBLIC PROC [fontKey: FontKey, char: CHAR] RETURNS [pa: ImagerBasic.PixelArray] ~ TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
object: Object ← GetObject[file, fontKey];
rasterData: RasterData ← NARROW[object.data];
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
data: F.BoundingBox ← rasterData.dataDesc[char-ix.bc];
offset: LONG 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[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];
};
CharsDrawChar: PROC [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: PROC [context: Graphics.Context, rasterData: RasterData, char: CHAR] = TRUSTED {
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
data: F.BoundingBox ← rasterData.dataDesc[char-ix.bc];
offset: LONG 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: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED {
splinesData: SplinesData ← NEW[SplinesDataRec];
ix: LONG POINTER TO splines F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
base: LONG POINTER ← fontFile.Pointer[];
nChars: NAT ← ix.ec - ix.bc + 1;
dataOffset, dirOffset, commandOffset: LONG CARDINAL;
IF ix.hdr.type # splines THEN ERROR Error[cantHappen, ix-base];
dataOffset ← F.LongCardinalFromBcplLongPointer[ix.startaddress];
dirOffset ← dataOffset + nChars*SIZE[F.BcplSplineData];
commandOffset ← dirOffset + nChars*SIZE[F.bcplLONGPOINTER];
IF commandOffset - dataOffset > F.BcplToMesaLongCardinal[ix.length] THEN
ERROR Error[dataSegmentTooLong, dataOffset];
splinesData.indexEntry ← ix;
splinesData.commandStart ← commandOffset;
splinesData.commandEnd ← dataOffset + F.BcplToMesaLongCardinal[ix.length];
splinesData.dataDesc ← DESCRIPTOR[base+dataOffset, nChars];
splinesData.dirDesc ← DESCRIPTOR[base+dirOffset, nChars];
splinesData.base ← base;
splinesData.fontKey ← fontKey;
fontVector[fontKey.fontIndex].procs ← splinesProcs;
fontVector[fontKey.fontIndex].data ← splinesData;
};
splinesProcs: Procs ← NEW[ProcsRec ← [
charInfoProc: SplinesCharInfo
]];
SplinesCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED {
splinesData: SplinesData ← NARROW[data];
ix: LONG POINTER TO splines F.RawIndex ← splinesData.indexEntry;
IF ix.hdr.type # splines THEN ERROR Error[cantHappen, 0];
IF (NOT char IN [ix.bc..ix.ec]) OR splinesData.dataDesc[char-ix.bc].xwidth = F.missingCharValue THEN RETURN[[zero,zero,zero,zero,zero,zero]]
ELSE {
data: F.BcplSplineData ← splinesData.dataDesc[char-ix.bc];
info.widthX ← BcplToMesaReal[data.xwidth];
info.widthY ← BcplToMesaReal[data.ywidth];
info.minX ← BcplToMesaReal[data.bbox];
info.minY ← BcplToMesaReal[data.bboy];
info.maxX ← BcplToMesaReal[data.rightx];
info.maxY ← BcplToMesaReal[data.topy];
};
};
BcplToMesaReal: PROC [b: F.BcplREAL] RETURNS [r: REAL] = TRUSTED {
r ← RealConvert.BcplToIeee[LOOPHOLE[b]]
};
BcplFractionToMesaReal: PROC [f: F.bcplFraction] RETURNS [r: REAL] = TRUSTED {
r ← F.BcplToMesaFraction[f]/(LAST[CARDINAL]+1.0);
};
GetCharOutline: PUBLIC PROC [
fontKey: FontKey,
char: CHAR,
moveToProc: MoveToProc,
lineToProc: LineToProc,
curveToProc: CurveToProc,
drawAreaProc: DrawAreaProc
] = TRUSTED {
p: LONG POINTER;
curX, curY: REAL ← 0.0;
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
object: Object ← GetObject[file, fontKey];
splinesData: SplinesData ← NARROW[object.data];
bc: CHAR ← splinesData.indexEntry.bc;
IF splinesData.indexEntry.hdr.type # splines THEN ERROR Error[cantHappen, 0];
IF NOT char IN [bc..splinesData.indexEntry.ec] THEN RETURN;
IF splinesData.dataDesc[char-bc].xwidth = F.missingCharValue THEN RETURN;
p ← BASE[splinesData.dirDesc] + F.LongCardinalFromBcplLongPointer[splinesData.dirDesc[char-bc]];
DO splineCommand: LONG POINTER TO F.BcplSplineCommand ← p;
IF (p-splinesData.base) < splinesData.commandStart
OR
(p-splinesData.base) >= splinesData.commandEnd THEN
ERROR Error[invalidPointerInFile, @(splinesData.dirDesc[char-bc])-splinesData.base];
SELECT splineCommand.type FROM
F.moveToCode => {cmd: LONG POINTER TO MoveTo F.BcplSplineCommand ← p;
moveToProc[curX ← BcplToMesaReal[cmd.x], curY ← BcplToMesaReal[cmd.y]];
p ← p + SIZE[MoveTo F.BcplSplineCommand];
};
F.drawToCode => {cmd: LONG POINTER TO DrawTo F.BcplSplineCommand ← p;
lineToProc[curX ← BcplToMesaReal[cmd.x], curY ← BcplToMesaReal[cmd.y]];
p ← p + SIZE[DrawTo F.BcplSplineCommand];
};
F.drawCurveCode => {cmd: LONG POINTER TO DrawCurve F.BcplSplineCommand ← p;
b: Bezier ← CoeffsToBezier[[
[curX, curY],
[BcplToMesaReal[cmd.x0], BcplToMesaReal[cmd.y0]],
[BcplToMesaReal[cmd.x1], BcplToMesaReal[cmd.y1]],
[BcplToMesaReal[cmd.x2], BcplToMesaReal[cmd.y2]]
]];
[curX, curY] ← b.b3;
curveToProc[b.b1.x, b.b1.y, b.b2.x, b.b2.y, b.b3.x, b.b3.y];
p ← p + SIZE[DrawCurve F.BcplSplineCommand];
};
F.newObjectCode => {
drawAreaProc[];
p ← p + SIZE[NewObject F.BcplSplineCommand];
};
F.endSplineCode => {
drawAreaProc[];
EXIT
};
ENDCASE => ERROR Error[invalidCodeInFile, p-splinesData.base];
ENDLOOP
};
Routines for converting to bezier points.
Vec: TYPE = RECORD[x, y: REAL];
VecAdd: PROC [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x+b.x,a.y+b.y]]};
VecSub: PROC [a: Vec, b: Vec] RETURNS [Vec] = INLINE {RETURN[[a.x-b.x,a.y-b.y]]};
VecDiv: PROC [a: Vec, s: REAL] RETURNS [Vec] = INLINE {RETURN[[a.x/s,a.y/s]]};
Coeffs: TYPE = RECORD[c0,c1,c2,c3: Vec];
Bezier: TYPE = RECORD[b0,b1,b2,b3: Vec];
CoeffsToBezier: PROC [c: Coeffs] RETURNS [b: Bezier] = {
OPEN b,c;
b0�
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: PROC [fontFile: FontFile, fontVector: FontVector, fontKey: FontKey] = TRUSTED {
widthsData: WidthsData ← NEW[WidthsDataRec];
ix: LONG POINTER TO widths F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
base: LONG POINTER ← fontFile.Pointer[];
nChars: NAT ← ix.ec - ix.bc + 1;
segOffset, xWidthOffset, yWidthOffset: LONG CARDINAL;
nx, ny: NAT;
IF ix.hdr.type # widths THEN ERROR Error[cantHappen, ix-base];
segOffset ← F.LongCardinalFromBcplLongPointer[ix.startaddress];
widthsData.indexEntry ← ix;
widthsData.widthSeg ← base + segOffset;
nx ← IF widthsData.widthSeg.XWidthFixed THEN 1 ELSE nChars;
ny ← IF widthsData.widthSeg.YWidthFixed THEN 1 ELSE nChars;
xWidthOffset ← segOffset + SIZE[F.BcplWidthSegment];
yWidthOffset ← xWidthOffset + nx;
widthsData.xWidthDesc ← DESCRIPTOR[fontFile.Pointer[]+xWidthOffset, nx];
widthsData.yWidthDesc ← DESCRIPTOR[fontFile.Pointer[]+yWidthOffset, ny];
IF yWidthOffset + ny - segOffset # F.BcplToMesaLongCardinal[ix.length] THEN
ERROR Error [consistencyCheck, ix-fontFile.Pointer[]];
fontVector[fontKey.fontIndex].procs ← widthsProcs;
fontVector[fontKey.fontIndex].data ← widthsData;
};
widthsProcs: Procs ← NEW[ProcsRec ← [
charInfoProc: WidthsCharInfo
]];
WidthsCharInfo: PROC [data: REF, char: CHAR] RETURNS [info: CharInfo] = TRUSTED {
widthsData: WidthsData ← NARROW[data];
ix: LONG POINTER TO widths F.RawIndex ← widthsData.indexEntry;
IF ix.hdr.type # widths THEN ERROR Error[cantHappen, 0];
IF (NOT char IN [ix.bc..ix.ec]) THEN RETURN[[zero,zero,zero,zero,zero,zero]]
ELSE {
data: F.BcplWidthSegment ← widthsData.widthSeg^;
scale: 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;
};
};
Font Class interface
pfClass: Font.Class ~ NEW[Font.ClassRep ← [
Contains: PFContains,
NextChar: PFNextChar,
Width: PFWidth,
Amplified: PFAmplified,
Correction: PFCorrection,
BoundingBox: PFBoundingBox,
FontBoundingBox: PFFontBoundingBox,
GetLigature: PFGetLigature,
NextLigature: PFNextLigature,
GetKern: PFGetKern,
NextKern: PFNextKern,
CharInfo: PFCharInfo,
DrawChar: PFDrawChar
]];
PFContains: PROC[font: FONT, char: CharCode] RETURNS[BOOLEAN] = {
data: Data ~ NARROW[font.data];
IF char IN[0..255] THEN {
charInfo: CharInfo ← GetCharInfo[[data.graphicsKey, 0], VAL[char]];
RETURN[charInfo#[zero,zero,zero,zero,zero,zero]];
};
RETURN[FALSE];
};
PFWidth: PROC[font: FONT, char: CharCode] RETURNS[Pair] = {
data: Data ~ NARROW[font.data];
IF char IN[0..255] THEN {
charInfo: CharInfo ~ GetCharInfo[[data.graphicsKey, 0], VAL[char]];
p: Vec ~ [charInfo.widthX, charInfo.widthY];
RETURN[ImagerTransform.Transform[p, font.transformation]];
};
RETURN[[0, 0]];
};
PFDrawChar: PROC[font: FONT, char: CharCode, context: REF] ~ {
data: Data ~ NARROW[font.data];
imager: Imager.Context ~ NARROW[context];
SELECT Representation[[data.graphicsKey, 0]] FROM
outline => {
GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
};
raster => {
pa: ImagerBasic.PixelArray ← GetCharRaster[[font.graphicsKey, 0], char];
mask: ImagerMasks.Mask;
bb: ImagerMasks.Mask;
size, xRes, yRes: REAL;
bitsPerEmX, bitsPerEmY: REAL;
IF pa = NIL THEN RETURN;
size ← Size[[font.graphicsKey, 0]]; -- in meters
[xRes, yRes] ← Resolution[[font.graphicsKey, 0]]; -- in bits per inch
bitsPerEmX ← xRes * size / 0.0254;
bitsPerEmY ← yRes * size / 0.0254;
mask ← ImagerMasks.FromPixelArray[pa, pa.m.Concat[ImagerTransform.Scale2[1/bitsPerEmX, 1/bitsPerEmY]].Concat[transformation]];
bb ← ImagerMasks.FromRectangle[ImagerMasks.BoundingBox[mask]];
ImagerMasks.GenerateRuns[mask: mask, clipper: bb, runProc: run];
};
ENDCASE => NULL;
};
PFMask: ENTRY PROC [font: FONT, transformation: UFont.Transformation, char: CHAR,
run: PROC [sMin, fMin: INTEGER, fSize: NAT]] = {
ENABLE UNWIND => NULL;
SELECT Representation[[font.graphicsKey, 0]] FROM
outline => {
GenPath: ImagerScanConverter.PathProc
-- move: PROC [s, f: REAL], --
-- line: PROC [s, f: REAL], --
-- curve: PROC [s1, f1, s2, f2, s3, f3: REAL] --
= {
m: ImagerTransform.TransformationRec ~ ImagerTransform.Contents[transformation];
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
m.a * p.x + m.b * p.y + m.c,
m.d * p.x + m.e * p.y + m.f
]]};
moveToProc: UFPressFontReader.MoveToProc ~ {
p: Pair = Xform[[x, y]];
move[p.x, p.y] };
lineToProc: UFPressFontReader.LineToProc ~ {
p: Pair = Xform[[x, y]];
line[p.x, p.y] };
curveToProc: UFPressFontReader.CurveToProc ~ {
p1: Pair = Xform[[x1, y1]];
p2: Pair = Xform[[x2, y2]];
p3: Pair = Xform[[x3, y3]];
curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y] };
drawAreaProc: UFPressFontReader.DrawAreaProc = {};
GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
devicePath ← ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: [-16000, -16000, 32000, 32000], scratch: devicePath];
devicePath.ConvertToRuns[runProc: run, clipBox: [-16000, -16000, 32000, 32000], parityFill: TRUE];
};
raster => {
pa: ImagerBasic.PixelArray ← GetCharRaster[[font.graphicsKey, 0], char];
mask: ImagerMasks.Mask;
bb: ImagerMasks.Mask;
size, xRes, yRes: REAL;
bitsPerEmX, bitsPerEmY: REAL;
IF pa = NIL THEN RETURN;
size ← Size[[font.graphicsKey, 0]]; -- in meters
[xRes, yRes] ← Resolution[[font.graphicsKey, 0]]; -- in bits per inch
bitsPerEmX ← xRes * size / 0.0254;
bitsPerEmY ← yRes * size / 0.0254;
mask ← ImagerMasks.FromPixelArray[pa, pa.m.Concat[ImagerTransform.Scale2[1/bitsPerEmX, 1/bitsPerEmY]].Concat[transformation]];
bb ← ImagerMasks.FromRectangle[ImagerMasks.BoundingBox[mask]];
ImagerMasks.GenerateRuns[mask: mask, clipper: bb, runProc: run];
};
ENDCASE => NULL;
};
PFBoundingBox: PROC [font: FONT, char: CHAR] RETURNS [box: UFont.Box] = {
m: ImagerTransform.TransformationRec ← font.actualTransformation.Contents;
B: PROC[p: Vec, box: UFont.Box] RETURNS [UFont.Box] = {
x: REAL ← m.a * p.x + m.b * p.y + m.c;
y: REAL ← m.d * p.x + m.e * p.y + m.f;
RETURN [[
xmin: MIN[box.xmin, x],
ymin: MIN[box.ymin, y],
xmax: MAX[box.xmax, x],
ymax: MAX[box.ymax, y]
]]
};
IF Representation[[font.graphicsKey, 0]] = outline THEN {
work harder to get a tighter bounding box
moveToProc: UFPressFontReader.MoveToProc ~ {
box ← B[[x, y], box]
};
lineToProc: UFPressFontReader.LineToProc ~ {
box ← B[[x, y], box]
};
curveToProc: UFPressFontReader.CurveToProc ~ {
box ← B[[x1, y1], B[[x2, y2], B[[x3, y3], box]]];
};
drawAreaProc: UFPressFontReader.DrawAreaProc = {};
box ← [xmin: 999999, ymin: 999999, xmax: -999999, ymax: -999999];
GetCharOutline[[font.graphicsKey, 0], char, moveToProc, lineToProc, curveToProc, drawAreaProc];
IF box=[xmin: 999999, ymin: 999999, xmax: -999999, ymax: -999999] THEN box ← [0,0,0,0];
}
ELSE {
charInfo: CharInfo ← GetCharInfo[[font.graphicsKey, 0], char];
x0: REAL ← charInfo.minX;
y0: REAL ← charInfo.minY;
x1: REAL ← charInfo.maxX;
y1: REAL ← charInfo.maxY;
box ← B[[x0,y0],B[[x0,y1],B[[x1,y0],[x1,y1,x1,y1]]]];
};
};
PFObjectInit: PROC [font: FONT] = {
font.fontGraphicsClass ← PFClass;
[font.bc, font.ec] ← Range[[font.graphicsKey, 0]];
font.PutProp[$PressFont, $PressFont];
};
PFClass: REF UFont.FontGraphicsClassRec←NEW[UFont.FontGraphicsClassRec←[
maskProc: PFMask,
boundingBoxProc: PFBoundingBox,
widthVectorProc: PFWidthVector,
containsProc: PFContains
]];
UFont.RegisterFontGraphicsClass[$Sd, PFObjectInit];
UFont.RegisterFontGraphicsClass[$Ac, PFObjectInit];
END.