Location: PHPKode > projects > DAlbum - personal PHP photo album > include/exif.php
<?
/*
    This file is a part of DAlbum.  Copyright (c) 2003 Alexei Shamov, DeltaX 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 2 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, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    This code is based on

    //--------------------------------------------------------------------------
    // Program to pull the information out of various types of EXIF digital
    // camera files and show it in a reasonably consistent way
    //
    // This module parses the very complicated exif structures.
    //
    // Matthias Wandel,  Dec 1999 - Dec 2002
    //--------------------------------------------------------------------------
    */

//--------------------------------------------------------------------------
// Describes format descriptor


define("NUM_FORMATS",   12);
define("FMT_BYTE",       1);
define("FMT_STRING",     2);
define("FMT_USHORT",     3);
define("FMT_ULONG",      4);
define("FMT_URATIONAL",  5);
define("FMT_SBYTE",      6);
define("FMT_UNDEFINED",  7);
define("FMT_SSHORT",     8);
define("FMT_SLONG",      9);
define("FMT_SRATIONAL", 10);
define("FMT_SINGLE",    11);
define("FMT_DOUBLE",    12);

//--------------------------------------------------------------------------
// Describes tag values

define("TAG_EXIF_OFFSET",       0x8769);
define("TAG_INTEROP_OFFSET",    0xa005);
define("TAG_MAKE",              0x010F);
define("TAG_MODEL",             0x0110);
define("TAG_ORIENTATION",       0x0112);
define("TAG_EXPOSURETIME",      0x829A);
define("TAG_FNUMBER",           0x829D);
define("TAG_SHUTTERSPEED",      0x9201);
define("TAG_APERTURE",          0x9202);
define("TAG_MAXAPERTURE",       0x9205);
define("TAG_FOCALLENGTH",       0x920A);
define("TAG_DATETIME_ORIGINAL", 0x9003);
define("TAG_USERCOMMENT",       0x9286);
define("TAG_SUBJECT_DISTANCE",  0x9206);
define("TAG_FLASH",             0x9209);
define("TAG_FOCALPLANEXRES",    0xa20E);
define("TAG_FOCALPLANEUNITS",   0xa210);
define("TAG_EXIF_IMAGEWIDTH",   0xA002);
define("TAG_EXIF_IMAGELENGTH",  0xA003);

// the following is added 05-jan-2001 vcs
define("TAG_EXPOSURE_BIAS",     0x9204);
define("TAG_WHITEBALANCE",      0x9208);
define("TAG_METERING_MODE",     0x9207);
define("TAG_EXPOSURE_PROGRAM",  0x8822);
define("TAG_ISO_EQUIVALENT",    0x8827);
define("TAG_THUMBNAIL_OFFSET",  0x0201);
define("TAG_THUMBNAIL_LENGTH",  0x0202);

