DIRECTORY
Commander USING [CommandProc, Handle, Register],
FS USING [Error, StreamOpen],
IO USING [CharClass, Close, EndOfStream, GetTokenRope, int, PutChar, PutF, PutFR, PutRope, real, RIS, rope, STREAM, Value],
NodeStyle USING [Ref],
ProcessExtras USING [CheckForAbort],
PutGet USING [FromFile],
Rope USING [Equal, Length, ROPE],
TextNode USING [Ref],
TSFont USING [ParcFontSpecification, Ref, Width],
TSJaMPageBuilder USING [RunPageBuilder],
TSObject USING [ItemList],
TSOutput USING [Handle, OutputRec],
TSTranslate USING [TreeToVlist],
TSTypes USING [AddDimn, Dimn];
TiogaToJaMImpl:
CEDAR PROGRAM
IMPORTS Rope, FS, IO, PutGet, Commander, TSTranslate, TSJaMPageBuilder, TSTypes, TSFont, ProcessExtras ~ BEGIN
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
stream: IO.STREAM,
cmd: Commander.Handle,
pageCount: INT ← 0,
font: TSFont.Ref ← NIL,
instring: BOOLEAN ← FALSE,
inpage: BOOLEAN ← FALSE,
indent: INT ← 0,
cpx: TSTypes.Dimn,
cpy: TSTypes.Dimn,
hue, saturation, brightness: REAL ← 0.0
];
Do:
PROC [document: TextNode.Ref, output:
IO.
STREAM, cmd: Commander.Handle] = {
galley: TSObject.ItemList;
style: NodeStyle.Ref;
outputHandle: TSOutput.Handle ← CreateOutputHandle[output, cmd];
isAborted: PROC RETURNS [BOOLEAN] = {ProcessExtras.CheckForAbort[]; RETURN[FALSE]};
[galley, style] ← TSTranslate.TreeToVlist[document];
output.PutRope["\n(leftParen) \"(\" .def (rightParen) \")\" .def\n"];
[] ← TSJaMPageBuilder.RunPageBuilder[
galley: galley,
style: style,
output: outputHandle,
abortCheckProc: isAborted
];
Finish[outputHandle];
};
NewLine:
PROC [data: Data] = {
data.stream.PutChar['\n];
THROUGH [0..data.indent) DO data.stream.PutChar['\t] ENDLOOP;
};
BeginPage:
PROC [data: Data] = {
IF data.inpage THEN ERROR;
data.pageCount ← data.pageCount + 1;
data.stream.PutF["(page-%g) (", IO.int[data.pageCount]];
data.indent ← data.indent + 1;
data.inpage ← TRUE;
NewLine[data];
data.font ← NIL;
data.hue ← data.saturation ← data.brightness ← 0.0;
};
EndPage:
PROC [data: Data] = {
IF NOT data.inpage OR data.instring THEN ERROR;
data.cmd.out.PutF[" [%g]", IO.int[data.pageCount]];
data.stream.PutRope[").cvx .def"];
data.inpage ← FALSE;
NewLine[data];
data.indent ← data.indent - 1;
NewLine[data];
};
BeginString:
PROC [data: Data] = {
IF data.instring THEN RETURN;
data.stream.PutRope[" \""];
data.instring ← TRUE;
};
EndString:
PROC [data: Data] = {
IF NOT data.instring THEN RETURN;
data.stream.PutRope["\" .show"];
NewLine[data];
data.instring ← FALSE;
};
Bp:
PROC [dimn: TSTypes.Dimn]
RETURNS [IO.Value] = {
RETURN [IO.real[dimn.texPts/1.00375]]
};
FontName:
PROC [font: TSFont.Ref]
RETURNS [
ROPE] = {
family: Rope.ROPE;
face: [0..255];
weight: [0..3);
slope: [0..2);
expansion: [0..3);
[family: family, face: face] ← TSFont.ParcFontSpecification[font];
slope ← face MOD 2;
weight ← (face/2) MOD 3;
expansion ← (face/6) MOD 3;
RETURN [IO.PutFR["Xerox/PressFonts/%g/%g%g%g",
IO.rope[family],
IO.rope[
SELECT weight
FROM
0 => "M",
1 => "B",
2 => "L",
ENDCASE => "?"],
IO.rope[
SELECT slope
FROM
0 => "R",
1 => "I",
ENDCASE => "?"],
IO.rope[
SELECT expansion
FROM
0 => "R",
1 => "C",
2 => "E",
ENDCASE => "?"]
]];
};
Same:
PROC [a, b: TSTypes.Dimn]
RETURNS [
BOOLEAN] = {
RETURN [ABS[a.texPts-b.texPts]<=0.01]
};
Char:
PROC [self: TSOutput.Handle, x, y: TSTypes.Dimn, char:
CHAR, font: TSFont.Ref] ~ {
data: Data ← NARROW[self.outputState];
IF char = '( OR char = ') THEN EndString[data];
IF Same[data.cpx, x] AND Same[data.cpy, y] THEN NULL
ELSE {
EndString[data];
data.stream.PutF["%g %g .setxy", Bp[x], Bp[y]];
data.cpx ← x;
data.cpy ← y;
NewLine[data];
};
IF data.font = font THEN NULL
ELSE {
EndString[data];
data.stream.PutF["(%g) %g 0 .nsrsetfont ", IO.rope[FontName[font]], Bp[font.size]];
data.font ← font;
NewLine[data];
};
IF char = '(
THEN {
data.stream.PutF["leftParen .show "];
NewLine[data];
RETURN
};
IF char = ')
THEN {
data.stream.PutF["rightParen .show "];
NewLine[data];
RETURN
};
IF data.instring THEN NULL
ELSE BeginString[data];
SELECT char
FROM
< ' , > '~ => data.stream.PutF["\\%03b", IO.int[ORD[char]]];
'\\, '\', '\" => {data.stream.PutChar['\\]; data.stream.PutChar[char]};
ENDCASE => data.stream.PutChar[char];
data.cpx ← TSTypes.AddDimn[data.cpx, TSFont.Width[font, char]];
};
Rule:
PROC [self: TSOutput.Handle, leftX, bottomY, width, height: TSTypes.Dimn] ~ {
data: Data ← NARROW[self.outputState];
EndString[data];
data.stream.PutF["%g %g %g %g .maskrectangle ",
Bp[leftX],
Bp[bottomY],
Bp[width],
Bp[height]
];
NewLine[data];
};
Color:
PROC [self: TSOutput.Handle, hue, saturation, brightness:
REAL] ~ {
data: Data ← NARROW[self.outputState];
IF hue = data.hue AND saturation = data.saturation AND brightness = data.brightness THEN NULL
ELSE {
EndString[data];
data.stream.PutF["%g %g %g .hsvcolor .setcolor ",
IO.real[hue/255],
IO.real[saturation/255],
IO.real[brightness/255]
];
data.hue ← hue;
data.saturation ← saturation;
data.brightness ← brightness;
NewLine[data];
};
};
NewPage:
PROC [self: TSOutput.Handle] ~ {
data: Data ← NARROW[self.outputState];
EndString[data];
IF data.inpage THEN EndPage[data];
BeginPage[data];
};
PageSize:
PROC [self: TSOutput.Handle, height, width: TSTypes.Dimn] ~ {
data: Data ← NARROW[self.outputState];
IF
NOT data.inpage
THEN {
data.stream.PutF["%% %g %g pagesize",
Bp[height],
Bp[width]
];
};
};
Finish:
PROC [self: TSOutput.Handle] ~ {
data: Data ← NARROW[self.outputState];
EndString[data];
IF data.inpage THEN EndPage[data];
};
CreateOutputHandle:
PROC [output:
IO.
STREAM, cmd: Commander.Handle]
RETURNS [TSOutput.Handle] = {
data: Data ←
NEW[DataRep ← [
stream: output,
cmd: cmd,
pageCount: 0,
font: NIL,
instring: FALSE,
cpx: [-1],
cpy: [-1]
]];
new: TSOutput.Handle ←
NEW[TSOutput.OutputRec ← [
charProc: Char,
ruleProc: Rule,
colorProc: Color,
newPageProc: NewPage,
pageSizeProc: PageSize,
finishProc: Finish,
outputState: data
]];
RETURN [new]
};
GetToken:
PROC [stream:
IO.
STREAM]
RETURNS [token:
ROPE ←
NIL] = {
token ← stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token;
};
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];
};
TiogaToJaMCommand: Commander.CommandProc ~ {
stream: IO.STREAM ← IO.RIS[cmd.commandLine];
outputName: ROPE ← GetToken[stream];
gets: ROPE ← GetToken[stream];
inputName: ROPE ← GetToken[stream];
IF
NOT gets.Equal["←"]
OR inputName.Length = 0
THEN {
RETURN [result: $Failure, msg: "Specify output ← input, please\n"];
}
ELSE {
output: IO.STREAM ← FS.StreamOpen[outputName, $create];
document: TextNode.Ref ← NIL;
errorMsg: ROPE ← NIL;
document ← PutGet.FromFile[inputName ! FS.Error => TRUSTED {errorMsg ← error.explanation; CONTINUE}];
IF errorMsg # NIL THEN RETURN [result: $Failure, msg: errorMsg];
output.PutRope["% Created from "];
output.PutRope[inputName];
Do[document, output, cmd];
output.Close;
};
};
Commander.Register["TiogaToJaM", TiogaToJaMCommand, "Format a Tioga document as a JaMImager file (output ← input)"];
END.