#!/usr/bin/env php
<?php
/*!
 *! FILE NAME  : exif.php
 *! DESCRIPTION: This program collects information about the camera (version, software, sensor)
 *! and combines it with the Exif template (default is /etc/Exif_template.xml) to prepare generation
 *! of Exif headers.
 *! It works in a command line with a single optional parameter - location of the template file and
 *! trough the CGI interface. In that case it accepts the following parameter
 *! init           - program Exif with default /etc/Exif_template.xml
 *! init=path      - program Exif with alternative file
 *! noGPS          - don't include GPS-related fields
 *! nocompass      - don't include compass-related fields
 *! template       - print currently loaded template data (hex dump)
 *! metadir        - print currently loaded meta directory that matches variable Exif fields with the template
 *! exif=0         - print current Exif page (updated in real time)
 *! exif=NNN       - print one of the Exif pages in the buffer (debug feature, current buffer pointer is not known here)
 *!
 *! Copyright (C) 2007-2016 Elphel, Inc
 *! -----------------------------------------------------------------------------**
 *!
 *!  This program is free software: you can redistribute it and/or modify
 *!  it under the terms of the GNU General Public License as published by
 *!  the Free Software Foundation, either version 3 of the License, or
 *!  (at your option) any later version.
 *!
 *!  This program is distributed in the hope that it will be useful,
 *!  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *!  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *!  GNU General Public License for more details.
 *!
 *!  You should have received a copy of the GNU General Public License
 *!  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *! -----------------------------------------------------------------------------**
 *!  $Log: exif.php,v $
 *!  Revision 1.2  2010/08/10 21:14:31  elphel
 *!  8.0.8.39 - added EEPROM support for multiplexor and sensor boards, so autocampars.php uses application-specific defaults. Exif Orientation tag support, camera Model reflects application and optional mode (i.e. camera number in Eyesis)
 *!
 *!  Revision 1.1.1.1  2008/11/27 20:04:01  elphel
 *!
 *!
 *!  Revision 1.2  2008/08/11 19:11:32  elphel
 *!  comments
 *!
 *!  Revision 1.1  2008/04/07 09:12:14  elphel
 *!  New Exif template generation
 *!
 *!  $Log: exif.php,v $
 *!  Revision 1.2  2010/08/10 21:14:31  elphel
 *!  8.0.8.39 - added EEPROM support for multiplexor and sensor boards, so autocampars.php uses application-specific defaults. Exif Orientation tag support, camera Model reflects application and optional mode (i.e. camera number in Eyesis)
 *!
 *!  Revision 1.1.1.1  2008/11/27 20:04:01  elphel
 *!
 *!
 *!  Revision 1.2  2008/08/11 19:11:32  elphel
 *!  comments
 *!
 *!  Revision 1.1  2008/04/07 09:12:14  elphel
 *!  New Exif template generation
 *!
 *!  Revision 1.1.1.1  2007/10/03 19:05:53  elphel
 *!  This is a fresh tree based on elphel353-2.10
 *!
 *!  Revision 1.6  2007/10/03 19:05:53  elphel
 *!  cosmetic
 *!
 *!  Revision 1.5  2007/10/03 18:37:53  elphel
 *!  Made nice html output to see the result data
 *!
 *!  Revision 1.4  2007/10/03 06:33:31  elphel
 *!  fixed wrong copyright year
 *!
 *!  Revision 1.3  2007/10/02 19:44:03  elphel
 *!  minor bug fixes, added "manufacturer note" field for raw frame metadata (36 bytes - /include/asm-cris/c313.h frame_params_t)
 *!
 *!  Revision 1.2  2007/09/25 23:34:02  elphel
 *!  Fixed time strings to the right length
 *!
 *!  Revision 1.1  2007/09/24 07:27:57  elphel
 *!  Started php script that prepares Exif header information for serving images by imgsrv
 *!
 */
//TODO: Decide with the start sequence, more data is availble after the sensor is initialized.
// Or leave it to imgsrv (it still needs sensor to start first), just put placeholders
// use http://www.exiv2.org/tags.html  for reference of the Exif fields if you need to add modify
// the data below