$_g_exifTagTable = array(
  0x000B => "ACDComment",
  0x00FE => "NewSubFile", /* better name it 'ImageType' ? */
  0x00FF => "SubFile",
  0x0100 => "ImageWidth",
  0x0101 => "ImageLength",
  0x0102 => "BitsPerSample",
  0x0103 => "Compression",
  0x0106 => "PhotometricInterpretation",
  0x010A => "FillOrder",
  0x010D => "DocumentName",
  0x010E => "ImageDescription",
  0x010F => "Make",
  0x0110 => "Model",
  0x0111 => "StripOffsets",
  0x0112 => "Orientation",
  0x0115 => "SamplesPerPixel",
  0x0116 => "RowsPerStrip",
  0x0117 => "StripByteCounts",
  0x0118 => "MinSampleValue",
  0x0119 => "MaxSampleValue",
  0x011A => "XResolution",
  0x011B => "YResolution",
  0x011C => "PlanarConfiguration",
  0x011D => "PageName",
  0x011E => "XPosition",
  0x011F => "YPosition",
  0x0120 => "FreeOffsets",
  0x0121 => "FreeByteCounts",
  0x0122 => "GrayResponseUnit",
  0x0123 => "GrayResponseCurve",
  0x0124 => "T4Options",
  0x0125 => "T6Options",
  0x0128 => "ResolutionUnit",
  0x0129 => "PageNumber",
  0x012D => "TransferFunction",
  0x0131 => "Software",
  0x0132 => "DateTime",
  0x013B => "Artist",
  0x013C => "HostComputer",
  0x013D => "Predictor",
  0x013E => "WhitePoint",
  0x013F => "PrimaryChromaticities",
  0x0140 => "ColorMap",
  0x0141 => "HalfToneHints",
  0x0142 => "TileWidth",
  0x0143 => "TileLength",
  0x0144 => "TileOffsets",
  0x0145 => "TileByteCounts",
  0x014A => "SubIFD",
  0x014C => "InkSet",
  0x014D => "InkNames",
  0x014E => "NumberOfInks",
  0x0150 => "DotRange",
  0x0151 => "TargetPrinter",
  0x0152 => "ExtraSample",
  0x0153 => "SampleFormat",
  0x0154 => "SMinSampleValue",
  0x0155 => "SMaxSampleValue",
  0x0156 => "TransferRange",
  0x0157 => "ClipPath",
  0x0158 => "XClipPathUnits",
  0x0159 => "YClipPathUnits",
  0x015A => "Indexed",
  0x015B => "JPEGTables",
  0x015F => "OPIProxy",
  0x0200 => "JPEGProc",
  0x0201 => "JPEGInterchangeFormat",
  0x0202 => "JPEGInterchangeFormatLength",
  0x0203 => "JPEGRestartInterval",
  0x0205 => "JPEGLosslessPredictors",
  0x0206 => "JPEGPointTransforms",
  0x0207 => "JPEGQTables",
  0x0208 => "JPEGDCTables",
  0x0209 => "JPEGACTables",
  0x0211 => "YCbCrCoefficients",
  0x0212 => "YCbCrSubSampling",
  0x0213 => "YCbCrPositioning",
  0x0214 => "ReferenceBlackWhite",
  0x02BC => "ExtensibleMetadataPlatform", /* XAP: Extensible Authoring Publishing, obsoleted by XMP: Extensible Metadata Platform */
  0x0301 => "Gamma",
  0x0302 => "ICCProfileDescriptor",
  0x0303 => "SRGBRenderingIntent",
  0x0320 => "ImageTitle",
  0x5001 => "ResolutionXUnit",
  0x5002 => "ResolutionYUnit",
  0x5003 => "ResolutionXLengthUnit",
  0x5004 => "ResolutionYLengthUnit",
  0x5005 => "PrintFlags",
  0x5006 => "PrintFlagsVersion",
  0x5007 => "PrintFlagsCrop",
  0x5008 => "PrintFlagsBleedWidth",
  0x5009 => "PrintFlagsBleedWidthScale",
  0x500A => "HalftoneLPI",
  0x500B => "HalftoneLPIUnit",
  0x500C => "HalftoneDegree",
  0x500D => "HalftoneShape",
  0x500E => "HalftoneMisc",
  0x500F => "HalftoneScreen",
  0x5010 => "JPEGQuality",
  0x5011 => "GridSize",
  0x5012 => "ThumbnailFormat",
  0x5013 => "ThumbnailWidth",
  0x5014 => "ThumbnailHeight",
  0x5015 => "ThumbnailColorDepth",
  0x5016 => "ThumbnailPlanes",
  0x5017 => "ThumbnailRawBytes",
  0x5018 => "ThumbnailSize",
  0x5019 => "ThumbnailCompressedSize",
  0x501A => "ColorTransferFunction",
  0x501B => "ThumbnailData",
  0x5020 => "ThumbnailImageWidth",
  0x5021 => "ThumbnailImageHeight",
  0x5022 => "ThumbnailBitsPerSample",
  0x5023 => "ThumbnailCompression",
  0x5024 => "ThumbnailPhotometricInterp",
  0x5025 => "ThumbnailImageDescription",
  0x5026 => "ThumbnailEquipMake",
  0x5027 => "ThumbnailEquipModel",
  0x5028 => "ThumbnailStripOffsets",
  0x5029 => "ThumbnailOrientation",
  0x502A => "ThumbnailSamplesPerPixel",
  0x502B => "ThumbnailRowsPerStrip",
  0x502C => "ThumbnailStripBytesCount",
  0x502D => "ThumbnailResolutionX",
  0x502E => "ThumbnailResolutionY",
  0x502F => "ThumbnailPlanarConfig",
  0x5030 => "ThumbnailResolutionUnit",
  0x5031 => "ThumbnailTransferFunction",
  0x5032 => "ThumbnailSoftwareUsed",
  0x5033 => "ThumbnailDateTime",
  0x5034 => "ThumbnailArtist",
  0x5035 => "ThumbnailWhitePoint",
  0x5036 => "ThumbnailPrimaryChromaticities",
  0x5037 => "ThumbnailYCbCrCoefficients",
  0x5038 => "ThumbnailYCbCrSubsampling",
  0x5039 => "ThumbnailYCbCrPositioning",
  0x503A => "ThumbnailRefBlackWhite",
  0x503B => "ThumbnailCopyRight",
  0x5090 => "LuminanceTable",
  0x5091 => "ChrominanceTable",
  0x5100 => "FrameDelay",
  0x5101 => "LoopCount",
  0x5110 => "PixelUnit",
  0x5111 => "PixelPerUnitX",
  0x5112 => "PixelPerUnitY",
  0x5113 => "PaletteHistogram",
  0x1000 => "RelatedImageFileFormat",
  0x800D => "ImageID",
  0x80E3 => "Matteing",   /* obsoleted by ExtraSamples */
  0x80E4 => "DataType",   /* obsoleted by SampleFormat */
  0x80E5 => "ImageDepth",
  0x80E6 => "TileDepth",
  0x828D => "CFARepeatPatternDim",
  0x828E => "CFAPattern",
  0x828F => "BatteryLevel",
  0x8298 => "Copyright",
  0x829A => "ExposureTime",
  0x829D => "FNumber",
  0x83BB => "IPTC/AA",
  0x84E3 => "IT8RasterPadding",
  0x84E5 => "IT8ColorTable",
  0x8649 => "ImageResourceInformation", /* PhotoShop */
  0x8769 => "Exif_IFD_Pointer",
  0x8773 => "ICC_Profile",
  0x8822 => "ExposureProgram",
  0x8824 => "SpectralSensity",
  0x8828 => "OECF",
  0x8825 => "GPS_IFD_Pointer",
  0x8827 => "ISOSpeedRatings",
  0x8828 => "OECF",
  0x9000 => "ExifVersion",
  0x9003 => "DateTimeOriginal",
  0x9004 => "DateTimeDigitized",
  0x9101 => "ComponentsConfiguration",
  0x9102 => "CompressedBitsPerPixel",
  0x9201 => "ShutterSpeedValue",
  0x9202 => "ApertureValue",
  0x9203 => "BrightnessValue",
  0x9204 => "ExposureBiasValue",
  0x9205 => "MaxApertureValue",
  0x9206 => "SubjectDistance",
  0x9207 => "MeteringMode",
  0x9208 => "LightSource",
  0x9209 => "Flash",
  0x920A => "FocalLength",
  0x920B => "FlashEnergy",                 /* 0xA20B  in JPEG   */
  0x920C => "SpatialFrequencyResponse",    /* 0xA20C    -  -    */
  0x920D => "Noise",
  0x920E => "FocalPlaneXResolution",       /* 0xA20E    -  -    */
  0x920F => "FocalPlaneYResolution",       /* 0xA20F    -  -    */
  0x9210 => "FocalPlaneResolutionUnit",    /* 0xA210    -  -    */
  0x9211 => "ImageNumber",
  0x9212 => "SecurityClassification",
  0x9213 => "ImageHistory",
  0x9214 => "SubjectLocation",             /* 0xA214    -  -    */
  0x9215 => "ExposureIndex",               /* 0xA215    -  -    */
  0x9216 => "TIFF/PStandardID",
  0x9217 => "SensingMethod",               /* 0xA217    -  -    */
  0x923F => "StoNits",
  0x927C => "MakerNote",
  0x9286 => "UserComment",
  0x9290 => "SubSecTime",
  0x9291 => "SubSecTimeOriginal",
  0x9292 => "SubSecTimeDigitized",
  0x935C => "ImageSourceData",             /* "Adobe Photoshop Document Data Block": 8BIM... */
  0x9c9b => "Title",                      /* Win XP specific, Unicode  */
  0x9c9c => "Comments",                   /* Win XP specific, Unicode  */
  0x9c9d => "Author",                     /* Win XP specific, Unicode  */
  0x9c9e => "Keywords",                   /* Win XP specific, Unicode  */
  0x9c9f => "Subject",                    /* Win XP specific, Unicode, not to be confused with SubjectDistance and SubjectLocation */
  0xA000 => "FlashPixVersion",
  0xA001 => "ColorSpace",
  0xA002 => "ExifImageWidth",
  0xA003 => "ExifImageLength",
  0xA004 => "RelatedSoundFile",
  0xA005 => "InteroperabilityOffset",
  0xA20B => "FlashEnergy",                 /* 0x920B in TIFF/EP */
  0xA20C => "SpatialFrequencyResponse",    /* 0x920C    -  -    */
  0xA20D => "Noise",
  0xA20E => "FocalPlaneXResolution",        /* 0x920E    -  -    */
  0xA20F => "FocalPlaneYResolution",       /* 0x920F    -  -    */
  0xA210 => "FocalPlaneResolutionUnit",    /* 0x9210    -  -    */
  0xA211 => "ImageNumber",
  0xA212 => "SecurityClassification",
  0xA213 => "ImageHistory",
  0xA214 => "SubjectLocation",             /* 0x9214    -  -    */
  0xA215 => "ExposureIndex",               /* 0x9215    -  -    */
  0xA216 => "TIFF/PStandardID",
  0xA217 => "SensingMethod",               /* 0x9217    -  -    */
  0xA300 => "FileSource",
  0xA301 => "SceneType",
  0xA302 => "CFAPattern",
  0xA401 => "CustomRendered",
  0xA402 => "ExposureMode",
  0xA403 => "WhiteBalance",
  0xA404 => "DigitalZoomRatio",
  0xA405 => "FocalLengthIn35mmFilm",
  0xA406 => "SceneCaptureType",
  0xA407 => "GainControl",
  0xA408 => "Contrast",
  0xA409 => "Saturation",
  0xA40A => "Sharpness",
  0xA40B => "DeviceSettingDescription",
  0xA40C => "SubjectDistanceRange",
  0xA420 => "ImageUniqueID"
);


