<?php
/* Read tags from tiff data.
*
* Written by: Chris Studholme
* Copyright: GPL (http://www.fsf.org/copyleft/gpl.html)
* $Id: read_tiff_data.php,v 1.1 2002/09/16 00:29:03 cstudhol Exp $
*/
// return unsigned 16-bit integer represented by 4-byte binary string
function bin2uint16($str,$big_endian=false) {
if ($big_endian)
return (ord($str{0})<<8)+ord($str{1});
return (ord($str{1})<<8)+ord($str{0});
}
// return unsigned 32-bit integer represented by 4-byte binary string
function bin2uint32($str,$big_endian=false) {
if ($big_endian)
return (ord($str{0})<<24)+(ord($str{1})<<16)+
(ord($str{2})<<8)+ord($str{3});
return (ord($str{3})<<24)+(ord($str{2})<<16)+
(ord($str{1})<<8)+ord($str{0});
}
// return unsigned 16-bit integer represented by 4-byte binary string
function bin2int16($str,$big_endian=false) {
if ($big_endian) {
$i = ord($str{0});
if ($i>127) $i-=256;
return ($i<<8)+ord($str{1});
}
$i = ord($str{1});
if ($i>127) $i-=256;
return ($i<<8)+ord($str{0});
}
// return unsigned 32-bit integer represented by 4-byte binary string
function bin2int32($str,$big_endian=false) {
if ($big_endian) {
$i = ord($str{0});
if ($i>127) $i-=256;
return ($i<<24)+(ord($str{1})<<16)+(ord($str{2})<<8)+ord($str{3});
}
$i = ord($str{3});
if ($i>127) $i-=256;
return ($i<<24)+(ord($str{2})<<16)+(ord($str{1})<<8)+ord($str{0});
}
function read_tiff_data_tag($fd,$str,$big_endian) {
$result = array();
$types = array(1=>"BYTE",
2=>"ASCII",
3=>"SHORT",
4=>"LONG",
5=>"RATIONAL",
6=>"SBYTE",
7=>"UNDEFINED",
8=>"SSHORT",
9=>"SLONG",
10=>"SRATIONAL",
11=>"FLOAT",
12=>"DOUBLE");
$tags = array(254=>"NewSubfileType",
255=>"SubfileType",
256=>"ImageWidth",
257=>"ImageLength",
258=>"BitsPerSample",
259=>"Compression",
262=>"PhotometricInterpretation",
263=>"Threshholding",
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=>"T4Options",
293=>"T6Options",
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=>"TileLength",
324=>"TileOffsets",
325=>"TileByteCounts",
330=>"SubIFD",
332=>"InkSet",
333=>"InkNames",
334=>"NumberOfInks",
336=>"DotRange",
337=>"TargetPrinter",
338=>"ExtraSamples",
339=>"SampleFormat",
340=>"SMinSampleValue",
341=>"SMaxSampleValue",
342=>"TransferRange",
512=>"JPEGProc",
513=>"JPEGInterchangeFormat",
514=>"JPEGInterchangeFormatLength",
515=>"JPEGRestartInterval",
517=>"JPEGLosslessPredictors",
518=>"JPEGPointTransforms",
519=>"JPEGQTables",
520=>"JPEGDCTables",
521=>"JPEGACTables",
529=>"YCbCrCoefficients",
530=>"YCbCrSubSampling",
531=>"YCbCrPositioning",
532=>"ReferenceBlackWhite",
33432=>"Copyright");
$result["tag_code"] = bin2uint16(substr($str,0,2),$big_endian);
if ($tags[$result["tag_code"]])
$result["tag"] = $tags[$result["tag_code"]];
$result["type_code"] = bin2uint16(substr($str,2,2),$big_endian);
if ($types[$result["type_code"]])
$result["type"] = $types[$result["type_code"]];
$result["count"] = bin2uint32(substr($str,4,4),$big_endian);
if ($result["count"]<=0)
return $result;
// figure out size of value
switch ($result["type_code"]) {
case 1: // BYTE
case 2: // ASCII
case 6: // SBYTE
case 7: // UNDEFINED (8 bit)
$size = 1;
break;
case 3: // SHORT
case 8: // SSHORT
$size = 2;
break;
case 4: // LONG
case 9: // SLONG
case 11: // FLOAT
$size = 4;
break;
case 5: // RATIONAL (2xLONG)
case 10: // SRATIONAL
case 12: // DOUBLE
$size = 8;
break;
default:
$result["value_offset"] = bin2uint32(substr($str,8,4),$big_endian);
return $result;
}
if ($size*$result["count"]<=4) {
$values = substr($str,8,4);
}
else {
$ofs = bin2uint32(substr($str,8,4),$big_endian);
if (fseek($fd,$ofs)!=0) {
$result["value_offset"] = $ofs;
return $result;
}
$values = fread($fd,$size*$result["count"]);
if (strlen($values)!=$size*$result["count"]) {
$result["value_offset"] = $ofs;
return $result;
}
}
$c = $result["count"];
switch ($result["type_code"]) {
case 2: // ASCII
case 7: // UNDEFINED (8 bit)
$size = $c;
$c = 1;
}
for ($i=0; $i<$c; ++$i) {
$str = substr($values,$size*$i,$size);
switch ($result["type_code"]) {
case 1: // BYTE
$value = ord($str{0});
break;
case 2: // ASCII
$end = strpos($str,"\0");
if ($end>0||$str{0}=="\0")
$value = substr($str,0,(int)$end);
else
$value = $str;
break;
case 3: // SHORT
$value = bin2uint16($str,$big_endian);
break;
case 4: // LONG
$value = bin2uint32($str,$big_endian);
if ($result["tag_code"]==330)
$value = read_tiff_data_ifd($fd,$value,$big_endian);
break;
case 5: // RATIONAL (2xLONG)
$numer = bin2uint32($str,$big_endian);
$denom = bin2uint32(substr($str,4,4),$big_endian);
$value = $numer/$denom;
break;
case 6: // SBYTE
$value = ord($str{0});
if ($value>127) $value-=256;
break;
case 7: // UNDEFINED (8 bit)
$value = $str;
break;
case 8: // SSHORT
$value = bin2int16($str,$big_endian);
break;
case 9: // SLONG
$value = bin2int32($str,$big_endian);
break;
case 10: // SRATIONAL
$numer = bin2int32($str,$big_endian);
$denom = bin2int32(substr($str,4,4),$big_endian);
$value = $numer/$denom;
break;
case 11: // FLOAT
$value = false; // not supported yet
break;
case 12: // DOUBLE
$value = false; // not supported yet
}
if ($c>1)
$result["values"][$i] = $value;
else
$result["value"] = $value;
}
return $result;
}
function read_tiff_data_ifd($fd,$ifd_ofs,$big_endian) {
if (fseek($fd,$ifd_ofs)!=0)
return array();
$str = fread($fd,2);
if (strlen($str)!=2)
return array();
$n = bin2uint16($str,$big_endian);
$result = array();
for ($i=0; $i<$n; ++$i) {
if (fseek($fd,$ifd_ofs+2+12*$i)!=0)
return $result;
$str = fread($fd,12);
if (strlen($str)!=12)
return $result;
$result[$i] = read_tiff_data_tag($fd,$str,$big_endian);
}
if (fseek($fd,$ifd_ofs+2+12*$n)!=0)
return $result;
$str = fread($fd,4);
if (strlen($str)==4) {
$ofs = bin2uint32($str,$big_endian);
if ($ofs>0) {
$result["ifd_ofs"] = $ofs;
$result["ifd"] = read_tiff_data_ifd($fd,$result["ifd_ofs"],$big_endian);
}
}
return $result;
}
function read_tiff_data_raw($filename) {
$fd = fopen($filename,"rb");
if (!$fd)
return false;
$result = array();
$bo = fread($fd,4);
switch ($bo) {
case "II*\0":
$result["byte_order"] = "little-endian";
$big_endian = false;
break;
case "MM\0*":
$result["byte_order"] = "big-endian";
$big_endian = true;
break;
default:
fclose($fd);
return false;
}
$str = fread($fd,4);
if (strlen($str)==4) {
$result["ifd_ofs"] = bin2uint32($str,$big_endian);
$result["ifd"] = read_tiff_data_ifd($fd,$result["ifd_ofs"],$big_endian);
}
fclose($fd);
return $result;
}
function read_tiff_data_arr($raw) {
$result = array();
for ($i=0; $i<count($raw); ++$i) {
if ($raw[$i]["tag"])
$tag = $raw[$i]["tag"];
else if ($raw[$i]["tag_code"])
$tag = $raw[$i]["tag_code"];
if (isset($raw[$i]["value"])) {
if ($raw[$i]["tag_code"]==330)
$result[$tag] = read_tiff_data_arr($raw[$i]["value"]);
else
$result[$tag] = $raw[$i]["value"];
}
else if (isset($raw[$i]["values"]))
$result[$tag] = $raw[$i]["values"];
}
return $result;
}
function read_tiff_data($filename) {
$raw = read_tiff_data_raw($filename);
if (!$raw)
return false;
return read_tiff_data_arr($raw["ifd"]);
}
?>