$chn = 0;

$ExifDeviceTemplateFilename="/dev/exif_template";
$ExifDeviceMetadirFilename= "/dev/exif_metadir";
$ExifDeviceExifFilename=    "/dev/exif_exif";
$ExifDeviceMetaFilename=    "/dev/exif_meta";

$DeviceSNFilename = "/sys/devices/soc0/elphel393-init/serial";
$DeviceRevisionFilename = "/sys/devices/soc0/elphel393-init/revision";
$DeviceBrand = "Elphel";
$DeviceModel = "Elphel393";

//! if called from the command line - accepts just one parameter - configuration file,
//! through CGI - accepts more parameters

$ExifXMLName="/etc/Exif_template.xml";
$init=false;
if ($_SERVER['REQUEST_METHOD']=="GET") {
	if ($_GET["init"]!==NULL) {
		if ($_GET["init"]) $ExifXMLName=$_GET["init"];
		$init=true; // in any case - filename specified or not
		$noGPS=     ($_GET["noGPS"]!==NULL);
		$nocompass= ($_GET["nocompass"]!==NULL);
	}
	if (isset($_GET['chn'])) $chn = $_GET['chn'];
	$ExifDeviceExifFilename .= $chn;
        $ExifDeviceMetaFilename .= $chn;
} else {
	foreach ($_SERVER['argv'] as $param) if (substr($param,0,4)=="init") {
		$param=substr($param,5);
		if (strlen($param)>0) $ExifXMLName=$param;
		$init=true; //
		break;
	}
	if ($init) {
		$noGPS=     in_array  ('noGPS'    , $_SERVER['argv']);
		$nocompass= in_array  ('nocompass', $_SERVER['argv']);
	} else {
		echo <<<USAGE

Usage: {$_SERVER['argv'][0]} [init[=filename.xml] [noGPS] [nocompass]]


USAGE;
		exit (0);
	}
}

define("EXIF_BYTE",      1);
define("EXIF_ASCII",     2);
define("EXIF_SHORT",     3);
define("EXIF_LONG",      4);
define("EXIF_RATIONAL",  5);
define("EXIF_SBYTE",     6);
define("EXIF_UNDEFINED", 7);
define("EXIF_SSHORT",    8); //obsolete?
define("EXIF_SLONG",     9);
define("EXIF_SRATIONAL",10);

define("EXIF_LSEEK_ENABLE", 2); // rebuild buffer

