DIRECTORY Atom, Basics, ColorRegistry, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerColor, ImagerColorFns, ImagerDevice, ImagerPD, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerTransformation, ImagerPDPublic, PDFileFormat, PDFileWriter, PrincOps, PrincOpsUtils, RefTab, Rope, Vector2, SF, ImagerPrivate, ImagerState, ImagerPath; ImagerPDImpl: CEDAR PROGRAM IMPORTS Atom, Basics, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerColor, ImagerState, ImagerTransformation, PDFileWriter, RefTab, Rope, SF EXPORTS ImagerPD, Imager ~ BEGIN ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Imager.VEC; PathProc: TYPE ~ ImagerPath.PathProc; Context: TYPE ~ Imager.Context; CharMask: TYPE ~ ImagerMaskCache.CharMask; Toner: TYPE ~ ImagerPDPublic.Toner; Toners: TYPE ~ LIST OF ImagerPDPublic.Toner; UCR: TYPE ~ ImagerPDPublic.UCR; PrinterType: TYPE ~ ImagerPD.PrinterType; Transformation: TYPE ~ ImagerTransformation.Transformation; DeviceCode: TYPE ~ PDFileFormat.DeviceCode; PD: TYPE ~ REF PDRep; PDRep: PUBLIC TYPE ~ ImagerPDPrivate.PDRep; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; DeviceData: TYPE ~ ImagerPDPrivate.DeviceData; DeviceDataRep: TYPE ~ ImagerPDPrivate.DeviceDataRep; deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep _ [ SetColor: ImagerPDPrivate.SetColor, SetPriority: SetPriority, MaskBoxes: MaskBoxes, MaskBitmap: MaskBitmap, MaskChar: MaskChar ]]; SampleMap: TYPE ~ ImagerSample.SampleMap; Tile: TYPE ~ ImagerPD.Tile; bitsPerWord: NAT ~ Basics.bitsPerWord; TonerSetFromToners: PROC [toners: Toners] RETURNS [tonerSet: PACKED ARRAY PDFileFormat.Toner OF BOOL] ~ { tonerSet _ ALL[FALSE]; FOR t: Toners _ toners, t.rest UNTIL t=NIL DO pdt: PDFileFormat.Toner ~ VAL[ORD[t.first]]; IF tonerSet[pdt] THEN Imager.Error[[$specification, "Multiple use of same toner in PD Create"]]; tonerSet[pdt] _ TRUE; ENDLOOP; }; rastWeight: REAL _ 1.7; fontCacheSize: NAT _ 8000; defaultDotShape: REAL _ 0.45; defaultScreenAngle: ARRAY PDFileFormat.Toner[black..yellow] OF REAL _ [ black: 90, cyan: 75, magenta: 105, yellow: 45 ]; GetDefaultBricks: PROC [deviceData: DeviceData, pixelsPerHalftoneDot: REAL] ~ { FOR t: PDFileFormat.Toner IN PDFileFormat.Toner DO IF deviceData.tonerSet[t] THEN { angle: REAL ~ IF t IN[black..yellow] THEN defaultScreenAngle[t] ELSE 0; brick: ImagerBrick.Brick ~ ImagerBrick.BrickFromDotScreen[pixelsPerDot: pixelsPerHalftoneDot, degrees: angle, shape: defaultDotShape]; deviceData.halftoneBrick[t] _ NEW[Tile _ brick]; }; ENDLOOP; }; CreateFromParameters: PUBLIC PROC [name: ROPE, deviceCode: CARDINAL, deviceType: ATOM, sResolution, fResolution: CARDINAL, -- pixels per inch imageSSize, imageFSize: CARDINAL, -- pixels toners: Toners, leftovers: BOOL, bandSSize: NAT, maxLoadWords: INT, fontTuning: ROPE, tonerUniverse: Toners _ NIL, pixelsPerHalftoneDot: REAL, ucr: UCR _ []] RETURNS [PD] ~ { writer: PDFileWriter.PDState ~ PDFileWriter.Create[ fileName: name, deviceCode: VAL[deviceCode], sResolution: sResolution, fResolution: fResolution, imageSSize: imageSSize, imageFSize: imageFSize, bandSSize: bandSSize, leftOverMode: leftovers, maxLoadWords: maxLoadWords ]; deviceData: DeviceData ~ NEW [DeviceDataRep _ [ deviceType: deviceType, writer: writer, sSize: imageSSize, fSize: imageFSize, feed: FALSE, strip: FALSE, imageStarted: FALSE, priorityImportant: FALSE, tonerSet: TonerSetFromToners[toners], tonerUniverseSet: TonerSetFromToners[IF tonerUniverse # NIL THEN tonerUniverse ELSE toners], toner: black, ucr: ucr, maskTab: RefTab.Create[997], colorKind: nil, tileTable: NEW[ImagerPDPrivate.TileTableRep[17]], colorTile: [0, NIL, 0], colorClear: FALSE, sampledSource: NIL, paToDevice: ImagerTransformation.Scale[0], halftoneBrick: ALL[NIL], pixelsRGB: ImagerPixel.ObtainScratchPixels[3, 1], pixelsCMYK: ImagerPixel.ObtainScratchPixels[4, 1], interpolate: FALSE ]]; sInches: REAL ~ REAL[imageSSize]/REAL[sResolution]; fInches: REAL ~ REAL[imageFSize]/REAL[fResolution]; fontCacheID: ATOM ~ IF Rope.Size[fontTuning] = 0 THEN $PD ELSE Atom.MakeAtom[fontTuning]; fontTuner: ImagerDevice.FontTuner _ -- IF Rope.Size[fontTuning] # 0 THEN FontTune.CreateFontTuner[fontTuning] ELSE -- NIL; deviceParm: ImagerDevice.DeviceParm _ NEW[ImagerDevice.DeviceParmRep _ [ class: deviceClass, sSize: imageSSize, fSize: imageFSize, scanMode: IF sInches>fInches THEN [slow: down, fast: right] ELSE [slow: right, fast: up], surfaceUnitsPerInch: [sResolution, fResolution], surfaceUnitsPerPixel: 1, fontTuner: fontTuner, fontCache: ImagerMaskCache.GetNamedCache[fontCacheID, fontCacheSize], rastWeight: rastWeight ]]; context: Context ~ ImagerRaster.Create[class: contextClass, deviceParm: deviceParm, data: deviceData, pixelUnits: FALSE]; pd: PD ~ NEW [PDRep _ [ context: context, toners: toners, deviceData: deviceData ]]; IF deviceType=$raven384 OR deviceType=$versatec OR deviceType=$bw400 THEN { black: ImagerColor.Color _ ImagerColor.Find["Xerox/Research/Distinct/Black"]; white: ImagerColor.Color _ ImagerColor.Find["Xerox/Research/Distinct/White"]; oldClass: REF ClassRep _ context.class; --crack the opaque type newClass: REF ClassRep _ NEW[ClassRep _ oldClass^]; ImagerRasterMaskVector _ oldClass.MaskVector; ImagerRasterMaskStroke _ oldClass.MaskStroke; newClass.MaskStroke _ MyMaskStroke; newClass.MaskVector _ MyMaskVector; Imager.PutProp[context, $DistinctBlack, black]; Imager.PutProp[context, $DistinctWhite, white]; context.class _ newClass; --replace the class with our local subclass }; IF pixelsPerHalftoneDot <= 0 THEN pixelsPerHalftoneDot _ 2; GetDefaultBricks[deviceData, pixelsPerHalftoneDot]; RETURN[pd]; }; ImagerRasterMaskVector: PROC [context: Context, p1, p2: VEC] _ NIL; ImagerRasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] _ NIL; DoDash: PROC[context: Context] RETURNS[BOOLEAN] = { state: REF StateRep _ context.state; WITH state.color SELECT FROM color: ImagerColor.SpecialColor => IF color.type=$Distinct THEN { nameData: ColorRegistry.Data _ NARROW[color.data]; IF nameData.id#$Black AND nameData.id#$White AND nameData.id#$Gray THEN { widthInPixels: REAL _ ImagerTransformation.TransformVec[state.T, [state.np.strokeWidth, 0]].x; IF widthInPixels < 10 THEN RETURN[TRUE]; --define 10 device pixels as ok for texture }}; ENDCASE; RETURN[FALSE]; }; MyMaskVector: PROC [context: Context, p1, p2: Imager.VEC] = { IF DoDash[context] THEN { black: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctWhite]]; path: ImagerPath.PathProc = {moveTo[p1]; lineTo[p2]}; color: ImagerColor.SpecialColor _ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskVector[context, p1, p2]; ImagerState.StateSetColor[context, black]; MaskDashedStroke[context, color, path, FALSE]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskVector[context, p1, p2]; }; MyMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] = { IF DoDash[context] THEN { black: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctWhite]]; color: ImagerColor.SpecialColor _ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskStroke[context, path, closed]; ImagerState.StateSetColor[context, black]; MaskDashedStroke[context, color, path, closed]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskStroke[context, path, closed]; }; yellowDash: ARRAY [0..3] OF REAL _ [0,2.5,0,2.5]; redDash: ARRAY [0..3] OF REAL _ [3,2,3,2]; greenDash: ARRAY [0..3] OF REAL _ [1,2,1,2]; blueDash: ARRAY [0..3] OF REAL _ [4,2,1,2]; MaskDashedStroke: PROC [context: Context, color: ImagerColor.SpecialColor, path: PathProc, closed: BOOL] = { deviceData: DeviceData ~ NARROW[context.data]; unit: REAL _ SELECT deviceData.deviceType FROM $raven384, $bw400 => 10, $versatec => 5, ENDCASE => 1; array: ARRAY [0..3] OF REAL; pattern: PROC[i: NAT] RETURNS[REAL] = { v: VEC _ ImagerTransformation.InverseTransformVec[state.T, [array[i]*unit, 0]]; RETURN[v.x]}; state: REF StateRep _ context.state; end: INT _ state.np.strokeEnd; joint: INT _ state.np.strokeJoint; nameData: ColorRegistry.Data _ NARROW[color.data]; Imager.SetStrokeEnd[context, round]; Imager.SetStrokeJoint[context, round]; array _ SELECT nameData.id FROM $Red => redDash, $Green => greenDash, $Blue => blueDash, $Yellow => yellowDash, ENDCASE => ERROR; Imager.MaskDashedStroke[context, path, 4, pattern, 0,0]; state.np.strokeEnd _ end; state.np.strokeJoint _ joint; }; Close: PUBLIC PROC [pd: PD] ~ { PDFileWriter.Close[pd.deviceData.writer]; [] _ FunctionCache.Obtain[x: FunctionCache.GlobalCache[], compare: FunctionCache.Any, limit: INT.LAST, clientID: pd.deviceData]; -- Flush our stuff from the cache. }; StartImage: PROC [deviceData: DeviceData] ~ { tonerSet: PACKED ARRAY PDFileFormat.Toner OF BOOL _ ALL[FALSE]; tonerSet[deviceData.toner] _ TRUE; PDFileWriter.StartImage[pdState: deviceData.writer, toners: tonerSet, feed: deviceData.feed, strip: deviceData.strip]; deviceData.imageStarted _ TRUE; }; identity: Transformation ~ ImagerTransformation.Scale[1]; DoPage: PUBLIC PROC [pd: PD, action: PROC [context: Context], pixelUnits: BOOL _ FALSE] ~ { proc: PROC ~ {action[pd.context]}; littleRectangle: PROC ~ { Imager.SetPriorityImportant[pd.context, TRUE]; Imager.SetGray[pd.context, 0]; ImagerBackdoor.SetT[pd.context, identity]; Imager.MaskRectangleI[pd.context, 0, 0, 1, 1]; }; pd.deviceData.feed _ TRUE; FOR t: Toners _ pd.toners, t.rest UNTIL t=NIL DO tonerCard: CARDINAL ~ ORD[t.first]; toner: PDFileFormat.Toner ~ VAL[tonerCard]; pd.deviceData.toner _ toner; pd.deviceData.imageStarted _ FALSE; pd.deviceData.strip _ t.rest=NIL; StartImage[pd.deviceData]; Imager.SetGray[pd.context, 1]; Imager.DoSaveAll[pd.context, littleRectangle]; Imager.DoSaveAll[pd.context, proc]; IF pd.deviceData.strip AND NOT pd.deviceData.imageStarted THEN { }; IF pd.deviceData.imageStarted THEN { PDFileWriter.EndPage[pd.deviceData.writer]; pd.deviceData.feed _ FALSE; }; ENDLOOP; }; SetPriority: PROC [context: Context, priorityImportant: BOOL] ~ { deviceData: DeviceData ~ NARROW[context.data]; deviceData.priorityImportant _ priorityImportant; [] _ PDFileWriter.SetPriorityImportant[deviceData.writer, priorityImportant]; }; MaskBox: PROC [context: Context, box: SF.Box] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f]; }; sampled => { size: SF.Vec ~ SF.Size[box]; DeliverLines: PROC[p: PROC [LONG POINTER]] ~ { buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f]; bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]]; brick: ImagerPD.Tile ~ deviceData.halftoneBrick[deviceData.toner]^; Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ { buffer.length _ pixels.length; ImagerSample.GetTileSamples[tile: brick.sampleMap, initIndex: min, buffer: buffer, phase: brick.phase]; ImagerSample.Halftone[map: bits, min: [0, 0], sampleBuffer: pixels[0], thresholdBuffer: buffer, function: [null, complement]]; TRUSTED { p[ImagerSample.GetBase[bits].word] }; }; Boxes: SF.BoxGenerator ~ {boxAction[box]}; ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action]; ImagerSample.ReleaseScratchSamples[buffer]; ImagerSample.ReleaseScratchMap[bits]; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines, tFlag: opaque]; }; sampledBlack => { size: SF.Vec ~ SF.Size[box]; DeliverLines: PROC[p: PROC [LONG POINTER]] ~ { bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]]; Action: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ { ImagerSample.PutSamples[map: bits, buffer: pixels[0]]; TRUSTED { p[ImagerSample.GetBase[bits].word] }; }; Boxes: SF.BoxGenerator ~ {boxAction[box]}; ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action]; ImagerSample.ReleaseScratchMap[bits]; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines, tFlag: IF deviceData.colorClear THEN transparent ELSE opaque]; }; tile => { tFlag: PDFileWriter.TFlag ~ IF deviceData.colorClear THEN transparent ELSE opaque; deliverLines: PROC[p: PROC [LONG POINTER]] ~ TRUSTED { FOR s: INTEGER IN [box.min.s..box.max.s) DO line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]]; ImagerSample.Tile[map: line, tile: deviceData.colorTile.sampleMap, phase: deviceData.colorTile.phase]; TRUSTED {p[ImagerSample.GetBase[line].word]}; ImagerSample.ReleaseScratchMap[line]; ENDLOOP; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines, tFlag: tFlag]; }; ENDCASE => ERROR; -- illegal case }; useLoadForChars: BOOL _ TRUE; GetLoadEntry: PROC [deviceData: DeviceData, mask: CharMask] RETURNS [REF INT] ~ { refInt: REF INT _ NARROW[RefTab.Fetch[deviceData.maskTab, mask].val]; load: INT _ 0; IF refInt#NIL THEN RETURN [refInt]; IF NOT useLoadForChars THEN RETURN [NIL]; WITH mask SELECT FROM raster: REF ImagerMaskCache.CharMaskRep.raster => { size: SF.Vec ~ SF.Size[raster.box]; loadWords: INT ~ SIZE[PDFileFormat.SampleArray] + Basics.LongMult[size.s, (size.f+bitsPerWord-1)/bitsPerWord]; IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL]; -- load full TRUSTED {load _ PDFileWriter.LoadContiguousSampleArray[pdState: deviceData.writer, sSize: size.s, fSize: size.f, bitsPtr: @(raster[0])]}; }; runs: REF ImagerMaskCache.CharMaskRep.runs => { Runs: PROC [run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ { s: INTEGER _ 0; sMax: INTEGER ~ SF.SizeS[runs.box]; FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO r: ImagerMaskCache.Run ~ runs[i]; IF r.fSize # 0 THEN run[s, r.fMin, r.fSize]; IF r.lastRun THEN s _ s + 1; ENDLOOP; }; loadWords: INT ~ SIZE[PDFileFormat.RunGroup] + runs.nRuns*SIZE[PDFileFormat.Run]; IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL]; load _ PDFileWriter.LoadRunGroup[deviceData.writer, Runs]; }; ENDCASE => ERROR; refInt _ NEW[INT _ load]; [] _ RefTab.Store[deviceData.maskTab, mask, refInt]; RETURN [refInt]; }; MaskChar: PROC [context: Context, delta: SF.Vec, mask: CharMask] ~ { deviceData: DeviceData ~ NARROW[context.data]; loadEntry: REF INT _ GetLoadEntry[deviceData, mask]; sMin: NAT ~ delta.s+mask.box.min.s; fMin: NAT ~ delta.f+mask.box.min.f; IF deviceData.colorKind#constant THEN ERROR; IF loadEntry#NIL THEN { load: LONG CARDINAL ~ loadEntry^; SELECT mask.rep FROM raster => PDFileWriter.MaskSamplesRef[deviceData.writer, load, sMin, fMin]; runs => PDFileWriter.MaskRunGroupRef[deviceData.writer, load, sMin, fMin]; ENDCASE => ERROR; } ELSE WITH mask SELECT FROM raster: REF ImagerMaskCache.CharMaskRep.raster => { bitmap: ImagerSample.SampleMap ~ ImagerMaskCache.BitmapFromCharMask[raster]; MaskBitmap[context, bitmap, delta, SF.Displace[box: mask.box, t: delta], NIL]; }; runs: REF ImagerMaskCache.CharMaskRep.runs => { Runs: PROC [run: PDFileWriter.CaptureRunProc] ~ { s: INTEGER _ sMin; sMax: INTEGER ~ delta.s+mask.box.max.s; FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO r: ImagerMaskCache.Run ~ runs[i]; IF r.fSize # 0 THEN run[s, r.fMin+fMin, r.fSize]; IF r.lastRun THEN s _ s + 1; ENDLOOP; }; PDFileWriter.MaskRunGroup[deviceData.writer, Runs]; }; ENDCASE => ERROR; }; MaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { constantBox: PROC [box: SF.Box] ~ { PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f]; }; boxes[constantBox]; }; ENDCASE => { hardBox: PROC[box: SF.Box] ~ { MaskBox[context, box] }; boxes[hardBox]; }; }; MaskBitmap: PROC [context: Context, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { constantBox: PROC [box: SF.Box] ~ { deliverLines: PROC [p: PROC [LONG POINTER]] ~ TRUSTED { FOR s: INTEGER IN [box.min.s..box.max.s) DO line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]]; ImagerSample.Transfer[dst: line, src: bitmap, delta: delta]; TRUSTED {p[ImagerSample.GetBase[line].word]}; ImagerSample.ReleaseScratchMap[line]; ENDLOOP; }; PDFileWriter.MaskSamples[pdState: deviceData.writer, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines]; }; IF boxes = NIL THEN constantBox[bounds] ELSE boxes[constantBox]; }; ENDCASE => ERROR; -- SetColor should not have allowed bitmaps }; contextClass: ImagerPrivate.Class ~ ImagerRaster.CreateClass[type: $PD, deviceClass: deviceClass]; END. ¤ImagerPDImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Michael Plass, March 6, 1987 7:02:52 pm PST Doug Wyatt, May 30, 1985 10:32:41 pm PDT Dave Rumph, September 20, 1985 9:41:20 pm PDT Tim Diebert: April 25, 1986 11:45:19 am PST Eric Nickell February 18, 1986 6:49:26 pm PST Stone, February 11, 1987 12:04:24 pm PST Last edited by: Mik Lamming - May 1, 1987 11:13:22 am PDT Maureen Stone, June 4, 1987 1:23:53 pm PDT Call the larger dimension y; see the Interpress standard, section 4.3.1. Stash the MaskStroke and the MaskVector Procs. The great, dashed line hack. In each of these procedures, the call to SetColor will be noticed by the call to Validate in ImagerRaster. The color has to be set at the conclusion of the call in case the next masks are areas. PD files don't work very well with empty separations. Were we more clever, we would not generate StartImage for empty separations. Êl˜codešœ™Kšœ Ïmœ7™BKšœ+™+Kšœ%Ïk™(Kšœ*ž™-Kšœ(ž™+Kšœ*ž™-Kšœ%ž™(K™9K™*—K˜šž ˜ JšœÏc œ§žœ)˜ü—K˜KšÑbln œžœž˜KšžœŸ œÌž˜òKšžœ˜šœž˜K˜Kšžœžœžœ˜Kšžœžœ žœ˜Kšœ žœ˜%Kšœ žœ˜Kšœ žœ˜*Kšœžœ˜#Kšœžœžœžœ˜,Kšžœžœžœ˜Kšœ žœ˜)Kšœžœ'˜;Kšœ žœ˜+Kšžœžœžœ˜Kšœžœžœ˜+Kšœ žœžœ˜/šœ žœžœ˜-K˜—Kšœ žœ˜.Kšœžœ!˜4šœ(žœ ˜KKšÏnœ˜#Kš¡ œ˜Kš¡ œ ˜Kš¡ œ ˜Kš¡œ ˜Kšœ˜K˜—Kšœ žœ˜)Kšœžœ˜Kšœ žœ˜&š¡œžœžœ žœžœžœžœ˜iKšœ žœžœ˜šžœžœžœž˜-Kšœžœžœ ˜,KšžœžœK˜`Kšœžœ˜Kšžœ˜—Kšœ˜K˜—Kšœ žœ˜šœžœ˜K˜—šœžœ˜K˜—šœžœ#žœžœ˜GK˜ K˜ K˜ K˜ Kšœ˜K˜—š¡œžœ0žœ˜Ošžœžœž˜2šžœžœ˜ Kš œžœžœžœžœžœ˜GKšœ†˜†Kšœžœ˜0Kšœ˜—Kšžœ˜—Kšœ˜K˜—š&¡œžœžœžœžœžœžœŸœžœŸ œžœ žœžœžœžœžœžœžœžœ˜èšœ3˜3Kšœ˜Kšœ žœ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœžœ˜/Kšœ˜Kšœ˜Kšœ˜Kšœ˜Jšœžœ˜ Jšœžœ˜ Jšœžœ˜Jšœžœ˜Jšœ%˜%Jš œ%žœžœžœžœ ˜\J˜ J˜ Jšœ˜J˜Jšœ žœ#˜1Jšœžœ˜Jšœ žœ˜Jšœžœ˜Jšœ*˜*Jšœžœžœ˜Jšœ1˜1Jšœ2˜2Jšœ ž˜Jšœ˜—Kšœ žœžœ žœ˜3Kšœ žœžœ žœ˜3Kš œ žœžœžœžœ˜YKšœ$ŸQœžœ˜zšœ&žœ˜HKšœ˜Kšœ˜Kšœ˜šœ žœžœžœ˜YK™H—Kšœ0˜0Kšœ˜Kšœ˜KšœE˜EKšœ˜Kšœ˜—K•StartOfExpansionr[class: ImagerPrivate.Class, deviceParm: ImagerDevice.DeviceParm, data: REF ANY, pixelUnits: BOOL _ FALSE]šœržœ˜yšœžœžœ ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šžœžœžœžœ˜KK™.K˜MK˜MKšœ žœŸ˜@Kšœ žœ žœ˜3Kšœ-˜-Kšœ-˜-Kšœ#˜#Kšœ#˜#K˜/K˜/KšœŸ+˜FK˜—Kšžœžœ˜;Kšœ3˜3Kšžœ˜ K˜K˜—K™áKš¡œžœžœžœ˜CKš¡œžœ,žœžœ˜TK˜š¡œžœžœžœ˜3Kšœžœ˜$šžœ žœž˜šœ#žœžœ˜AKšœžœ ˜2Kšžœžœžœžœ˜IKšœžœK˜^Kš žœžœžœžœŸ+˜UK˜—Kšžœ˜Kšžœžœ˜—K˜—š¡ œžœ#žœ˜=šžœžœ˜Jšœžœ*˜KJšœžœ*˜KKšœ5˜5Kšœ"žœ%˜MK˜*Kšœ(˜(K˜*Kšœ'žœ˜.K˜*K˜—Kšœžœ)˜.K˜—š¡ œžœ,žœ˜Gšžœžœ˜Jšœžœ*˜KJšœžœ*˜KKšœ"žœ%˜MK˜*Kšœ.˜.K˜*Kšœ/˜/K˜*K˜—Kšžœ/˜3K˜—K˜Kšœ žœžœžœ˜1Kšœ žœžœžœ ˜*Kšœ žœžœžœ ˜,Kšœ žœžœžœ ˜+š¡œžœMžœ˜lKšœžœ˜.Kš œžœžœžœ+žœ˜fKšœžœžœžœ˜š œ žœžœžœžœ˜'KšœžœI˜OKšžœ˜ —Kšœžœ˜$Kšœžœ˜Kšœžœ˜"Kšœžœ ˜2K˜$K˜&šœžœ ž˜Kšœ%˜%Kšœ)˜)Kšžœžœ˜—K˜8Kšœ˜Kšœ˜K˜K˜—š¡œžœžœžœ˜Kšœ)˜)K–|[x: FunctionCache.Cache, compare: FunctionCache.CompareProc, limit: INT _ 1, clientID: FunctionCache.ClientID _ NIL]šœ]žœžœŸ"˜£Kšœ˜K˜—š¡ œžœ˜-Kš œ žœžœžœžœžœžœ˜?Kšœžœ˜"K–s[pdState: PDFileWriter.PDState, toners: PDFileWriter.TonerSet, feed: BOOLEAN _ TRUE, strip: BOOLEAN _ TRUE]šœv˜vKšœžœ˜Kšœ˜K˜—šœ9˜9K˜—š¡œžœžœžœ žœ!žœžœ˜[Jšœžœ˜"šœžœ˜Jšžœ3™5Jšœ(žœ˜.Jšœ˜Kšœ*˜*Kšœ.˜.K˜—Kšœžœ˜šžœžœžœž˜0Jšœ žœžœ ˜#Jšœžœ ˜+Jšœ˜Jšœžœ˜#Jšœžœ˜!Jšœ˜Jšœ˜Jšœ.˜.Jšœ#˜#šžœžœžœžœ˜@J™LJšœ˜—šžœžœ˜$Jšœ+˜+Jšœžœ˜Jšœ˜—Jšžœ˜—Kšœ˜K˜—š¡ œžœ'žœ˜AKšœžœ˜.Kšœ1˜1KšœM˜MK˜K˜—š¡œžœžœ ˜1Kšœžœ˜.šžœž˜ KšœžœŸ˜&šœ ˜ Kšœn˜nK˜—šœ ˜ Kšœžœžœ ˜š ¡ œžœžœžœžœ˜.KšœN˜NKšœW˜WKšœC˜Cš¡œžœžœ(žœ ˜DKšœ˜Kšœg˜gKšœ~˜~Kšžœ(˜/Kšœ˜—Jš¡œžœ!˜*JšœŸ˜ŸKšœ+˜+Kšœ%˜%Kšœ˜—šœN˜NKšœZ˜ZKšœ˜—Kšœ˜—šœ˜Kšœžœžœ ˜š ¡ œžœžœžœžœ˜.KšœW˜Wš¡œžœ(žœ ˜?K–€[map: ImagerSample.SampleMap, initIndex: SF.Vec _ [s: 0, f: 0], delta: SF.Vec _ [s: 0, f: 1], buffer: ImagerSample.SampleBuffer, start: NAT _ 0, count: NAT _ 32767, function: ImagerSample.Function _ [dstFunc: null, srcFunc: null]]šœ6˜6Kšžœ(˜/Kšœ˜—Jš¡œžœ!˜*JšœŸ˜ŸKšœ%˜%Kšœ˜—šœN˜NKšœZ˜ZKšœžœžœ žœ ˜>—K˜—šœ ˜ Kšœžœžœ žœ˜Rš œžœžœžœžœžœ˜6šžœžœžœž˜+K–X[box: SF.Box, bitsPerSample: ImagerSample.BitsPerSample _ 1, bitsPerLine: INT _ 0]šœv˜vK–ï[map: ImagerSample.SampleMap, box: SF.Box _ [min: [s: -32768, f: -32768], max: [s: 32767, f: 32767]], tile: ImagerSample.SampleMap, phase: NAT _ 0, function: ImagerSample.Function _ [dstFunc: null, srcFunc: null]]šœf˜fKšžœ&˜-Kšœ%˜%Kšžœ˜—Kšœ˜—KšœÒ˜ÒKšœ˜—KšžœžœŸ˜!—Kšœ˜K˜—šœžœžœ˜K˜—š ¡ œžœ*žœžœžœ˜QKšœžœžœžœ-˜EKšœžœ˜Kšžœžœžœžœ ˜#Kš žœžœžœžœžœ˜)šžœžœž˜šœžœ(˜3Kšœžœžœ˜#Kšœ žœžœY˜nKš žœ?žœžœžœŸ ˜_Kšžœ‚˜‰Jšœ˜—šœžœ&˜/š ¡œžœžœžœ žœ˜