2147483647) $top = $top - 4294967296; // this makes the number signed instead of unsigned if ($bottom != 0) $data=$top/$bottom; else if ($top == 0) $data = 0; else $data = $top.'/'.$bottom; if (($tag == '011a' || $tag == '011b') && $bottom == 1) { // XResolution YResolution $data = $top.' dots per ResolutionUnit'; } else if ($tag == '829a') { // Exposure Time if (($bottom % $top) == 0) { $data = '1/'.round($bottom/$top, 0).' sec'; } else { if ($bottom == 1) { $data = $top.' sec'; } else { $data = $top.'/'.$bottom.' sec'; } } } else if ($tag == '829d') { // FNumber $data = 'f/'.$data; } else if ($tag == '9204') { // ExposureBiasValue $data = round($data, 2) . ' EV'; } else if ($tag == '9205' || $tag == '9202') { // ApertureValue and MaxApertureValue // ApertureValue is given in the APEX Mode. Many thanks to Matthieu Froment for this code // The formula is : Aperture = 2*log2(FNumber) <=> FNumber = e((Aperture.ln(2))/2) $data = exp(($data*log(2))/2); $data = round($data, 1);// Focal is given with a precision of 1 digit. $data='f/'.$data; } else if ($tag == '920a') { // FocalLength $data = $data.' mm'; } else if ($tag == '9201') { // ShutterSpeedValue // The ShutterSpeedValue is given in the APEX mode. Many thanks to Matthieu Froment for this code // The formula is : Shutter = - log2(exposureTime) (Appendix C of EXIF spec.) // Where shutter is in APEX, log2(exposure) = ln(exposure)/ln(2) // So final formula is : exposure = exp(-ln(2).shutter) // The formula can be developed : exposure = 1/(exp(ln(2).shutter)) $data = exp($data * log(2)); if ($data > 1) $data = floor($data); // Drop the decimal. if ($data > 0) { $data = 1/$data; // Final calculation. We now have a floating number. Transform it in a pretty number $n=0; $d=0; ConvertToFraction($data, $n, $d); if ($n >= 1 && $d == 1) $data = $n.' sec'; // To avoid exposure times style 3/1 sec. else $data = $n.'/'.$d.' sec'; } else { $data = 'Bulb'; } } } else if ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' || $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') { $data = bin2hex($data); if ($intel == 1) $data = intel2Moto($data); if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) $data = substr($data,0,4); $data = hexdec($data); if ($type == 'SSHORT' && $data > 32767) $data = $data - 65536; // this makes the number signed instead of unsigned if ($type == 'SLONG' && $data > 2147483647) $data = $data - 4294967296; // this makes the number signed instead of unsigned if ($tag == '0112') { // Orientation // Example of how all of these tag formatters should be... switch ($data) { case 1 : $data = '1: Normal (0 deg)'; break; case 2 : $data = '2: Mirrored'; break; case 3 : $data = '3: Upsidedown'; break; case 4 : $data = '4: Upsidedown Mirrored'; break; case 5 : $data = '5: 90 deg CW Mirrored'; break; case 6 : $data = '6: 90 deg CCW'; break; case 7 : $data = '7: 90 deg CCW Mirrored'; break; case 8 : $data = '8: 90 deg CW'; break; default : $data = 'Unknown: '.$data; } } else if ($tag == '0128' || $tag == 'a210' || $tag == '0128') { // ResolutionUnit and FocalPlaneResolutionUnit and ThumbnailResolutionUnit if ($data == 1) $data = 'No Unit'; else if ($data == 2) $data = 'Inch'; else if ($data == 3) $data = 'Centimeter'; } else if ($tag == '0213') { // YCbCrPositioning if ($data == 1) $data = 'Center of Pixel Array'; else if ($data == 2) $data = 'Datum Point'; } else if ($tag == '8822') { // ExposureProgram if ($data == 1) $data = 'Manual'; else if ($data == 2) $data = 'Program'; else if ($data == 3) $data = 'Aperture Priority'; else if ($data == 4) $data = 'Shutter Priority'; else if ($data == 5) $data = 'Program Creative'; else if ($data == 6) $data = 'Program Action'; else if ($data == 7) $data = 'Portrait'; else if ($data == 8) $data = 'Landscape'; else if ($data == 9) $data = 'Blub'; else $data = 'Unknown: '.$data; } else if ($tag == '9207') { // MeteringMode if ($data == 0) $data = 'Unknown'; else if ($data == 1) $data = 'Average'; else if ($data == 2) $data = 'Center Weighted Average'; else if ($data == 3) $data = 'Spot'; else if ($data == 4) $data = 'Multi-Spot'; else if ($data == 5) $data = 'Multi-Segment'; else if ($data == 6) $data = 'Partial'; else if ($data == 255) $data = 'Other'; else $data = 'Unknown: '.$data; } else if ($tag == '9208') { // LightSource if ($data == 0) $data = 'Unknown or Auto'; else if ($data == 1) $data = 'Daylight'; else if ($data == 2) $data = 'Flourescent'; else if ($data == 3) $data = 'Tungsten'; else if ($data == 4) $data = 'Flash'; else if ($data == 9) $data = 'Fine Weather'; else if ($data == 10) $data = 'Cloudy'; else if ($data == 11) $data = 'Shade'; else if ($data == 17) $data = 'Standard Light A'; else if ($data == 18) $data = 'Standard Light B'; else if ($data == 19) $data = 'Standard Light C'; else if ($data == 20) $data = 'D55'; else if ($data == 21) $data = 'D65'; else if ($data == 22) $data = 'D75'; else if ($data == 23) $data = 'D50'; else if ($data == 24) $data = 'ISO Studio Tungsten'; else if ($data == 255) $data = 'Other'; else $data = 'Unknown: '.$data; } else if ($tag == '9209') { // Flash if ($data == 0) $data = 'No Flash'; else if ($data == 1) $data = 'Flash'; else if ($data == 5) $data = 'Flash, strobe return light not detected'; else if ($data == 7) $data = 'Flash, strob return light detected'; else if ( $data == 8) $data ='On, Did not fire'; else if ($data == 9) $data = 'Compulsory Flash'; else if ($data == 13) $data = 'Compulsory Flash, Return light not detected'; else if ($data == 15) $data = 'Compulsory Flash, Return light detected'; else if ($data == 16) $data = 'Off, Did not fire'; else if ( $data == 20 ) $data = 'Off, Did not fire, Return not detected'; else if ($data == 24) $data = 'Auto, Did not fire'; else if ($data == 25) $data = 'Flash, Auto-Mode'; else if ($data == 29) $data = 'Flash, Auto-Mode, Return light not detected'; else if ($data == 31) $data = 'Flash, Auto-Mode, Return light detected'; else if ($data == 32) $data = 'No flash function'; else if ( $data == 48 ) $data = 'Off'; else if ($data == 65) $data = 'Red Eye'; else if ($data == 69) $data = 'Red Eye, Return light not detected'; else if ($data == 71) $data = 'Red Eye, Return light detected'; else if ($data == 73) $data = 'Red Eye, Compulsory Flash'; else if ($data == 77) $data = 'Red Eye, Compulsory Flash, Return light not detected'; else if ($data == 79) $data = 'Red Eye, Compulsory Flash, Return light detected'; else if ( $data == 80 ) $data = 'Off, Red-eye reduction'; else if ( $data == 88 ) $data = 'Auto, Did not fire, Red-eye reduction'; else if ($data == 89) $data = 'Red Eye, Auto-Mode'; else if ($data == 93) $data = 'Red Eye, Auto-Mode, Return light not detected'; else if ($data == 95) $data = 'Red Eye, Auto-Mode, Return light detected'; else $data = 'Unknown: '.$data; } else if ($tag == 'a001') { // ColorSpace if ($data == 1) $data = 'sRGB'; else $data = 'Uncalibrated'; } else if ($tag == 'a002' || $tag == 'a003') { // ExifImageWidth/Height $data = $data. ' pixels'; } else if ($tag == '0103') { // Compression if ($data == 1) $data = 'No Compression'; else if ($data == 6) $data = 'Jpeg Compression'; else $data = 'Unknown: '.$data; } else if ($tag == 'a217') { // SensingMethod if ($data == 1) $data = 'Not defined'; else if ($data == 2) $data = 'One Chip Color Area Sensor'; else if ($data == 3) $data = 'Two Chip Color Area Sensor'; else if ($data == 4) $data = 'Three Chip Color Area Sensor'; else if ($data == 5) $data = 'Color Sequential Area Sensor'; else if ($data == 7) $data = 'Trilinear Sensor'; else if ($data == 8) $data = 'Color Sequential Linear Sensor'; else $data = 'Unknown: '.$data; } else if ($tag == '0106') { // PhotometricInterpretation if ($data == 1) $data = 'Monochrome'; else if ($data == 2) $data = 'RGB'; else if ($data == 6) $data = 'YCbCr'; else $data = 'Unknown: '.$data; } else if ( $tag == 'a407' ) { // GainControl if ( $data == 0 ) $data = "None"; if ( $data == 1 ) $data = "Low gain up"; if ( $data == 2 ) $data = "High gain up"; if ( $data == 3 ) $data = "Low gain down"; if ( $data == 4 ) $data = "High gain down"; } else if ( $tag == 'a408' || $tag == 'a409') { // Contrast & Saturation if ( $data == 0 ) $data = "Normal"; if ( $data == 1 ) $data = "Low"; if ( $data == 2 ) $data = "High"; } else if ( $tag == 'a40a' ) { // Sharpness if ( $data == 0 ) $data = "Normal"; if ( $data == 1 ) $data = "Soft"; if ( $data == 2 ) $data = "Hard"; } else if ( $tag == 'a402' ) { // ExposureMode if ( $data == 0 ) $data = "Auto"; if ( $data == 1 ) $data = "Manual"; if ( $data == 2 ) $data = "Auto bracket"; } else if ( $tag == 'a401' ) { // CustomRendered if ( $data == 0 ) $data = "Normal"; if ( $data == 1 ) $data = "Custom"; } else if ( $tag == 'a403' ) { // WhiteBalance if ( $data == 0 ) $data = "Auto"; if ( $data == 1 ) $data = "Manual"; } else if ( $tag == 'a406' ) { // SceneCaptureType if ( $data == 0 ) $data = "Standard"; if ( $data == 1 ) $data = "Landscape"; if ( $data == 2 ) $data = "Portrait"; if ( $data == 3 ) $data = "Night"; } } else if ($type == 'UNDEFINED') { if ($tag == '9000' || $tag == 'a000' || $tag == '0002') { // ExifVersion,FlashPixVersion,InteroperabilityVersion $data='version '.$data/100; } if ($tag == 'a300') { // FileSource $data = bin2hex($data); $data = str_replace('00','',$data); $data = str_replace('03','Digital Still Camera',$data); } if ($tag == 'a301') { // SceneType $data = bin2hex($data); $data = str_replace('00','',$data); $data = str_replace('01','Directly Photographed',$data); } if ($tag == '9101') { // ComponentsConfiguration $data = bin2hex($data); $data = str_replace('01','Y',$data); $data = str_replace('02','Cb',$data); $data = str_replace('03','Cr',$data); $data = str_replace('04','R',$data); $data = str_replace('05','G',$data); $data = str_replace('06','B',$data); $data = str_replace('00','',$data); } } else { $data = bin2hex($data); if ($intel == 1) $data = intel2Moto($data); } return $data; } //================================================================================================ // Reads one standard IFD entry //================================================================================================ function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) { if (feof($in)) { // test to make sure we can still read. $result['Errors'] = $result['Errors']+1; return; } // 2 byte tag $tag = bin2hex(fread($in, 2)); if ($intel == 1) $tag = intel2Moto($tag); $tag_name = lookup_tag($tag); // 2 byte datatype $type = bin2hex(fread($in, 2)); if ($intel == 1) $type = intel2Moto($type); lookup_type($type, $size); // 4 byte number of elements $count = bin2hex(fread($in, 4)); if ($intel == 1) $count = intel2Moto($count); $bytesofdata = $size*hexdec($count); // 4 byte value or pointer to value if larger than 4 bytes $value = fread( $in, 4 ); if ($bytesofdata <= 4) { // if datatype is 4 bytes or less, its the value $data = $value; } else if ($bytesofdata < 100000) { // otherwise its a pointer to the value, so lets go get it $value = bin2hex($value); if ($intel == 1) $value = intel2Moto($value); $v = fseek($seek, $globalOffset+hexdec($value)); // offsets are from TIFF header which is 12 bytes from the start of the file if ($v == 0) { $data = fread($seek, $bytesofdata); } else if ($v == -1) { $result['Errors'] = $result['Errors']+1; } } else { // bytesofdata was too big, so the exif had an error $result['Errors'] = $result['Errors']+1; return; } if ($tag_name == 'MakerNote') { // if its a maker tag, we need to parse this specially $make = $result['IFD0']['Make']; if ($result['VerboseOutput'] == 1) { $result[$ifd_name]['MakerNote']['RawData'] = $data; } if (stripos($make, 'NIKON') !== FALSE) { require_once('makers/nikon.php'); parseNikon($data,$result); $result[$ifd_name]['KnownMaker'] = 1; } else if (stripos($make, 'OLYMPUS') !== FALSE) { require_once('makers/olympus.php'); parseOlympus($data,$result,$seek,$globalOffset); $result[$ifd_name]['KnownMaker'] = 1; } else if (stripos($make, 'CANON') !== FALSE) { require_once('makers/canon.php'); parseCanon($data,$result,$seek,$globalOffset); $result[$ifd_name]['KnownMaker'] = 1; } else if (stripos($make, 'FUJIFILM') !== FALSE) { require_once('makers/fujifilm.php'); parseFujifilm($data,$result); $result[$ifd_name]['KnownMaker'] = 1; } else if (stripos($make, 'SANYO') !== FALSE) { require_once('makers/sanyo.php'); parseSanyo($data,$result,$seek,$globalOffset); $result[$ifd_name]['KnownMaker'] = 1; } else if (stripos($make, 'PANASONIC') !== FALSE) { require_once('makers/panasonic.php'); parsePanasonic($data,$result,$seek,$globalOffset); $result[$ifd_name]['KnownMaker'] = 1; } else { $result[$ifd_name]['KnownMaker'] = 0; } } else if ($tag_name == 'GPSInfoOffset') { require_once('makers/gps.php'); $formated_data = formatData($type,$tag,$intel,$data); $result[$ifd_name]['GPSInfo'] = $formated_data; parseGPS($data,$result,$formated_data,$seek,$globalOffset); } else { // Format the data depending on the type and tag $formated_data = formatData($type,$tag,$intel,$data); $result[$ifd_name][$tag_name] = $formated_data; if ($result['VerboseOutput'] == 1) { if ($type == 'URATIONAL' || $type == 'SRATIONAL' || $type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' || $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') { $data = bin2hex($data); if ($intel == 1) $data = intel2Moto($data); } $result[$ifd_name][$tag_name.'_Verbose']['RawData'] = $data; $result[$ifd_name][$tag_name.'_Verbose']['Type'] = $type; $result[$ifd_name][$tag_name.'_Verbose']['Bytes'] = $bytesofdata; } } } //================================================================================================ // Pass in a file and this reads the EXIF data // // Usefull resources // http:// www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html // http:// www.w3.org/Graphics/JPEG/jfif.txt // http:// exif.org/ // http:// www.ozhiker.com/electronics/pjmt/library/list_contents.php4 // http:// www.ozhiker.com/electronics/pjmt/jpeg_info/makernotes.html // http:// pel.sourceforge.net/ // http:// us2.php.net/manual/en/function.exif-read-data.php //================================================================================================ function read_exif_data_raw($path,$verbose) { echo $path; if ($path == '' || $path == 'none') return; $in = @fopen($path, 'rb'); // the b is for windows machines to open in binary mode $seek = @fopen($path, 'rb'); // There may be an elegant way to do this with one file handle. $globalOffset = 0; if (!isset($verbose)) $verbose=0; $result['VerboseOutput'] = $verbose; $result['Errors'] = 0; if (!$in || !$seek) { // if the path was invalid, this error will catch it $result['Errors'] = 1; $result['Error'][$result['Errors']] = 'The file could not be found.'; return $result; } // First 2 bytes of JPEG are 0xFFD8 $data = bin2hex(fread( $in, 2 )); if ($data == 'ffd8') { $result['ValidJpeg'] = 1; } else { $result['ValidJpeg'] = 0; fclose($in); fclose($seek); return $result; } $result['ValidIPTCData'] = 0; $result['ValidJFIFData'] = 0; $result['ValidEXIFData'] = 0; $result['ValidAPP2Data'] = 0; $result['ValidCOMData'] = 0; // Next 2 bytes are MARKER tag (0xFFE#) $data = bin2hex(fread( $in, 2 )); $size = bin2hex(fread( $in, 2 )); // LOOP THROUGH MARKERS TILL YOU GET TO FFE1 (exif marker) while(!feof($in) && $data!='ffe1' && $data!='ffc0' && $data!='ffd9') { if ($data == 'ffe0') { // JFIF Marker $result['ValidJFIFData'] = 1; $result['JFIF']['Size'] = hexdec($size); if (hexdec($size)-2 > 0) { $data = fread( $in, hexdec($size)-2); $result['JFIF']['Data'] = $data; } $result['JFIF']['Identifier'] = substr($data,0,5); $result['JFIF']['ExtensionCode'] = bin2hex(substr($data,6,1)); $globalOffset+=hexdec($size)+2; } else if ($data == 'ffed') { // IPTC Marker $result['ValidIPTCData'] = 1; $result['IPTC']['Size'] = hexdec($size); if (hexdec($size)-2 > 0) { $data = fread( $in, hexdec($size)-2); $result['IPTC']['Data'] = $data ; } $globalOffset+=hexdec($size)+2; } else if ($data == 'ffe2') { // EXIF extension Marker $result['ValidAPP2Data'] = 1; $result['APP2']['Size'] = hexdec($size); if (hexdec($size)-2 > 0) { $data = fread( $in, hexdec($size)-2); $result['APP2']['Data'] = $data ; } $globalOffset+=hexdec($size)+2; } else if ($data == 'fffe') { // COM extension Marker $result['ValidCOMData'] = 1; $result['COM']['Size'] = hexdec($size); if (hexdec($size)-2 > 0) { $data = fread( $in, hexdec($size)-2); $result['COM']['Data'] = $data ; } $globalOffset+=hexdec($size)+2; } else if ($data == 'ffe1') { $result['ValidEXIFData'] = 1; } $data = bin2hex(fread( $in, 2 )); $size = bin2hex(fread( $in, 2 )); } // END MARKER LOOP if ($data == 'ffe1') { $result['ValidEXIFData'] = 1; } else { fclose($in); fclose($seek); return $result; } // Size of APP1 $result['APP1Size'] = hexdec($size); // Start of APP1 block starts with 'Exif' header (6 bytes) $header = fread( $in, 6 ); // Then theres a TIFF header with 2 bytes of endieness (II or MM) $header = fread( $in, 2 ); if ($header==='II') { $intel=1; $result['Endien'] = 'Intel'; } else if ($header==='MM') { $intel=0; $result['Endien'] = 'Motorola'; } else { $intel=1; // not sure what the default should be, but this seems reasonable $result['Endien'] = 'Unknown'; } // 2 bytes of 0x002a $tag = bin2hex(fread( $in, 2 )); // Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header) $offset = bin2hex(fread( $in, 4 )); if ($intel == 1) $offset = intel2Moto($offset); // Check for extremely large values here if (hexdec($offset) > 100000) { $result['ValidEXIFData'] = 0; fclose($in); fclose($seek); return $result; } if (hexdec($offset)>8) $unknown = fread( $in, hexdec($offset)-8); // fixed this bug in 1.3 // add 12 to the offset to account for TIFF header $globalOffset+=12; //=========================================================== // Start of IFD0 $num = bin2hex(fread( $in, 2 )); if ($intel == 1) $num = intel2Moto($num); $num = hexdec($num); $result['IFD0NumTags'] = $num; if ($num<1000) { // 1000 entries is too much and is probably an error. for($i=0; $i<$num; $i++) { read_entry($result,$in,$seek,$intel,'IFD0',$globalOffset); } } else { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Illegal size for IFD0'; } // store offset to IFD1 $offset = bin2hex(fread( $in, 4 )); if ($intel == 1) $offset = intel2Moto($offset); $result['IFD1Offset'] = hexdec($offset); // Check for SubIFD if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) { fclose($in); fclose($seek); return $result; } // seek to SubIFD (Value of ExifOffset tag) above. $ExitOffset = $result['IFD0']['ExifOffset']; $v = fseek($in,$globalOffset+$ExitOffset); if ($v == -1) { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Couldnt Find SubIFD'; } //=========================================================== // Start of SubIFD $num = bin2hex(fread( $in, 2 )); if ($intel == 1) $num = intel2Moto($num); $num = hexdec($num); $result['SubIFDNumTags'] = $num; if ($num<1000) { // 1000 entries is too much and is probably an error. for($i=0; $i<$num; $i++) { read_entry($result,$in,$seek,$intel,'SubIFD',$globalOffset); } } else { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Illegal size for SubIFD'; } // Add the 35mm equivalent focal length: $result['SubIFD']['FocalLength35mmEquiv'] = get35mmEquivFocalLength($result); // Check for IFD1 if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) { fclose($in); fclose($seek); return $result; } // seek to IFD1 $v = fseek($in,$globalOffset+$result['IFD1Offset']); if ($v == -1) { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Couldnt Find IFD1'; } //=========================================================== // Start of IFD1 $num = bin2hex(fread( $in, 2 )); if ($intel == 1) $num = intel2Moto($num); $num = hexdec($num); $result['IFD1NumTags'] = $num; if ($num<1000) { // 1000 entries is too much and is probably an error. for($i=0; $i<$num; $i++) { read_entry($result,$in,$seek,$intel,'IFD1',$globalOffset); } } else { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Illegal size for IFD1'; } // If verbose output is on, include the thumbnail raw data... if ($result['VerboseOutput'] == 1 && $result['IFD1']['JpegIFOffset']>0 && $result['IFD1']['JpegIFByteCount']>0) { $v = fseek($seek,$globalOffset+$result['IFD1']['JpegIFOffset']); if ($v == 0) { $data = fread($seek, $result['IFD1']['JpegIFByteCount']); } else if ($v == -1) { $result['Errors'] = $result['Errors']+1; } $result['IFD1']['ThumbnailData'] = $data; } // Check for Interoperability IFD if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) || $result['SubIFD']['ExifInteroperabilityOffset'] == 0) { fclose($in); fclose($seek); return $result; } // Seek to InteroperabilityIFD $v = fseek($in,$globalOffset+$result['SubIFD']['ExifInteroperabilityOffset']); if ($v == -1) { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Couldnt Find InteroperabilityIFD'; } //=========================================================== // Start of InteroperabilityIFD $num = bin2hex(fread( $in, 2 )); if ($intel == 1) $num = intel2Moto($num); $num = hexdec($num); $result['InteroperabilityIFDNumTags'] = $num; if ($num<1000) { // 1000 entries is too much and is probably an error. for($i=0; $i<$num; $i++) { read_entry($result,$in,$seek,$intel,'InteroperabilityIFD',$globalOffset); } } else { $result['Errors'] = $result['Errors']+1; $result['Error'][$result['Errors']] = 'Illegal size for InteroperabilityIFD'; } fclose($in); fclose($seek); return $result; } //================================================================================================ // Converts a floating point number into a fraction. Many thanks to Matthieu Froment for this code //================================================================================================ function ConvertToFraction($v, &$n, &$d) { $MaxTerms = 15; // Limit to prevent infinite loop $MinDivisor = 0.000001; // Limit to prevent divide by zero $MaxError = 0.00000001; // How close is enough $f = $v; // Initialize fraction being converted $n_un = 1; // Initialize fractions with 1/0, 0/1 $d_un = 0; $n_deux = 0; $d_deux = 1; for ($i = 0; $i<$MaxTerms; $i++) { $a = floor($f); // Get next term $f = $f - $a; // Get new divisor $n = $n_un * $a + $n_deux; // Calculate new fraction $d = $d_un * $a + $d_deux; $n_deux = $n_un; // Save last two fractions $d_deux = $d_un; $n_un = $n; $d_un = $d; if ($f < $MinDivisor) // Quit if dividing by zero break; if (abs($v - $n / $d) < $MaxError) break; $f = 1 / $f; // Take reciprocal } } //================================================================================================ // Calculates the 35mm-equivalent focal length from the reported sensor resolution, by Tristan Harward. //================================================================================================ function get35mmEquivFocalLength(&$result) { $width = $result['SubIFD']['ExifImageWidth']; $units = $result['SubIFD']['FocalPlaneResolutionUnit']; $unitfactor = 1; switch ($units) { case 'Inch' : $unitfactor = 25.4; break; case 'Centimeter' : $unitfactor = 10; break; case 'No Unit' : $unitfactor = 25.4; break; default : $unitfactor = 25.4; } $xres = $result['SubIFD']['FocalPlaneXResolution']; $fl = $result['SubIFD']['FocalLength']; if (($width != 0) && !empty($units) && !empty($xres) && !empty($fl) && !empty($width)) { $ccdwidth = ($width * $unitfactor) / $xres; $equivfl = $fl / $ccdwidth*36+0.5; return $equivfl; } return null; } ?>