/// ===== init/init= CGI parameters or command line mode =======
if ($init) { // configure Exif data
	$exif_head= array (
		0xff, 0xe1, // APP1 marker
		// offset=2 - start of Exif header
		0x00, 0x00, // - length of the Exif header (including this length bytes) - we'll fill it later, we do not know it yet
		0x45,0x78,0x69,0x66,0x00,0x00); // Exif header
	$Exif_length_offset= 2; // put total length here (big endian, 2 bytes)

	$exif_data= array (// start of TIFF Header, data offsets will match indexes in this array
		0x4d,0x4d, // (MM) Big endian, MSB goes first in multi-byte data
		0x00,0x2a,  // Tag Mark
		0x00,0x00,0x00,0x08); //offset to first IDF (from the beginning of the TIFF header, so 8 is minimum)
	$xml_exif = simplexml_load_file($ExifXMLName);
	if ($xml_exif->GPSInfo) {
		/// remove all tags named "Compass..."
		if ($nocompass) {
			$tounset=array();
			foreach ($xml_exif->GPSInfo->children() as $entry) if (strpos  ($entry->getName()  , "Compass" )!==false) $tounset[]=$entry->getName();
			foreach ($tounset as $entry) unset ($xml_exif->GPSInfo->{$entry});
		}
		if ($noGPS) {
			unset($xml_exif->GPSInfo);
			unset($xml_exif->Image->GPSTag);
		}
	}

	$IFD_offset=      count($exif_data);
	$SUB_IFD_offset=  12*count($xml_exif->Image->children())+2+4+$IFD_offset;
	$GPSInfo_offset=  12*count($xml_exif->Photo->children())+2+4+$SUB_IFD_offset;
	$data_offset=     $GPSInfo_offset+(($xml_exif->GPSInfo)?(12*count($xml_exif->GPSInfo->children())+2+4):0); //$GPSInfo is optional
	if ($_SERVER['REQUEST_METHOD']) {
		echo "<pre>";
		printf ("IFD_offset=0x%x\n",$IFD_offset);
		printf ("SUB_IFD_offset=0x%x\n",$SUB_IFD_offset);
		printf ("GPSInfo_offset=0x%x\n",$GPSInfo_offset);
		printf ("data_offset=0x%x\n",$data_offset);
	}

	/// Now modify variable fields substituting values
	foreach ($xml_exif->Image->children()   as $entry) substitute_value($entry);
	foreach ($xml_exif->Photo->children()   as $entry) substitute_value($entry);
	if ($xml_exif->GPSInfo) {
		foreach ($xml_exif->GPSInfo->children() as $entry) substitute_value($entry);
	}
	$ifd_pointer=$IFD_offset;
	$data_pointer=$data_offset;
	start_ifd(count($xml_exif->Image->children()));
	foreach ($xml_exif->Image->children() as $entry) process_ifd_entry($entry,0);
	finish_ifd();

	$ifd_pointer=$SUB_IFD_offset;
	start_ifd(count($xml_exif->Photo->children()));
	foreach ($xml_exif->Photo->children() as $entry) process_ifd_entry($entry,1);
	finish_ifd();

	if ($xml_exif->GPSInfo) {
		$ifd_pointer=$GPSInfo_offset;
		start_ifd(count($xml_exif->GPSInfo->children()));
		foreach ($xml_exif->GPSInfo->children() as $entry) process_ifd_entry($entry,2);
		finish_ifd();
	}
	$exif_len=count($exif_head)+count($exif_data)-$Exif_length_offset;
	$exif_head[$Exif_length_offset]=  ($exif_len >> 8) & 0xff;
	$exif_head[$Exif_length_offset+1]= $exif_len       & 0xff;

	$Exif_str="";
	for ($i=0; $i<count($exif_head);$i++) $Exif_str.= chr ($exif_head[$i]);
	for ($i=0; $i<count($exif_data);$i++) $Exif_str.= chr ($exif_data[$i]);

	$Exif_file  = fopen($ExifDeviceTemplateFilename, 'w');
	fwrite ($Exif_file,$Exif_str); /// will disable and invalidate Exif data
	fclose($Exif_file);

	///Exif template is done, now we need a directory to map frame meta data to fields in the template.
	$dir_sequence=array();
	$dir_entries=array();

	foreach ($xml_exif->Image->children()   as $entry) addDirEntry($entry);
	foreach ($xml_exif->Photo->children()   as $entry) addDirEntry($entry);
	if ($xml_exif->GPSInfo) {
		foreach ($xml_exif->GPSInfo->children() as $entry) addDirEntry($entry);
	}
	array_multisort($dir_sequence,$dir_entries);

	$frame_meta_size=0;
	for ($i=0;$i<count($dir_entries);$i++) {
		$dir_entries[$i]["src"]=$frame_meta_size;
		$frame_meta_size+=$dir_entries[$i]["len"];
	}

	$Exif_str="";
	foreach ($dir_entries as $entry) $Exif_str.=pack("V*",$entry["ltag"],$entry["len"],$entry["src"],$entry["dst"]);
	$Exif_meta_file  = fopen($ExifDeviceMetadirFilename, 'w');
	fwrite ($Exif_meta_file,$Exif_str); /// will disable and invalidate Exif data
	fclose($Exif_meta_file);

	///Rebuild buffer and enable Exif generation/output:

	$Exif_file  = fopen($ExifDeviceTemplateFilename, 'w');
	fseek ($Exif_file, EXIF_LSEEK_ENABLE, SEEK_END) ;
	fclose($Exif_file);

	if ($_SERVER['REQUEST_METHOD']) {
		echo "</pre>";
	}
	if ($_SERVER['REQUEST_METHOD']) {
		echo "<hr/>\n";
		test_print_header();
		echo "<hr/>\n";
		test_print_directory();
	}
} //if ($init) // configure Exif data
/// ===== processing optional parameters =======
/// ===== read template =======
if ($_GET["description"]!==NULL) {

	/// Read metadir to find the length of the description field

	$Exif_file  = fopen($ExifDeviceMetadirFilename, 'r');
	fseek ($Exif_file, 0, SEEK_END) ;
	fseek ($Exif_file, 0, SEEK_SET) ;
	$metadir=fread ($Exif_file, 4096);
	fclose($Exif_file);
	$dir_entries=array();
	for ($i=0; $i<strlen($metadir);$i+=16) {
		$dir_entries[]=unpack("V*",substr($metadir,$i,16));
	}
	foreach ($dir_entries as $entry) 
		if ($entry[1]==0x010e) {
			$descr=$_GET["description"];
			$Exif_file  = fopen($ExifDeviceMetaFilename, 'w+');
			fseek ($Exif_file, $entry[3], SEEK_SET) ;
			$descr_was=fread ($Exif_file, $entry[2]);
			$zero=strpos($descr_was,chr(0));
			if ($zero!==false) $descr_was=substr($descr_was,0, $zero);
			if ($descr) {
				$descr= str_pad($descr, $entry[2], chr(0));
				fseek ($Exif_file, $entry[3], SEEK_SET) ;
				fwrite($Exif_file, $descr,$entry[2]);
			}
			fclose($Exif_file);
			var_dump($descr_was); echo "<br/>\n";
			break;
		}
}
/// ===== read template =======
if ($_GET["template"]!==NULL) {
	$Exif_file  = fopen($ExifDeviceTemplateFilename, 'r');
	fseek ($Exif_file, 0, SEEK_END) ;
	echo "<hr/>\n";
	echo "ftell()=".ftell($Exif_file).", ";
	fseek ($Exif_file, 0, SEEK_SET) ;
	$template=fread ($Exif_file, 4096);
	fclose($Exif_file);
	echo "read ".strlen($template)." bytes<br/>\n";
	hexdump($template);
}
/// ===== read meta directory =======
if ($_GET["metadir"]!==NULL) {
	$Exif_file  = fopen($ExifDeviceMetadirFilename, 'r');
	fseek ($Exif_file, 0, SEEK_END) ;
	echo "<hr/>\n";
	echo "ftell()=".ftell($Exif_file).", ";
	fseek ($Exif_file, 0, SEEK_SET) ;
	$metadir=fread ($Exif_file, 4096);
	fclose($Exif_file);
	echo "read ".strlen($metadir)." bytes<br/>\n";
	$dir_entries=array();
	for ($i=0; $i<strlen($metadir);$i+=16) {
		$dir_entries[]=unpack("V*",substr($metadir,$i,16));
	}
	print_directory($dir_entries);
}
/// ===== read one of the Exif pages (0 - current, 1..512 - buffer) =======
if ($_GET["exif"]!==NULL) {
	$frame=$_GET["exif"]+0;
	echo "<hr/>\n";
	printf ("Reading frame %d, ",$frame);
	$Exif_file  = fopen($ExifDeviceExifFilename, 'r');
	fseek ($Exif_file, 1, SEEK_END) ;
	$exif_size=ftell($Exif_file);
	if ($frame) fseek ($Exif_file, $frame, SEEK_END) ;
	else        fseek ($Exif_file, 0, SEEK_SET) ;
	echo "ftell()=".ftell($Exif_file).", ";
	$exif_data=fread ($Exif_file, $exif_size);
	fclose($Exif_file);
	echo "read ".strlen($exif_data)." bytes<br/>\n";
	hexdump($exif_data);
}
exit(0);
/// ======================================= Functions ======================================
function hexdump($data) {
	global $exif_head, $exif_data;
	$l=strlen($data);
	printf ("<h2>Exif size=%d bytes</h2>\n",$l);
	printf ("<table border=\"0\">\n");
	for ($i=0; $i<$l;$i=$i+16) {
		printf("<tr><td>%03x</td><td>|</td>\n",$i);
		for ($j=$i; $j<$i+16;$j++) {
			printf("<td>");
			if ($j<$l) {
				$d=ord($data[$j]);
				printf(" %02x",$d);
			} else printf ("   ");
			printf("</td>");
		}
		printf("<td>|</td>");
		for ($j=$i; $j< ($i+16);$j++) {
			printf("<td>");
			if ($j<$l) {
				$d=ord($data[$j]);
				if ($d<32 or $d>126) printf(".");
				else printf ("%c",$d);
			} else printf (" ");
			printf("</td>");

		}
		printf("</tr>\n");
	}
	printf ("</table>");
}

