SDtoSFImpl.mesa
Michael Plass, January 13, 1984 10:20 am
DIRECTORY Commander, Convert, FS, IO, Imager, ImagerBasic, ImagerConic, Real, Rope, UFFileManager, UFPressFontReader, SDtoSF;
SDtoSFImpl: CEDAR PROGRAM
IMPORTS Commander, Convert, FS, IO, ImagerConic, Real, Rope, UFFileManager, UFPressFontReader
EXPORTS SDtoSF
SHARES Imager ~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Pair: TYPE ~ ImagerBasic.Pair;
Trajectory: TYPE ~ Imager.Trajectory;
TrajectoryRep: TYPE ~ Imager.TrajectoryRep;
Outline: TYPE ~ Imager.Outline;
Polygon: TYPE ~ Imager.Polygon;
Coeffs: TYPE ~ RECORD [c0, c1, c2, c3: ImagerBasic.Pair];
Sub: PROC [a: Pair, b: Pair] RETURNS [Pair] ~ INLINE { RETURN[[a.x-b.x, a.y-b.y]] };
Add: PROC [a: Pair, b: Pair] RETURNS [Pair] ~ INLINE { RETURN[[a.x+b.x, a.y+b.y]] };
Mul: PROC [a: Pair, s: REAL] RETURNS [Pair] ~ INLINE { RETURN[[a.x*s, a.y*s]] };
BezierToCoeffs: PROC [bezier: ImagerBasic.Bezier] RETURNS[coeffs: Coeffs] ~ {
Compute the cubic coefficients from the Bezier points.
c0 ← b0;
c1 ← 3(b1-b0);
c2 ← 3(b0-2b1+b2); [ = 3(b2-b1) - c1 ]
c3 ← -b0 +3(b1-b2) + b3; [ = b3 - (b0 + 3(b2-b1)) ]
t: Pair ← Mul[Sub[bezier.b2,bezier.b1],3];
coeffs.c0 ← bezier.b0;
coeffs.c1 ← Mul[Sub[bezier.b1, bezier.b0], 3];
coeffs.c2 ← Sub[t, coeffs.c1];
coeffs.c3 ← Sub[bezier.b3, Add[bezier.b0, t]];
RETURN [coeffs];
};
MapTrajectory: ImagerBasic.PathMapType = {
t: Trajectory = NARROW[data];
move[t.lp];
FOR x: Trajectory ← t, x.prev UNTIL x.prev=NIL DO
p0: Pair = x.prev.lp;
WITH x SELECT FROM
x: REF TrajectoryRep[line] => line[p0];
x: REF TrajectoryRep[curve] => curve[x.p2, x.p1, p0];
x: REF TrajectoryRep[conic] => conic[x.p1, p0, x.r];
x: REF TrajectoryRep[move] => ERROR;
ENDCASE => ERROR;
ENDLOOP;
};
MapTrajectoryList: ImagerBasic.PathMapType = {
FOR x: LIST OF Trajectory ← NARROW[data], x.rest UNTIL x = NIL DO
MapTrajectory[x.first, move, line, curve, conic]
ENDLOOP;
};
MapPolygon: ImagerBasic.PathMapType = {
p: Polygon = NARROW[data];
IF p.length>0 THEN {
move[p[0]];
FOR i: NAT IN[1..MIN[p.length, p.maxLength]) DO line[p[i]] ENDLOOP;
};
};
SDCharToRope: PUBLIC PROC [sdName: ROPE, charCode: NAT, fiducial: INT] RETURNS [ROPE] ~ {
stream: IO.STREAMIO.ROS[];
SDCharToStream[stream, sdName, charCode, fiducial];
RETURN [IO.RopeFromROS[stream]];
};
SDCharToStream: PUBLIC PROC [dest: IO.STREAM, sdName: ROPE, charCode: NAT, fiducial: INT] ~ {
fontKey: UFPressFontReader.FontKey ← [UFFileManager.KeyOf[sdName], 0];
charInfo: UFPressFontReader.CharInfo ← UFPressFontReader.GetCharInfo[fontKey, VAL[charCode]];
pathMap: ImagerBasic.PathMapType;
pathData: REF;
[pathMap, pathData] ← UFPressFontReader.GetCharPath[fontKey, VAL[charCode]];
PathToStream[dest, pathMap, pathData, UFPressFontReader.Family[fontKey], DecodeFace[UFPressFontReader.Face[fontKey]], VAL[charCode], charInfo.widthX, charInfo.widthY, fiducial];
};
DecodeFace: PROC [faceCode: [0..255]] RETURNS [face: ROPE] ~ {
IF faceCode IN [0..18) THEN {
slope: ROPE ~ IF (faceCode MOD 2) = 0 THEN "Roman" ELSE "Italic";
weight: ROPE ~ SELECT (faceCode/2 MOD 3) FROM
0 => "Medum",
1 => "Bold",
2 => "Light",
ENDCASE => ERROR;
expansion: ROPE ~ SELECT (faceCode/6 MOD 3) FROM
0 => "Regular",
1 => "Condensed",
2 => "Expanded",
ENDCASE => ERROR;
face ← weight.Cat[" ", slope, " ", expansion];
}
ELSE face ← Convert.RopeFromInt[faceCode];
};
OutlineToRope: PUBLIC PROC [outline: REF, family: ROPE, face: ROPE, charCode: NAT, widthx: REAL, widthy: REAL, fiducial: INT] RETURNS [ROPE] ~ {
stream: IO.STREAMIO.ROS[];
OutlineToStream[stream, outline, family, face, charCode, widthx, widthy, fiducial];
RETURN [IO.RopeFromROS[stream]];
};
OutlineToStream: PUBLIC PROC [dest: IO.STREAM, outline: REF, family: ROPE, face: ROPE, charCode: NAT, widthx: REAL, widthy: REAL, fiducial: INT] ~ {
WITH outline SELECT FROM
o: Outline => PathToStream[dest, MapTrajectoryList, o.list, family, face, charCode, widthx, widthy, fiducial];
list: LIST OF Trajectory => PathToStream[dest, MapTrajectoryList, list, family, face, charCode, widthx, widthy, fiducial];
t: Trajectory => PathToStream[dest, MapTrajectory, t, family, face, charCode, widthx, widthy, fiducial];
p: Polygon => PathToStream[dest, MapPolygon, p, family, face, charCode, widthx, widthy, fiducial];
ENDCASE => ERROR;
};
PathToStream: PUBLIC PROC [dest: IO.STREAM, pathMap: ImagerBasic.PathMapType, pathData: REF, family: ROPE, face: ROPE, charCode: NAT, widthx: REAL, widthy: REAL, fiducial: INT] ~ {
Grid: PROC [vec: Pair] RETURNS [gridVec: Pair] ~ {
gridVec.x ← Real.RoundLI[(vec.x)*fiducial];
gridVec.y ← Real.RoundLI[(vec.y)*fiducial];
};
CountCon: PROC RETURNS [maxBeziers: NAT ← 0] ~ {
beziers: NAT ← 0;
p0: Pair ← [0, 0];
Move: PROC [p: Pair] ~ {maxBeziers ← MAX[maxBeziers, beziers]; beziers ← 0; p0 ← Grid[p]};
Line: PROC [p: Pair] ~ {beziers ← beziers+1; p0 ← Grid[p]};
Curve: PROC [p1, p2, p3: Pair] ~ {beziers ← beziers+1; p0 ← Grid[p3]};
Conic: PROC [p1, p2: Pair, r: REAL] ~ {ImagerConic.ToCurves[p0, p1, p2, r, Curve]};
pathMap[pathData, Move, Line, Curve, Conic];
maxBeziers ← MAX[maxBeziers, beziers];
};
BezierSequenceRec: TYPE ~ RECORD[SEQUENCE length: NAT OF ImagerBasic.Bezier];
bezier: REF BezierSequenceRec ← NEW[BezierSequenceRec[CountCon[]]];
k: NAT ← 0;
p0: Pair ← [0, 0];
Move: PROC [p: Pair] ~ {
WriteCurrentOutline[];
p0 ← Grid[p];
};
Line: PROC [p: Pair] ~ {
p ← Grid[p];
bezier[k] ← [
b0: p0,
b1: [(p0.x+p0.x+p.x)/3, (p0.y+p0.y+p.y)/3],
b2: [(p0.x+p.x+p.x)/3, (p0.y+p.y+p.y)/3],
b3: p
];
k ← k+1;
p0 ← p;
};
Curve: PROC [p1, p2, p3: Pair] ~ {
p1 ← Mul[p1, fiducial];
p2 ← Mul[p2, fiducial];
p3 ← Grid[p3];
bezier[k] ← [b0: p0, b1: p1, b2: p2, b3: p3];
k ← k+1;
p0 ← p3;
};
Conic: PROC [p1, p2: Pair, r: REAL] ~ {ImagerConic.ToCurves[p0, p1, p2, r, Curve]};
WriteCurrentOutline: SAFE PROC ~ CHECKED {
IF k=0 THEN RETURN;
dest.PutF["\n ((%d (", IO.int[k+1]];
FOR i: NAT IN [0..k) DO
dest.PutF["(%d %d) ", IO.int[Real.RoundLI[bezier[i].b0.x]], IO.int[Real.RoundLI[bezier[i].b0.y]]];
ENDLOOP;
dest.PutF["(%d %d)", IO.int[Real.RoundLI[bezier[0].b0.x]], IO.int[Real.RoundLI[bezier[0].b0.y]]];
dest.PutRope[")\n NIL ("];
FOR i: NAT IN [0..k) DO
coeffs: Coeffs ← BezierToCoeffs[bezier[i]];
IF i>0 THEN dest.PutRope[" "];
dest.PutF["(%g %g", IO.real[coeffs.c1.x], IO.real[coeffs.c1.y]];
dest.PutF[" %g %g", IO.real[coeffs.c2.x*2.0], IO.real[coeffs.c2.y*2.0]];
dest.PutF[" %g %g)", IO.real[coeffs.c3.x*6.0], IO.real[coeffs.c3.y*6.0]];
ENDLOOP;
dest.PutRope[")\n NATURAL))"];
k ← 0;
};
dest.PutRope["\n((FAMILY "]; dest.PutRope[family]; dest.PutRope[") "];
dest.PutF[" (CHARACTER %d)\n", IO.int[charCode]];
dest.PutRope[" (FACE "]; dest.PutRope[face]; dest.PutRope[")\n"];
dest.PutF[" (WIDTH %d %d)\n", IO.int[Real.RoundLI[widthx*fiducial]], IO.int[Real.RoundLI[widthy*fiducial]]];
dest.PutF[" (FIDUCIAL %d %d)\n", IO.int[fiducial], IO.int[fiducial]];
dest.PutRope[" (SPLINES"];
pathMap[data: pathData, move: Move, line: Line, curve: Curve, conic: Conic];
WriteCurrentOutline[];
dest.PutRope["))\n"];
};
ConvertFile: PUBLIC PROC [sdName: ROPE, sfName: ROPE] ~ {
fontKey: UFPressFontReader.FontKey ← [UFFileManager.KeyOf[sdName], 0];
dest: IO.STREAMFS.StreamOpen[sfName, $create];
bc, ec: CHAR;
[bc, ec] ← UFPressFontReader.Range[fontKey];
FOR c: CHAR IN [bc..ec] DO
SDCharToStream[dest, sdName, VAL[c], 15840];
ENDLOOP;
dest.PutRope["\nSTOP\n"];
IO.Close[dest];
};
Break: PROC [char: CHAR] RETURNS [IO.CharClass] = {
IF char = '← OR char = '; THEN RETURN [break];
IF char = ' OR char = '  OR char = ', OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
GetToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPENIL] = {
rope ← stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token;
};
SDtoSFCommand: Commander.CommandProc ~ {
stream: IO.STREAMIO.RIS[cmd.commandLine];
outputName: ROPE ← GetToken[stream];
gets: ROPE ← GetToken[stream];
inputName: ROPE ← GetToken[stream];
IF NOT gets.Equal["←"] THEN {
cmd.out.PutRope["Specify output ← input, please\n"];
RETURN;
};
ConvertFile[inputName, outputName];
};
Commander.Register["SDtoSF", SDtoSFCommand, "Convert SD file to SF format (output ← input)"];
END.