🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.postscript
Article #6696

Re: Display external TIFF file

#6696
From: Ken Sharp
Date: Thu, 03 May 2007 07:12
877 lines
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-mail
References: <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