function print_directory($dir_entries) {
	$meta_size=0;
	foreach ($dir_entries as $entry)  if (($entry[3]+$entry[2])>$meta_size) $meta_size=$entry[3]+$entry[2];
	printf ("<h2>Frame meta data size=%d bytes</h2>\n",$meta_size);
	printf ("<table border=\"1\">\n");
	printf ("<tr><td>ltag</td><td>meta offset</td><td>Exif offset</td><td>length</td></tr>\n");
	foreach ($dir_entries as $entry) {
		printf ("<tr><td>0x%x</td><td>0x%x</td><td>0x%x</td><td>0x%x</td></tr>\n",$entry[1],$entry[3],$entry[4],$entry[2]);
	}
	printf ("</table>");
}

function test_print_header() {
	global $exif_head, $exif_data;
	$lh=count($exif_head);
	$ld=count($exif_data);
	printf ("<h2>Exif size=%d bytes (head=%d, data=%d)</h2>\n",$lh+$ld,$lh,$ld);
	printf ("<table border=\"0\">\n");
	for ($i=0; $i<$lh+$ld;$i=$i+16) {
		printf("<tr><td>%03x</td><td>|</td>\n",$i);
		for ($j=$i; $j<$i+16;$j++) {
			printf("<td>");
			if ($j<($lh+$ld)) {
				$d=($j<$lh)?$exif_head[$j]:$exif_data[$j-$lh];
				printf(" %02x",$d);
			} else printf ("   ");
			printf("</td>");
		}
		printf("<td>|</td>");
		for ($j=$i; $j< ($i+16);$j++) {
			printf("<td>");
			if ($j<($lh+$ld)) {
				$d=($j<$lh)?$exif_head[$j]:$exif_data[$j-$lh];
				if ($d<32 or $d>126) printf(".");
				else printf ("%c",$d);
			} else printf (" ");
			printf("</td>");

		}
		printf("</tr>\n");
	}
	printf ("</table>");
}

