1package PDF::Builder::Resource::XObject::Image::TIFF::File; 2 3use strict; 4use warnings; 5 6our $VERSION = '3.023'; # VERSION 7our $LAST_UPDATE = '3.023'; # manually update whenever code is changed 8 9use IO::File; 10 11=head1 NAME 12 13PDF::Builder::Resource::XObject::Image::TIFF::File - support routines for TIFF image library 14 15=cut 16 17sub new { 18 my ($class, $file) = @_; 19 20 my $self = {}; 21 bless ($self, $class); 22 if (ref($file)) { 23 $self->{'fh'} = $file; 24 seek($self->{'fh'}, 0, 0); 25 } else { 26 $self->{'fh'} = IO::File->new(); 27 open($self->{'fh'}, '<', $file) or die "$!: $file"; 28 } 29 binmode($self->{'fh'}, ':raw'); 30 my $fh = $self->{'fh'}; 31 32 $self->{'offset'} = 0; 33 $fh->seek($self->{'offset'}, 0); 34 35 # checking byte order of data 36 $fh->read($self->{'byteOrder'}, 2); 37 $self->{'byte'} = 'C'; 38 $self->{'short'} = (($self->{'byteOrder'} eq 'MM')? 'n': 'v'); 39 $self->{'long'} = (($self->{'byteOrder'} eq 'MM')? 'N': 'V'); 40 $self->{'rational'} = (($self->{'byteOrder'} eq 'MM')? 'NN': 'VV'); 41 42 # get/check version id 43 $fh->read($self->{'version'}, 2); 44 $self->{'version'} = unpack($self->{'short'}, $self->{'version'}); 45 die "Wrong TIFF Id '" . $self->{'version'} . "' (should be 42)." if $self->{'version'} != 42; 46 47 # get the offset to the first tag directory. 48 $fh->read($self->{'ifdOffset'}, 4); 49 $self->{'ifdOffset'} = unpack($self->{'long'}, $self->{'ifdOffset'}); 50 51 $self->readTags(); 52 53 return $self; 54} 55 56sub readTag { 57 my $self = shift; 58 59 my $fh = $self->{'fh'}; 60 my $buf; 61 $fh->read($buf, 12); 62 my $tag = unpack($self->{'short'}, substr($buf, 0, 2)); 63 my $type = unpack($self->{'short'}, substr($buf, 2, 2)); 64 my $count = unpack($self->{'long'}, substr($buf, 4, 4)); 65 my $len = 0; 66 67 $len = ($type == 1? $count : # byte 68 $type == 2? $count : # char2 69 $type == 3? $count*2: # int16 70 $type == 4? $count*4: # int32 71 $type == 5? $count*8: # rational: 2 * int32 72 $count); 73 74 my $off = substr($buf, 8, 4); 75 76 if ($len > 4) { 77 $off = unpack($self->{'long'}, $off); 78 } else { 79 $off = ($type == 1? unpack($self->{'byte'}, $off): 80 $type == 2? unpack($self->{'long'}, $off): 81 $type == 3? unpack($self->{'short'}, $off): 82 $type == 4? unpack($self->{'long'}, $off): 83 unpack($self->{'short'}, $off) ); 84 } 85 86 return ($tag, $type, $count, $len, $off); 87} 88 89sub close { ## no critic 90 my $self = shift; 91 92 return $self->{'fh'}->close(); 93} 94 95sub readTags { 96 my $self = shift; 97 98 my $fh = $self->{'fh'}; 99 $self->{'fillOrder'} = 1; 100 $self->{'ifd'} = $self->{'ifdOffset'}; 101 102 while ($self->{'ifd'} > 0) { 103 $fh->seek($self->{'ifd'}, 0); 104 $fh->read($self->{'ifdNum'}, 2); 105 $self->{'ifdNum'} = unpack($self->{'short'}, $self->{'ifdNum'}); 106 $self->{'bitsPerSample'} = 1; 107 foreach (1 .. $self->{'ifdNum'}) { 108 my ($valTag, $valType, $valCount, $valLen, $valOffset) = $self->readTag(); 109 # print "tag=$valTag type=$valType count=$valCount len=$valLen off=$valOffset\n"; 110 if ($valTag == 0) { 111 # no-op 112 } elsif ($valTag == 256) { 113 $self->{'imageWidth'} = $valOffset; 114 } elsif ($valTag == 257) { 115 $self->{'imageHeight'} = $valOffset; 116 } elsif ($valTag == 258) { 117 # bits per sample 118 if ($valCount > 1) { 119 my $here = $fh->tell(); 120 my $val; 121 $fh->seek($valOffset, 0); 122 $fh->read($val, 2); 123 $self->{'bitsPerSample'} = unpack($self->{'short'}, $val); 124 $fh->seek($here, 0); 125 } else { 126 $self->{'bitsPerSample'} = $valOffset; 127 } 128 } elsif ($valTag == 259) { 129 # compression 130 $self->{'filter'} = $valOffset; 131 if ($valOffset == 1) { 132 delete $self->{'filter'}; 133 } elsif ($valOffset == 3 || $valOffset == 4) { 134 $self->{'filter'} = 'CCITTFaxDecode'; 135 $self->{'ccitt'} = $valOffset; 136 } elsif ($valOffset == 5) { 137 $self->{'filter'} = 'LZWDecode'; 138 } elsif ($valOffset == 6 || $valOffset == 7) { 139 $self->{'filter'} = 'DCTDecode'; 140 } elsif ($valOffset == 8 || $valOffset == 0x80b2) { 141 $self->{'filter'} = 'FlateDecode'; 142 } elsif ($valOffset == 32773) { 143 $self->{'filter'} = 'RunLengthDecode'; 144 } else { 145 die "unknown/unsupported TIFF compression method with id '" . $self->{'filter'} . "'."; 146 } 147 } elsif ($valTag == 262) { 148 # photometric interpretation 149 $self->{'colorSpace'} = $valOffset; 150 if ($valOffset == 0) { 151 $self->{'colorSpace'} = 'DeviceGray'; 152 $self->{'whiteIsZero'} = 1; 153 } elsif ($valOffset == 1) { 154 $self->{'colorSpace'} = 'DeviceGray'; 155 $self->{'blackIsZero'} = 1; 156 } elsif ($valOffset == 2) { 157 $self->{'colorSpace'} = 'DeviceRGB'; 158 } elsif ($valOffset == 3) { 159 $self->{'colorSpace'} = 'Indexed'; 160 # } elsif ($valOffset == 4) { 161 # $self->{'colorSpace'} = 'TransMask'; 162 } elsif ($valOffset == 5) { 163 $self->{'colorSpace'} = 'DeviceCMYK'; 164 } elsif ($valOffset == 6) { 165 $self->{'colorSpace'} = 'DeviceRGB'; 166 } elsif ($valOffset == 8) { 167 $self->{'colorSpace'} = 'Lab'; 168 } else { 169 die "unknown/unsupported TIFF photometric interpretation with id '" . $self->{'colorSpace'} . "'."; 170 } 171 } elsif ($valTag == 266) { 172 $self->{'fillOrder'} = $valOffset; 173 } elsif ($valTag == 270) { 174 # ImageDescription 175 my $here = $fh->tell(); 176 $fh->seek($valOffset, 0); 177 $fh->read($self->{'imageDescription'}, $valLen); 178 $fh->seek($here, 0); 179 } elsif ($valTag == 282) { 180 # xRes 181 my $here = $fh->tell(); 182 $fh->seek($valOffset, 0); 183 $fh->read($self->{'xRes'}, $valLen); 184 $fh->seek($here, 0); 185 $self->{'xRes'} = [unpack($self->{'rational'}, $self->{'xRes'})]; 186 $self->{'xRes'} = ($self->{'xRes'}->[0] / $self->{'xRes'}->[1]); 187 } elsif ($valTag == 283) { 188 # yRes 189 my $here = $fh->tell(); 190 $fh->seek($valOffset, 0); 191 $fh->read($self->{'yRes'}, $valLen); 192 $fh->seek($here, 0); 193 $self->{'yRes'} = [unpack($self->{'rational'}, $self->{'yRes'})]; 194 $self->{'yRes'} = ($self->{'yRes'}->[0] / $self->{'yRes'}->[1]); 195 } elsif ($valTag == 296) { 196 # resolution Unit 197 $self->{'resUnit'} = $valOffset; 198 } elsif ($valTag == 273) { 199 # image data offset/strip offsets 200 if ($valCount == 1) { 201 $self->{'imageOffset'} = $valOffset; 202 } else { 203 my $here = $fh->tell(); 204 my $val; 205 $fh->seek($valOffset, 0); 206 $fh->read($val, $valLen); 207 $fh->seek($here, 0); 208 $self->{'imageOffset'} = [unpack($self->{'long'} . '*', $val)]; 209 } 210 } elsif ($valTag == 277) { 211 $self->{'samplesPerPixel'} = $valOffset; 212 } elsif ($valTag == 278) { 213 $self->{'RowsPerStrip'} = $valOffset; 214 } elsif ($valTag == 279) { 215 # image data length/strip lengths 216 if ($valCount == 1) { 217 $self->{'imageLength'} = $valOffset; 218 } else { 219 my $here = $fh->tell(); 220 my $val; 221 $fh->seek($valOffset, 0); 222 $fh->read($val, $valLen); 223 $fh->seek($here, 0); 224 $self->{'imageLength'} = [unpack($self->{'long'} . '*', $val)]; 225 } 226 } elsif ($valTag == 292) { 227 $self->{'g3Options'} = $valOffset; 228 } elsif ($valTag == 293) { 229 $self->{'g4Options'} = $valOffset; 230 } elsif ($valTag == 320) { 231 # color map 232 $self->{'colorMapOffset'} = $valOffset; 233 $self->{'colorMapSamples'} = $valCount; 234 $self->{'colorMapLength'} = $valCount*2; # shorts! 235 } elsif ($valTag == 317) { 236 $self->{'Predictor'} = $valOffset; 237 } elsif ($valTag == 0x800d) { 238 # imageID 239 my $here = $fh->tell(); 240 $fh->seek($valOffset, 0); 241 $fh->read($self->{'imageId'}, $valLen); 242 $fh->seek($here, 0); 243 # } else { 244 # print "tag=$valTag, type=$valType, len=$valLen\n"; 245 } 246 } # end foreach loop 247 248 $fh->read($self->{'ifd'}, 4); 249 $self->{'ifd'} = unpack($self->{'long'}, $self->{'ifd'}); 250 } # end while loop 251 252 return $self; 253} 254 2551; 256