PressFontWriterImpl:
PROGRAM
IMPORTS IO, Graphics, GraphicsOps, PressFontFormat, Real, RealConvert, Rope
EXPORTS PressFontWriter
= BEGIN
OPEN PressFontWriter, F: PressFontFormat;
NewFont: PUBLIC TYPE = REF NewFontRec;
NewFontRec:
PUBLIC
TYPE =
RECORD [
family: ROPE ← NIL,
face: [0..256),
bc: CHAR,
ec: CHAR,
size: REAL,
rotation: REAL,
representation: CharShapeRepresentation,
xRes, yRes: REAL,
context: Graphics.Context,
file: IO.STREAM,
endLoc: Location, -- word length of file
startaddressLoc, dataLoc, directoryLoc: Location ← LAST[Location]
];
Location: TYPE = LONG CARDINAL; -- a word index into the file.
WriteBlock:
PROCEDURE [newFont: NewFont, base:
LONG
POINTER, size:
NAT]
RETURNS [location: Location] = {
location ← newFont.endLoc;
newFont.file.UnsafePutBlock[[base: base, startIndex: 0, stopIndexPlusOne: size*2]];
newFont.endLoc ← newFont.endLoc + size;
};
FixUp:
PROCEDURE [newFont: NewFont, location: Location, base:
LONG
POINTER, size:
NAT] = {
newFont.file.SetIndex[location*2];
newFont.file.UnsafePutBlock[[base: base, startIndex: 0, stopIndexPlusOne: size*2]];
newFont.file.SetIndex[newFont.endLoc*2];
};
WriteError: PUBLIC SIGNAL[writeErrorCode: WriteErrorCode] = CODE;
Create:
PUBLIC
PROCEDURE [filename:
ROPE,
representation: CharShapeRepresentation,
family:
ROPE,
face: [0..256) ← 0,
size:
REAL ← 0,
-- in meters, or 0 for a scalable font
rotation:
REAL ← 0,
-- in degrees
bc:
CHAR ←
FIRST[
CHAR],
-- first character in the font
ec:
CHAR ←
LAST[
CHAR],
-- last character in the font
xRes, yRes:
REAL ← 0
-- In bits per inch. Only used for a raster font.
] RETURNS [newFont: NewFont] = {
newFont ← NEW[NewFontRec];
newFont.family ← family;
newFont.face ← face;
newFont.size ← size;
newFont.rotation ← rotation;
newFont.bc ← bc;
newFont.ec ← ec;
newFont.representation ← representation;
newFont.xRes ← xRes;
newFont.yRes ← yRes;
IF representation=outline AND size#0.0 THEN ERROR WriteError[inconsistentParameters];
IF representation=raster AND (size<=0.0 OR xRes<=0.0 OR yRes<=0.0) THEN ERROR WriteError[inconsistentParameters];
IF bc > ec THEN ERROR WriteError[inconsistentParameters];
IF representation#outline THEN ERROR WriteError[notImplemented];
newFont.file ← IO.CreateFileStream[filename, overwrite];
newFont.endLoc ← 0;
WriteNameIndex[newFont];
newFont.context ← GraphicsOps.NewContextFromBitmap[GraphicsOps.NewBitmap[1,1]];
IF representation=outline
THEN {
WriteSplineSkeleton[newFont];
newFont.context.procs←NEW[Graphics.GraphicsProcs ← newFont.context.procs^];
newFont.context.procs.FlushPath ← NIL;
newFont.context.procs.Close ← NIL;
newFont.context.procs.Rectangle ← NIL;
newFont.context.procs.DrawTo ← NIL;
newFont.context.procs.DrawPath ← NIL;
newFont.context.procs.DrawBox ← NIL;
newFont.context.procs.DrawImage ← NIL;
newFont.context.procs.SetColor ← NIL;
newFont.context.procs.GetColor ← NIL;
newFont.context.procs.SetPaintMode ← NIL;
newFont.context.procs.GetDefaultFont ← NIL;
newFont.context.procs.SetDefaultFont ← NIL;
newFont.context.procs.DrawChars ← NIL;
newFont.context.procs.ClipArea ← NIL;
newFont.context.procs.ClipBox ← NIL;
newFont.context.procs.IsPointVisible ← NIL;
newFont.context.procs.IsRectangular ← NIL;
newFont.context.procs.GetBounds ← NIL;
newFont.context.procs.Visible ← NIL;
newFont.context.procs.DrawBits ← NIL;
newFont.context.procs.GetYMode ← NIL;
newFont.context.procs.SetYMode ← NIL;
newFont.context.procs.DrawTexturedBox ← NIL;
};
};
WriteNameIndex:
PROCEDURE [newFont: NewFont] = {
nameIndex: NameIndex;
nameLength: [1..20) ← newFont.family.Length[];
nameIndex.hdr.type ← name;
nameIndex.hdr.length ← SIZE[NameIndex];
nameIndex.code ← 1;
nameIndex.fontname[0] ← LOOPHOLE[nameLength];
FOR i:
NAT
IN [0..nameLength)
DO
nameIndex.fontname[i+1] ← newFont.family.Fetch[i];
ENDLOOP;
[] ← WriteBlock[newFont, @nameIndex, SIZE[NameIndex]];
};
WriteSplineSkeleton:
PROCEDURE [newFont: NewFont] = {
splinesIndex: splines F.RawIndex;
splinesIndexLoc: Location;
splinesIndex.hdr ← [type: splines, length: SIZE[splines F.RawIndex]];
splinesIndex.family ← 1;
splinesIndex.face ← newFont.face;
splinesIndex.bc ← newFont.bc;
splinesIndex.ec ← newFont.ec;
splinesIndex.size ← 0;
splinesIndex.rotation ← Real.RoundC[newFont.rotation*60.0];
splinesIndexLoc ← WriteBlock[newFont, @splinesIndex, SIZE[splines F.RawIndex]];
newFont.startaddressLoc ←
newFont.endLoc - (SIZE[F.bcplLONGPOINTER] + SIZE[F.bcplLONGCARDINAL]);
WriteEndIndex[newFont];
newFont.dataLoc ← newFont.endLoc;
WriteDummySplineCharData[newFont];
newFont.directoryLoc ← newFont.endLoc;
WriteDummyDirectory[newFont];
};
WriteEndIndex:
PROCEDURE [newFont: NewFont] = {
endIndex: end F.RawIndex;
endIndex.hdr ← [type: end, length: SIZE[end F.RawIndex]];
[] ← WriteBlock[newFont, @endIndex, SIZE[end F.RawIndex]];
};
WriteDummySplineCharData:
PROCEDURE [newFont: NewFont] = {
bcplSplineData: F.BcplSplineData ← [F.missingCharValue, F.missingCharValue, F.missingCharValue, F.missingCharValue, F.missingCharValue, F.missingCharValue];
FOR i:
CHAR
IN [newFont.bc..newFont.ec]
DO
[] ← WriteBlock[newFont, @bcplSplineData, SIZE[F.BcplSplineData]];
ENDLOOP
};
WriteDummyDirectory:
PROCEDURE [newFont: NewFont] = {
dummyLP: F.bcplLONGPOINTER ← LocationToBcplPointer[LAST[Location]];
FOR i:
CHAR
IN [newFont.bc..newFont.ec]
DO
[] ← WriteBlock[newFont, @dummyLP, SIZE[F.bcplLONGPOINTER]];
ENDLOOP
};
PaintChar:
PUBLIC
PROCEDURE [newFont: NewFont, char:
CHAR, charInfo: CharInfo, paintProc: PaintProc] = {
OPEN charInfo;
Undefined:
PROCEDURE [r:
REAL]
RETURNS [
BOOLEAN] = {
RETURN[
LOOPHOLE[r, LONG CARDINAL] =
LOOPHOLE[Real.NonTrappingNaN, LONG CARDINAL]
]
};
mark: Graphics.Mark ← Graphics.Save[newFont.context];
Graphics.SetCP[newFont.context, 0, 0];
IF Undefined[minX] OR Undefined[minY] OR Undefined[maxX] OR Undefined[maxY] THEN ERROR WriteError[notImplemented];
SELECT newFont.representation
FROM
outline => PaintSplineChar[newFont, char, charInfo, paintProc];
ENDCASE => ERROR WriteError[notImplemented];
Graphics.Restore[newFont.context];
};
PaintSplineChar:
PROCEDURE [newFont: NewFont, char:
CHAR, charInfo: CharInfo, paintProc: PaintProc] = {
MoveTo:
PROC[self: Graphics.Context, x,y:
REAL, rel:
BOOLEAN] = {
wx, wy: REAL;
bcplSplineCommand: MoveTo F.BcplSplineCommand;
IF rel THEN {[wx, wy] ← Graphics.GetCP[self, FALSE]; x ← x+wx; y←y+wy};
[wx, wy] ← Graphics.UserToWorld[self, x, y];
bcplSplineCommand.type ← F.moveToCode;
bcplSplineCommand.x ← MesaToBcplReal[wx];
bcplSplineCommand.y ← MesaToBcplReal[wy];
[] ← WriteBlock[newFont, @bcplSplineCommand, SIZE[DrawTo F.BcplSplineCommand]];
Graphics.SetCP[self, x, y];
};
LineTo:
PROC[self: Graphics.Context, x,y:
REAL, rel:
BOOLEAN] = {
wx, wy: REAL;
bcplSplineCommand: DrawTo F.BcplSplineCommand;
IF rel THEN {[wx, wy] ← Graphics.GetCP[self, FALSE]; x ← x+wx; y←y+wy};
[wx, wy] ← Graphics.UserToWorld[self, x, y];
bcplSplineCommand.type ← F.drawToCode;
bcplSplineCommand.x ← MesaToBcplReal[wx];
bcplSplineCommand.y ← MesaToBcplReal[wy];
[] ← WriteBlock[newFont, @bcplSplineCommand, SIZE[DrawTo F.BcplSplineCommand]];
Graphics.SetCP[self, x, y];
};
CurveTo:
PROC[self: Graphics.Context, x1,y1,x2,y2,x3,y3:
REAL, rel:
BOOLEAN] = {
wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3: REAL;
bcplSplineCommand: DrawCurve F.BcplSplineCommand;
c: Coeffs;
[wx0, wy0] ← Graphics.GetCP[self, FALSE];
IF rel
THEN {
x1 ← x1+wx0; y1←y1+wy0;
x1 ← x2+wx0; y1←y2+wy0;
x1 ← x3+wx0; y1←y3+wy0;
};
[wx0, wy0] ← Graphics.UserToWorld[self, wx0, wy0];
[wx1, wy1] ← Graphics.UserToWorld[self, x1, y1];
[wx2, wy2] ← Graphics.UserToWorld[self, x2, y2];
[wx3, wy3] ← Graphics.UserToWorld[self, x3, y3];
c ← BezierToCoeffs[[[wx0, wy0], [wx1, wy1], [wx2, wy2], [wx3, wy3]]];
bcplSplineCommand.type ← F.drawCurveCode;
bcplSplineCommand.x0 ← MesaToBcplReal[c.c1.x];
bcplSplineCommand.y0 ← MesaToBcplReal[c.c1.y];
bcplSplineCommand.x1 ← MesaToBcplReal[c.c2.x];
bcplSplineCommand.y1 ← MesaToBcplReal[c.c2.y];
bcplSplineCommand.x2 ← MesaToBcplReal[c.c3.x];
bcplSplineCommand.y2 ← MesaToBcplReal[c.c3.y];
[] ← WriteBlock[newFont, @bcplSplineCommand, SIZE[DrawCurve F.BcplSplineCommand]];
Graphics.SetCP[self, x3, y3];
};
DrawArea:
PROC[self: Graphics.Context, parityFill:
BOOLEAN] = {
IF areaDrawn THEN ERROR WriteError[notImplemented];
areaDrawn ← TRUE;
};
areaDrawn: BOOLEAN ← FALSE;
endSplineCommand: EndDefinition F.BcplSplineCommand;
splineDataLP: F.bcplLONGPOINTER ← LocationToBcplPointer[newFont.endLoc - newFont.directoryLoc];
bcplSplineData: F.BcplSplineData;
bcplSplineData.xwidth ← MesaToBcplReal[charInfo.widthX];
bcplSplineData.ywidth ← MesaToBcplReal[charInfo.widthY];
bcplSplineData.bbox ← MesaToBcplReal[charInfo.minX];
bcplSplineData.bboy ← MesaToBcplReal[charInfo.minY];
bcplSplineData.rightx ← MesaToBcplReal[charInfo.maxX];
bcplSplineData.topy ← MesaToBcplReal[charInfo.maxY];
FixUp[newFont, newFont.dataLoc + (char-newFont.bc)*SIZE[F.BcplSplineData], @bcplSplineData, SIZE[F.BcplSplineData]];
newFont.context.procs.MoveTo ← MoveTo;
newFont.context.procs.LineTo ← LineTo;
newFont.context.procs.CurveTo ← CurveTo;
newFont.context.procs.DrawArea ← DrawArea;
paintProc[newFont.context];
newFont.context.procs.MoveTo ← NIL;
newFont.context.procs.LineTo ← NIL;
newFont.context.procs.CurveTo ← NIL;
newFont.context.procs.DrawArea ← NIL;
endSplineCommand.type ← F.endSplineCode;
[] ← WriteBlock[newFont, @endSplineCommand, SIZE[EndDefinition F.BcplSplineCommand]];
FixUp[newFont, newFont.directoryLoc + (char-newFont.bc)*SIZE[F.bcplLONGPOINTER], @splineDataLP, SIZE[F.bcplLONGPOINTER]];
};
NameIndex:
TYPE =
MACHINE
DEPENDENT
RECORD [
hdr: F.IndexHeader,
code: CARDINAL,
fontname: PACKED ARRAY [0..20) OF CHAR
];
FontDataDescriptor:
TYPE =
MACHINE
DEPENDENT
RECORD [
startaddress: F.bcplLONGPOINTER, -- Starting address of data part
length: F.bcplLONGCARDINAL -- Length of data part
];
Close:
PUBLIC
PROCEDURE [newFont: NewFont] = {
IF newFont.startaddressLoc #
LAST[Location]
THEN {
fontDataDescriptor: FontDataDescriptor;
fontDataDescriptor.startaddress ← LocationToBcplPointer[newFont.dataLoc];
fontDataDescriptor.length ← F.MesaToBcplLongCardinal[newFont.endLoc - newFont.dataLoc];
FixUp[newFont, newFont.startaddressLoc, @fontDataDescriptor, SIZE[FontDataDescriptor]];
};
newFont.file.Close[]; newFont.file ← NIL
};
MesaToBcplReal:
PROCEDURE [r:
REAL]
RETURNS [b:
F.BcplREAL] = {
b ← LOOPHOLE[RealConvert.IeeeToBcpl[r]]
};
LocationToBcplPointer:
PROCEDURE [location: Location]
RETURNS [
F.bcplLONGPOINTER] =
INLINE {
RETURN[LOOPHOLE[F.MesaToBcplLongCardinal[location]]]
};
-- Routines for converting from 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]]};
VecMul: 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];
BezierToCoeffs:
PROCEDURE [b: Bezier]
RETURNS [c: Coeffs] = {
OPEN b,c;
temp: Vec;
c0 ← b0;
c1 ← VecMul[VecSub[b1,b0],3];
c2 ← VecSub[(temp←VecMul[VecSub[b2,b1],3]),c1];
c3 ← VecSub[b3,VecAdd[b0,temp]];
};
END.