function test_print_directory() {
	global $dir_entries,$frame_meta_size;
	printf ("<h2>Frame meta data size=%d bytes</h2>\n",$frame_meta_size);
	printf ("<table border=\"1\">\n");
	printf ("<tr><td>ltag</td><td>meta offset</td><td>Exif offset</td><td>length</td></tr>\n");
	foreach ($dir_entries as $entry)
		printf ("<tr><td>0x%x</td><td>0x%x</td><td>0x%x</td><td>0x%x</td></tr>\n",$entry["ltag"],$entry["src"],$entry["dst"],$entry["len"]);
	printf ("</table>");
}




function start_ifd($count) {
	global $exif_data, $ifd_pointer;
	// printf("start_ifd: ifd_pointer=0x%04x \n", $ifd_pointer);
	$exif_data[$ifd_pointer++]= ($count >> 8) & 0xff;
	$exif_data[$ifd_pointer++]= $count & 0xff; // may apply & 0xff in the end to all elements
}
function finish_ifd() { // we do not have additional IFDs
	global $exif_data, $ifd_pointer;
	// printf("finish_ifd: ifd_pointer=0x%04x \n", $ifd_pointer);
	$exif_data[$ifd_pointer++]=0;
	$exif_data[$ifd_pointer++]=0;
	$exif_data[$ifd_pointer++]=0;
	$exif_data[$ifd_pointer++]=0;
}


