#!/usr/local/sbin/php -q
<?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-2008 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

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


//! 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);
  }
} 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;
 $attrs = $ifd_entry->attributes();
 
 switch ($attrs["function"]) {
   case "BRAND":
      $ifd_entry->addChild ('value',exec("bootblocktool -x BRAND"));
     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= exec("bootblocktool -x MODEL").exec("bootblocktool -x REVISION");
    }
///      $ifd_entry->addChild ('value',exec("bootblocktool -x MODEL").exec("bootblocktool -x REVISION"));
     $ifd_entry->addChild ('value',$model);

     break;
   case "SOFTWARE":
      $ifd_entry->addChild ('value',exec("ls /usr/html/docs/")); // filter
     break;
   case "SERIAL":
      $s=exec("bootblocktool -x SERNO");
      $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];
     }
 }
}

?>