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
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: CARDINALLAST[CARDINAL];
fMax: CARDINALFIRST[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.