-- SplineFontWriterJaM.mesa
-- Written by Michael Plass on November 8, 1982 10:58 am
-- Last edit by Michael Plass on December 2, 1982 1:17 pm
DIRECTORY IO, FileIO, JaMBasic, JaMInternal, JaMOps, Real, Rope, TJaMGraphicsInfo, CGCubic, CGPath, GraphicsBasic;
SplineFontWriterJaM: PROGRAM
IMPORTS JaMOps, IO, FileIO, Real, Rope, TJaMGraphicsInfo, CGCubic, CGPath
= BEGIN
ROPE: TYPE = Rope.ROPE;
Frame: TYPE = JaMInternal.Frame;
fontFile: IO.STREAMNIL;
face: ROPE ← "M R R";
family: ROPE ← "UNKNOWN";
originx, originy: REAL ← 0.0;
fiducial: INT ← 15840; -- this is 2*2*2*2*2*3*3*5*11 - lots of small factors.
quad: REAL ← 1.0;
PopRope: PROCEDURE [f: Frame] RETURNS [rope: ROPE] = {
s: string JaMBasic.Object ← JaMOps.PopString[f.opstk];
C: PROC[c: CHAR] RETURNS [BOOLEAN] =
{t[t.length]𡤌 t.length ← t.length+1; RETURN[FALSE]};
t: REF TEXTNEW[TEXT[s.length]];
t.length ← 0;
JaMOps.StringForAll[s, C];
rope ← Rope.FromRefText[t];
};
Open: PROCEDURE [f: Frame] = {
fileName: ROPE ← PopRope[f];
IF fontFile # NIL THEN fontFile.Close[];
fontFile ← NIL;
IF fileName.Length[] > 0 THEN fontFile ← FileIO.Open[fileName, append];
};
Family: PROCEDURE [f: Frame] = {
family ← PopRope[f];
};
Face: PROCEDURE [f: Frame] = {
face ← PopRope[f];
};
Origin: PROCEDURE [f: Frame] = {
originy ← JaMOps.PopReal[f.opstk];
originx ← JaMOps.PopReal[f.opstk];
};
Quad: PROCEDURE [f: Frame] = {
quad ← JaMOps.PopReal[f.opstk];
};
Close: PROCEDURE [f: Frame] = {
IF fontFile # NIL THEN fontFile.Close[];
fontFile ← NIL;
};
Char: PROCEDURE [f: Frame] = {
widthy: REAL ← JaMOps.PopReal[f.opstk];
widthx: REAL ← JaMOps.PopReal[f.opstk];
charCode: NAT ← JaMOps.PopInteger[f.opstk];
info: TJaMGraphicsInfo.Info ← TJaMGraphicsInfo.GetInfo[f];
path: GraphicsBasic.Path ← info.path;
CountCon: PROC RETURNS [maxBeziers: NAT ← 0] = {
beziers: NAT ← 0;
MoveTo1: SAFE PROC [vec: GraphicsBasic.Vec] = CHECKED {maxBeziers ← MAX[maxBeziers, beziers]; beziers ← 0};
LineTo1: SAFE PROC [vec: GraphicsBasic.Vec] = CHECKED {beziers ← beziers+1};
CurveTo1: SAFE PROC [b1, b2, b3: GraphicsBasic.Vec] = CHECKED {beziers ← beziers+1};
CGPath.Generate[path, MoveTo1, LineTo1, CurveTo1];
maxBeziers ← MAX[maxBeziers, beziers];
};
BezierSequenceRec: TYPE = RECORD[SEQUENCE length: NAT OF CGCubic.Bezier];
bezier: REF BezierSequenceRec ← NEW[BezierSequenceRec[CountCon[]]];
k: NAT ← 0;
curpt: GraphicsBasic.Vec;
MoveTo2: SAFE PROC [vec: GraphicsBasic.Vec] = CHECKED {
WriteCurrentOutline[];
curpt ← Grid[vec];
};
LineTo2: SAFE PROC [vec: GraphicsBasic.Vec] = CHECKED {
b3: GraphicsBasic.Vec ← Grid[vec];
bezier[k] ← [b0: curpt, b1: [(curpt.x+curpt.x+b3.x)/3, (curpt.y+curpt.y+b3.y)/3], b2: [(curpt.x+b3.x+b3.x)/3, (curpt.y+b3.y+b3.y)/3], b3: b3];
k ← k+1;
curpt ← b3;
};
CurveTo2: SAFE PROC [b1, b2, b3: GraphicsBasic.Vec] = CHECKED {
b3 ← Grid[b3];
bezier[k] ← [b0: curpt, b1: Transform[b1], b2: Transform[b2], b3: b3];
k ← k+1;
curpt ← b3;
};
Transform: SAFE PROC [vec: GraphicsBasic.Vec] RETURNS [gridVec: GraphicsBasic.Vec] = CHECKED {
gridVec.x ← (vec.x-originx)*fiducial/quad;
gridVec.y ← (vec.y-originy)*fiducial/quad;
};
Grid: SAFE PROC [vec: GraphicsBasic.Vec] RETURNS [gridVec: GraphicsBasic.Vec] = CHECKED {
gridVec.x ← Real.RoundLI[(vec.x-originx)*fiducial/quad];
gridVec.y ← Real.RoundLI[(vec.y-originy)*fiducial/quad];
};
WriteCurrentOutline: SAFE PROC = CHECKED {
IF k=0 THEN RETURN;
fontFile.PutF["\n ((%d (", IO.int[k+1]];
FOR i: NAT IN [0..k) DO
fontFile.PutF["(%d %d) ", IO.int[Real.RoundLI[bezier[i].b0.x]], IO.int[Real.RoundLI[bezier[i].b0.y]]];
ENDLOOP;
fontFile.PutF["(%d %d)", IO.int[Real.RoundLI[bezier[0].b0.x]], IO.int[Real.RoundLI[bezier[0].b0.y]]];
fontFile.PutRope[")\n NIL ("];
FOR i: NAT IN [0..k) DO
coeffs: CGCubic.Coeffs ← CGCubic.BezierToCoeffs[bezier[i]];
IF i>0 THEN fontFile.PutRope[" "];
fontFile.PutF["(%g %g", IO.real[coeffs.c1.x], IO.real[coeffs.c1.y]];
fontFile.PutF[" %g %g", IO.real[coeffs.c2.x*2.0], IO.real[coeffs.c2.y*2.0]];
fontFile.PutF[" %g %g)", IO.real[coeffs.c3.x*6.0], IO.real[coeffs.c3.y*6.0]];
ENDLOOP;
fontFile.PutRope[")\n NATURAL))"];
k ← 0;
};
fontFile.PutRope["\n((FAMILY "]; fontFile.PutRope[family];fontFile.PutRope[")\n"];
fontFile.PutF[" (CHARACTER %d)\n", IO.int[charCode]];
fontFile.PutRope[" (FACE "]; fontFile.PutRope[face];fontFile.PutRope[")\n"];
fontFile.PutF[" (WIDTH %d %d)\n", IO.int[Real.RoundLI[widthx*fiducial/quad]], IO.int[Real.RoundLI[widthy*fiducial/quad]]];
fontFile.PutF[" (FIDUCIAL %d %d)\n", IO.int[fiducial], IO.int[fiducial]];
fontFile.PutRope[" (SPLINES"];
CGPath.Generate[self: path, move: MoveTo2, line: LineTo2, curve: CurveTo2];
WriteCurrentOutline[];
fontFile.PutRope["))\n"];
};
Operations for creating a .SF format font file via JaMGraphics.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Open", Open]; -- <filename> -> . Opens .sf file in append mode
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Family", Family]; -- <family: string> -> . sets the family for subsequent DrawChar operations.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Face", Face]; -- <face: string> -> . sets the face for subsequent DrawChar operations.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Origin", Origin]; -- <x: number> <y: number> -> . says what the character origins are.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Quad", Quad]; -- <baselineSpacing: number> -> . says what the units are.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Close", Close]; -- -> . closes .sf file.
JaMOps.RegisterExplicit[JaMOps.defaultFrame, "SF.Char", Char]; -- . <ascii code: integer> <widthx: number> <widthy: number> -> . Takes the current path and writes it out as a character in .sf format.
END.