//pass2 - building map from frame meta to Exif template
function addDirEntry($ifd_entry) {
	global $dir_sequence,$dir_entries,$exif_head;
	$lh=count($exif_head);

	$attrs = $ifd_entry->attributes();
	//  var_dump($attrs);
	//  if  (array_key_exists  ( "seq"  , $attrs  )) {
	if  ($attrs["seq"]) {
		//     echo $attrs["seq"].;
		$dir_sequence[]=((string) $attrs["seq"])+0;
		$len= (integer) $ifd_entry->value_length;
		$offs=$lh+(integer) $ifd_entry->value_offest;
		//     if (array_key_exists  ( "dlen"  , $attrs  )) 
		if  ($attrs["dlen"])  $len=min($len,((string) $attrs["dlen"])+0);
		$dir_entries[]=array("ltag"=>((integer)$ifd_entry->ltag),"dst"=>$offs,"len"=>$len);
	}
}

function substitute_value($ifd_entry) {
	global $SUB_IFD_offset,$GPSInfo_offset;
	global $DeviceSNFilename, $DeviceRevisionFilename, $DeviceBrand, $DeviceModel;
	$attrs = $ifd_entry->attributes();

	switch ($attrs["function"]) {
	case "BRAND":
		$ifd_entry->addChild('value', $DeviceBrand);
		break;
	case "MODEL":
		if (file_exists ('/var/state/APPLICATION')) {
			$model= file_get_contents('/var/state/APPLICATION');
			if (file_exists ('/var/state/APPLICATION_MODE')) {
				$model.=' CHN'.file_get_contents('/var/state/APPLICATION_MODE');
			}
		} else {
			$model = $DeviceModel;
		}
		$ifd_entry->addChild ('value',$model);
		break;
	case "SOFTWARE":
		if (file_exists("/usr/html/docs/")) {
			$ifd_entry->addChild ('value',exec("ls /usr/html/docs/")); // filter
		}
		break;
	case "SERIAL":
		$s = "";
		if (file_exists($DeviceSNFilename)) {
			$s = exec('cat '.$DeviceSNFilename);
		}
		$ifd_entry->addChild ('value',substr($s,0,2).":".substr($s,2,2).":".substr($s,4,2).":".substr($s,6,2).":".substr($s,8,2).":".substr($s,10,2));
		break;
	case "EXIFTAG":
		$ifd_entry->addChild ('value',$SUB_IFD_offset);
		break;
	case "GPSTAG":
		$ifd_entry->addChild ('value',$GPSInfo_offset);
		break;
	}
}