//--------------------------------------------------------------------------
// Parse the marker stream until SOS or EOI is seen;
//--------------------------------------------------------------------------
function _exif_php_read_jpeg_sections(&$II, &$infile)
{
    $a = fgetc($infile);

    if (ord($a) != 0xff || ord(fgetc($infile)) != 0xD8)
        return false;

    while(!feof($infile))
    {
        $marker = 0;

        for ($a=0;$a<7;$a++)
        {
            $marker = fgetc($infile);
            if ($marker===false)
                return false;

            $marker=ord($marker);

            if ($marker != 0xff)
                break;

            if ($a >= 6)
                return false;
        }

        if ($marker == 0xff)
            // 0xff is legal padding, but if we get that many, something's wrong.
            return false;
        if ($marker== 0xda || $marker==0xd9)
            return true;

        // Read the length of the section.
        $lh = ord(fgetc($infile));
        $ll = ord(fgetc($infile));

        $itemlen = ($lh << 8) | $ll;

        if ($itemlen < 2)
        {
            // Invalid marker
            return false;
        }

        if (0xE1==$marker ||
            (($marker>=0xC0 && $marker<=0xCF) && $marker!=0xC4 && $marker!=0xCC))
        {
            // Store first two pre-read bytes.
            fseek($infile,-2,SEEK_CUR);
            $Data= fread($infile, $itemlen); // Read the whole section.
            if (strlen($Data)!=$itemlen)
                return false;

            if (0xE1==$marker)
            {
                // Seen files from some 'U-lead' software with Vivitar scanner
                // that uses marker 31 for non exif stuff.  Thus make sure
                // it says 'Exif' in the section before treating it as exif.
                if (substr($Data,2,4)=="Exif")
                    _exif_php_process_exif(&$II,&$Data);
            }
            else
            {
                $d=unpack("nlen/Cprecision/nheight/nwidth/Cnumcomp",$Data);
                if (count($d)==5)
                {
                    $II['COMPUTED']['Height']=$d['height'];
                    $II['COMPUTED']['Width']=$d['width'];
                    $II['COMPUTED']['html']="width=\"{$d['width']}\" height=\"{$d['height']}\"";
                    $II['COMPUTED']['IsColor']=(($d['numcomp']==3)?1:0);
                }
            }
        }
        else
            if (fseek($infile,$itemlen-2,SEEK_CUR)==-1)
                return false;
    }
    return true;
}

