Article View: comp.lang.postscript
Article #6696Re: Display external TIFF file
From: Ken Sharp
Date: Thu, 03 May 2007 07:12
Date: Thu, 03 May 2007 07:12
877 lines
20354 bytes
20354 bytes
In article <1178155922.955360.233590@c35g2000hsg.googlegroups.com>,
asproat1@hotmail.com says...
> But when I execute the same code with a TIFF file, substituting /
> RunLengthDecode for the encoding, all I get is a one pixel high line.
TIFF files are not (or need not) be RunLength Encoded. They can also use
CCITT, LZW and later versions can use Flate.
They also have a complex structure (unlike JPEG files) and, again unlike
JPEG files, PostScript does not understand the TIFF file format.
> I suspect I'm not using the correct filter or have made some other
> mistake in one of the many portions of the image matrix but after
> several days, I have been unable to determine how to make it work.
>
> If anyone knows where I've made my mistake, please let me know.
I'm afraid its rather more complicated than that. As PostScript is a
programming language its perfectly possible to write a program to read a
TIFF file, either from disk if your interpreter supports that or in-
line. Note that unless the TIFF file is small enough to hold in memory,
or is a single contiguous strip, it may well not be possible to process
some TIFF files.
Here's a program which works for some TIFF files. This only works with
files on disk. No warranty, express or implied....
To use this, :
%%
%% TiffPrint reads and prints a TIFF file.
%% Syntax: x y xscale yscale filename TiffRead
%%
So download as a job by appending a command to read a file, or install
as a ProcSet, or outside the server loop to make permanent.
----------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%
%%
%%
%% Ken's amazing Tiff reader in PostScript.
%%
%%
%%
%% Do not modify this file, under pain of pain.
%%
%%
%%
%% Created: 31/10/2002
%%
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%
/TiffDict 100 dict def
/TIFFdebug false def
TiffDict begin
%%
%% GetByte reads a single byte from the file.
%% Syntax: GetByte
%% Returns: int and true, or false
%%
/GetByte{
TiffDict begin
RawFile read
end
}bind def
%%
%% GetTiffByte just points straight to GetByte
%%
/GetTiffByte{
GetByte
}bind def
%%
%% GetTiffASCII just points straight to GetByte
%%
/GetTiffASCII{
GetByte
}bind def
%%
%% GetTiffShort reads 2 bytes as a short from the file, and munges
according
%% to the data format, Motorola or Intel
%% Syntax: GetTiffShort
%% Returns: int and true, or false
%%
/GetTiffShort{
TiffDict begin
GetByte {
/b0 exch def
GetByte{
/b1 exch def
Format 73 eq{
b1 256 mul b0 add
}{
b0 256 mul b1 add
}ifelse
true
}{
false
}ifelse
}{
false
}ifelse
end
}bind def
%%
%% GetTiffLong reads 4 bytes as a long from the file, and munges
according
%% to the data format, Motorola or Intel
%% Syntax: GetTiffLong
%% Returns: int and true, or false
%%
/GetTiffLong{
TiffDict begin
GetByte {
/b0 exch def
GetByte{
/b1 exch def
GetByte{
/b2 exch def
GetByte{
/b3 exch def
Format 73 eq{
b3 16777216 mul b2 65536 mul
add b1 256 mul add b0 add
}{
b0 16777216 mul b1 65536 mul
add b2 256 mul add b3 add
}ifelse
true
}{
}ifelse
}{
false
}ifelse
}{
false
}ifelse
}{
false
}ifelse
end
} bind def
%%
%% GetTiffRational reads two LONGs, the first is the numerator, the
seond
%% the denominator of a fraction.
%% Syntax: GetTiffRational
%% Returns: float and true, or false
%%
/GetTiffRational{
GetTiffLong{
GetTiffLong{
div
true
}{
pop
(Unable to read denominator of Rational)==
false
}ifelse
}{
(Unable to read numerator of Rational)==
false
}ifelse
}bind def
%%
%% GetTiffSByte reads a signed byte from the file
%% Syntax: GetTiffSByte
%% Returns: int and true, or false
%%
/GetTiffSByte{
GetTiffByte{
dup 128 ge{
128 sub -1 mul
}if
true
}{
(Error reading byte) ==
false
}
}bind def
%%
%% GetTiffSShort reads a signed short from the file
%% Syntax: GetTiffSShort
%% Returns: int and true, or false
%%
/GetTiffSShort{
GetTiffShort{
dup 32768 ge{
32768 sub -1 mul
}if
true
}{
(Error reading short) ==
false
}ifelse
}bind def
%%
%% GetTiffSLong reads a signed short from the file
%% Syntax: GetTiffSLong
%% Returns: int and true, or false
%%
/GetTiffSLong{
GetTiffSLong{
dup 2147483648 ge{
2147483648 sub -1 mul
}if
true
}{
(Error reading long) ==
false
}ifelse
}bind def
%%
%% GetTiffSRational reads two LONGs, the first is the numerator, the
seond
%% the denominator of a fraction.
%% Syntax: GetTiffSRational
%% Returns: float and true, or false
%%
/GetTiffSRational{
GetTiffSLong{
GetTiffSLong{
div
true
}{
pop
(Unable to read denominator of Rational)==
false
}ifelse
}{
(Unable to read numerator of Rational)==
false
}ifelse
}bind def
/UnHandledType{
(Unknown TIff data type)==
false
}bind def
/TypeReader [/UnHandledType load 1 /GetTiffByte load 1 /GetTiffASCII
load 1 /GetTiffShort load 2 /GetTiffLong load 4
/GetTiffRational load 8 /GetTiffSByte load 1
/UnHandledType load 1 /GetTiffSShort load 2
/GetTiffSLong load 4 /GetTiffSRational load 8
/UnHandledType load 4 /UnHandledType load 4] def
/TagHandler [254 /NewSubFileType
255 /SubFileType
256 /ImageWidth
257 /ImageLength
258 /BitsPerSample
259 /Compression
262 /PhotometricInterpretation
263 /Thresholding
264 /CellWidth
265 /CellLength
266 /FillOrder
269 /DocumentName
270 /ImageDescription
271 /Make
272 /Model
273 /StripOffsets
274 /Orientation
277 /SamplesPerPixel
278 /RowsPerStrip
279 /StripByteCounts
280 /MinSampleValue
281 /MaxSampleValue
282 /XResolution
283 /YResolution
284 /PlanarConfiguration
285 /PageName
286 /XPosition
287 /YPosition
288 /FreeOffsets
289 /FreeByteCounts
290 /GrayResponseUnit
291 /GrayResponseCurve
292 /Group3Options
293 /Group4Options
296 /ResolutionUnit
297 /PageNumber
301 /TransferFunction
305 /Software
306 /DateTime
315 /Artist
316 /HostComputer
317 /Predictor
318 /WhitePoint
319 /PrimaryChromaticities
320 /ColorMap
321 /HalftoneHints
322 /TileWidth
323 /TileHeight
324 /TileOffsets
325 /TileByteCounts
332 /InkSet
333 /InkNames
336 /DotRange
337 /TargetPrinter
338 /ExtraSamples
339 /SampleFormat
340 /SMinSampleValue
341 /SMaxSampleValue
512 /JPEGProc
513 /JPEGInterchangeFormat
514 /JPEGInterchangeFormatLength
515 /JPEGRestartInterval
517 /JPEGLosslessPredictors
518 /JPEGPointTransforms
519 /JPEGQTables
520 /JPEGDCTTables
521 /JPEGACTables
529 /YCbCrCoefficients
530 /YCbCrSubSampling
531 /YCbCrPositioning
532 /ReferenceBlackWhite
] def
%%
%% TiffStore stores a tag, and an array of data in a new dictionary in
TiffDict
%% Syntax: Type Length Offset Tag TiffStore
%% Return: none
%%
/TiffStore{
TiffDict begin
TiffDict /ParamsDict known not {
/ParamsDict 100 dict def
}if
ParamsDict begin
/LastTag exch def
/LastOffset exch def
/LastLength exch def
/LastType exch def
%% First we need to figure out the size of the data. If its 4 byts
or less
%% then Offset is the *actual* data, otherwise its a pointer...
LastType TypeReader length gt{
(Unknown Tiff data type ) print LastType ==
false
}{
TypeReader LastType 2 mul 1 add get %% Get the
field size from the table
LastLength mul 4 le { %%
See if field size * count <= 4
/LastArray 1 array def %%
If it is, define a single entry array
LastArray 0 LastOffset put %% and
store the offset as a value
true
}{
/LastArray LastLength array def %%
otherwise, define a big array to hold the data
/SavePos RawFile fileposition def %% save the
current file position
RawFile LastOffset setfileposition %% move to the
stored data
true
0 1 LastLength 1 sub{
TypeReader LastType 2 mul get exec {
LastArray 3 1 roll put
}{
pop
(error reading data for this tag) ==
pop % true exit code
false % say we failed
exit
}ifelse
} for
RawFile SavePos setfileposition
}ifelse
}ifelse
false 0 1 TagHandler length {
2 mul dup TagHandler exch get LastTag eq{
1 add TagHandler exch get
dup 255 string cvs print ( ) print 0 1 LastArray
length 1 sub {LastArray exch get 255 string cvs print ( ) print} for
(\n) print
LastArray def
pop
true
exit
}
{
pop
}ifelse
}for
not{
(Unhandled tag ) print LastTag ==
false
}{
true
}ifelse
and %% the boolean from the data retrieval and the
boolean from the tag handling
end %% ParamsDict
end %%TiffDict
}bind def
%%
%% ReadIFD reads a TIFF IFD from the file, and stores it in the Tiff
dictionary
%% Syntax: offset ReadIFD
%% Return: true if succesful false if not
%%
/ReadIFD{
TiffDict begin
%% First, move the file to the position specified by the offset
RawFile exch setfileposition
%% Now read the number of Directory entries in this IFD
GetTiffShort {
true exch %% marker for succesful read
%% Now read each directory entry in turn
{
%% Read the tag
GetTiffShort{
/Tag exch def
GetTiffShort{
/Type exch def
GetTiffLong{
/Length exch def
GetTiffLong{
/Offset exch def
Type Length Offset Tag
TiffStore not{
pop %% the true
(Problem storing
tag data) ==
false exit
}if
}{
pop %% the true
(Error reading Directory
Value offset) ==
false
exit
}ifelse
}{
pop %% the true
(Error reading Directory
Length) ==
false
exit
}ifelse
}{
pop %% the true
(Error reading Directory type) ==
false
exit
}ifelse
}{
pop %% the true
(Error reading Directory tag) ==
false
exit
}ifelse
}repeat
}{
(error reading number of IFD entries) ==
false
}ifelse
end
}bind def
%%
%% ReadHeader reads the tags from a TIFF file, and stores them.
%% Syntax: ReadHeader
%% Return: true for success, false for error
%%
/ReadHeader{
TiffDict begin
%% First, let's read the 2 byte format header
RawFile read {
dup 73 eq{
pop
RawFile read{
73 eq{
(Tiff file is Intel format)==
/Format 73 def
true
}{
(First two bytes of file do not
match!) ==
false
}ifelse
}{
(Failed to read second byte of file)==
false
}ifelse
}{
77 eq{
RawFile read {
77 eq{
(Tiff file is Motorola format)
==
/Format 77 def
true
}{
(First two bytes of file do not
match!) ==
false
}ifelse
}{
(Failed to read second byte of file)==
false
}ifelse
}{
(First byte of file neither 'I' nor 'M') ==
false
}ifelse
}ifelse
}{
(failed to read any data from file!) ==
false
}
ifelse
{
%% OK, we found an apparent header, now lets check the
version
RawFile read {
42 eq {
RawFile read {
pop
/IFDIndex 0 def
%% Looks like it may even be a TIFF
file :-)
%% Now we need to get the offset of
the 'next' IFD
%% and then read all the IFDs
{
GetTiffLong
{
dup /NextIFD exch def
dup 0 eq{pop true exit}
if
ReadIFD not {(Unable to
read IFD ) print IFDIndex == false exit}if
}{
(Unable to read offset
to IFD ) print IFDIndex ==
false %% return value
}ifelse
/IFDIndex 1 IFDIndex add def
}loop
}{
(unable to read version number, byte
2) ==
false
}ifelse
}{
(version number not 42) ==
false
}ifelse
}
{
(unable to read version number) ==
false
}ifelse
}{
false %% return value
}ifelse
end %% TiffDict
} bind def
%%
%% MoveToStrip moves the underlying file to the offset of the 'next'
strip
%% It also instantiates the filter (if required) to decode the
compressed
%% data.
%% Syntax: int MoveToStrip
%% Returns: Boolean
%%
/MoveToStrip{
TiffDict begin
ParamsDict begin
/CurrentStrip exch def
StripOffsets CurrentStrip get RawFile exch setfileposition
Compression 0 get 0 eq{
(Invalid compression scheme ) print Compression==
false
}{
Compression 0 get 1 eq{
%% No compression
/FilterFile RawFile def
}{
Compression 0 get 2 eq{
%% Old-fashioned CCITT Group 3 (pure 1d
encoding)
/FilterFile RawFile
<<
/Columns ImageWidth 0 get
/Rows ImageLength 0 get
/K 0
/BlackIs1 true
/EndOfBlock false
>> /CCITTFaxDecode filter def
}{
Compression 0 get 3 eq{
%% New CCITT Group 3 (specify
encoding)
/FilterFile RawFile
<<
/Columns ImageWidth 0 get
/Rows ImageLength 0 get
ParamsDict /Group3Options known
{
Group3Options 0 get 1
and {
/K 1
}{
/K 0
}ifelse
Group3Options 0 get 2
and {
/Uncompressed true
}if
Group3Options 0 get 4
and{
/EncodedByteAlign
true
}if
}{
/K 0
}ifelse
/BlackIs1 true
/EndOfBlock false
>> /CCITTFaxDecode filter def
}{
Compression 0 get 4 eq{
%% CCITT Group 4
/FilterFile RawFile
<<
/Columns ImageWidth 0
get
/Rows ImageLength 0 get
/K -1
ParamsDict
/Group4Options known{
Group4Options 0
get 2 and {
/Uncompressed true
}if
}if
/BlackIs1 true
/EndOfBlock false
>> /CCITTFaxDecode filter def
}{
Compression 0 get 5 eq{
TiffDict /Predictor
known{
Predictor 0 get 2
eq{
/FilterFile
RawFile
<<
/Columns ImageWidth 0 get
/Predictor 2
/Colours SamplesPerPixel 0 get
/BitsPerComponent BitsPerPixel 0 get 255 and
>>
/LZWDecode filter def
}{
/FilterFile
RawFile /LZWDecode filter def
}ifelse
}{
/FilterFile
RawFile /LZWDecode filter def
}ifelse
}{
Compression 0 get 6 eq{
(Unsupported
compression scheme 'JPEG') ==
}{
Compression 0 get
32773 eq{
%% Packbits
/FilterFile
RawFile /RunLengthDecode filter def
}{
(Unrecognised compression scheme ) print Compression 0 get ==
false
}ifelse
}ifelse
}ifelse
}ifelse
}ifelse
}ifelse
}ifelse
}ifelse
end
end
}bind def
%%
%% TiffDataRead is the routine which extracts data from the TIFF file
and
%% passes it to the PostScript 'image' operator. DO not call this
directly...
%%
/TiffDataRead{
TiffDict begin
ParamsDict begin
/DataString ImageWidth 0 get string def
NextRow RowsPerStrip 0 get ge{
CurrentStrip 1 add MoveToStrip
}if
FilterFile DataString readstring pop
end
}bind def
end %%TiffDict
%%
%% TiffPrint reads and prints a TIFF file.
%% Syntax: x y xscale yscale filename TiffRead
%%
/TiffPrint{
gsave
TiffDict begin
/TIFFFileName exch def
/TIFFYScale exch def
/TIFFXScale exch def
/TIFFYPos exch def
/TIFFXPos exch def
/RawFile TIFFFileName (r) file def
ReadHeader{
TIFFdebug
{
(Tiff dictionary) == flush
ParamsDict
{exch 256 string cvs print ( ) print 256 string cvs
==}
forall
}
if
ParamsDict /BitsPerSample known not
ParamsDict /BitsPerSample [1] put
ParamsDict /SamplesPerPixel known not
ParamsDict /SamplesPerPixel [1] put
%% check required keys
ParamsDict /ImageWidth known
ParamsDict /ImageLength known and
ParamsDict /Compression known and
ParamsDict /RowsPerStrip known and
ParamsDict /StripOffsets known and
ParamsDict /StripByteCounts known and
ParamsDict /PhotometricInterpretation known and
{
/ImageDict 7 dict def
ParamsDict /PhotometricInterpretation get 0 get
dup 0 eq{
pop
/DeviceGray setcolorspace
ImageDict /BitsPerComponent 1 put
ParamsDict /BitsPerSample get 0 get 1 eq{
ImageDict /Decode [1 0] put}{
ImageDict /Decode [0 1] put
}ifelse
}{
dup 1 eq{
pop
/DeviceGray setcolorspace
ImageDict /BitsPerComponent 1 put
ParamsDict /BitsPerSample get 0 get 1
eq{
ImageDict /Decode [0 1] put
}{
ImageDict /Decode [1 0] put
}ifelse
}{
dup 2 eq{
%%FIXME, need to check BitsPerSample, theoretically they could be
different, and we can't support that
pop
/DeviceRGB setcolorspace
ImageDict /BitsPerComponent 8
put
ImageDict /Decode [0 1 0 1 0 1]
put
}{
dup 3 eq{
%%FIXME, need to convert ColorMap to /Indexed colour space
pop
/Indexed [/DeviceRGB ]
setcolorspace
ImageDict
/BitsPerComponent 8 put
ImageDict /Decode [0 1]
put
}{
dup 4 eq{
pop
(This code does
not handle transparency masks) ==
}{
(Unknown
PhotometricInterpretation type) print ==
}ifelse
}ifelse
}ifelse
}ifelse
}ifelse
ParamsDict begin
/NextRow 0 def
0 MoveToStrip
ImageDict begin
/ImageType 1 def
/Width ParamsDict /ImageWidth get 0 get def
/Height ParamsDict /ImageLength get 0 get def
%%FIXME, if the TIFF file isn't a top-down file,then we should take that
into account here
/ImageMatrix [TIFFXScale 0 0 TIFFYScale -1 mul
TIFFXPos TIFFYPos ParamsDict /ImageLength get 0 get add ] def
/DataSource /TiffDataRead load def
end
end
ImageDict dup == image
}{
(Missing required TIFF tag in header) ==
}ifelse
}{
(error reading Tiff header) ==
}ifelse
TiffDict /ParamsDict undef
end %% TiffDict
grestore
} bind def
Technical Details
Message-ID:
<MPG.20a39b8e20444daa9898f0@news.eclipse.co.uk>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!local01.nntp.dca.giganews.com!nntp.eclipse.net.uk!news.eclipse.net.uk.POSTED!not-for-mailReferences:
<1178155922.955360.233590@c35g2000hsg.googlegroups.com>Show Full Headers
Complete Article Headers
X-Google-Thread: f62f2,c4b31354dcc63574 X-Google-Thread: 10c61a,c4b31354dcc63574 X-Google-Attributes: gidf62f2,gid10c61a,public X-Google-Language: ENGLISH,ASCII-7-bit Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!local01.nntp.dca.giganews.com!nntp.eclipse.net.uk!news.eclipse.net.uk.POSTED!not-for-mail NNTP-Posting-Date: Thu, 03 May 2007 02:12:09 -0500 From: Ken Sharp <ken@spamcop.net> Newsgroups: comp.lang.postscript,adobe.postscript.programming Subject: Re: Display external TIFF file Date: Thu, 3 May 2007 08:12:02 +0100 Message-ID: <MPG.20a39b8e20444daa9898f0@news.eclipse.co.uk> References: <1178155922.955360.233590@c35g2000hsg.googlegroups.com> Organization: Global Graphics MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: 7bit X-Newsreader: MicroPlanet Gravity v2.60 Lines: 878 NNTP-Posting-Host: 81.5.138.33 X-Trace: sv3-goXvdv7FHxPt/Pax5VeIlfVHcfVygPazala+RBOyssadKxxnV8Fg8G7uY/yhxR3+Rag4qjX1qsXfRjn!lPtKa+u0yDm0hbQhn3eYRzNa5a/SXHqley4+bMYDDF2W6ntp8s5DnH70jzIOdbFFce+UPlfqGY2K!jZ7pCKUipfJ26rk= X-Complaints-To: abuse@eclipse.net.uk X-DMCA-Complaints-To: abuse@eclipse.net.uk X-Abuse-and-DMCA-Info: Please be sure to forward a copy of ALL headers X-Abuse-and-DMCA-Info: Otherwise we will be unable to process your complaint properly X-Postfilter: 1.3.34