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];
UFPressFontReaderImpl:
CEDAR
MONITOR
-- monitored because of devicePath
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:
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: UFont.Key]
RETURNS [
NAT] = {
file: FontFile ← UFFileManager.Open[fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
RETURN[data.length];
};
Family:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [family:
ROPE] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
family ← NameFromCode[data.names, p.family];
};
Face:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [face: [0..256)] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
face ← p.face;
};
Range:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [bc, ec:
CHAR] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
bc ← p.bc;
ec ← p.ec;
};
Size:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [size:
REAL] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
size ← p.size/100000.0;
};
Rotation:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [rotation:
REAL] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
rotation ← p.rotation/60.0;
};
Representation:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [representation: CharShapeRepresentation] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
representation ←
SELECT p.hdr.type
FROM
splines => outline,
chars => raster,
widths => widthsOnly,
ENDCASE => ERROR;
};
Resolution:
PUBLIC
PROCEDURE [fontKey: FontKey]
RETURNS [xRes, yRes:
REAL] =
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
data: FontVector ← NARROW[file.GetData[]];
p: LONG POINTER TO chars F.RawIndex ← data[fontKey.fontIndex].rawIndexPtr;
IF p.hdr.type # chars THEN {xRes ← yRes ← zero}
ELSE {
xRes ← p.resolutionx/10.0;
yRes ← p.resolutiony/10.0;
}
};
GetCharInfo:
PUBLIC
PROCEDURE [fontKey: FontKey, char:
CHAR]
RETURNS [info: CharInfo] =
TRUSTED {
fontFile: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
object: Object ← GetObject[fontFile, fontKey];
info ← object.procs.charInfoProc[object.data, char];
};
CharPathData: TYPE = RECORD [fontKey: FontKey, char: CHAR];
GenerateCharPath:
PROCEDURE [
data: REF,
move: PROC [Pair],
line: PROC [Pair],
curve: PROC [Pair, Pair, Pair],
conic: PROC [Pair, Pair, REAL]
] = {
moveToProc: UFPressFontReader.MoveToProc = {move[[x, y]]};
lineToProc: UFPressFontReader.LineToProc = {line[[x, y]]};
curveToProc: UFPressFontReader.CurveToProc = {curve[[x1, y1], [x2, y2], [x3, y3]]};
drawAreaProc: UFPressFontReader.DrawAreaProc = {};
d: REF CharPathData ← NARROW[data];
GetCharOutline[d.fontKey, d.char, moveToProc, lineToProc, curveToProc, drawAreaProc];
};
GetCharPath:
PUBLIC PROCEDURE [fontKey: FontKey, char:
CHAR]
RETURNS [pathMap: ImagerBasic.PathMapType, pathData:
REF] = {
RETURN[pathMap: GenerateCharPath, pathData: NEW[CharPathData ← [fontKey, char]]];
};
GetObject:
PROCEDURE [fontFile: FontFile, fontKey: FontKey]
RETURNS [object: Object] =
TRUSTED {
fontVector: FontVector ← NARROW[fontFile.GetData[]];
charsIndexPtr: LONG POINTER TO chars F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
IF fontVector[fontKey.fontIndex].data =
NIL
THEN {
charsIndexPtr: LONG POINTER TO chars F.RawIndex ← fontVector[fontKey.fontIndex].rawIndexPtr;
SELECT charsIndexPtr.hdr.type
FROM
chars => MakeCharsObject[fontFile, fontVector, fontKey];
splines => MakeSplinesObject[fontFile, fontVector, fontKey];
widths => MakeWidthsObject[fontFile, fontVector, fontKey];
ENDCASE => ERROR;
};
object ← [fontVector[fontKey.fontIndex].procs, fontVector[fontKey.fontIndex].data];
};
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;
};
};
GetCharRaster:
PUBLIC
PROCEDURE [fontKey: FontKey, char:
CHAR]
RETURNS [pa: ImagerBasic.PixelArray] ~
TRUSTED {
file: FontFile ← UFFileManager.Open[fontKey.fileKey, OpenInitialization];
object: Object ← GetObject[file, fontKey];
rasterData: RasterData ← NARROW[object.data];
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
data: F.BoundingBox ← rasterData.dataDesc[char-ix.bc];
offset: LONG CARDINAL ← F.LongCardinalFromBcplLongPointer[rasterData.dirDesc[char-ix.bc]];
rawRasterPointer: LONG POINTER ← BASE[rasterData.dirDesc] + offset;
r: F.RasterPointer ← rawRasterPointer;
rawRasterPointer ← rawRasterPointer + SIZE[F.RasterDimension];
IF data.BBdy = LAST[CARDINAL] THEN RETURN[NIL];
IF offset = LAST[LONG CARDINAL] THEN RETURN[NIL];
IF (rawRasterPointer-rasterData.base) < rasterData.rasterStart
OR ((rawRasterPointer-rasterData.base) >= rasterData.rasterEnd AND MIN[data.BBdy, data.BBdx] > 0) THEN
ERROR Error[invalidPointerInFile, @(rasterData.dirDesc[char-ix.bc]) - rasterData.base];
pa ← Imager.MakePixelArrayFromBits[
bitPointer: rawRasterPointer,
bitsPerLine: r.height*Basics.bitsPerWord,
samplesPerLine: data.BBdy,
numberOfLines: data.BBdx
];
pa.m ← ImagerTransform.Translate[data.BBox, data.BBoy];
};
CharsDrawChar: PROCEDURE [data: REF, context: Graphics.Context, char: CHAR] = TRUSTED {
rasterData: RasterData ← NARROW[data];
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
scalex: REAL ← IF ix.size = 0 THEN 1.0 ELSE 25400.0/(INT[ix.resolutionx]*ix.size);
scaley: REAL ← IF ix.size = 0 THEN 1.0 ELSE 25400.0/(INT[ix.resolutiony]*ix.size);
Graphics.Scale[context, scalex, scaley];
Graphics.Rotate[context, - ix.rotation/60.0];
DrawCharRasterPrivate[context, rasterData, char];
};
DrawCharRasterPrivate: PROCEDURE [context: Graphics.Context, rasterData: RasterData, char: CHAR] = TRUSTED {
ix: LONG POINTER TO chars F.RawIndex ← rasterData.indexEntry;
data: F.BoundingBox ← rasterData.dataDesc[char-ix.bc];
offset: LONG CARDINAL ← F.LongCardinalFromBcplLongPointer[rasterData.dirDesc[char-ix.bc]];
rawRasterPointer: LONG POINTER ← BASE[rasterData.dirDesc] + offset;
r: F.RasterPointer ← rawRasterPointer;
rawRasterPointer ← rawRasterPointer + SIZE[F.RasterDimension];
IF data.BBdy = LAST[CARDINAL] THEN RETURN;
IF offset = LAST[LONG CARDINAL] THEN RETURN;
IF (rawRasterPointer-rasterData.base) < rasterData.rasterStart
OR (rawRasterPointer-rasterData.base) >= rasterData.rasterEnd THEN
ERROR Error[invalidPointerInFile, @(rasterData.dirDesc[char-ix.bc]) - rasterData.base];
Graphics.SetCP[self: context, x: data.BBox, y: data.BBoy, rel: TRUE];
Graphics.Rotate[context, 90];
CGPrivate.DrawBits[
self: context,
base: rawRasterPointer,
raster: r.height,
bitsPerPixel: 0,
x: 0, y: 0,
w: data.BBdy, h: data.BBdx,
xorigin:0, yorigin: 0
];
};
Spline format operations.
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: 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;
};
};
FontGraphicsClass interface
devicePath: ImagerScanConverter.DevicePath ← NIL;
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:
PROCEDURE [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]]]];
};
};
PFWidthVector:
PROCEDURE [font:
FONT, char:
CHAR]
RETURNS [Pair] = {
m: ImagerTransform.TransformationRec ← font.actualTransformation.Contents;
charInfo: CharInfo ← GetCharInfo[[font.graphicsKey, 0], char];
p: Vec ← [charInfo.widthX, charInfo.widthY];
RETURN [[
x: m.a * p.x + m.b * p.y + m.c,
y: m.d * p.x + m.e * p.y + m.f
]];
};
PFContains:
PROCEDURE [font:
FONT, char:
CHAR]
RETURNS [
BOOLEAN] = {
charInfo: CharInfo ← GetCharInfo[[font.graphicsKey, 0], char];
RETURN[charInfo#[zero,zero,zero,zero,zero,zero]];
};
PFObjectInit:
PROCEDURE [font:
FONT] = {
font.fontGraphicsClass ← PFClass;
[font.bc, font.ec] ← Range[[font.graphicsKey, 0]];
font.PutProp[$PressFont, $PressFont];
};
PFClass:
REF UFont.FontGraphicsClassRec←
NEW[UFont.FontGraphicsClassRec←[
maskProc: PFMask,
boundingBoxProc: PFBoundingBox,
widthVectorProc: PFWidthVector,
containsProc: PFContains
]];
UFont.RegisterFontGraphicsClass[$Sd, PFObjectInit];
UFont.RegisterFontGraphicsClass[$Ac, PFObjectInit];
END.