function _exif_php_get16u(&$str,$off,$Motorola)
{
    $a=unpack($Motorola?"na":"va",substr($str,$off,2));
    return $a['a'];
}
function _exif_php_get32u(&$str,$off,$Motorola)
{
    $a=unpack($Motorola?"Na":"Va",substr($str,$off,4));
    return $a['a'];
}

//--------------------------------------------------------------------------
// Process one of the nested EXIF directories.
//--------------------------------------------------------------------------
function _exif_php_process_exif_dir(&$II, &$Data, $nDirOffsetBase, $NestingLevel, $Motorola, $path)
{
    global $_g_exifTagTable;
    if ($NestingLevel > 4)
        return;

    static $BytesPerFormat = array(0,1,1,2,4,8,1,1,2,4,8,4,8);

    $NumDirEntries = _exif_php_get16u($Data,$nDirOffsetBase,$Motorola);

    for ($de=0;$de<$NumDirEntries;$de++)
    {
        $DirEntry = $nDirOffsetBase +2+12*$de;

        if (strlen($Data)<$DirEntry+8)
            return;

        $Tag = _exif_php_get16u($Data,          $DirEntry     , $Motorola);
        $Format = _exif_php_get16u($Data,   $DirEntry + 2 , $Motorola);
        $Components = _exif_php_get32u($Data, $DirEntry + 4 , $Motorola);

        if ($Format-1 >= NUM_FORMATS)
        {
            // (-1) catches illegal zero case as unsigned underflows to positive large.
            continue;
        }

        $ByteCount = $Components * $BytesPerFormat[$Format];

        if ($ByteCount > 4)
        {
            $OffsetVal = _exif_php_get32u($Data, $DirEntry+8 , $Motorola);
            $ValuePtr = $OffsetVal;
        }
        else
        {
            // 4 bytes or less and value is in the dir entry itself
            $ValuePtr = $DirEntry+8;
        }

        $sTag="";
        if (!isset($_g_exifTagTable[$Tag]))
            $sTag=sprintf("UndefinedTag:0x%04x" ,$Tag);
        else
            $sTag=$_g_exifTagTable[$Tag];

        // Convert type
        unset($v);
        switch ($Format)
        {
            case FMT_SBYTE:
            case FMT_BYTE:
                $v=ord(substr($Data,$ValuePtr,1));
                break;

            case FMT_USHORT:
            case FMT_SSHORT:
                $v=_exif_php_get16u($Data,$ValuePtr,$Motorola);
                break;

            case FMT_URATIONAL:
            case FMT_SRATIONAL:
                $Num = _exif_php_get32u($Data,$ValuePtr,$Motorola);
                $Den = _exif_php_get32u($Data,$ValuePtr+4,$Motorola);
                if ($Den == 0)
                    $v="";
                else
                    $v="$Num/$Den";
                break;

            case FMT_SLONG:
            case FMT_ULONG:
                $v=(int)_exif_php_get32u($Data,$ValuePtr,$Motorola);
                break;

            case FMT_STRING:
                // remove extra 00
                $v=substr($Data, $ValuePtr, $ByteCount);
                while (!ord(substr($v,-1)) && strlen($v))
                    $v=substr($v,0,-1);
                break;
            case FMT_UNDEFINED:
                $v=substr($Data, $ValuePtr, $ByteCount);
                break;
        }

        if (isset($v))
        {
            if (empty($path))
            {
                if (!isset($II[$sTag]))
                    $II[$sTag]=$v;
            }
            else
            {
                if (!isset($II[$path][$sTag]))
                    $II[$path][$sTag]=$v;
            }
        }

        if ($Tag==TAG_EXIF_OFFSET || $Tag==TAG_INTEROP_OFFSET)
        {
            $SubdirStart = _exif_php_get32u($Data,$ValuePtr,$Motorola);
            if ($SubdirStart >= 0)
            {
                $p=($Tag==TAG_INTEROP_OFFSET)?"INTEROP":"EXIF";
                _exif_php_process_exif_dir(&$II, &$Data, $SubdirStart, $NestingLevel+1, $Motorola, $p);
            }
        }
    }

    // In addition to linking to subdirectories via exif tags,
    // there's also a potential link to another directory at the end of each
    // directory.  this has got to be the result of a comitee!
    if ($nDirOffsetBase+2+12*$NumDirEntries + 4 <= strlen($Data))
    {
        $Offset = _exif_php_get32u($Data, $nDirOffsetBase+2+12*$NumDirEntries,$Motorola);
        if ($Offset)
        {
            $SubdirStart = $Offset;
            _exif_php_process_exif_dir(&$II, &$Data, $SubdirStart, $NestingLevel+1,$Motorola,"THUMBNAIL");
        }
    }
}