function process_ifd_entry($ifd_entry, $group) {
	global $exif_data, $ifd_pointer, $data_pointer,$SUB_IFD_offset,$GPSInfo_offset;
	$attrs = $ifd_entry->attributes();
	$ifd_tag=   ((string) $attrs["tag"])+0;
	$ifd_format=constant("EXIF_".$attrs["format"]);
	$ifd_count= $attrs["count"];
	// echo "\nifd_tag=$ifd_tag, entry=";print_r($ifd_entry);
	// echo "\nifd_count=$ifd_count";
	// echo "\nifd_bytes:";var_dump($ifd_bytes);
	if (!$ifd_count) {
		if($ifd_format==EXIF_ASCII) $ifd_count=strlen($ifd_entry->value)+1;
		else $ifd_count=1 ; /// may change?
	}
	//echo "\nifd_count=$ifd_count";
	$exif_data[$ifd_pointer++]= ($ifd_tag    >>  8)  & 0xff;
	$exif_data[$ifd_pointer++]=  $ifd_tag            & 0xff;
	$exif_data[$ifd_pointer++]= ($ifd_format >>  8 ) & 0xff;
	$exif_data[$ifd_pointer++]=  $ifd_format         & 0xff;
	$exif_data[$ifd_pointer++]= ($ifd_count >> 24)  & 0xff;
	$exif_data[$ifd_pointer++]= ($ifd_count >> 16)  & 0xff;
	$exif_data[$ifd_pointer++]= ($ifd_count >>  8)   & 0xff;
	$exif_data[$ifd_pointer++]=  $ifd_count          & 0xff;

	$ifd_bytes=0;
	switch ($ifd_format) {
	case EXIF_SHORT:
	case EXIF_SSHORT:    $ifd_bytes=2; break;
	case EXIF_LONG:
	case EXIF_SLONG:     $ifd_bytes=4; break;
	case EXIF_RATIONAL:
	case EXIF_SRATIONAL: $ifd_bytes=8; break;
	default: $ifd_bytes=1; //1,2,6,7
	}
	$ifd_bytes=$ifd_bytes*$ifd_count;
	// now prepare ifd_data - array of bytes
	switch ($ifd_format) {
	case EXIF_BYTE:
	case EXIF_SBYTE:
		$ifd_data= array ();
		foreach ($ifd_entry->value as $a) $ifd_data[]= $a & 0xff;
		break;
	case EXIF_ASCII:
		$ifd_data= str_split($ifd_entry->value);
		foreach($ifd_data as &$d) $d=ord($d);
		break;
	case EXIF_SHORT:
	case EXIF_SSHORT:
		$ifd_data= array ();
		foreach ($ifd_entry->value as $a) $ifd_data=array_merge($ifd_data,array(($a >> 8) & 0xff, $a & 0xff));
		break;
	case EXIF_LONG:
	case EXIF_SLONG:
		$ifd_data= array ();
		foreach ($ifd_entry->value as $a) $ifd_data=array_merge($ifd_data,array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff));
		break;
	case EXIF_RATIONAL:
	case EXIF_SRATIONAL:
		$nom= array ();
		foreach ($ifd_entry->nominator as $a)   $nom[]=  array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff);
		$denom= array ();
		foreach ($ifd_entry->denominator as $a) $denom[]=array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff);
		$ifd_data= array ();
/*
var_dump($nom);
echo "\n";
var_dump($denom);
echo "\n";
//exit(0);
 */
		for ($i=0;$i<count($nom);$i++) {
			//            echo "i=$i\n";
			//          echo "\nnom=";var_dump($nom[$i]);
			//        echo "\ndenom=";var_dump($denom[$i]);
			$ifd_data=array_merge($ifd_data,$nom[$i],$denom[$i]);
		}
		break; // rational, (un)signed

	case EXIF_UNDEFINED: // undefined
		$ifd_data= array_fill(0,$ifd_bytes,0); // will just fill with "0"-s
		break; 
	}
	// echo "\nifd_tag=$ifd_tag, entry=";print_r($i=$ifd_entry->value);
	// echo "\nifd_data:";var_dump($ifd_data);
	// echo "\nifd_bytes:";var_dump($ifd_bytes);

	$ifd_data=array_pad($ifd_data,$ifd_bytes,0); 

	$ifd_entry->addChild ('value_length',count($ifd_data));
	// if  (array_key_exists  ( "ltag"  , $attrs  )) $ltag= ((string) $attrs["ltag"])+0;
	if  ($attr["ltag"]) $ltag= ((string) $attrs["ltag"])+0;
	else  $ltag= $ifd_tag+($group<<16) ;
	$ifd_entry->addChild ("ltag",$ltag );
	if (count($ifd_data) <=4) {
		$ifd_entry->addChild ('value_offest',$ifd_pointer);
		$ifd_data= array_pad($ifd_data,-4,0); // add leading zeroes if <4 bytes
		for ($i=0;$i<4;$i++)   $exif_data[$ifd_pointer++]=$ifd_data[$i];
	} else { //pointer, not data
		$ifd_entry->addChild ('value_offest',$data_pointer);
		$exif_data[$ifd_pointer++]= ($data_pointer >> 24)  & 0xff;
		$exif_data[$ifd_pointer++]= ($data_pointer >> 16)  & 0xff;
		$exif_data[$ifd_pointer++]= ($data_pointer >>  8)  & 0xff;
		$exif_data[$ifd_pointer++]=  $data_pointer         & 0xff;
		for ($i=0;$i<count($ifd_data);$i++)  {
			$exif_data[$data_pointer++]=$ifd_data[$i];
		}
	}
}

?>