DIRECTORY
Atom USING [GetPName],
BasicTime USING [Now],
Commander USING [CommandProc, Handle, Register],
Convert USING [Error, IntFromRope, RopeFromTime],
FS USING [ComponentPositions, Error, ExpandName, FileInfo],
Imager USING [Context, Error, PixelArray, Transformation],
ImagerColorOperator USING [GrayLinearColorModel],
ImagerFastShow USING [Create],
ImagerInterpressFragment USING [Close, Create, PushColorOperator, PushInt, PushPixelArray, PushVector, Ref, VectorProc, StreamFromRef],
ImagerOps USING [PixelArrayFromPixelMaps],
ImagerPixelArray USING [FromAIS, MaxSampleValue],
ImagerPixelMap USING [Clear, Create, PixelMap, Trim],
ImagerTransformation USING [Create],
Interpress USING [DoPage, LogProc, Master, Open],
IO USING [atom, CharClass, EndOfStream, GetIndex, GetTokenRope, int, PutF, PutFR, PutRope, RIS, rope, SetIndex, STREAM],
IPMaster USING [PutInt, PutOp, PutRational],
PixelMapOps USING [StoreAIS],
Real USING [Round],
Rope USING [Cat, Equal, Index, ROPE, Substr];
~
BEGIN
Stolen shamelessly from MakeFIS, wherein a Font Interchange Standard Interpress master is created to contain an Interpress Font.
ROPE: TYPE ~ Rope.ROPE;
resHeader: ROPE ← "Interpress/Xerox/2.1/RasterEncoding/1.0 ";
resSignature:
INT = 13086;
Complain: ERROR [complaint: ROPE] ~ CODE;
MakeRESFromAIS:
PROC [inputName, outputName:
ROPE] ~ {
ip: ImagerInterpressFragment.Ref ~ ImagerInterpressFragment.Create[outputName, resHeader];
pa: Imager.PixelArray ← ImagerPixelArray.FromAIS[inputName];
IF ImagerPixelArray.MaxSampleValue[pa, 0] # 1
THEN
Complain["AIS file must have only 1 bits per sample"];
PushRESImage[inputName, ip, pa, 300];
ImagerInterpressFragment.Close[ip];
};
PushRESImage:
PROC [inputName: Rope.
ROPE, ip: ImagerInterpressFragment.Ref, pa: Imager.PixelArray, res:
INT] ~ {
PushScaleVector:
PROC ~ {
the scale vector must conform to several limitations imposed by ancient implementations
stream: IO.STREAM ← ImagerInterpressFragment.StreamFromRef[ip];
IF res # 72 AND res # 300 THEN Complain["RES bitmap resolution must be 72 or 300"];
IPMaster.PutRational[stream, 254, res*10000];
IPMaster.PutOp[stream, dup];
IPMaster.PutInt[stream, 2];
IPMaster.PutOp[stream, makevec];
};
attributeVector: ImagerInterpressFragment.VectorProc ~ {
putIdentifier[$name];
putString[inputName];
putIdentifier[$creationTime];
putString[Convert.RopeFromTime[from: BasicTime.Now[], end: seconds]];
};
PushScaleVector[];
ImagerInterpressFragment.PushInt[ip, pa.fSize];
ImagerInterpressFragment.PushInt[ip, pa.sSize];
ImagerInterpressFragment.PushInt[ip, 0]; -- no mask image
ImagerInterpressFragment.PushPixelArray[ip, pa];
ImagerInterpressFragment.PushColorOperator[ip, ImagerColorOperator.GrayLinearColorModel[sWhite: 0, sBlack: 1, maxSampleValue: 0, sampleMap: NIL]]; -- bitmap values
ImagerInterpressFragment.PushVector[ip, attributeVector];
ImagerInterpressFragment.PushInt[ip, resSignature];
};
ActionProc:
TYPE ~
PROC [inputName:
ROPE, outputName:
ROPE, cmd: Commander.Handle, cmds:
IO.
STREAM];
MakeRESFromAISAction: ActionProc ~ {
MakeRESFromAIS[inputName, outputName];
};
Direction: TYPE ~ {left, right, up, down};
ScanMode:
TYPE ~
RECORD [slow, fast: Direction];
SFToXY:
PROC [scanMode: ScanMode, sSize, fSize:
INT]
RETURNS [Imager.Transformation] ~ {
a: REAL ~ SELECT scanMode.slow FROM right => 1.0, left => -1.0, ENDCASE => 0.0;
b: REAL ~ SELECT scanMode.fast FROM right => 1.0, left => -1.0, ENDCASE => 0.0;
d: REAL ~ SELECT scanMode.slow FROM up => 1.0, down => -1.0, ENDCASE => 0.0;
e: REAL ~ SELECT scanMode.fast FROM up => 1.0, down => -1.0, ENDCASE => 0.0;
tX: REAL ~ MAX[-(a*sSize + b*fSize), 0];
tY: REAL ~ MAX[-(d*sSize + e*fSize), 0];
RETURN[ImagerTransformation.Create[a, b, tX, d, e, tY]];
};
The result transforms [s, f] to [x, y], given the source (e.g., pixel array) scan mode.
MakeAISFromInterpressAction: ActionProc ~ {
Log: Interpress.LogProc ~ {
cmd.out.PutF["Interpress error (class %g) %g: %g\n", IO.int[class], IO.atom[code], IO.rope[explanation]];
};
master: Interpress.Master ~ Interpress.Open[inputName, Log];
res: INT ~ Convert.IntFromRope[GetCmdToken[cmds]];
sSize: NAT ~ Real.Round[res*8.5];
fSize: NAT ~ Real.Round[res*11.0];
pixelMap: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [0, 0, sSize, fSize]];
context: Imager.Context ~ ImagerFastShow.Create[pm: pixelMap, pixelsPerInch: res, pixelUnits: FALSE];
pa: Imager.PixelArray ← NIL;
ImagerPixelMap.Clear[pixelMap];
Interpress.DoPage[master: master, page: 1, context: context, log: Log];
pixelMap ← ImagerPixelMap.Trim[pixelMap];
PixelMapOps.StoreAIS[Rope.Cat[outputName, ".ais"], [pixelMap, TRUE, NIL]];
};
MakeRESFromInterpressAction: ActionProc ~ {
Log: Interpress.LogProc ~ {
cmd.out.PutF["Interpress error (class %g) %g: %g\n", IO.int[class], IO.atom[code], IO.rope[explanation]];
};
master: Interpress.Master ~ Interpress.Open[inputName, Log];
res: INT ~ Convert.IntFromRope[GetCmdToken[cmds]];
sSize: NAT ~ Real.Round[res*8.5];
fSize: NAT ~ Real.Round[res*11.0];
pixelMap: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [0, 0, sSize, fSize]];
context: Imager.Context ~ ImagerFastShow.Create[pm: pixelMap, pixelsPerInch: res, pixelUnits: FALSE];
pa: Imager.PixelArray ← NIL;
ip: ImagerInterpressFragment.Ref ~ ImagerInterpressFragment.Create[outputName, resHeader];
ImagerPixelMap.Clear[pixelMap];
Interpress.DoPage[master: master, page: 1, context: context, log: Log];
pixelMap ← ImagerPixelMap.Trim[pixelMap];
pa ← ImagerOps.PixelArrayFromPixelMaps[
pms: LIST[pixelMap],
um: SFToXY[[slow: down, fast: right], pixelMap.sSize, pixelMap.fSize]
];
IF ImagerPixelArray.MaxSampleValue[pa, 0] # 1
THEN
Complain["AIS file must have only 1 bits per sample"];
PushRESImage[inputName, ip, pa, res];
ImagerInterpressFragment.Close[ip];
};
FindFullName:
PROC [inputName:
ROPE]
RETURNS [
ROPE] ~ {
fullFName: ROPE ← NIL;
fullFName ← FS.FileInfo[inputName].fullFName;
RETURN [fullFName]
};
Command: Commander.CommandProc ~ {
refAction: REF ActionProc ~ NARROW[cmd.procData.clientData];
stream: IO.STREAM ← IO.RIS[cmd.commandLine];
outputName: Rope.ROPE ← GetCmdToken[stream];
secondTokenIndex: INT ← IO.GetIndex[stream];
gets: Rope.ROPE ← GetCmdToken[stream];
inputName: Rope.ROPE ← NIL;
IF
NOT gets.Equal["←"]
THEN {
inputName ← outputName;
outputName ← NIL;
stream.SetIndex[secondTokenIndex];
}
ELSE inputName ← GetCmdToken[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["Converting "];
cmd.out.PutRope[inputName];
cmd.out.PutRope[" . . . "];
refAction^[inputName, outputName, cmd, stream !
Convert.Error => {result ← $Failure; msg ← "Syntax error in command line: expected a number"; GOTO Quit};
Complain => {result ← $Failure; msg ← complaint; GOTO Quit};
FS.Error => {result ← $Failure; msg ← error.explanation; GOTO Quit};
Imager.Error => {result ← $Failure; msg ← IO.PutFR["Imager.Error[[$%g, %g]]", IO.rope[Atom.GetPName[error.code]], IO.rope[error.explanation]]; GOTO Quit}
];
outputName ← FindFullName[outputName !
FS.Error => {
outputName ← "Output file(s)"; CONTINUE};
];
cmd.out.PutRope[outputName];
cmd.out.PutRope[" written.\n"];
EXITS Quit => NULL
};
GetCmdToken:
PROC [stream:
IO.
STREAM]
RETURNS [rope: Rope.
ROPE] = {
rope ← NIL;
rope ← stream.GetTokenRope[CmdTokenBreak ! IO.EndOfStream => CONTINUE].token;
};
CmdTokenBreak:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '← THEN RETURN [break];
IF char = ' OR char = '\t OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
MakeOutputName:
PROC [inputName: Rope.
ROPE, doc: Rope.
ROPE]
RETURNS [Rope.
ROPE] ~ {
start: INT ← Rope.Index[s1: doc, s2: " to "]+4;
end: INT ← Rope.Index[s1: doc, pos1: start, s2: " "];
cp: FS.ComponentPositions;
[inputName, cp] ← FS.ExpandName[inputName];
RETURN [Rope.Cat[Rope.Substr[inputName, cp.base.start, cp.base.length], ".", Rope.Substr[doc, start, end-start]]];
};
Commander.Register["MakeRESFromAIS", Command, "Convert an AIS image to RES format image",
NEW[ActionProc ← MakeRESFromAISAction]];
Commander.Register["MakeRESBitmapFromInterpress", Command, "Convert an Interpress file to RES format bitmap ([<outputFileName> ←] <inputFileName> <resolution>)",
NEW[ActionProc ← MakeRESFromInterpressAction]];
Commander.Register["MakeAISBitmapFromInterpress", Command, "Convert an Interpress file to AIS format bitmap ([<outputFileName> ←] <inputFileName> <resolution>)",
NEW[ActionProc ← MakeAISFromInterpressAction]];