//--------------------------------------------------------------------------
// Process a EXIF marker
// Describes all the drivel that most digital cameras include...
//--------------------------------------------------------------------------
function _exif_php_process_exif(&$II, &$Data)
{
    if (substr($Data,2,4)!="Exif")
        return false;

    $order=substr($Data,8,2);
    switch ($order)
    {
        case "II":
            $Motorola = 0;
            break;
        case "MM":
            $Motorola = 1;
            break;
        default:
            return false;
    }
    $II['COMPUTED']['ByteOrderMotorola']=$Motorola;

    // Check the next value for correctness.
    if (_exif_php_get16u($Data,10,$Motorola)!=0x2a)
        return false;

    $FirstOffset = _exif_php_get32u($Data,12,$Motorola);

    if ($FirstOffset < 8 || $FirstOffset > 16)
    {
        // I used to ensure this was set to 8 (website I used indicated its 8)
        // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002)
        return false;
    }

    // First directory starts 16 bytes in.  All offset are relative to 8 bytes in.
    _exif_php_process_exif_dir(&$II,substr($Data,$FirstOffset), 16-$FirstOffset, 0, $Motorola, "IFD0");
    return true;
}

//--------------------------------------------------------------------------
// Read image data.
//--------------------------------------------------------------------------
function &exif_php_read_data($FileName,$bArray=false)
{
    $infile = fopen($FileName, "rb"); // Unix ignores 'b', windows needs it.

    if ($infile == null)
        return false;

    $II=array();

    // Scan the JPEG headers.
    $ret = _exif_php_read_jpeg_sections(&$II,$infile);
    fclose($infile);
    if (!$ret)
        return false;

    // Add [FILE] section
    $II['FILE']['FileName'] = basename(strval($FileName));
    $II['FILE']['FileDateTime'] = @filemtime($FileName);
    $II['FILE']['FileSize'] = @filesize($FileName);
    $II['FILE']['FileType'] = 2;            // We don't process anything but JPEGs
    $II['FILE']['MimeType'] = "image/jpeg";

    // Calculate ApertureFNumber
    if (!isset($II['COMPUTED']['ApertureFNumber']))
    {
        unset($v);

        $simple=false;
        if (isset($II['EXIF']['FNumber']))
        {
            $simple=true;
            $v=$II['EXIF']['FNumber'];
        }
        else if (isset($II['EXIF']['ApertureValue']))
            $v=$II['EXIF']['ApertureValue'];
        else if (isset($II['EXIF']['MaxApertureValue']))
            $v=$II['EXIF']['MaxApertureValue'];

        if (!empty($v))
        {
            $x=split('/',$v);
            if (count($x)>=2 && $x[1]>0)
            {
                $b=(double)$x[0]/$x[1];
                if (!$simple)
                    $b=exp($b*log(2)*0.5);
                $II['COMPUTED']['ApertureFNumber']=sprintf("f/%.1f",$b);
            }
            else
                $II['COMPUTED']['ApertureFNumber']=$v;
        }

    }

    if ($bArray)
        return $II;

    //print_r($II);
    reset($II);
    $IR=array();
    while (list($key, $val) = each($II))
    {
        if ($key=='COMPUTED' || $key=='THUMBNAIL')
            $IR[$key]=$val;
        else
            $IR=array_merge($IR,$val);
    }
    return $IR;
}
?>
Return current item: DAlbum - personal PHP photo album