<> <> <<>> 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], PDImageReader USING [Handle, ImageLink, ImageRep, PaintActionProc, Rep], PreView USING [PDData, Rep], ViewerClasses USING [Viewer]; PDImageReaderImpl: CEDAR PROGRAM IMPORTS BiScrollers, PDFileReader, IO, ImagerPixelMap EXPORTS PDImageReader = BEGIN bitsPerWord: NAT = Basics.bitsPerWord; bytesPerWord: NAT = Basics.bytesPerWord; ImageLink: TYPE = PDImageReader.ImageLink; ImageRep: TYPE = PDImageReader.ImageRep; <> LeftoverRef: TYPE = REF Leftover; Leftover: TYPE = RECORD [ link: LeftoverRef _ NIL, command: REF ANY, colorType: PDFileReader.ColorType, colorTileLoadAddress: INT _ -1, priority: INT ]; InterpretImage: PUBLIC PROC [handle: PDFileReader.Handle, viewer: ViewerClasses.Viewer, paintAction: PDImageReader.PaintActionProc] RETURNS [lastImage: BOOLEAN _ FALSE] = { <> <> <> <<>> ENABLE IO.EndOfStream, IO.Error => GOTO Quit; --Viewers closed the stream imageHandle: PDImageReader.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 _ [0,0,0,0,0,NIL]; cachedColorReference: INT _ -1; GetColorTile: PROC = { <> IF currentColorTileLoadAddress # cachedColorReference THEN { cachedColorTile _ PDFileReader.ColorTileFromLoad[handle: handle, colorTileLoadAddress: (cachedColorReference _ currentColorTileLoadAddress), scratch: cachedColorTile.refRep]; }; }; 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> readHead_readHead.link; } ELSE { commandRef _ PDFileReader.Get[handle: handle, scanning: FALSE]; currentColorTileLoadAddress_handle.colorTileLoadAddress; currentColorType_handle.colorType; currentPriority_handle.priority; }; IF pdData.abort 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; bandMaxS_band.sOrigin+band.sMin+band.sSize; leftovers _ handle.image.leftOverMode; }; imageEnd => { paintAction[self: viewer, whatChanged: imageHandle]; readHead_writeHead_writeTail_NIL; RETURN [FALSE]; }; priorityChange => NULL; colorChange => NULL; bandChange => { IF pdData.abort THEN {readHead_writeHead_writeTail_NIL; RETURN [TRUE];}; paintAction[self: viewer, whatChanged: imageHandle]; <> imageHandle.pixelMap.sOrigin _ handle.herald.bandSSize*(handle.bandNumber+handle.image.passBands); ImagerPixelMap.Clear[imageHandle.pixelMap]; band _ imageHandle.pixelMap; bandMaxS_band.sOrigin+band.sMin+band.sSize; IF readHead#NIL THEN ERROR; --DEBUGGING CHECK !! readHead_writeHead; writeHead_NIL; }; loadChange => NULL; documentEnd => RETURN [TRUE]; 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_fMaxBox-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]; <> 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; EXITS Quit => NULL; }; StartImage: PROC [fileHandle: PDFileReader.Handle, oldPixelMap: ImagerPixelMap.PixelMap_[,,,,,,NIL] ] RETURNS [handle: PDImageReader.Handle] = TRUSTED { handle _ NEW[PDImageReader.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 [list: ImageLink, imageCount: INT] = { <> images, imageTail: ImageLink _ NIL; currentImageNumber: INT _ 0; 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 { newImage: ImageLink _ NEW[ImageRep _[link: NIL, type: imageStart, imageNumber: (currentImageNumber_currentImageNumber+1), index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StartImage]+SIZE[PDFileFormat.Command])*bytesPerWord]]; IF images=NIL THEN images_imageTail_newImage ELSE {imageTail.link_newImage; imageTail_newImage}; }; }; imageEnd => NULL; priorityChange => NULL; colorChange => NULL; bandChange => NULL; loadChange => { newImage: ImageLink_NEW[ImageRep _[link: NIL, type: loadChange, imageNumber: -1, index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StoreLoad]+SIZE[PDFileFormat.Command]+stateChange.loadChangeLength)*bytesPerWord]]; IF images=NIL THEN images_imageTail_newImage ELSE {imageTail.link_newImage; imageTail_newImage; }; }; documentEnd => { handle.stream.SetIndex[streamInitialIndex]; --restore PD stream RETURN [images, currentImageNumber]; }; ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Image structure Get error"]; }; ENDCASE => NULL; ENDLOOP; }; ResetToImage: PUBLIC PROC [data: REF ANY] RETURNS [ImageLink] = { <> << In this case, pdData.pageNumber contains an image number rather than a page number.>> <> <> <<>> rebuildLoad: BOOLEAN _ TRUE; pdData: PreView.PDData _ NARROW[data]; handle: PDFileReader.Handle _ pdData.pdhandle; imageNumber: INT _ pdData.pageNumber; nextImage: ImageLink _ pdData.pageStructure; handle.priority_0; handle.colorType_none; --fake the reader into rebuilding the load IF pdData.atImage#NIL AND pdData.atImage.imageNumber >= imageNumber THEN rebuildLoad_FALSE; UNTIL nextImage=NIL DO SELECT nextImage.type FROM imageStart => IF nextImage.imageNumber=imageNumber THEN { handle.stream.SetIndex[nextImage.index]; --set stream to point to StartImage command pdData.atImage _ nextImage; RETURN[nextImage]; }; loadChange => { IF rebuildLoad THEN { handle.stream.SetIndex[nextImage.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"]; --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"]; nextImage_nextImage.link; ENDLOOP; RETURN[NIL]; --never found the requested imageNumber }; END.