<<>> <> <> <> <> <> DIRECTORY RefTab, DecomposerRegistry, Atom, Basics, BasicTime, Commander, CommanderOps, Convert, CountedVM, Imager, ImagerError, ImagerBackdoor, ImagerColor, ImagerHighlightContext, ImagerHighlightContextBackdoor, ImagerInterpress, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerTransformation, IO, ImagerSys, Process, Rope, SF, XeroxCompress; HighlightDummy: CEDAR MONITOR IMPORTS RefTab, DecomposerRegistry, Atom, BasicTime, Commander, CommanderOps, Convert, CountedVM, Imager, ImagerError, ImagerBackdoor, ImagerColor, ImagerHighlightContext, ImagerHighlightContextBackdoor, ImagerInterpress, ImagerPixel, ImagerPixelArray, ImagerSample, IO, ImagerSys, Process, Rope, XeroxCompress = { Context: TYPE = Imager.Context; RasterSampleMap: TYPE = ImagerSample.RasterSampleMap; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Transformation: TYPE = ImagerTransformation.Transformation; fastDimX10: NAT ¬ 110; <> slowDimX10: NAT ¬ 85; <> maxCopies: NAT ¬ 100; <> <> ByteTable: TYPE = PACKED ARRAY BYTE OF BYTE; blackByteTable: REF ByteTable ¬ MakeByteTable[TRUE]; highlightByteTable: REF ByteTable ¬ MakeByteTable[FALSE]; MakeByteTable: PROC [black: BOOL] RETURNS [REF ByteTable] = { new: REF ByteTable ¬ NEW[ByteTable ¬ ALL[0]]; FOR b: BYTE IN BYTE DO bit: WORD ¬ 1; bb: BYTE ¬ IF black THEN bb ¬ b / 2 ELSE b; acc: BYTE ¬ (bb MOD 2) + ((bb / 4) MOD 2) * 2 + ((bb / 16) MOD 2) * 4 + ((bb / 64) MOD 2) * 8; new[b] ¬ acc; ENDLOOP; RETURN [new]; }; scanMode: Imager.ScanMode ¬ [slow: right, fast: up]; <> <<>> ImageSeparation: PROC [context: Imager.Context, black: BOOL, srcMap: RasterSampleMap, dstMap: RasterSampleMap] = TRUSTED { BytesPtr: TYPE = POINTER TO Basics.RawBytes; box: SF.Box ¬ ImagerSample.GetBox[dstMap]; bitmapAsPixelArray: ImagerPixelArray.PixelArray = ImagerPixelArray.FromPixelMap[ pixelMap: ImagerPixel.MakePixelMap[dstMap], box: ImagerSample.GetBox[dstMap], scanMode: scanMode, immutable: FALSE]; srcPtr: BytesPtr ¬ LOOPHOLE[ImagerSample.GetBase[srcMap].word]; dstPtr: BytesPtr ¬ LOOPHOLE[ImagerSample.GetBase[dstMap].word]; srcBitsPerLine: NAT = ImagerSample.GetBitsPerLine[srcMap]; srcBytesPerLine: NAT = srcBitsPerLine/8; srcInc: CARDINAL = SIZE[Basics.RawBytes[srcBytesPerLine]]; dstBitsPerLine: NAT = ImagerSample.GetBitsPerLine[dstMap]; dstBytesPerLine: NAT = dstBitsPerLine/8; dstInc: CARDINAL = SIZE[Basics.RawBytes[dstBytesPerLine]]; table: REF ByteTable = IF black THEN blackByteTable ELSE highlightByteTable; lines: NAT = box.max.s - box.min.s; IF dstBytesPerLine*2 > srcBytesPerLine THEN ERROR; IF srcBitsPerLine MOD BITS[WORD] # 0 THEN ERROR; IF dstBitsPerLine MOD BITS[WORD] # 0 THEN ERROR; ImagerSample.Clear[dstMap]; FOR line: CARDINAL IN [0..lines) DO nextSrcPtr: BytesPtr = srcPtr + srcInc; nextDstPtr: BytesPtr = dstPtr + dstInc; FOR bx: CARDINAL IN [0..dstBytesPerLine) DO sByteL: BYTE ¬ srcPtr[bx*2]; sByteR: BYTE ¬ srcPtr[bx*2+1]; dByte: BYTE ¬ (table[sByteL]*16+table[sByteR]) MOD 256; dstPtr[bx] ¬ dByte; ENDLOOP; srcPtr ¬ nextSrcPtr; dstPtr ¬ nextDstPtr; ENDLOOP; Imager.MaskPixel[context: context, pa: XeroxCompress.CompressPixelArray[bitmapAsPixelArray]]; }; SwitchColors: PROC [map: RasterSampleMap] = { <> }; <> AllocBuffer: ENTRY PROC [words: INT] RETURNS [CountedVM.Handle] = { handle: CountedVM.Handle = CountedVM.SimpleAllocate[words]; RETURN [handle]; }; FreeBuffer: ENTRY PROC [handle: CountedVM.Handle] RETURNS [CountedVM.Handle] = TRUSTED { IF handle # NIL THEN CountedVM.Free[handle]; RETURN [NIL]; }; <> MakeSampleMap: ENTRY PROC [handle: CountedVM.Handle, words: CARD, scans: NAT, items: NAT, bps: [1..2]] RETURNS [RasterSampleMap] ~ TRUSTED { map: RasterSampleMap ¬ NIL; fSize: NAT ~ items; sSize: NAT ~ scans; wordsPerLine: CARD ~ (fSize * bps + BITS[WORD] - 1) / BITS[WORD]; pointer: POINTER ¬ handle.pointer; IF (wordsPerLine * sSize) > words THEN ERROR; map ¬ ImagerSample.UnsafeNewSampleMap[ box: [min: [0, 0], max: [sSize, fSize]], bitsPerSample: bps, bitsPerLine: fSize*bps, base: [word: LOOPHOLE[pointer, POINTER TO WORD], bit: 0], ref: handle, words: words]; ImagerSample.Clear[map]; RETURN [map]; }; HighlightContextFromSampleMap: PROC [sampleMap: RasterSampleMap, spi: NAT, pph: REAL, highlight: Imager.ConstantColor, colorLookupTable: ImagerHighlightContextBackdoor.ColorLookupTable, iotparam: ROPE] RETURNS [context: Context] = { context ¬ ImagerHighlightContext.Create[ deviceSpaceSize: ImagerSample.GetSize[sampleMap], scanMode: scanMode, surfaceUnitsPerInch: [spi, spi], pixelUnits: FALSE, fontCacheName: $Print, highlight: highlight]; ImagerSample.Clear[sampleMap]; ImagerHighlightContext.SetSampleMap[context, sampleMap]; ImagerHighlightContextBackdoor.SetColorLookupTable[context, colorLookupTable]; Imager.SetGray[context, 1]; Imager.SetWarn[context: context, warn: TRUE]; Imager.ClipRectangle[context, ImagerBackdoor.GetBounds[context]]; IF iotparam # NIL THEN { Imager.PutProp[context, $IOTSpecificPSSetup, iotparam]; -- royal crock! }; }; InstanceData: TYPE ~ DecomposerRegistry.InstanceData; gunk: LIST OF REF ¬ NIL; NVFeedbackProc: DecomposerRegistry.FeedbackProc ~ { <> err: IO.STREAM ~ instance.sequencer.err; WITH info SELECT FROM rope: ROPE => {err.PutRope["\n *** "]; err.PutRope[rope]; err.PutRope["\n"]}; text: REF TEXT => {err.PutRope["\n *** "]; err.PutText[text]; err.PutRope["\n"]}; ENDCASE => IF info # NIL THEN { gunk ¬ CONS[info, gunk]; err.PutF[" !(%L%08xH%L) ", [rope["b"]], [cardinal[LOOPHOLE[info]]], [rope["B"]]]; }; IF severity = fatal THEN ERROR ABORTED; }; NVGetAttrProc: DecomposerRegistry.GetAttrProc ~ { <> WITH sd.private SELECT FROM x: RefTab.Ref => RETURN [NARROW[RefTab.Fetch[x, key].val]]; ENDCASE => RETURN [NIL]; }; NVSetAttrProc: DecomposerRegistry.SetAttrProc ~ { <> WITH sd.private SELECT FROM x: RefTab.Ref => [] ¬ RefTab.Store[x, key, values]; ENDCASE; }; PrintCommand: Commander.CommandProc = { FileKind: TYPE = {unknown, interpress, postscript, genoa}; startPulses: BasicTime.Pulses ¬ BasicTime.GetClockPulses[]; showTimes: BOOL ¬ FALSE; forceKind: FileKind ¬ unknown; useMonitor: BOOL ¬ FALSE; combined: BOOL ¬ FALSE; switch: BOOL ¬ FALSE; highlight: Imager.ConstantColor ¬ ImagerColor.ColorFromRGB[ [R: 1, G: 0, B: 0] ]; colorLookupTable: ImagerHighlightContextBackdoor.ColorLookupTable ¬ NIL; spi: INT ¬ 300; iotparam: ROPE ¬ NIL; pph: REAL ¬ 10.0; copies: INT ¬ 1; debugging: BOOL ¬ FALSE; allowCopiesToChange: BOOL ¬ TRUE; ShiftArgs: PROC = { IF argMax > 0 THEN { FOR i: NAT IN [2..argMax) DO argv[i-1] ¬ argv[i]; ENDLOOP; argMax ¬ argMax - 1; argv[argMax] ¬ NIL; }; }; argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd ! CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}; ]; argMax: NAT ¬ argv.argc; buffer: REF TEXT = NEW[TEXT[256]]; outerHandle1: CountedVM.Handle ¬ NIL; outerHandle2: CountedVM.Handle ¬ NIL; ipOut: ImagerInterpress.Ref ¬ NIL; Bye: PROC = { IF ipOut # NIL THEN ImagerInterpress.Close[ipOut]; ipOut ¬ NIL; outerHandle1 ¬ FreeBuffer[outerHandle1]; outerHandle2 ¬ FreeBuffer[outerHandle2]; }; WHILE argMax > 1 DO arg: ROPE = argv[1]; ShiftArgs[]; IF Rope.Length[arg] = 0 THEN LOOP; IF Rope.Match["-*", arg] THEN { <> SELECT TRUE FROM Rope.Match["-debug", arg, FALSE] => debugging ¬ TRUE; Rope.Match["-times", arg, FALSE] => showTimes ¬ TRUE; Rope.Match["-notimes", arg, FALSE] => showTimes ¬ FALSE; Rope.Match["-ps", arg, FALSE] => forceKind ¬ postscript; Rope.Match["-ip", arg, FALSE] => forceKind ¬ interpress; Rope.Match["-genoa", arg, FALSE] => forceKind ¬ genoa; Rope.Match["-auto", arg, FALSE] => forceKind ¬ unknown; Rope.Match["-monitor", arg, FALSE] => useMonitor ¬ TRUE; Rope.Match["-Hb", arg, FALSE] => highlight ¬ ImagerColor.ColorFromRGB[ [R: 0, G: 0, B: 1] ]; Rope.Match["-Hg", arg, FALSE] => highlight ¬ ImagerColor.ColorFromRGB[ [R: 0, G: 1, B: 0] ]; Rope.Match["-Hr", arg, FALSE] => highlight ¬ ImagerColor.ColorFromRGB[ [R: 1, G: 0, B: 0] ]; Rope.Match["-table", arg, FALSE] => TRUSTED { name: ROPE ~ argv[1]; stream: IO.STREAM = ImagerSys.OpenInputFile[name ! ImagerSys.FileError => CommanderOps.Failed[explanation]]; IF stream = NIL THEN CommanderOps.Failed[Rope.Concat[name, " file missing"]] ELSE { t: REF ImagerHighlightContextBackdoor.ColorLookupArray ~ NEW[ImagerHighlightContextBackdoor.ColorLookupArray]; -- should use the right zone for this. nBytes: NAT = BYTES[ImagerHighlightContextBackdoor.ColorLookupArray]; bytesRead: INT ~ IO.UnsafeGetBlock[stream, [base: LOOPHOLE[t], startIndex: 0, count: nBytes]]; IF bytesRead # nBytes THEN { CommanderOps.Failed[Rope.Concat[name, ": table file is too short"]]; }; colorLookupTable ¬ NEW[ImagerHighlightContextBackdoor.ColorLookupTableRep ¬ [ toner: Atom.MakeAtom[name], tablePointer: LOOPHOLE[t], ref: t ]]; IO.Close[stream]; ShiftArgs[]; }; }; Rope.Match["-combined", arg, FALSE] => combined ¬ TRUE; Rope.Match["-separate", arg, FALSE] => combined ¬ FALSE; Rope.Match["-switch", arg, FALSE] => switch ¬ TRUE; Rope.Match["-out", arg, FALSE] => IF argMax >= 1 THEN { name: ROPE ¬ argv[1]; ShiftArgs[]; IF ipOut # NIL THEN {ImagerInterpress.Close[ipOut]; ipOut ¬ NIL}; ipOut ¬ ImagerInterpress.Create[name ! ImagerSys.FileError => {msg ¬ explanation; GO TO failed}; Imager.Error => {msg ¬ error.explanation; GO TO failed}; ]; }; Rope.Match["-copies", arg, FALSE] => { syntax: BOOL ¬ FALSE; copies ¬ 0; IF argMax >= 1 THEN copies ¬ Convert.CardFromRope[argv[1] ! Convert.Error => {syntax ¬ TRUE; CONTINUE}]; IF copies NOT IN [1..maxCopies] THEN { IF syntax THEN IO.PutRope[cmd.out, "Can't parse nCopies number, 1 used\n"] ELSE IO.PutF1[cmd.out, "Unacceptable nCopies (%g), 1 used\n", [integer[copies]] ]; copies ¬ 1; }; ShiftArgs[]; allowCopiesToChange ¬ FALSE; }; Rope.Match["-spi", arg, FALSE] => { syntax: BOOL ¬ FALSE; spi ¬ 0; IF argMax >= 1 THEN spi ¬ Convert.CardFromRope[argv[1] ! Convert.Error => {syntax ¬ TRUE; CONTINUE}]; IF spi < 1 OR spi > 1200 THEN { IF syntax THEN IO.PutRope[cmd.out, "Can't parse spi number, 300 used\n"] ELSE IO.PutF1[cmd.out, "Unacceptable spi (%g), 300 used\n", [integer[spi]] ]; spi ¬ 300; }; ShiftArgs[]; }; Rope.Match["-iot", arg, FALSE] => { syntax: BOOL ¬ FALSE; iotparam ¬ NIL; IF argMax >= 1 THEN iotparam ¬ argv[1] ELSE syntax ¬ TRUE; ShiftArgs[]; }; Rope.Match["-pph", arg, FALSE] => { syntax: BOOL ¬ FALSE; pph ¬ 0.0; IF argMax >= 1 THEN pph ¬ Convert.RealFromRope[argv[1] ! Convert.Error => {syntax ¬ TRUE; CONTINUE}]; IF syntax OR pph < 0.1 OR pph > 1000.0 THEN { IF syntax THEN IO.PutRope[cmd.out, "Can't parse pph number, 10.0 used\n"] ELSE IO.PutF1[cmd.out, "Unacceptable pph (%g), 10.0 used\n", [real[spi]] ]; pph ¬ 10.0; }; ShiftArgs[]; }; ENDCASE => IO.PutF1[cmd.out, "Ignoring invalid switch: %g\n", [rope[arg]]]; LOOP; }; { ENABLE ImagerSys.FileError => {msg ¬ explanation; GO TO oops}; fastItems: NAT = (spi*fastDimX10+9)/10; slowItems: NAT = (spi*slowDimX10+9)/10; scanLines: NAT = slowItems; dstWordsWide: NAT = (fastItems+BITS[WORD]-1) / BITS[WORD]; dstBitsWide: NAT = dstWordsWide*BITS[WORD]; srcWordsWide: NAT = 2*dstWordsWide; srcBitsWide: NAT = srcWordsWide*BITS[WORD]; srcWords: INT ~ srcWordsWide*scanLines; dstWords: INT ~ dstWordsWide*scanLines; handle1: CountedVM.Handle ¬ outerHandle1 ¬ AllocBuffer[srcWords]; handle2: CountedVM.Handle ¬ outerHandle2 ¬ AllocBuffer[dstWords]; out: STREAM = cmd.out; fileName: Rope.ROPE ¬ arg; map: RasterSampleMap ¬ MakeSampleMap[handle1, srcWords, scanLines, dstBitsWide, 2]; dMap: RasterSampleMap ¬ MakeSampleMap[handle2, dstWords, scanLines, dstBitsWide, 1]; context: Context ¬ HighlightContextFromSampleMap[map, spi, pph, highlight, colorLookupTable, iotparam]; fileStream: IO.STREAM ¬ NIL; fileKind: FileKind ¬ unknown; pixelsToMeters: ImagerTransformation.Transformation = ImagerBackdoor.GetTransformation[context: context, from: device, to: client]; DoSeparations: PROC = { PageAction1: PROC [context: Imager.Context] ~ { Imager.ConcatT[context, pixelsToMeters]; Imager.SetGray[context, 1]; ImageSeparation[context, TRUE, map, dMap]; Imager.SetColor[context, highlight]; ImageSeparation[context, FALSE, map, dMap]; }; IF switch THEN SwitchColors[map]; SELECT TRUE FROM ipOut = NIL => {}; <> combined => <> ImagerInterpress.DoPage[ipOut, PageAction1, 1]; ENDCASE => { PageAction2: PROC [context: Imager.Context] ~ { Imager.ConcatT[context, pixelsToMeters]; Imager.SetPriorityImportant[context, FALSE]; ImageSeparation[context, isBlack, map, dMap]; }; isBlack: BOOL ¬ TRUE; <> ImagerInterpress.DoPage[ipOut, PageAction2, 1]; isBlack ¬ FALSE; ImagerInterpress.DoPage[ipOut, PageAction2, 1]; }; }; Require: PROC [rope: ROPE] ~ { [] ¬ CommanderOps.DoCommand[Rope.Cat["Require Cedar ", rope, " ", rope], cmd]; }; instanceData: DecomposerRegistry.InstanceData ¬ NIL; best: DecomposerRegistry.DecomposerData ¬ NIL; prob: REAL ¬ 0.0; sequencerProcs: REF DecomposerRegistry.SequencerProcs ~ NEW[DecomposerRegistry.SequencerProcs ¬ [ feedback: NVFeedbackProc, getAttr: NVGetAttrProc, setAttr: NVSetAttrProc ]]; streamIn: IO.STREAM = ImagerSys.OpenInputFile[fileName]; seq: DecomposerRegistry.SequencerData ¬ NEW[DecomposerRegistry.SequencerDataRep ¬ [ in: streamIn, out: cmd.out, err: cmd.err, title: fileName, file: ImagerSys.StreamFileName[streamIn], procs: sequencerProcs, private: RefTab.Create[] ]]; IF Rope.Match["*.ip*", fileName, FALSE] THEN Require["IPRegister"]; IF Rope.Match["*.interpress*", fileName, FALSE] THEN Require["IPRegister"]; IF Rope.Match["*.cip*", fileName, FALSE] THEN Require["IPRegister"]; IF Rope.Match["*.ps*", fileName, FALSE] THEN Require["PSRegister"]; FOR tail: LIST OF DecomposerRegistry.DecomposerData ¬ DecomposerRegistry.Enumerate[], tail.rest UNTIL tail = NIL DO d: DecomposerRegistry.DecomposerData ~ tail.first; p: REAL ~ d.procs.guess[d, seq]; IF p > prob THEN {prob ¬ p; best ¬ d}; ENDLOOP; IF best = NIL THEN {CommanderOps.Failed[Rope.Concat["could not guess file type of ", fileName]]} ELSE { instanceData ¬ best.procs.open[best, seq]; IF instanceData = NIL THEN CommanderOps.Failed["Failed to open"]; IF instanceData # NIL THEN { instanceData.copies ¬ 1; instanceData.copy ¬ 1; instanceData.pages ¬ -1; instanceData.procs.attributes[instanceData]; }; }; <<>> { seconds: REAL = BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[] - startPulses]; IO.PutF1[out, "%4.2f elapsed seconds to alloc map and open", [real[seconds]]]; IO.PutF1[out, " %g\n", [rope[fileName]]]; startPulses ¬ BasicTime.GetClockPulses[]; }; SELECT fileKind FROM unknown => { ENABLE { UNWIND => Bye[]; Imager.Warning => { IO.PutF[cmd.out, "-- Imager.Warning: %g, %g\n", [atom[ImagerError.AtomFromErrorCode[error.code]]], [rope[error.explanation]]]; RESUME; }; Imager.Error => { IO.PutF[cmd.out, "\n** Imager.Error: %g, %g\n", [atom[ImagerError.AtomFromErrorCode[error.code]]], [rope[error.explanation]]]; IF NOT debugging THEN GO TO Out; }; IO.EndOfStream, IO.Error => { IO.PutRope[out, "\n** Unknown IO Error\n"]; IF NOT debugging THEN GO TO Out; }; ImagerSys.FileError => { IO.PutF[cmd.out, "\n** ImagerSys.FileError inside of master: %g, %g\n", [atom[code]], [rope[explanation]]]; IF NOT debugging THEN GO TO Out; }; }; IO.PutF1[out, "file: %g", [rope[fileName]] ]; IO.PutF1[out, ", spi: %g", [cardinal[spi]] ]; IO.PutF1[out, ", copies: %g\n", [cardinal[copies]] ]; FOR j: INT IN [1 .. copies] DO pageCount: INT ¬ 0; done: BOOL ¬ FALSE; IO.PutF1[out, "Copy %g: ", [cardinal[j]] ]; instanceData.copy ¬ j; FOR i: INT IN [1..999999) UNTIL done DO pageFlags: DecomposerRegistry.PageFlags; pagePulses: BasicTime.Pulses ¬ BasicTime.GetClockPulses[]; Process.CheckForAbort[]; ImagerSample.Clear[map]; instanceData.page ¬ i; {P: PROC ~ { instanceData.context ¬ context; pageFlags ¬ instanceData.procs.page[instanceData, instanceData.page, instanceData.copy]; }; Imager.DoSaveAll[context, P] }; IF pageFlags.last THEN done ¬ TRUE; IF pageFlags.imaged THEN { IO.PutF1[out, " [%g", [cardinal[i]] ]; pageCount ¬ pageCount + 1; DoSeparations[]; IO.PutChar[out, ']]; IF showTimes THEN { endPagePulses: BasicTime.Pulses ¬ BasicTime.GetClockPulses[]; seconds: REAL = BasicTime.PulsesToSeconds[endPagePulses-pagePulses]; IO.PutF1[out, " (%4.2f)", [real[seconds]] ]; }; }; ENDLOOP; IO.PutChar[out, '\n]; { nextTime: BasicTime.Pulses = BasicTime.GetClockPulses[]; PerfLine[out, nextTime-startPulses, pageCount]; startPulses ¬ nextTime; }; ENDLOOP; instanceData.procs.close[instanceData]; GO TO Out; EXITS Out => Bye[]; }; ENDCASE => {msg ¬ "Unknown page description language"; GO TO oops}; Bye[]; EXITS oops => {Bye[]; GO TO failed}; }; ENDLOOP; EXITS failed => result ¬ $Failure; }; PerfLine: PROC [out: STREAM, pulses: BasicTime.Pulses, pages: INT] = { seconds: REAL = BasicTime.PulsesToSeconds[pulses]; IO.PutRope[out, "Done"]; IF seconds > 0.0 THEN { IO.PutF1[out, " in %4.2f seconds", [real[seconds]] ]; IF pages > 0 THEN IO.PutF1[out, " (ppm: %3.1f)", [real[(pages*60) / seconds]] ]; }; IO.PutRope[out, ".\n\n"]; }; Commander.Register[key: "HighlightDummy", proc: PrintCommand, doc: "HighlightDummy {switch | filename} -spi # of spots per inch (default: 300) -copies # of copies to make (default: 1) -times show per page times -notimes don't show per page times -ps force PostScript interpretation -ip force Interpress interpretation -out specify output file name as -combined combined black & highlight output -separate put out separations (default) -switch switch black & highlight -Hr make highlight color red (default) -Hg make highlight color green -Hb make highlight color blue -table use table-driven mapping -auto automatically determine file type (default) -debug allow errors to remain uncaught -iot ($(cat LIB/IOTSpecific.ps)) use IOT-specific PS setup "]; }.