-- PDInterpReaderImpl.mesa -- Michael Plass, December 2, 1983 5:11 pm -- Last Edited by: Pier, November 22, 1983 2:45 pm -- Last Edited by: Lamming, December 9, 1983 1:27 pm -- Tim Diebert, 5-Sep-86 13:36:47 DIRECTORY Environment, Inline, PDInterpBasic, PDFileFormat, PDInterpBitmap, PDInterpReader, PDInterpSysCalls, Heap, Stream; PDInterpReaderImpl: PROGRAM IMPORTS Inline, PDInterpBitmap, PDInterpSysCalls, Heap, Stream EXPORTS PDInterpReader = BEGIN OPEN PDInterpReader; bitsPerWord: NAT = Environment.bitsPerWord; zone: UNCOUNTED ZONE = Heap.systemZone; CheckHerald: PROC [herald: PDFileFormat.Herald] = { status: PDInterpBasic.Status ¬ nil; IF herald.maxLoadWord > 200000 THEN status ¬ unreasonableLoadSize; IF NOT (herald.copies IN [1..1000]) THEN status ¬ unreasonableNumberOfCopies; IF NOT (herald.imageSSize IN [80..32767]) OR NOT (herald.imageFSize IN [80..32767]) THEN status ¬ unreasonableImageSize; IF NOT (herald.bandSSize IN [8..32767]) THEN status ¬ unreasonableBandSSize; IF NOT (herald.sResolution IN [10..10000]) OR NOT (herald.fResolution IN [10..10000]) THEN status ¬ unreasonableResolution; IF herald.version # PDFileFormat.versionValue THEN status ¬ wrongFormatVersion; IF herald.password # PDFileFormat.passwordValue THEN status ¬ invalidPassword; IF status # nil THEN Error[NIL, status, 0, SIZE[PDFileFormat.Herald]]; }; AllocateSpace: PROC [handle: Handle] = { private: Private ¬ handle.private; bufferWords: INT ¬ (Inline.LongMult[handle.herald.bandSSize, (handle.herald.imageFSize+bitsPerWord-1)/bitsPerWord]+100)*2; private.loadPointer ¬ PDInterpSysCalls.AllocateSpace[handle.herald.maxLoadWord+bufferWords]; private.loadWordsAllocated ¬ handle.herald.maxLoadWord; private.bufferOriginPointer ¬ private.loadPointer + private.loadWordsAllocated; private.bufferWordsAllocated ¬ bufferWords; private.bufferWordCount ¬ 0; }; Open: PUBLIC PROC [stream: Stream.Handle] RETURNS [handle: Handle] = { bytesTransferred: CARDINAL; why: Stream.CompletionCode; sst: Stream.SubSequenceType; herald: PDFileFormat.Herald; heraldPtr: LONG POINTER = @herald; [bytesTransferred, why, sst] ¬ Stream.GetBlock[stream, [blockPointer: heraldPtr, startIndex: 0, stopIndexPlusOne: Environment.bytesPerWord*SIZE[PDFileFormat.Herald]]]; IF bytesTransferred < Environment.bytesPerWord*SIZE[PDFileFormat.Herald] THEN Error[NIL, unexpectedEOF, 0, SIZE[PDFileFormat.Herald]]; CheckHerald[herald]; BEGIN private: Private ¬ zone.NEW[PrivateRep]; handle ¬ zone.NEW[Rep ¬ [ stream: stream, herald: herald, image: [0, FALSE, FALSE, FALSE, black, 0, 0, 0, 0], bandNumber: 0, sMinBand: 0, sSizeBand: 0, colorType: none, colorTileLoadAddress: -1, priority: 0, loadWords: 0, private: private, index: 0, page: 0, pass: 0, status: nil, warningCount: 0 ]]; handle.index ¬ SIZE[PDFileFormat.Herald]; handle.sSizeBand ¬ herald.bandSSize; {ENABLE UNWIND => { zone.FREE[@private]; zone.FREE[@handle]; }; AllocateSpace[handle]; }; END; PDInterpSysCalls.SetDisplayLights[107]; }; Close: PUBLIC PROC [handle: Handle] RETURNS [stream: Stream.Handle] = { private: Private ¬ handle.private; stream ¬ handle.stream; IF private.loadPointer # NIL THEN PDInterpSysCalls.FreeSpace[private.loadPointer]; zone.FREE[@private]; zone.FREE[@handle]; PDInterpSysCalls.SetDisplayLights[109]; }; ColorTileFromLoad: PUBLIC PROC [handle: Handle, colorTileLoadAddress: INT, scratchPointer: LONG POINTER, scratchWords: INT] RETURNS [PDInterpBitmap.Tile] = { private: Private = handle.private; tile: LONG POINTER TO PDFileFormat.Tile = private.loadPointer + colorTileLoadAddress; words: INT = Inline.LongMult[tile.sSize, (tile.fSize+(bitsPerWord-1))/bitsPerWord]; bitmap: PDInterpBitmap.BitmapDesc¬ PDInterpBitmap.Reshape[ tile + SIZE[PDFileFormat.Tile], words, [tile.sMin, tile.fMin, tile.sSize, tile.fSize] ]; RETURN [PDInterpBitmap.CreateTile[ rectangle: [tile.sMin, tile.fMin, tile.sSize, tile.fSize], phase: tile.phase, rasterPointer: tile + SIZE[PDFileFormat.Tile], scratchPointer: scratchPointer, scratchWords: scratchWords ]]; }; LongBlockTransfer: PROC [source: LONG POINTER, count: INT, dest: LONG POINTER] = { WHILE count > LAST[CARDINAL] DO Inline.LongCOPY[source, LAST[CARDINAL], dest]; source ¬ source + LAST[CARDINAL]; dest ¬ dest + LAST[CARDINAL]; count ¬ count - LAST[CARDINAL]; ENDLOOP; Inline.LongCOPY[source, count, dest]; }; Private: TYPE = LONG POINTER TO PrivateRep; PrivateRep: PUBLIC TYPE = RECORD [ loadPointer: LONG POINTER ¬ NIL, loadWordsAllocated: INT ¬ 0, bufferOriginPointer: LONG POINTER ¬ NIL, bufferWordsAllocated: INT ¬ 0, bufferWordCount: INT ¬ 0 ]; BlockDescription: TYPE = RECORD [pointer: LONG POINTER, words: CARDINAL]; ReadLocate: PROC [handle: Handle, words: INT] RETURNS [blockDescription: BlockDescription] = { -- The buffer storage may get re-used with the next ReadLocate or ReadBlock; private: Private ¬ handle.private; bytesWanted: CARDINAL ¬ Environment.bytesPerWord*MIN[words, private.bufferWordsAllocated]; bytesTransferred: CARDINAL; why: Stream.CompletionCode; sst: Stream.SubSequenceType; [bytesTransferred, why, sst] ¬ Stream.GetBlock[handle.stream, [blockPointer: private.bufferOriginPointer, startIndex: 0, stopIndexPlusOne: bytesWanted]]; IF bytesTransferred < bytesWanted THEN {handle.status ¬ unexpectedEOF; Error[handle, unexpectedEOF, handle.index, words]}; blockDescription.pointer ¬ private.bufferOriginPointer; blockDescription.words ¬ private.bufferWordCount ¬ bytesTransferred/Environment.bytesPerWord; handle.index ¬ handle.index + blockDescription.words; }; ReadBlock: PROC [handle: Handle, dest: LONG POINTER, words: INT] = { bytesWanted: CARDINAL ¬ Environment.bytesPerWord*words; bytesTransferred: CARDINAL; why: Stream.CompletionCode; sst: Stream.SubSequenceType; [bytesTransferred, why, sst] ¬ Stream.GetBlock[handle.stream, [blockPointer: dest, startIndex: 0, stopIndexPlusOne: bytesWanted]]; IF bytesTransferred < bytesWanted THEN {handle.status ¬ unexpectedEOF; Error[handle, unexpectedEOF, handle.index, words]}; handle.index ¬ handle.index + bytesTransferred/Environment.bytesPerWord; }; CheckBB: PROC [handle: Handle, sMin, fMin, sSize, fSize: CARDINAL, errorWordCount: INT] = { IF LONG[sMin] + sSize > handle.herald.imageSSize OR LONG[fMin] + fSize > handle.herald.imageFSize OR fMin < handle.image.fMinPage OR fMin + fSize > handle.image.fMinPage + handle.image.fSizePage THEN RaisePDError[handle, objectOutOfBounds, errorWordCount]; IF sMin + sSize <= handle.sMinBand OR sMin >= handle.sMinBand+handle.sSizeBand THEN { RaisePDWarning[handle, objectOutOfBand, errorWordCount]; }; }; Get: PUBLIC PROC [handle: Handle] RETURNS [CommandBuffer] = { private: Private ¬ handle.private; command: PDFileFormat.Command; BadLoadReference: PROC [dataWords: INT] = { RaisePDError[handle, badLoadReference, SIZE[PDFileFormat.Command]+dataWords]; }; IF handle.priority = LAST[INT] THEN { ans: CommandBuffer.stateChange; ans.loadChangeStart ¬ ans.loadChangeLength ¬ 0; handle.sMinBand ¬ handle.sMinBand + handle.sSizeBand; handle.bandNumber ¬ handle.bandNumber + 1; handle.priority ¬ 0; handle.colorType ¬ ink; ans.whatChanged ¬ bandChange; IF handle.bandNumber = handle.image.nBands THEN { ans.whatChanged ¬ imageEnd; handle.colorType ¬ none; handle.status ¬ betweenPages; }; RETURN [ans]; }; ReadBlock[handle, @command, SIZE[PDFileFormat.Command]]; WITH command SELECT FROM imagingCommand: PDFileFormat.Command.imaging => { IF handle.colorType = none THEN { RaisePDError[handle, missingStartImage, SIZE[PDFileFormat.Command]]; ERROR }; SELECT imagingCommand.com FROM maskSamplesRef => { ans: CommandBuffer.maskSamples; maskSamplesRef: PDFileFormat.MaskSamplesRef; loadAddress: Environment.LongNumber; samples: LONG POINTER TO PDFileFormat.SampleArray; words: INT; ReadBlock[handle, @maskSamplesRef, SIZE[PDFileFormat.MaskSamplesRef]]; loadAddress.highbits ¬ imagingCommand.addrHighBits; loadAddress.lowbits ¬ maskSamplesRef.addrLowBits; ans.loadAddress ¬ loadAddress.li; IF ans.loadAddress < 0 OR ans.loadAddress + SIZE[PDFileFormat.SampleArray] > handle.loadWords THEN BadLoadReference[SIZE[PDFileFormat.MaskSamplesRef]]; samples ¬ private.loadPointer + loadAddress.li; words ¬ Inline.LongMult[samples.sSize, (samples.fSize+(bitsPerWord-1))/bitsPerWord]; IF ans.loadAddress + SIZE[PDFileFormat.SampleArray] + words > handle.loadWords THEN BadLoadReference[SIZE[PDFileFormat.MaskSamplesRef]]; CheckBB[handle, maskSamplesRef.sMin, maskSamplesRef.fMin, samples.sSize, samples.fSize, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskSamplesRef]]; ans.samples ¬ [sOrigin: maskSamplesRef.sMin, fOrigin: maskSamplesRef.fMin, sMin: 0, fMin: 0, sSize: samples.sSize, fSize: samples.fSize, pointer: samples+SIZE[PDFileFormat.SampleArray], rast: (samples.fSize+(bitsPerWord-1))/bitsPerWord, lines: samples.sSize]; RETURN [ans] }; maskRunGroupRef => { ans: CommandBuffer.maskRunGroup; maskRunGroupRef: PDFileFormat.MaskRunGroupRef; loadAddress: Environment.LongNumber; runGroup: LONG POINTER TO PDFileFormat.RunGroup; run: LONG POINTER TO PDFileFormat.Run; s: CARDINAL ¬ 0; runCount: CARDINAL ¬ 0; words: INT ¬ 0; fSize: CARDINAL ¬ 0; ReadBlock[handle, @maskRunGroupRef, SIZE[PDFileFormat.MaskRunGroupRef]]; loadAddress.highbits ¬ imagingCommand.addrHighBits; loadAddress.lowbits ¬ maskRunGroupRef.addrLowBits; ans.loadAddress ¬ loadAddress.li; IF ans.loadAddress < 0 OR ans.loadAddress + SIZE[PDFileFormat.RunGroup] > handle.loadWords THEN BadLoadReference[SIZE[PDFileFormat.MaskRunGroupRef]]; runGroup ¬ private.loadPointer + loadAddress.li; ans.pointer ¬ run ¬ private.loadPointer + loadAddress.li + SIZE[PDFileFormat.RunGroup]; WHILE s < runGroup.sSize DO IF INT[run-private.loadPointer] >= handle.loadWords THEN BadLoadReference[SIZE[PDFileFormat.MaskRunGroupRef]]; IF LONG[run.fMin] + run.fSize > fSize THEN fSize ¬ LONG[run.fMin] + run.fSize; IF run.lastRun THEN s ¬ s + 1; run ¬ run + SIZE[PDFileFormat.Run]; ENDLOOP; ans.runCount ¬ runCount; CheckBB[handle, maskRunGroupRef.sMin, maskRunGroupRef.fMin, runGroup.sSize, fSize, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskRunGroupRef]]; ans.sMin ¬ maskRunGroupRef.sMin; ans.fMin ¬ ans.fOffset ¬ maskRunGroupRef.fMin; ans.sSize ¬ runGroup.sSize; ans.fSize ¬ fSize; RETURN [ans] }; maskRectangle => { ans: CommandBuffer.maskRectangle; maskRectangle: PDFileFormat.MaskRectangle; ReadBlock[handle, @maskRectangle, SIZE[PDFileFormat.MaskRectangle]]; CheckBB[handle, maskRectangle.sMin, maskRectangle.fMin, maskRectangle.sSize, maskRectangle.fSize, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskRectangle]]; ans.sMin ¬ maskRectangle.sMin; ans.fMin ¬ maskRectangle.fMin; ans.sSize ¬ maskRectangle.sSize; ans.fSize ¬ maskRectangle.fSize; RETURN [ans] }; maskTrapezoid => { ans: CommandBuffer.maskTrapezoid; maskTrapezoid: PDFileFormat.MaskTrapezoid; fMin, fMax: INT; ReadBlock[handle, @maskTrapezoid, SIZE[PDFileFormat.MaskTrapezoid]]; fMin ¬ MIN[maskTrapezoid.fMin, maskTrapezoid.fMinLast]; fMax ¬ MAX[LONG[maskTrapezoid.fMin] + maskTrapezoid.fSize, LONG[maskTrapezoid.fMinLast] + maskTrapezoid.fSizeLast]; CheckBB[handle, maskTrapezoid.sMin, fMin, maskTrapezoid.sSize, fMax-fMin, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskTrapezoid]]; ans.sMin ¬ maskTrapezoid.sMin; ans.fMin ¬ maskTrapezoid.fMin; ans.fMinLast ¬ maskTrapezoid.fMinLast; ans.sSize ¬ maskTrapezoid.sSize; ans.fSize ¬ maskTrapezoid.fSize; ans.fSizeLast ¬ maskTrapezoid.fSizeLast; RETURN [ans] }; maskRunGroup => { ans: CommandBuffer.maskRunGroup; maskRunGroup: PDFileFormat.MaskRunGroup; runGroup: PDFileFormat.RunGroup; s: CARDINAL ¬ 0; runCount: INT ¬ 0; words: INT ¬ 0; fMin: CARDINAL ¬ LAST[CARDINAL]; fMax: CARDINAL ¬ FIRST[CARDINAL]; run: LONG POINTER TO PDFileFormat.Run; ReadBlock[handle, @maskRunGroup, SIZE[PDFileFormat.MaskRunGroup]]; ReadBlock[handle, @runGroup, SIZE[PDFileFormat.RunGroup]]; run ¬ private.bufferOriginPointer; private.bufferWordCount ¬ 0; WHILE s < runGroup.sSize DO IF runCount*SIZE[PDFileFormat.Run] >= private.bufferWordCount THEN { -- Read a chunk, assuming one run group per scan line. words: CARDINAL ¬ (runGroup.sSize-s)*SIZE[PDFileFormat.Run]; IF words + private.bufferWordCount > private.bufferWordsAllocated THEN RaisePDError[handle, runGroupTooLong, private.bufferWordCount]; ReadBlock[handle, run, words]; private.bufferWordCount ¬ private.bufferWordCount + words; }; IF run.fMin < fMin THEN fMin ¬ run.fMin; IF LONG[run.fMin] + run.fSize > fMax THEN fMax ¬ LONG[run.fMin] + run.fSize; IF run.lastRun THEN s ¬ s + 1; runCount ¬ runCount + 1; run ¬ run + SIZE[PDFileFormat.Run]; ENDLOOP; IF runCount*SIZE[PDFileFormat.Run] # private.bufferWordCount THEN ERROR; ans.runCount ¬ runCount; ans.loadAddress ¬ -1; IF runCount = 0 THEN { ans.pointer ¬ NIL; RaisePDWarning[handle, emptyRunGroup, SIZE[PDFileFormat.MaskRunGroup]+SIZE[PDFileFormat.RunGroup]]; fMin ¬ fMax; } ELSE { ans.pointer ¬ private.bufferOriginPointer; CheckBB[handle, maskRunGroup.sMin, fMin, runGroup.sSize, fMax-fMin, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskRunGroup]+private.bufferWordCount] }; ans.sMin ¬ maskRunGroup.sMin; ans.fMin ¬ fMin; ans.fOffset ¬ 0; ans.sSize ¬ runGroup.sSize; ans.fSize ¬ fMax; RETURN [ans]; }; maskSamples => { ans: CommandBuffer.maskSamples; maskSamples: PDFileFormat.MaskSamples; samples: PDFileFormat.SampleArray; ReadBlock[handle, @maskSamples, SIZE[PDFileFormat.MaskSamples]]; ReadBlock[handle, @samples, SIZE[PDFileFormat.SampleArray]]; CheckBB[handle, maskSamples.sMin, maskSamples.fMin, samples.sSize, samples.fSize, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskSamples]+SIZE[PDFileFormat.SampleArray]]; ans.loadAddress ¬ -1; {raster: BlockDescription ¬ ReadLocate[ handle, Inline.LongMult[samples.sSize, (samples.fSize+(bitsPerWord-1))/bitsPerWord] ]; ans.samples ¬ PDInterpBitmap.Reshape[ raster.pointer, raster.words, [maskSamples.sMin, maskSamples.fMin, samples.sSize, samples.fSize] ! PDInterpBitmap.InsufficientSpace => { RaisePDError[handle, bitmapTooBig, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskSamples]+SIZE[PDFileFormat.SampleArray]]; ERROR } ]; }; RETURN [ans]; }; colorSamples => { ans: CommandBuffer.colorSamples; colorSamples: PDFileFormat.ColorSamples; samples: PDFileFormat.SampleArray; ReadBlock[handle, @colorSamples, SIZE[PDFileFormat.ColorSamples]]; ReadBlock[handle, @samples, SIZE[PDFileFormat.SampleArray]]; CheckBB[handle, colorSamples.sMin, colorSamples.fMin, samples.sSize, samples.fSize, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.ColorSamples]+SIZE[PDFileFormat.SampleArray]]; {raster: BlockDescription ¬ ReadLocate[ handle, Inline.LongMult[samples.sSize, (samples.fSize+(bitsPerWord-1))/bitsPerWord] ]; ans.samples ¬ PDInterpBitmap.Reshape[ raster.pointer, raster.words, [colorSamples.sMin, colorSamples.fMin, samples.sSize, samples.fSize] ! PDInterpBitmap.InsufficientSpace => { RaisePDError[handle, bitmapTooBig, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.MaskSamples]+SIZE[PDFileFormat.SampleArray]]; ERROR } ]; }; RETURN [ans]; }; ENDCASE => { RaisePDError[handle, unrecognisedImagingCommand, SIZE[PDFileFormat.Command]]; ERROR}; }; controlCommand: PDFileFormat.Command.control => { ans: CommandBuffer.stateChange; ans.loadChangeStart ¬ ans.loadChangeLength ¬ 0; IF handle.colorType = none THEN SELECT controlCommand.com FROM startImage, deviceCommand, storeLoad, endDocument => NULL; ENDCASE => { RaisePDError[handle, missingStartImage, SIZE[PDFileFormat.Command]]; ERROR }; SELECT controlCommand.com FROM startImage => { startImage: PDFileFormat.StartImage; ReadBlock[handle, @startImage, SIZE[PDFileFormat.StartImage]]; IF startImage.filler # 0 THEN { RaisePDWarning[handle, nonZeroFill, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.StartImage]]; }; IF (LONG[startImage.passBands]+startImage.nBands)*handle.sSizeBand >= handle.herald.imageSSize+handle.sSizeBand OR LONG[startImage.fMinPage]+startImage.fSizePage > handle.herald.imageFSize THEN { RaisePDWarning[handle, imageBoundsExceedPageBounds, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.StartImage]]; }; IF handle.colorType # none AND handle.bandNumber < handle.image.nBands THEN RaisePDWarning[handle, tooFewBands, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.StartImage]]; handle.image ¬ startImage; ans.whatChanged ¬ imageStart; handle.priority ¬ 0; handle.colorType ¬ ink; handle.bandNumber ¬ 0; handle.sMinBand ¬ startImage.passBands*handle.sSizeBand; IF NOT startImage.strip THEN {handle.pass ¬ handle.pass + 1}; IF startImage.feed THEN {handle.page ¬ handle.page + 1; handle.pass ¬ 1}; handle.status ¬ constructingImage; RETURN [ans] }; setPriority => { priorityLow: PDFileFormat.Priority; priority: Environment.LongNumber; ReadBlock[handle, @priorityLow, SIZE[PDFileFormat.Priority]]; priority.highbits ¬ controlCommand.rest; priority.lowbits ¬ priorityLow; handle.priority ¬ priority.li; ans.whatChanged ¬ priorityChange; RETURN [ans] }; setColorInk => { handle.colorType ¬ ink; ans.whatChanged ¬ colorChange; RETURN [ans] }; setColorClear => { handle.colorType ¬ clear; ans.whatChanged ¬ colorChange; RETURN [ans] }; setColorTile => { setColorTile: PDFileFormat.SetColorTile; ReadBlock[handle, @setColorTile, SIZE[PDFileFormat.SetColorTile]]; handle.colorTileLoadAddress ¬ setColorTile.addr; SELECT controlCommand.rest FROM 0 => handle.colorType ¬ opaqueTile; 1 => handle.colorType ¬ transparentTile; ENDCASE => { handle.colorType ¬ opaqueTile; RaisePDWarning[handle, unknownColorTileFlag, SIZE[PDFileFormat.Command]]; }; ans.whatChanged ¬ colorChange; RETURN [ans]; }; endBand => { handle.priority ¬ LAST[INT]; ans.whatChanged ¬ priorityChange; RETURN [ans]; }; endDocument => { ans.whatChanged ¬ documentEnd; handle.status ¬ transmissionComplete; RETURN [ans]; }; storeLoad => { storeLoad: PDFileFormat.StoreLoad; loadWordsAllocated: LONG CARDINAL ¬ private.loadWordsAllocated; ReadBlock[handle, @storeLoad, SIZE[PDFileFormat.StoreLoad]]; IF storeLoad.firstAddress > loadWordsAllocated OR storeLoad.firstAddress + storeLoad.wordCount > loadWordsAllocated OR storeLoad.firstAddress + storeLoad.wordCount > handle.herald.maxLoadWord THEN {RaisePDError[handle, loadOutOfBounds, SIZE[PDFileFormat.Command]+SIZE[PDFileFormat.StoreLoad]]; ERROR}; ReadBlock[handle, private.loadPointer+storeLoad.firstAddress, storeLoad.wordCount]; ans.whatChanged ¬ loadChange; ans.loadChangeStart ¬ storeLoad.firstAddress; ans.loadChangeLength ¬ storeLoad.wordCount; handle.loadWords ¬ MAX[handle.loadWords, ans.loadChangeStart + ans.loadChangeLength]; handle.colorTileLoadAddress ¬ -1; RETURN [ans]; }; deviceCommand => { deviceCommand: CommandBuffer.deviceCommand; wordCount: CARDINAL; block: BlockDescription; ReadBlock[handle, @wordCount, SIZE[CARDINAL]]; block ¬ ReadLocate[handle, wordCount]; IF block.words < wordCount THEN RaisePDError[handle, unexpectedEOF, block.words+SIZE[CARDINAL]]; deviceCommand.deviceCommandPointer ¬ block.pointer; deviceCommand.deviceCommandWords ¬ block.words; RETURN [deviceCommand]; }; ENDCASE => {RaisePDError[handle, unrecognisedControlCommand, SIZE[PDFileFormat.Command]]; ERROR}; }; ENDCASE => {RaisePDError[handle, unrecognisedCommandType, SIZE[PDFileFormat.Command]]; ERROR}; }; Error: PUBLIC ERROR [handle: Handle, code: PDInterpBasic.PDErrorCode, wordIndex, wordCount: INT] = CODE; Warning: PUBLIC SIGNAL [handle: Handle, code: PDInterpBasic.PDWarningCode, wordIndex, wordCount: INT] = CODE; RaisePDError: PROC [handle: Handle, code: PDInterpBasic.PDErrorCode, words: INT] = { handle.status ¬ code; ERROR Error[handle, code, handle.index-words, words]; }; RaisePDWarning: PROC [handle: Handle, code: PDInterpBasic.PDWarningCode, words: INT] = { handle.warningCount ¬ handle.warningCount + 1; handle.status ¬ code; SIGNAL Warning[handle, code, handle.index-words, words]; }; END.