InterpressPageImpl.mesa
Copyright Ó 1986, 1989, 1990, 1992 by Xerox Corporation. All rights reserved.
Michael Plass, April 15, 1992 3:53 pm PDT
Last tweaked by Mike Spreitzer on May 23, 1989 9:28:53 am PDT
Tim Diebert: November 12, 1990 8:58 am PST
Provides commands for making an Interpress package by splitting out and combining pages of other Interpress masters.
Extracting pages
PageRange: TYPE ~ RECORD [startPage, nPages: INT];
SpecError: PUBLIC ERROR [offset: INT] ~ CODE;
DoExtract:
PUBLIC
PROC [inputFile, outputFile:
ROPE, pageSpec:
LIST
OF PageRange, log:
IO.
STREAM]
RETURNS [success:
BOOLEAN ¬
TRUE] ~ {
logProc: InterpressInterpreter.LogProc ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
success ¬ FALSE;
};
n: ExpandedName ~ ExpandName[FS.FileInfo[inputFile].fullFName];
master: InterpressInterpreter.Master ~ InterpressInterpreter.Open[n.fullFName, logProc];
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[outputFile];
IO.PutRope[log, "Reading "];
IO.PutRope[log, n.fullFName];
IO.PutRope[log, " . . . "];
FOR each:
LIST
OF PageRange ¬ pageSpec, each.rest
UNTIL each=
NIL
DO
pageRange: PageRange ~ each.first;
FOR i:
INT
IN [pageRange.startPage..
MIN[pageRange.startPage+pageRange.nPages-1, master.pages]]
DO
OnePage: PROC [context: Imager.Context] ~ {InterpressInterpreter.DoPage[master, i, context, logProc]};
IO.PutF1[log, " [%g", [integer[i]]];
ImagerInterpress.DoPage[out, OnePage];
IO.PutRope[self: log, r: "]"];
ENDLOOP;
ENDLOOP;
ImagerInterpress.Close[out];
IO.PutRope[log, " written.\n"];
};
InterpressExtractCommand: Commander.CommandProc ~ {
GetToken:
PROC [stream:
IO.
STREAM]
RETURNS [token:
ROPE ¬
NIL] = {
Break:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '← THEN RETURN [break];
IF char = ' OR char = ' OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
token ¬ stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token;
};
ParsePageSpec:
PUBLIC
PROC [pageSpecRope:
ROPE]
RETURNS [pageSpec:
LIST
OF PageRange, charsParsed:
INT] ~ {
Reverse:
PROC [pageSpec:
LIST
OF PageRange]
RETURNS [reversed:
LIST
OF PageRange] ~ {
WHILE pageSpec #
NIL
DO
t: LIST OF PageRange ¬ pageSpec;
pageSpec ¬ t.rest;
t.rest ¬ reversed;
reversed ¬ t;
ENDLOOP;
};
text: REF TEXT ~ pageSpecRope.ToRefText;
i: NAT ¬ 0;
c: CHAR;
SkipSpaces: PROC ~ {c ¬ ' ; WHILE i < text.length AND ((c ¬ text[i]) = ', OR c = ' OR c = ' OR c = '\n) DO i ¬ i+1 ENDLOOP};
GetChar: PROC RETURNS [CHAR] ~ {IF i < text.length THEN {i ¬ i+1; RETURN [text[i-1]]} ELSE RETURN ['\000]};
Int:
PROC
RETURNS [value:
INT¬0] ~ {
SkipSpaces[];
IF NOT c IN ['0..'9] THEN ERROR SpecError[i];
WHILE i < text.length
AND (c ¬ text[i])
IN ['0..'9]
DO
value ¬ value * 10 + (c-'0);
i ¬ i+1;
ENDLOOP;
};
spec: LIST OF PageRange ¬ NIL;
SkipSpaces[];
WHILE i < text.length
DO
SELECT text[i]
FROM
IN ['0..'9] => spec ¬ CONS[[Int[], 1], spec];
'[, '( => {
open: CHAR ¬ GetChar[];
start: INT ¬ Int[];
end: INT;
SkipSpaces[];
IF i < text.length AND text[i] = '. THEN i ¬ i+1 ELSE ERROR SpecError[i];
IF i < text.length AND text[i] = '. THEN i ¬ i+1 ELSE ERROR SpecError[i];
end ¬ Int[];
SkipSpaces[];
IF (c ¬ GetChar[]) = ']
OR c = ')
THEN {
IF open = '( THEN start ¬ start + 1;
IF c = '] THEN end ¬ end + 1;
IF end > start THEN spec ¬ CONS[[start, end-start], spec]
}
ELSE ERROR SpecError[i];
};
ENDCASE => EXIT;
SkipSpaces[];
ENDLOOP;
RETURN [Reverse[spec], i]
};
stream: IO.STREAM ¬ IO.RIS[cmd.commandLine];
outputName: ROPE ¬ GetToken[stream];
inputName: ROPE;
pagesToken: ROPE;
pageSpec: LIST OF PageRange;
IF outputName.Length = 0 THEN {cmd.out.PutRope["Output file missing.\n"]; RETURN};
IF NOT GetToken[stream].Equal["←"] THEN {cmd.out.PutRope["Missing \"←\".\n"]; RETURN};
inputName ¬ GetToken[stream];
pagesToken ¬ GetToken[stream];
IF pagesToken.Equal["PAGE",
FALSE]
OR pagesToken.Equal["PAGES",
FALSE]
THEN {
skip: INT;
[pageSpec, skip] ¬ ParsePageSpec[cmd.commandLine.Substr[stream.GetIndex]];
stream.SetIndex[stream.GetIndex+skip];
}
ELSE pageSpec ¬ LIST[[1, 1000000]];
IF
NOT DoExtract[inputName, outputName, pageSpec, cmd.out]
THEN {
cmd.out.PutRope["Unable to extract any pages from "];
cmd.out.PutRope[inputName];
cmd.out.PutChar['\n];
}
ELSE {cmd.out.PutRope[outputName]; cmd.out.PutRope[" Written.\n"]};
IF NOT stream.EndOf THEN {cmd.out.PutRope["Ignored: "]; cmd.out.PutRope[cmd.commandLine.Substr[stream.GetIndex]]; cmd.out.PutChar['\n]};
};
Breaking into single pages
BreakupStyle: TYPE ~ {pageCount, numFiles};
DoBreakup:
PROC [inputName:
ROPE, log:
IO.
STREAM, style: BreakupStyle, div:
INT] ~ {
logProc: InterpressInterpreter.LogProc ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
n: ExpandedName ~ ExpandName[FS.FileInfo[inputName].fullFName];
base: ROPE ~ Rope.Substr[n.fullFName, n.cp.base.start, n.cp.base.length];
ext: ROPE ~ Rope.Substr[n.fullFName, n.cp.ext.start, n.cp.ext.length];
logPages: BOOL ~ div#1 OR style#pageCount;
master: InterpressInterpreter.Master;
nFiles, j: INT ¬ 0;
IO.PutRope[log, "Reading "];
IO.PutRope[log, n.fullFName];
IO.PutRope[log, " . . . "];
master ¬ InterpressInterpreter.Open[n.fullFName, logProc];
IO.PutF1[log, IF logPages THEN " %g pages\n" ELSE " %g pages; ", [integer[master.pages]]];
SELECT style
FROM
pageCount => nFiles ¬ (master.pages+div-1)/div;
numFiles => nFiles ¬ div;
ENDCASE => ERROR;
FOR i:
INT
IN [1..nFiles]
DO
lim:
INT ~
SELECT style
FROM
pageCount => MIN[j+div, master.pages],
numFiles => (i*master.pages + div/2)/div,
ENDCASE => ERROR;
name: ROPE ~ Rope.Cat[base, "-", Convert.RopeFromInt[i], ".", ext];
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[name];
IO.PutRope[log, FS.ExpandName[name].fullFName];
WHILE j<lim
DO
onePage:
PROC [context: Imager.Context] ~ {
IF logPages THEN IO.PutF1[log, " [%g", [integer[j]]];
InterpressInterpreter.DoPage[master, j, context, logProc];
IF logPages THEN IO.PutRope[log, "]"];
};
j ¬ j+1;
ImagerInterpress.DoPage[out, onePage];
ENDLOOP;
IO.PutRope[log, IF logPages THEN "\n" ELSE " "];
ImagerInterpress.Close[out];
ENDLOOP;
IO.PutRope[log, "done.\n"];
};
InterpressBreakupCommand: Commander.CommandProc ~ {
ENABLE {
Convert.Error => CommanderOps.Failed[cmd.procData.doc];
FS.Error => IF error.group = user THEN CommanderOps.Failed[error.explanation];
};
style: BreakupStyle ¬ pageCount;
div: INT ¬ 1;
inputName: ROPE ¬ CommanderOps.NextArgument[cmd];
IF inputName.Equal["-by"]
THEN {
div ¬ Convert.IntFromRope[CommanderOps.NextArgument[cmd]];
IF div<=0 THEN RETURN [result: $Failure, msg: IO.PutFR1["Must divide by a positive number of pages, not %g", [integer[div]] ]];
inputName ¬ CommanderOps.NextArgument[cmd];
}
ELSE
IF inputName.Equal["-into"]
THEN {
style ¬ numFiles;
div ¬ Convert.IntFromRope[CommanderOps.NextArgument[cmd]];
IF div<=1 THEN RETURN [result: $Failure, msg: IO.PutFR1["Number of output files must be greater than 1, not %g", [integer[div]] ]];
inputName ¬ CommanderOps.NextArgument[cmd];
};
IF inputName = NIL THEN GOTO SyntaxError;
DoBreakup[inputName, cmd.out, style, div ! FS.Error => IF error.group = user THEN {result ¬ $Failure; msg ¬ error.explanation; CONTINUE}];
EXITS SyntaxError => RETURN[result: $Failure, msg: cmd.procData.doc]};
Concatenating masters together
DoConcatenate:
PROC [outputName:
ROPE, cmdLineStream:
IO.
STREAM, log:
IO.
STREAM] ~ {
pageNumber: INT ¬ 0;
logProc: InterpressInterpreter.LogProc ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
n: ExpandedName ~ ExpandName[outputName];
extendedName:
ROPE ~
IF n.cp.ext.length = 0
THEN Rope.Substr[n.fullFName, 0, n.cp.base.start+n.cp.base.length].Concat[".interpress"]
ELSE n.fullFName;
tempName:
ROPE ¬ Rope.Cat["/vux/tmp/", Rope.Substr[n.fullFName, 0, n.cp.base.start+n.cp.base.length], ".interpress"];
Write it with a temp name to avoid lock conflicts if it is also an input.
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[tempName];
IO.PutRope[log, "Reading "];
FOR inputName:
ROPE ¬ GetCmdToken[cmdLineStream], GetCmdToken[cmdLineStream]
UNTIL inputName =
NIL
DO
fullInputName: ROPE ~ FS.FileInfo[inputName].fullFName;
master: InterpressInterpreter.Master ~ InterpressInterpreter.Open[fullInputName, logProc];
IO.PutRope[log, fullInputName];
IO.PutRope[log, " "];
FOR i:
INT
IN [1..master.pages]
DO
onePage: PROC [context: Imager.Context] ~ {InterpressInterpreter.DoPage[master, i, context, logProc]};
IO.PutRope[log, "["];
IO.PutRope[log, Convert.RopeFromInt[pageNumber ¬ pageNumber + 1]];
ImagerInterpress.DoPage[out, onePage];
IO.PutRope[log, "] "];
ENDLOOP;
ENDLOOP;
ImagerInterpress.Close[out];
[] ¬ FS.Copy[from: tempName, to: extendedName];
FS.Delete[tempName];
IO.PutRope[log, FS.FileInfo[extendedName].fullFName];
IO.PutRope[log, " written.\n"];
};
InterpressConcatenateCommand: Commander.CommandProc ~ {
stream: IO.STREAM ¬ IO.RIS[cmd.commandLine];
outputName: ROPE ¬ GetCmdToken[stream];
gets: ROPE ¬ GetCmdToken[stream];
IF outputName = NIL OR NOT (gets.Equal["←"] OR gets.Equal["¬"]) THEN RETURN[result: $Failure, msg: cmd.procData.doc];
DoConcatenate[outputName, stream, cmd.out ! FS.Error => IF error.group = user THEN {result ¬ $Failure; msg ¬ error.explanation; CONTINUE}];
};
Page Counting
InterpressLS: Commander.CommandProc ~ {
ENABLE FS.Error => IF error.group = user THEN CommanderOps.Failed[error.explanation];
log: IO.STREAM ~ cmd.out;
args: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd: cmd];
logProc: InterpressInterpreter.LogProc ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
FOR i:
NAT
IN [1 .. args.argc)
DO
name: ROPE ~ args[i];
fullInputName: ROPE ~ FS.FileInfo[name].fullFName;
master: InterpressInterpreter.Master;
IO.PutF1[log, "%g... ", [rope[fullInputName]]];
master ¬ InterpressInterpreter.Open[fullInputName, logProc];
IO.PutF1[log, " %g pages\n", [integer[master.pages]]];
InterpressInterpreter.Close[master];
ENDLOOP;
};
InterpressOverlay:
PROC [output, input1, input2:
ROPE, m: ImagerTransformation.Transformation] ~ {
prevMsg: ROPE ¬ NIL;
matchCount: INT ¬ 0;
Log: InterpressInterpreter.LogProc ~ {
WITH ProcessProps.GetProp[$ErrOut]
SELECT
FROM
errOut:
IO.
STREAM => {
IF Rope.Equal[explanation, prevMsg]
THEN {matchCount ¬ matchCount + 1}
ELSE {
IF matchCount > 0
THEN {
IO.PutF1[errOut, " (and %g more)\n", IO.int[matchCount]];
};
IO.PutRope[errOut, explanation];
IF explanation # NIL THEN IO.PutChar[errOut, '\n];
prevMsg ¬ explanation;
matchCount ¬ 0;
};
};
ENDCASE => NULL;
};
ref: ImagerInterpress.Ref ~ ImagerInterpress.Create[fileName: output];
master1: InterpressInterpreter.Master ~ InterpressInterpreter.Open[fileName: input1, log: Log];
master2: InterpressInterpreter.Master ~ InterpressInterpreter.Open[fileName: input2, log: Log];
FOR page:
INT
IN [1..master1.pages]
DO
WriteAction:
PROC [context: Imager.Context] ~ {
Write out the corresponding page of the first input, then apply the transformation, then write out the corresponding page of the second input.
context.SetGray [1];
InterpressInterpreter.DoPage[master: master1, page: page, context: context, log: Log];
Imager.ConcatT[context: context, m: m];
InterpressInterpreter.DoPage[master: master2, page: page2, context: context, log: Log];
};
page2: INT ~ ((page-1) MOD master2.pages)+1; --Cycle through the second master as needed
ImagerInterpress.DoPage[self: ref, action: WriteAction];
ENDLOOP;
Log[-1, ok, NIL, NIL];
ImagerInterpress.Close[self: ref];
};
InterpressOverlayCmd: Commander.CommandProc ~ {
ENABLE FS.Error => IF error.group = user THEN CommanderOps.Failed[error.explanation];
IsIt:
PROC [rope:
ROPE, parmsNeeded:
INT]
RETURNS [
BOOLEAN] ~ {
RETURN [Rope.Equal[rope, tokens[tokenIndex], FALSE] AND tokens.argc > tokenIndex+parmsNeeded];
};
SkipArgs: PROC [args: INT] ~ INLINE {tokenIndex ¬ args+tokenIndex+1};
Real:
PROC [index:
INT]
RETURNS [real:
REAL] ~ {
RETURN [Convert.RealFromRope[r: tokens[index]]];
};
tokens: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd: cmd];
Arg 0 .../InterpressOverlay
Arg 1 output
Arg 2 ←
Arg 3 input1
Arg 4 input2
Arg 5+ Transformation options...
tokenIndex: INT ¬ 5; --Initialized to start of transformation options
m: ImagerTransformation.Transformation ¬ ImagerTransformation.Scale[s: 1];
IF tokens.argc < 5 OR ~Rope.Equal[s1: "←", s2: tokens[2]] THEN CommanderOps.Failed[cmd.procData.doc];
Go through the transformation options...
WHILE tokenIndex<tokens.argc
DO
SELECT
TRUE
FROM
IsIt["translate", 2] => {
m ¬ ImagerTransformation.PreTranslate[m: m, t: [Real[tokenIndex+1], Real[tokenIndex+2]]];
SkipArgs[2];
};
IsIt["rotate", 1] => {
m ¬ ImagerTransformation.PreRotate[m: m, r: Real[tokenIndex+1]];
SkipArgs[1];
};
IsIt["scale", 1] => {
m ¬ ImagerTransformation.PreScale[m: m, s: Real[tokenIndex+1]];
SkipArgs[1];
};
IsIt["scale2", 2] => {
m ¬ ImagerTransformation.PreScale2[m: m, s: [Real[tokenIndex+1], Real[tokenIndex+2]]];
SkipArgs[2];
};
ENDCASE => CommanderOps.Failed[cmd.procData.doc];
ENDLOOP;
InterpressOverlay[output: tokens[1], input1: tokens[3], input2: tokens[4], m: m];
};
InterpressFromRESAction:
PROC [inputName:
ROPE, outputName:
ROPE, cmd: Commander.Handle, cmds:
IO.
STREAM] ~ {
res: RasterEncodingStandardIO.RES ~ RasterEncodingStandardIO.Read[inputName];
OnePage:
PROC [context: Imager.Context] ~ {
ImagerRES.ShowRES[context: context, res: res, useImageScale: TRUE];
};
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[outputName];
ImagerInterpress.DoPage[out, OnePage];
ImagerInterpress.Close[out];
};
MakeOutputName:
PROC [inputName:
ROPE, doc:
ROPE]
RETURNS [
ROPE] ~ {
start: INT ¬ Rope.Index[s1: doc, s2: " to "]+4;
end: INT ¬ Rope.Index[s1: doc, pos1: start, s2: " "];
space: INT;
cp: FS.ComponentPositions;
[inputName, cp] ¬ FS.ExpandName[inputName];
space ¬ Rope.Index[s1: Rope.Substr[inputName, cp.base.start, cp.base.length], s2: " "]; -- = cp.base.length if no spaces in the base
RETURN [Rope.Cat[Rope.Substr[inputName, cp.base.start, space], ".", Rope.Substr[doc, start, end-start]]] --if spaces were in the base, this uses just the first word
};
FindFullName:
PROC [inputName:
ROPE]
RETURNS [
ROPE] ~ {
fullFName: ROPE ¬ NIL;
fullFName ¬ FS.FileInfo[inputName].fullFName;
RETURN [fullFName]
};
QuotedTokenBreak:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '" THEN RETURN [break];
RETURN [other];
};
GetQuotedToken:
PROC [stream:
IO.
STREAM]
RETURNS [
ROPE] = {
rope: ROPE ¬ NIL;
rope ¬ stream.GetTokenRope[QuotedTokenBreak ! IO.EndOfStream => CONTINUE].token;
[] ¬ stream.GetTokenRope[QuotedTokenBreak ! IO.EndOfStream => CONTINUE]; --skip over final quote
RETURN [rope];
};
InterpressFromRESCommand: Commander.CommandProc ~ {
stream: IO.STREAM ¬ IO.RIS[cmd.commandLine];
outputName: ROPE ¬ GetCmdToken[stream];
secondTokenIndex: INT ¬ IO.GetIndex[stream];
gets: ROPE ¬ GetCmdToken[stream];
inputName: ROPE ¬ NIL;
IF outputName.Equal["\""]
THEN {
--reset and do it again, allowing spaces in name
stream.SetIndex[secondTokenIndex];
outputName ¬ GetQuotedToken[stream];
secondTokenIndex ¬ IO.GetIndex[stream];
gets ¬ GetCmdToken[stream];
};
IF
NOT (gets.Equal["←"]
OR gets.Equal["¬"])
THEN {
inputName ¬ outputName;
outputName ¬ NIL;
stream.SetIndex[secondTokenIndex];
}
ELSE {
inputName ¬ GetCmdToken[stream];
IF inputName.Equal["\""] THEN inputName ¬ GetQuotedToken[stream];
};
IF inputName = NIL THEN RETURN[result: $Failure, msg: cmd.procData.doc];
inputName ¬ FindFullName[inputName !
FS.Error => {
IF error.group = user THEN {result ¬ $Failure; msg ¬ error.explanation; GOTO Quit}
}];
IF outputName =
NIL
THEN {
outputName ¬ MakeOutputName[inputName, cmd.procData.doc];
};
cmd.out.PutRope["Reading "];
cmd.out.PutRope[inputName];
cmd.out.PutRope[" . . . "];
InterpressFromRESAction[inputName, outputName, cmd, stream];
outputName ¬ FindFullName[outputName];
cmd.out.PutRope[outputName];
cmd.out.PutRope[" written.\n"];
EXITS Quit => NULL
};
Command Registration
Commander.Register[key: "InterpressExtract", proc: InterpressExtractCommand, doc: "Extract pages from interpress files, e.g. result.ip ← source.ip PAGES 1, 3, [10..15]"];
Commander.Register[key: "IPExtract", proc: InterpressExtractCommand, doc: "Extract pages from interpress files, e.g. result.ip ← source.ip PAGES 1, 3, [10..15] (alias for InterpressExtract)"];
Commander.Register[key: "InterpressBreakup", proc: InterpressBreakupCommand, doc: "Breaks an interpress master into a collection of masters: InterpressBreakup [-by <pages> | -into <files>] <input>"];
Commander.Register[key: "IPBreakup", proc: InterpressBreakupCommand, doc: "Breaks an interpress master into a collection of masters (alias for InterpressBreakup)"];
Commander.Register[key: "InterpressConcatenate", proc: InterpressConcatenateCommand, doc: "Concatenates a collection of Interpress masters into one. (output ← input1 input2 . . .)"];
Commander.Register[key: "IPConcatenate", proc: InterpressConcatenateCommand, doc: "Concatenates a collection of Interpress masters into one. (output ← input1 input2 . . .) (alias for InterpressConcatenate)"];
Commander.Register[key: "InterpressLS", proc: InterpressLS, doc: "List page count of files, e.g. InterpressLS foo.ip bar.ip"];
Commander.Register[key: "IPLS", proc: InterpressLS, doc: "List page count of files (alias for InterpressLS)"];
Commander.Register["InterpressOverlay", InterpressOverlayCmd, "Merge two interpress masters (<output> ← <input1> <input2> {translate <x> <y> | rotate <angle> | scale <scale> | scale2 <sx> <sy>})"];
Commander.Register["InterpressFromRES", InterpressFromRESCommand, "Convert an RES file to Interpress"];