DIRECTORY
Basics USING [bitsPerWord, bytesPerWord, LongNumber],
BiScrollers USING [ClientDataOfViewer],
ImagerPixelMap USING [Clear, Clip, Create, DeviceRectangle, Fill, Intersect, PixelMap, Reshape, Tile, Transfer, TransferTile, Window],
IO USING [EndOfStream, Error, GetIndex, SetIndex],
PDFileFormat USING [Command, Herald, Run, StartImage, StoreLoad],
PDFileReader USING [Close, ColorSamples, ColorTileFromLoad, ColorType, DeviceCommand, Error, Get, Handle, Keep, MaskRectangle, MaskRunGroup, MaskSamples, MaskTrapezoid, Rep, StateChange],
PDPageReader USING [Handle, PageRec, PageRecRef, PaintActionProc, Rep],
PreView USING [PDData, Rep],
ViewerClasses USING [Viewer];
PDPageReaderImpl:
CEDAR
PROGRAM
IMPORTS BiScrollers, PDFileReader, IO, ImagerPixelMap
EXPORTS PDPageReader = BEGIN
bitsPerWord: NAT = Basics.bitsPerWord;
bytesPerWord: NAT = Basics.bytesPerWord;
PageRecRef: TYPE = PDPageReader.PageRecRef;
PageRec: TYPE = PDPageReader.PageRec;
Leftover list types
LeftoverRef: TYPE = REF Leftover;
Leftover:
TYPE =
RECORD [
link: LeftoverRef ← NIL,
command: REF ANY,
colorType: PDFileReader.ColorType,
colorTileLoadAddress: INT ← -1,
priority: INT
];
InterpretPage:
PUBLIC
PROC [handle: PDFileReader.Handle, viewer: ViewerClasses.Viewer, pages: PageRecRef, paintAction: PDPageReader.PaintActionProc]
RETURNS [ok:
BOOLEAN ←
FALSE] = {
Interprets one page (may be multiple passes ??), and calls PaintAction to paint a viewer.
May raise PDFileReader.Warning or PDFileReader.Error. Returns FALSE upon document end.
ENABLE IO.EndOfStream, IO.Error => GOTO Quit; --Viewers closed the stream
imageHandle: PDPageReader.Handle←NIL;
pdData: PreView.PDData ← NARROW[BiScrollers.ClientDataOfViewer[viewer]];
readHead, writeHead, writeTail: LeftoverRef ← NIL; --heads and tail of leftover lists
currentColorType: PDFileReader.ColorType;
currentColorTileLoadAddress: INT ← -1;
currentPriority: INT ← -1;
leftovers: BOOLEAN; --can't be filled in until StartImage has happened
commandRef: REF ANY;
band: ImagerPixelMap.PixelMap;
bandMaxS: INT;
cachedColorTile: ImagerPixelMap.Tile;
cachedColorReference: INT ← -1;
GetColorTile:
PROC = {
IF currentColorTileLoadAddress # cachedColorReference
THEN {
cachedColorTile ← PDFileReader.ColorTileFromLoad[handle, cachedColorReference ← currentColorTileLoadAddress];
};
};
TransferRectangle:
PROC [rectangle: ImagerPixelMap.DeviceRectangle] = {
SELECT currentColorType
FROM
none => ERROR;
clear => ImagerPixelMap.Fill[band, rectangle, 0];
ink => ImagerPixelMap.Fill[band, rectangle, 1];
opaqueTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile];
};
transparentTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile, [or, null]];
};
ENDCASE => ERROR;
};
DoLine:
PROCEDURE [sMin,fMin,fSize:
CARDINAL] = {
SELECT currentColorType
FROM
none => ERROR;
clear => ImagerPixelMap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 0];
opaqueTile, ink => ImagerPixelMap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 1];
transparentTile =>
ImagerPixelMap.TransferTile[band.Clip[[sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize]], cachedColorTile, [or, null]];
ENDCASE => ERROR;
};
TempColor:
PROC [rectangle: ImagerPixelMap.DeviceRectangle] = {
SELECT currentColorType
FROM
none => ERROR;
clear => NULL;
ink => NULL;
opaqueTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile, [xor, complement]];
};
transparentTile => GetColorTile[];
ENDCASE => ERROR;
};
AddLeftover:
PROC = {
[]←PDFileReader.Keep[handle: handle, ref: commandRef];
IF writeHead=NIL THEN writeHead←writeTail←NEW[Leftover ←[link: NIL, command: commandRef, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]]
ELSE {
writeTail.link←NEW[Leftover ←[link: NIL, command: commandRef, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]];
writeTail←writeTail.link;
};
};
DO
ENABLE UNWIND => {readHead←writeHead←writeTail←NIL; handle.Close[];};
IF readHead#
NIL
AND readHead.priority<handle.priority
THEN {
commandRef ← readHead.command;
currentColorTileLoadAddress←readHead.colorTileLoadAddress;
currentColorType←readHead.colorType;
currentPriority←readHead.priority;
now remove this record from readList
readHead←readHead.link;
}
ELSE {
commandRef ← PDFileReader.Get[handle: handle, scanning: FALSE];
currentColorTileLoadAddress←handle.colorTileLoadAddress;
currentColorType←handle.colorType;
currentPriority←handle.priority;
};
IF pdData.abortPainting THEN {readHead←writeHead←writeTail←NIL; RETURN [TRUE];};
WITH commandRef
SELECT
FROM
stateChange: PDFileReader.StateChange => {
SELECT stateChange.whatChanged
FROM
imageStart => {
pdData.image←imageHandle ← StartImage[fileHandle: handle, oldPixelMap: IF imageHandle#NIL THEN imageHandle.pixelMap ELSE [,,,,,,NIL] ];
band ← imageHandle.pixelMap;
bandMaxSnd.sOrigin+band.sMin+band.sSize;
leftovers ← handle.image.leftOverMode;
};
imageEnd => {
paintAction[self: viewer, whatChanged: imageHandle];
readHead←writeHead←writeTail←NIL;
RETURN [TRUE];
};
priorityChange => NULL;
colorChange => NULL;
bandChange => {
IF pdData.abortPainting THEN {readHead←writeHead←writeTail←NIL; RETURN [TRUE];};
paintAction[self: viewer, whatChanged: imageHandle];
update pixelMap in handle
imageHandle.pixelMap.sOrigin ← handle.herald.bandSSize*(handle.bandNumber+handle.image.passBands);
ImagerPixelMap.Clear[imageHandle.pixelMap];
band ← imageHandle.pixelMap;
bandMaxSnd.sOrigin+band.sMin+band.sSize;
IF readHead#NIL THEN ERROR; --DEBUGGING CHECK !!
readHead←writeHead;
writeHead←NIL;
};
loadChange => NULL;
documentEnd => RETURN [FALSE];
ENDCASE => ERROR;
};
maskRectangle: PDFileReader.MaskRectangle => {
TransferRectangle[[maskRectangle.sMin, maskRectangle.fMin, maskRectangle.sSize, maskRectangle.fSize]];
IF leftovers AND maskRectangle.sMin+maskRectangle.sSize>bandMaxS THEN AddLeftover[];
};
maskTrapezoid: PDFileReader.MaskTrapezoid => {
EdgeBlock: TYPE = RECORD [fPos, fIncr: Basics.LongNumber];
MakeEdge:
PROC[fMinLast, fMin, sSize:
CARDINAL]
RETURNS [edge: EdgeBlock] = {
edge.fPos.highbits ← fMin;
edge.fPos.lowbits ← 0;
edge.fIncr.highbits ← fMinLast - fMin;
edge.fIncr.lowbits ← 0;
IF sSize > 1 THEN edge.fIncr.li ← edge.fIncr.li/sSize;
};
fMinBox: CARDINAL←MIN[maskTrapezoid.fMin, maskTrapezoid.fMinLast];
fMaxBox: CARDINAL←MAX[maskTrapezoid.fMin+maskTrapezoid.fSize, maskTrapezoid.fMinLast+maskTrapezoid.fSizeLast];
fSizeBox: CARDINAL𡤏MaxBox-fMinBox;
trapMaxS: CARDINAL ← maskTrapezoid.sMin+maskTrapezoid.sSize;
s: CARDINAL ← maskTrapezoid.sMin;
sMinBand: CARDINAL ← MIN[band.sOrigin+band.sMin, maskTrapezoid.sMin+maskTrapezoid.sSize];
sMaxBand: CARDINAL ← MIN[bandMaxS, trapMaxS];
calculate initial edges:
minEdge: EdgeBlock ← MakeEdge[fMinLast: maskTrapezoid.fMinLast, fMin: maskTrapezoid.fMin, sSize: maskTrapezoid.sSize];
maxEdge: EdgeBlock ← MakeEdge[fMinLast: maskTrapezoid.fMinLast+maskTrapezoid.fSizeLast, fMin: maskTrapezoid.fMin+maskTrapezoid.fSize, sSize: maskTrapezoid.sSize];
TempColor[[sMin: maskTrapezoid.sMin, fMin: fMinBox, sSize: maskTrapezoid.sSize, fSize: fSizeBox]];
WHILE s < sMinBand
DO
minEdge.fPos.lc ← minEdge.fPos.lc + minEdge.fIncr.li;
maxEdge.fPos.lc ← maxEdge.fPos.lc + maxEdge.fIncr.li;
s ← s + 1;
ENDLOOP;
WHILE s < sMaxBand
DO
DoLine[sMin: s, fMin: minEdge.fPos.highbits, fSize: maxEdge.fPos.highbits-minEdge.fPos.highbits];
minEdge.fPos.lc ← minEdge.fPos.lc + minEdge.fIncr.li;
maxEdge.fPos.lc ← maxEdge.fPos.lc + maxEdge.fIncr.li;
s ← s + 1;
ENDLOOP;
TempColor[[sMin: maskTrapezoid.sMin, fMin: fMinBox, sSize: maskTrapezoid.sSize, fSize: fSizeBox]];
IF leftovers AND trapMaxS>bandMaxS THEN AddLeftover[];
};
maskRunGroup: PDFileReader.MaskRunGroup =>
TRUSTED {
s: CARDINAL ← maskRunGroup.sMin;
sMinBand: CARDINAL ← MIN[band.sOrigin+band.sMin, maskRunGroup.sMin+maskRunGroup.sSize];
sMaxBand: CARDINAL ← MIN[band.sOrigin+band.sMin+band.sSize, maskRunGroup.sMin+maskRunGroup.sSize];
run: LONG POINTER TO PDFileFormat.Run ← maskRunGroup.pointer;
TempColor[[maskRunGroup.sMin, maskRunGroup.fMin, maskRunGroup.sSize, maskRunGroup.fSize]];
WHILE s < sMinBand
DO
IF run.lastRun THEN s ← s + 1;
run ← run + SIZE[PDFileFormat.Run];
ENDLOOP;
WHILE s < sMaxBand
DO
DoLine[sMin: s, fMin: run.fMin+maskRunGroup.fOffset, fSize: run.fSize];
IF run.lastRun THEN s ← s + 1;
run ← run + SIZE[PDFileFormat.Run];
ENDLOOP;
TempColor[[maskRunGroup.sMin, maskRunGroup.fMin, maskRunGroup.sSize, maskRunGroup.fSize]];
IF leftovers
AND maskRunGroup.loadAddress>=0
AND maskRunGroup.sMin+maskRunGroup.sSize>bandMaxS
THEN {
maskRunGroup.sSize←maskRunGroup.sSize-(s-maskRunGroup.sMin);
maskRunGroup.sMin←s;
maskRunGroup.runCount ← -1; --unused, eventually eliminate from DEF
maskRunGroup.pointer←run;
AddLeftover[];
};
};
maskSamples: PDFileReader.MaskSamples => {
rectangle: ImagerPixelMap.DeviceRectangle ← ImagerPixelMap.Window[maskSamples.samples];
TempColor[rectangle];
SELECT currentColorType
FROM
none => ERROR;
clear => ImagerPixelMap.Transfer[band, maskSamples.samples, [and, complement]];
opaqueTile, ink => ImagerPixelMap.Transfer[band, maskSamples.samples, [or, null]];
transparentTile => {
bounds: ImagerPixelMap.DeviceRectangle ← ImagerPixelMap.Intersect[rectangle, ImagerPixelMap.Window[band]];
destDesc: ImagerPixelMap.PixelMap←ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: bounds];
GetColorTile[];
ImagerPixelMap.TransferTile[dest: destDesc, tile: cachedColorTile];
ImagerPixelMap.Transfer[dest: destDesc, source: maskSamples.samples, function: [and, null]];
ImagerPixelMap.Transfer[dest: band, source: destDesc, function: [or, null]];
};
ENDCASE => ERROR;
TempColor[rectangle];
IF leftovers AND maskSamples.loadAddress>=0 AND maskSamples.samples.sOrigin+maskSamples.samples.sMin+maskSamples.samples.sSize > bandMaxS THEN AddLeftover[];
};
colorSamples: PDFileReader.ColorSamples => {
ImagerPixelMap.Transfer[band, colorSamples.samples];
};
deviceCommand: PDFileReader.DeviceCommand => {
};
ENDCASE => NULL;
ENDLOOP;
};
StartImage:
PROC [fileHandle: PDFileReader.Handle, oldPixelMap: ImagerPixelMap.PixelMap←[,,,,,,
NIL] ]
RETURNS [handle: PDPageReader.Handle] =
TRUSTED {
handle←NEW[PDPageReader.Rep];
IF oldPixelMap.refRep=NIL THEN handle.pixelMap←ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: 0, sSize: fileHandle.herald.bandSSize, fSize: fileHandle.herald.imageFSize]]
ELSE handle.pixelMap←ImagerPixelMap.Reshape[refRep: oldPixelMap.refRep, lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: fileHandle.image.fMinPage, sSize: fileHandle.herald.bandSSize, fSize: fileHandle.image.fSizePage]];
handle.pixelMap.sOrigin ← fileHandle.herald.bandSSize*fileHandle.image.passBands;
ImagerPixelMap.Clear[handle.pixelMap];
};
GetPageStructure:
PUBLIC
PROC [handle: PDFileReader.Handle]
RETURNS [PageRecRef,
INT] = {
assumes filestream is available from Handle and Gets can be executed safely
pages, pageTail: PageRecRef←NIL;
currentPageNumber: INT𡤀
copyHandle: PDFileReader.Handle←NEW[PDFileReader.Rep ← handle^];
streamInitialIndex: INT←handle.stream.GetIndex[];
handle.stream.SetIndex[SIZE[PDFileFormat.Herald]*bytesPerWord];--start of file just past Herald
DO
WITH copyHandle.Get[scanning:
TRUE]
SELECT
FROM
stateChange: PDFileReader.StateChange => {
SELECT stateChange.whatChanged
FROM
imageStart => {
IF copyHandle.image.nBands>0
THEN {
newPage: PageRecRef←NEW[PageRec ←[link: NIL, type: imageStart, pageNumber: (currentPageNumber𡤌urrentPageNumber+1), index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StartImage]+SIZE[PDFileFormat.Command])*bytesPerWord]];
IF pages=NIL THEN pages←pageTail←newPage
ELSE {pageTail.link←newPage; pageTail←newPage};
};
};
imageEnd => NULL;
priorityChange => NULL;
colorChange => NULL;
bandChange => NULL;
loadChange => {
newPage: PageRecRef←NEW[PageRec ←[link: NIL, type: loadChange, pageNumber: -1, index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StoreLoad]+SIZE[PDFileFormat.Command]+stateChange.loadChangeLength)*bytesPerWord]];
IF pages=NIL THEN pages←pageTail←newPage
ELSE {pageTail.link←newPage; pageTail←newPage; };
};
documentEnd => {
handle.stream.SetIndex[streamInitialIndex]; --restore PD stream
RETURN [pages, currentPageNumber];
};
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure Get error"];
};
ENDCASE => NULL;
ENDLOOP;
};
ResetToPage:
PUBLIC
PROC [data:
REF
ANY]
RETURNS [PageRecRef] = {
a DUMB routine to reconstruct the load for the given page
requires that a page structure has already been built using GetPageStructure
rebuildLoad: BOOLEAN ← TRUE;
pdData: PreView.PDData ← NARROW[data];
handle: PDFileReader.Handle ← pdData.pdhandle;
pageNumber: INT ← pdData.pageNumber;
nextPage: PageRecRef ← pdData.pageStructure;
handle.priority𡤀 handle.colorType←none; --fake the reader into rebuilding the load
IF pdData.atPage#NIL AND pdData.atPage.pageNumber >= pageNumber THEN rebuildLoad←FALSE;
UNTIL nextPage=
NIL
DO
SELECT nextPage.type
FROM
imageStart =>
IF nextPage.pageNumber=pageNumber
THEN {
handle.stream.SetIndex[nextPage.index]; --set stream to point to StartImage command
pdData.atPage ← nextPage;
RETURN[nextPage];
};
loadChange => {
IF rebuildLoad
THEN {
handle.stream.SetIndex[nextPage.index]; --set stream to point to storeLoad command
WITH handle.Get[scanning:
FALSE]
SELECT
FROM
--handle.Get[] has the side effect of building the load!!
sc: PDFileReader.StateChange =>
SELECT sc.whatChanged
FROM
loadChange => NULL; --found the expected loadChange command
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure index error"]; --found a StateChange which was not a loadChange
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure index error"]; --pages tried to point to a loadChange but reader didn't find one
};
};
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure type error"];
nextPage←nextPage.link;
ENDLOOP;
RETURN[NIL]; --never found the requested pageNumber
};