Back to index

php5  5.3.10
exif.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
00016    |          Marcus Boerger <helly@php.net>                              |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /* $Id: exif.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 /*  ToDos
00023  *
00024  *     See if example images from http://www.exif.org have illegal
00025  *            thumbnail sizes or if code is corrupt.
00026  *     Create/Update exif headers.
00027  *     Create/Remove/Update image thumbnails.
00028  */
00029 
00030 /*  Security
00031  *
00032  *  At current time i do not see any security problems but a potential
00033  *  attacker could generate an image with recursive ifd pointers...(Marcus)
00034  */
00035 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 #include "php.h"
00041 #include "ext/standard/file.h"
00042 
00043 #ifdef HAVE_STDINT_H
00044 # include <stdint.h>
00045 #endif
00046 #ifdef HAVE_INTTYPES_H
00047 # include <inttypes.h>
00048 #endif
00049 #ifdef PHP_WIN32
00050 # include "win32/php_stdint.h"
00051 #endif
00052 
00053 #if HAVE_EXIF
00054 
00055 /* When EXIF_DEBUG is defined the module generates a lot of debug messages
00056  * that help understanding what is going on. This can and should be used
00057  * while extending the module as it shows if you are at the right position.
00058  * You are always considered to have a copy of TIFF6.0 and EXIF2.10 standard.
00059  */
00060 #undef EXIF_DEBUG
00061 
00062 #ifdef EXIF_DEBUG
00063 #define EXIFERR_DC , const char *_file, size_t _line TSRMLS_DC
00064 #define EXIFERR_CC , __FILE__, __LINE__ TSRMLS_CC
00065 #else
00066 #define EXIFERR_DC TSRMLS_DC
00067 #define EXIFERR_CC TSRMLS_CC
00068 #endif
00069 
00070 #undef EXIF_JPEG2000
00071 
00072 #include "php_exif.h"
00073 #include <math.h>
00074 #include "php_ini.h"
00075 #include "ext/standard/php_string.h"
00076 #include "ext/standard/php_image.h"
00077 #include "ext/standard/info.h" 
00078 
00079 #if defined(PHP_WIN32) || (HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING))
00080 #define EXIF_USE_MBSTRING 1
00081 #else
00082 #define EXIF_USE_MBSTRING 0
00083 #endif
00084 
00085 #if EXIF_USE_MBSTRING
00086 #include "ext/mbstring/mbstring.h"
00087 #endif
00088 
00089 /* needed for ssize_t definition */
00090 #include <sys/types.h>
00091 
00092 typedef unsigned char uchar;
00093 
00094 #ifndef safe_emalloc
00095 # define safe_emalloc(a,b,c) emalloc((a)*(b)+(c))
00096 #endif
00097 #ifndef safe_erealloc
00098 # define safe_erealloc(p,a,b,c) erealloc(p, (a)*(b)+(c))
00099 #endif
00100 
00101 #ifndef TRUE
00102 #      define TRUE 1
00103 #      define FALSE 0
00104 #endif
00105 
00106 #ifndef max
00107 #      define max(a,b) ((a)>(b) ? (a) : (b))
00108 #endif
00109 
00110 #define EFREE_IF(ptr)       if (ptr) efree(ptr)
00111 
00112 #define MAX_IFD_NESTING_LEVEL 100
00113 
00114 /* {{{ arginfo */
00115 ZEND_BEGIN_ARG_INFO(arginfo_exif_tagname, 0)
00116        ZEND_ARG_INFO(0, index)
00117 ZEND_END_ARG_INFO()
00118 
00119 ZEND_BEGIN_ARG_INFO_EX(arginfo_exif_read_data, 0, 0, 1)
00120        ZEND_ARG_INFO(0, filename)
00121        ZEND_ARG_INFO(0, sections_needed)
00122        ZEND_ARG_INFO(0, sub_arrays)
00123        ZEND_ARG_INFO(0, read_thumbnail)
00124 ZEND_END_ARG_INFO()
00125 
00126 ZEND_BEGIN_ARG_INFO_EX(arginfo_exif_thumbnail, 0, 0, 1)
00127        ZEND_ARG_INFO(0, filename)
00128        ZEND_ARG_INFO(1, width)
00129        ZEND_ARG_INFO(1, height)
00130        ZEND_ARG_INFO(1, imagetype)
00131 ZEND_END_ARG_INFO()
00132 
00133 ZEND_BEGIN_ARG_INFO(arginfo_exif_imagetype, 0)
00134        ZEND_ARG_INFO(0, imagefile)
00135 ZEND_END_ARG_INFO()
00136 
00137 /* }}} */
00138 
00139 /* {{{ exif_functions[]
00140  */
00141 const zend_function_entry exif_functions[] = {
00142        PHP_FE(exif_read_data, arginfo_exif_read_data)
00143        PHP_FALIAS(read_exif_data, exif_read_data, arginfo_exif_read_data)
00144        PHP_FE(exif_tagname, arginfo_exif_tagname)
00145        PHP_FE(exif_thumbnail, arginfo_exif_thumbnail)
00146        PHP_FE(exif_imagetype, arginfo_exif_imagetype)
00147        PHP_FE_END
00148 };
00149 /* }}} */
00150 
00151 #define EXIF_VERSION "1.4 $Id: exif.c 321634 2012-01-01 13:15:04Z felipe $"
00152 
00153 /* {{{ PHP_MINFO_FUNCTION
00154  */
00155 PHP_MINFO_FUNCTION(exif)
00156 {
00157        php_info_print_table_start();
00158        php_info_print_table_row(2, "EXIF Support", "enabled");
00159        php_info_print_table_row(2, "EXIF Version", EXIF_VERSION);
00160        php_info_print_table_row(2, "Supported EXIF Version", "0220");
00161        php_info_print_table_row(2, "Supported filetypes", "JPEG,TIFF");
00162        php_info_print_table_end();
00163        DISPLAY_INI_ENTRIES();
00164 }
00165 /* }}} */
00166 
00167 ZEND_BEGIN_MODULE_GLOBALS(exif)
00168        char * encode_unicode;
00169        char * decode_unicode_be;
00170        char * decode_unicode_le;
00171        char * encode_jis;
00172        char * decode_jis_be;
00173        char * decode_jis_le;
00174 ZEND_END_MODULE_GLOBALS(exif) 
00175 
00176 ZEND_DECLARE_MODULE_GLOBALS(exif)
00177 
00178 #ifdef ZTS
00179 #define EXIF_G(v) TSRMG(exif_globals_id, zend_exif_globals *, v)
00180 #else
00181 #define EXIF_G(v) (exif_globals.v)
00182 #endif
00183  
00184 /* {{{ PHP_INI
00185  */
00186 
00187 ZEND_INI_MH(OnUpdateEncode)
00188 {
00189 #if EXIF_USE_MBSTRING
00190        if (new_value && strlen(new_value) && !php_mb_check_encoding_list(new_value TSRMLS_CC)) {
00191               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Illegal encoding ignored: '%s'", new_value);
00192               return FAILURE;
00193        }
00194 #endif
00195        return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
00196 }
00197 
00198 ZEND_INI_MH(OnUpdateDecode)
00199 {
00200 #if EXIF_USE_MBSTRING
00201        if (!php_mb_check_encoding_list(new_value TSRMLS_CC)) {
00202               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Illegal encoding ignored: '%s'", new_value);
00203               return FAILURE;
00204        }
00205 #endif
00206        return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
00207 }
00208 
00209 PHP_INI_BEGIN()
00210     STD_PHP_INI_ENTRY("exif.encode_unicode",          "ISO-8859-15", PHP_INI_ALL, OnUpdateEncode, encode_unicode,    zend_exif_globals, exif_globals)
00211     STD_PHP_INI_ENTRY("exif.decode_unicode_motorola", "UCS-2BE",     PHP_INI_ALL, OnUpdateDecode, decode_unicode_be, zend_exif_globals, exif_globals)
00212     STD_PHP_INI_ENTRY("exif.decode_unicode_intel",    "UCS-2LE",     PHP_INI_ALL, OnUpdateDecode, decode_unicode_le, zend_exif_globals, exif_globals)
00213     STD_PHP_INI_ENTRY("exif.encode_jis",              "",            PHP_INI_ALL, OnUpdateEncode, encode_jis,        zend_exif_globals, exif_globals)
00214     STD_PHP_INI_ENTRY("exif.decode_jis_motorola",     "JIS",         PHP_INI_ALL, OnUpdateDecode, decode_jis_be,     zend_exif_globals, exif_globals)
00215     STD_PHP_INI_ENTRY("exif.decode_jis_intel",        "JIS",         PHP_INI_ALL, OnUpdateDecode, decode_jis_le,     zend_exif_globals, exif_globals)
00216 PHP_INI_END()
00217 /* }}} */
00218  
00219 /* {{{ PHP_GINIT_FUNCTION
00220  */
00221 static PHP_GINIT_FUNCTION(exif)
00222 {
00223        exif_globals->encode_unicode    = NULL;
00224        exif_globals->decode_unicode_be = NULL;
00225        exif_globals->decode_unicode_le = NULL;
00226        exif_globals->encode_jis        = NULL;
00227        exif_globals->decode_jis_be     = NULL;
00228        exif_globals->decode_jis_le     = NULL;
00229 }
00230 /* }}} */
00231 
00232 /* {{{ PHP_MINIT_FUNCTION(exif)
00233    Get the size of an image as 4-element array */
00234 PHP_MINIT_FUNCTION(exif)
00235 {
00236        REGISTER_INI_ENTRIES();
00237        REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", EXIF_USE_MBSTRING, CONST_CS | CONST_PERSISTENT); 
00238        return SUCCESS;
00239 }
00240 /* }}} */
00241 
00242 /* {{{ PHP_MSHUTDOWN_FUNCTION
00243  */
00244 PHP_MSHUTDOWN_FUNCTION(exif)
00245 {
00246        UNREGISTER_INI_ENTRIES();
00247        return SUCCESS;
00248 }
00249 /* }}} */
00250 
00251 /* {{{ exif dependencies */
00252 static const zend_module_dep exif_module_deps[] = {
00253        ZEND_MOD_REQUIRED("standard")
00254 #if EXIF_USE_MBSTRING
00255        ZEND_MOD_REQUIRED("mbstring")
00256 #endif
00257        ZEND_MOD_END
00258 };
00259 /* }}} */
00260 
00261 /* {{{ exif_module_entry
00262  */
00263 zend_module_entry exif_module_entry = {
00264        STANDARD_MODULE_HEADER_EX, NULL,
00265        exif_module_deps,
00266        "exif",
00267        exif_functions,
00268        PHP_MINIT(exif), 
00269        PHP_MSHUTDOWN(exif),
00270        NULL, NULL,
00271        PHP_MINFO(exif),
00272 #if ZEND_MODULE_API_NO >= 20010901
00273        EXIF_VERSION,
00274 #endif
00275 #if ZEND_MODULE_API_NO >= 20060613
00276        PHP_MODULE_GLOBALS(exif),
00277        PHP_GINIT(exif),
00278        NULL,
00279        NULL,
00280        STANDARD_MODULE_PROPERTIES_EX
00281 #else  
00282        STANDARD_MODULE_PROPERTIES
00283 #endif
00284 };
00285 /* }}} */
00286 
00287 #ifdef COMPILE_DL_EXIF
00288 ZEND_GET_MODULE(exif)
00289 #endif
00290 
00291 /* {{{ php_strnlen
00292  * get length of string if buffer if less than buffer size or buffer size */
00293 static size_t php_strnlen(char* str, size_t maxlen) {
00294        size_t len = 0;
00295 
00296        if (str && maxlen && *str) {
00297               do {
00298                      len++;
00299               } while (--maxlen && *(++str));
00300        }
00301        return len;
00302 }
00303 /* }}} */
00304 
00305 /* {{{ error messages
00306 */
00307 static const char * EXIF_ERROR_FILEEOF   = "Unexpected end of file reached";
00308 static const char * EXIF_ERROR_CORRUPT   = "File structure corrupted";
00309 static const char * EXIF_ERROR_THUMBEOF  = "Thumbnail goes IFD boundary or end of file reached";
00310 static const char * EXIF_ERROR_FSREALLOC = "Illegal reallocating of undefined file section";
00311 
00312 #define EXIF_ERRLOG_FILEEOF(ImageInfo)    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_FILEEOF);
00313 #define EXIF_ERRLOG_CORRUPT(ImageInfo)    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_CORRUPT);
00314 #define EXIF_ERRLOG_THUMBEOF(ImageInfo)   exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_THUMBEOF);
00315 #define EXIF_ERRLOG_FSREALLOC(ImageInfo)  exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_FSREALLOC);
00316 /* }}} */
00317 
00318 /* {{{ format description defines
00319    Describes format descriptor
00320 */
00321 static int php_tiff_bytes_per_format[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1};
00322 #define NUM_FORMATS 13
00323 
00324 #define TAG_FMT_BYTE       1
00325 #define TAG_FMT_STRING     2
00326 #define TAG_FMT_USHORT     3
00327 #define TAG_FMT_ULONG      4
00328 #define TAG_FMT_URATIONAL  5
00329 #define TAG_FMT_SBYTE      6
00330 #define TAG_FMT_UNDEFINED  7
00331 #define TAG_FMT_SSHORT     8
00332 #define TAG_FMT_SLONG      9
00333 #define TAG_FMT_SRATIONAL 10
00334 #define TAG_FMT_SINGLE    11
00335 #define TAG_FMT_DOUBLE    12
00336 #define TAG_FMT_IFD       13
00337 
00338 #ifdef EXIF_DEBUG
00339 static char *exif_get_tagformat(int format)
00340 {
00341        switch(format) {
00342               case TAG_FMT_BYTE:      return "BYTE";
00343               case TAG_FMT_STRING:    return "STRING";
00344               case TAG_FMT_USHORT:    return "USHORT";
00345               case TAG_FMT_ULONG:     return "ULONG";
00346               case TAG_FMT_URATIONAL: return "URATIONAL";
00347               case TAG_FMT_SBYTE:     return "SBYTE";
00348               case TAG_FMT_UNDEFINED: return "UNDEFINED";
00349               case TAG_FMT_SSHORT:    return "SSHORT";
00350               case TAG_FMT_SLONG:     return "SLONG";
00351               case TAG_FMT_SRATIONAL: return "SRATIONAL";
00352               case TAG_FMT_SINGLE:    return "SINGLE";
00353               case TAG_FMT_DOUBLE:    return "DOUBLE";
00354               case TAG_FMT_IFD:       return "IFD";
00355        }
00356        return "*Illegal";
00357 }
00358 #endif
00359 
00360 /* Describes tag values */
00361 #define TAG_GPS_VERSION_ID              0x0000
00362 #define TAG_GPS_LATITUDE_REF            0x0001
00363 #define TAG_GPS_LATITUDE                0x0002
00364 #define TAG_GPS_LONGITUDE_REF           0x0003
00365 #define TAG_GPS_LONGITUDE               0x0004
00366 #define TAG_GPS_ALTITUDE_REF            0x0005
00367 #define TAG_GPS_ALTITUDE                0x0006
00368 #define TAG_GPS_TIME_STAMP              0x0007
00369 #define TAG_GPS_SATELLITES              0x0008
00370 #define TAG_GPS_STATUS                  0x0009
00371 #define TAG_GPS_MEASURE_MODE            0x000A
00372 #define TAG_GPS_DOP                     0x000B
00373 #define TAG_GPS_SPEED_REF               0x000C
00374 #define TAG_GPS_SPEED                   0x000D
00375 #define TAG_GPS_TRACK_REF               0x000E
00376 #define TAG_GPS_TRACK                   0x000F
00377 #define TAG_GPS_IMG_DIRECTION_REF       0x0010
00378 #define TAG_GPS_IMG_DIRECTION           0x0011
00379 #define TAG_GPS_MAP_DATUM               0x0012
00380 #define TAG_GPS_DEST_LATITUDE_REF       0x0013
00381 #define TAG_GPS_DEST_LATITUDE           0x0014
00382 #define TAG_GPS_DEST_LONGITUDE_REF      0x0015
00383 #define TAG_GPS_DEST_LONGITUDE          0x0016
00384 #define TAG_GPS_DEST_BEARING_REF        0x0017
00385 #define TAG_GPS_DEST_BEARING            0x0018
00386 #define TAG_GPS_DEST_DISTANCE_REF       0x0019
00387 #define TAG_GPS_DEST_DISTANCE           0x001A
00388 #define TAG_GPS_PROCESSING_METHOD       0x001B
00389 #define TAG_GPS_AREA_INFORMATION        0x001C
00390 #define TAG_GPS_DATE_STAMP              0x001D
00391 #define TAG_GPS_DIFFERENTIAL            0x001E
00392 #define TAG_TIFF_COMMENT                0x00FE /* SHOUDLNT HAPPEN */
00393 #define TAG_NEW_SUBFILE                 0x00FE /* New version of subfile tag */
00394 #define TAG_SUBFILE_TYPE                0x00FF /* Old version of subfile tag */
00395 #define TAG_IMAGEWIDTH                  0x0100
00396 #define TAG_IMAGEHEIGHT                 0x0101
00397 #define TAG_BITS_PER_SAMPLE             0x0102
00398 #define TAG_COMPRESSION                 0x0103
00399 #define TAG_PHOTOMETRIC_INTERPRETATION  0x0106
00400 #define TAG_TRESHHOLDING                0x0107
00401 #define TAG_CELL_WIDTH                  0x0108
00402 #define TAG_CELL_HEIGHT                 0x0109
00403 #define TAG_FILL_ORDER                  0x010A
00404 #define TAG_DOCUMENT_NAME               0x010D
00405 #define TAG_IMAGE_DESCRIPTION           0x010E
00406 #define TAG_MAKE                        0x010F
00407 #define TAG_MODEL                       0x0110
00408 #define TAG_STRIP_OFFSETS               0x0111
00409 #define TAG_ORIENTATION                 0x0112
00410 #define TAG_SAMPLES_PER_PIXEL           0x0115
00411 #define TAG_ROWS_PER_STRIP              0x0116
00412 #define TAG_STRIP_BYTE_COUNTS           0x0117
00413 #define TAG_MIN_SAMPPLE_VALUE           0x0118
00414 #define TAG_MAX_SAMPLE_VALUE            0x0119
00415 #define TAG_X_RESOLUTION                0x011A
00416 #define TAG_Y_RESOLUTION                0x011B
00417 #define TAG_PLANAR_CONFIGURATION        0x011C
00418 #define TAG_PAGE_NAME                   0x011D
00419 #define TAG_X_POSITION                  0x011E
00420 #define TAG_Y_POSITION                  0x011F
00421 #define TAG_FREE_OFFSETS                0x0120
00422 #define TAG_FREE_BYTE_COUNTS            0x0121
00423 #define TAG_GRAY_RESPONSE_UNIT          0x0122
00424 #define TAG_GRAY_RESPONSE_CURVE         0x0123
00425 #define TAG_RESOLUTION_UNIT             0x0128
00426 #define TAG_PAGE_NUMBER                 0x0129
00427 #define TAG_TRANSFER_FUNCTION           0x012D
00428 #define TAG_SOFTWARE                    0x0131
00429 #define TAG_DATETIME                    0x0132
00430 #define TAG_ARTIST                      0x013B
00431 #define TAG_HOST_COMPUTER               0x013C
00432 #define TAG_PREDICTOR                   0x013D
00433 #define TAG_WHITE_POINT                 0x013E
00434 #define TAG_PRIMARY_CHROMATICITIES      0x013F
00435 #define TAG_COLOR_MAP                   0x0140
00436 #define TAG_HALFTONE_HINTS              0x0141
00437 #define TAG_TILE_WIDTH                  0x0142
00438 #define TAG_TILE_LENGTH                 0x0143
00439 #define TAG_TILE_OFFSETS                0x0144
00440 #define TAG_TILE_BYTE_COUNTS            0x0145
00441 #define TAG_SUB_IFD                     0x014A
00442 #define TAG_INK_SETMPUTER               0x014C
00443 #define TAG_INK_NAMES                   0x014D
00444 #define TAG_NUMBER_OF_INKS              0x014E
00445 #define TAG_DOT_RANGE                   0x0150
00446 #define TAG_TARGET_PRINTER              0x0151
00447 #define TAG_EXTRA_SAMPLE                0x0152
00448 #define TAG_SAMPLE_FORMAT               0x0153
00449 #define TAG_S_MIN_SAMPLE_VALUE          0x0154
00450 #define TAG_S_MAX_SAMPLE_VALUE          0x0155
00451 #define TAG_TRANSFER_RANGE              0x0156
00452 #define TAG_JPEG_TABLES                 0x015B
00453 #define TAG_JPEG_PROC                   0x0200
00454 #define TAG_JPEG_INTERCHANGE_FORMAT     0x0201
00455 #define TAG_JPEG_INTERCHANGE_FORMAT_LEN 0x0202
00456 #define TAG_JPEG_RESTART_INTERVAL       0x0203
00457 #define TAG_JPEG_LOSSLESS_PREDICTOR     0x0205
00458 #define TAG_JPEG_POINT_TRANSFORMS       0x0206
00459 #define TAG_JPEG_Q_TABLES               0x0207
00460 #define TAG_JPEG_DC_TABLES              0x0208
00461 #define TAG_JPEG_AC_TABLES              0x0209
00462 #define TAG_YCC_COEFFICIENTS            0x0211
00463 #define TAG_YCC_SUB_SAMPLING            0x0212
00464 #define TAG_YCC_POSITIONING             0x0213
00465 #define TAG_REFERENCE_BLACK_WHITE       0x0214
00466 /* 0x0301 - 0x0302 */
00467 /* 0x0320 */
00468 /* 0x0343 */
00469 /* 0x5001 - 0x501B */
00470 /* 0x5021 - 0x503B */
00471 /* 0x5090 - 0x5091 */
00472 /* 0x5100 - 0x5101 */
00473 /* 0x5110 - 0x5113 */
00474 /* 0x80E3 - 0x80E6 */
00475 /* 0x828d - 0x828F */
00476 #define TAG_COPYRIGHT                   0x8298
00477 #define TAG_EXPOSURETIME                0x829A
00478 #define TAG_FNUMBER                     0x829D
00479 #define TAG_EXIF_IFD_POINTER            0x8769
00480 #define TAG_ICC_PROFILE                 0x8773
00481 #define TAG_EXPOSURE_PROGRAM            0x8822
00482 #define TAG_SPECTRAL_SENSITY            0x8824
00483 #define TAG_GPS_IFD_POINTER             0x8825
00484 #define TAG_ISOSPEED                    0x8827
00485 #define TAG_OPTOELECTRIC_CONVERSION_F   0x8828
00486 /* 0x8829 - 0x882b */
00487 #define TAG_EXIFVERSION                 0x9000
00488 #define TAG_DATE_TIME_ORIGINAL          0x9003
00489 #define TAG_DATE_TIME_DIGITIZED         0x9004
00490 #define TAG_COMPONENT_CONFIG            0x9101
00491 #define TAG_COMPRESSED_BITS_PER_PIXEL   0x9102
00492 #define TAG_SHUTTERSPEED                0x9201
00493 #define TAG_APERTURE                    0x9202
00494 #define TAG_BRIGHTNESS_VALUE            0x9203
00495 #define TAG_EXPOSURE_BIAS_VALUE         0x9204
00496 #define TAG_MAX_APERTURE                0x9205
00497 #define TAG_SUBJECT_DISTANCE            0x9206
00498 #define TAG_METRIC_MODULE               0x9207
00499 #define TAG_LIGHT_SOURCE                0x9208
00500 #define TAG_FLASH                       0x9209
00501 #define TAG_FOCAL_LENGTH                0x920A
00502 /* 0x920B - 0x920D */
00503 /* 0x9211 - 0x9216 */
00504 #define TAG_SUBJECT_AREA                0x9214
00505 #define TAG_MAKER_NOTE                  0x927C
00506 #define TAG_USERCOMMENT                 0x9286
00507 #define TAG_SUB_SEC_TIME                0x9290
00508 #define TAG_SUB_SEC_TIME_ORIGINAL       0x9291
00509 #define TAG_SUB_SEC_TIME_DIGITIZED      0x9292
00510 /* 0x923F */
00511 /* 0x935C */
00512 #define TAG_XP_TITLE                    0x9C9B
00513 #define TAG_XP_COMMENTS                 0x9C9C
00514 #define TAG_XP_AUTHOR                   0x9C9D
00515 #define TAG_XP_KEYWORDS                 0x9C9E
00516 #define TAG_XP_SUBJECT                  0x9C9F
00517 #define TAG_FLASH_PIX_VERSION           0xA000
00518 #define TAG_COLOR_SPACE                 0xA001
00519 #define TAG_COMP_IMAGE_WIDTH            0xA002 /* compressed images only */
00520 #define TAG_COMP_IMAGE_HEIGHT           0xA003
00521 #define TAG_RELATED_SOUND_FILE          0xA004
00522 #define TAG_INTEROP_IFD_POINTER         0xA005 /* IFD pointer */
00523 #define TAG_FLASH_ENERGY                0xA20B
00524 #define TAG_SPATIAL_FREQUENCY_RESPONSE  0xA20C
00525 #define TAG_FOCALPLANE_X_RES            0xA20E
00526 #define TAG_FOCALPLANE_Y_RES            0xA20F
00527 #define TAG_FOCALPLANE_RESOLUTION_UNIT  0xA210
00528 #define TAG_SUBJECT_LOCATION            0xA214
00529 #define TAG_EXPOSURE_INDEX              0xA215
00530 #define TAG_SENSING_METHOD              0xA217
00531 #define TAG_FILE_SOURCE                 0xA300
00532 #define TAG_SCENE_TYPE                  0xA301
00533 #define TAG_CFA_PATTERN                 0xA302
00534 #define TAG_CUSTOM_RENDERED             0xA401
00535 #define TAG_EXPOSURE_MODE               0xA402
00536 #define TAG_WHITE_BALANCE               0xA403
00537 #define TAG_DIGITAL_ZOOM_RATIO          0xA404
00538 #define TAG_FOCAL_LENGTH_IN_35_MM_FILM  0xA405
00539 #define TAG_SCENE_CAPTURE_TYPE          0xA406
00540 #define TAG_GAIN_CONTROL                0xA407
00541 #define TAG_CONTRAST                    0xA408
00542 #define TAG_SATURATION                  0xA409
00543 #define TAG_SHARPNESS                   0xA40A
00544 #define TAG_DEVICE_SETTING_DESCRIPTION  0xA40B
00545 #define TAG_SUBJECT_DISTANCE_RANGE      0xA40C
00546 #define TAG_IMAGE_UNIQUE_ID             0xA420
00547 
00548 /* Olympus specific tags */
00549 #define TAG_OLYMPUS_SPECIALMODE         0x0200
00550 #define TAG_OLYMPUS_JPEGQUAL            0x0201
00551 #define TAG_OLYMPUS_MACRO               0x0202
00552 #define TAG_OLYMPUS_DIGIZOOM            0x0204
00553 #define TAG_OLYMPUS_SOFTWARERELEASE     0x0207
00554 #define TAG_OLYMPUS_PICTINFO            0x0208
00555 #define TAG_OLYMPUS_CAMERAID            0x0209
00556 /* end Olympus specific tags */
00557 
00558 /* Internal */
00559 #define TAG_NONE                                 -1 /* note that -1 <> 0xFFFF */
00560 #define TAG_COMPUTED_VALUE                       -2
00561 #define TAG_END_OF_LIST                 0xFFFD
00562 
00563 /* Values for TAG_PHOTOMETRIC_INTERPRETATION */
00564 #define PMI_BLACK_IS_ZERO       0
00565 #define PMI_WHITE_IS_ZERO       1
00566 #define PMI_RGB                 2
00567 #define PMI_PALETTE_COLOR       3
00568 #define PMI_TRANSPARENCY_MASK   4
00569 #define PMI_SEPARATED           5
00570 #define PMI_YCBCR               6
00571 #define PMI_CIELAB              8
00572 
00573 /* }}} */
00574 
00575 /* {{{ TabTable[]
00576  */
00577 typedef const struct {
00578        unsigned short Tag;
00579        char *Desc;
00580 } tag_info_type;
00581 
00582 typedef tag_info_type  tag_info_array[];
00583 typedef tag_info_type  *tag_table_type;
00584 
00585 #define TAG_TABLE_END \
00586   {TAG_NONE,           "No tag value"},\
00587   {TAG_COMPUTED_VALUE, "Computed value"},\
00588   {TAG_END_OF_LIST,    ""}  /* Important for exif_get_tagname() IF value != "" function result is != false */
00589 
00590 static tag_info_array tag_table_IFD = {
00591   { 0x000B, "ACDComment"},
00592   { 0x00FE, "NewSubFile"}, /* better name it 'ImageType' ? */
00593   { 0x00FF, "SubFile"},
00594   { 0x0100, "ImageWidth"},
00595   { 0x0101, "ImageLength"},
00596   { 0x0102, "BitsPerSample"},
00597   { 0x0103, "Compression"},
00598   { 0x0106, "PhotometricInterpretation"},
00599   { 0x010A, "FillOrder"},
00600   { 0x010D, "DocumentName"},
00601   { 0x010E, "ImageDescription"},
00602   { 0x010F, "Make"},
00603   { 0x0110, "Model"},
00604   { 0x0111, "StripOffsets"},
00605   { 0x0112, "Orientation"},
00606   { 0x0115, "SamplesPerPixel"},
00607   { 0x0116, "RowsPerStrip"},
00608   { 0x0117, "StripByteCounts"},
00609   { 0x0118, "MinSampleValue"},
00610   { 0x0119, "MaxSampleValue"},
00611   { 0x011A, "XResolution"},
00612   { 0x011B, "YResolution"},
00613   { 0x011C, "PlanarConfiguration"},
00614   { 0x011D, "PageName"},
00615   { 0x011E, "XPosition"},
00616   { 0x011F, "YPosition"},
00617   { 0x0120, "FreeOffsets"},
00618   { 0x0121, "FreeByteCounts"},
00619   { 0x0122, "GrayResponseUnit"},
00620   { 0x0123, "GrayResponseCurve"},
00621   { 0x0124, "T4Options"},
00622   { 0x0125, "T6Options"},
00623   { 0x0128, "ResolutionUnit"},
00624   { 0x0129, "PageNumber"},
00625   { 0x012D, "TransferFunction"},
00626   { 0x0131, "Software"},
00627   { 0x0132, "DateTime"},
00628   { 0x013B, "Artist"},
00629   { 0x013C, "HostComputer"},
00630   { 0x013D, "Predictor"},
00631   { 0x013E, "WhitePoint"},
00632   { 0x013F, "PrimaryChromaticities"},
00633   { 0x0140, "ColorMap"},
00634   { 0x0141, "HalfToneHints"},
00635   { 0x0142, "TileWidth"},
00636   { 0x0143, "TileLength"},
00637   { 0x0144, "TileOffsets"},
00638   { 0x0145, "TileByteCounts"},
00639   { 0x014A, "SubIFD"},
00640   { 0x014C, "InkSet"},
00641   { 0x014D, "InkNames"},
00642   { 0x014E, "NumberOfInks"},
00643   { 0x0150, "DotRange"},
00644   { 0x0151, "TargetPrinter"},
00645   { 0x0152, "ExtraSample"},
00646   { 0x0153, "SampleFormat"},
00647   { 0x0154, "SMinSampleValue"},
00648   { 0x0155, "SMaxSampleValue"},
00649   { 0x0156, "TransferRange"},
00650   { 0x0157, "ClipPath"},
00651   { 0x0158, "XClipPathUnits"},
00652   { 0x0159, "YClipPathUnits"},
00653   { 0x015A, "Indexed"},
00654   { 0x015B, "JPEGTables"},
00655   { 0x015F, "OPIProxy"},
00656   { 0x0200, "JPEGProc"},
00657   { 0x0201, "JPEGInterchangeFormat"},
00658   { 0x0202, "JPEGInterchangeFormatLength"},
00659   { 0x0203, "JPEGRestartInterval"},
00660   { 0x0205, "JPEGLosslessPredictors"},
00661   { 0x0206, "JPEGPointTransforms"},
00662   { 0x0207, "JPEGQTables"},
00663   { 0x0208, "JPEGDCTables"},
00664   { 0x0209, "JPEGACTables"},
00665   { 0x0211, "YCbCrCoefficients"},
00666   { 0x0212, "YCbCrSubSampling"},
00667   { 0x0213, "YCbCrPositioning"},
00668   { 0x0214, "ReferenceBlackWhite"},
00669   { 0x02BC, "ExtensibleMetadataPlatform"}, /* XAP: Extensible Authoring Publishing, obsoleted by XMP: Extensible Metadata Platform */
00670   { 0x0301, "Gamma"}, 
00671   { 0x0302, "ICCProfileDescriptor"}, 
00672   { 0x0303, "SRGBRenderingIntent"}, 
00673   { 0x0320, "ImageTitle"}, 
00674   { 0x5001, "ResolutionXUnit"}, 
00675   { 0x5002, "ResolutionYUnit"}, 
00676   { 0x5003, "ResolutionXLengthUnit"}, 
00677   { 0x5004, "ResolutionYLengthUnit"}, 
00678   { 0x5005, "PrintFlags"}, 
00679   { 0x5006, "PrintFlagsVersion"}, 
00680   { 0x5007, "PrintFlagsCrop"}, 
00681   { 0x5008, "PrintFlagsBleedWidth"}, 
00682   { 0x5009, "PrintFlagsBleedWidthScale"}, 
00683   { 0x500A, "HalftoneLPI"}, 
00684   { 0x500B, "HalftoneLPIUnit"}, 
00685   { 0x500C, "HalftoneDegree"}, 
00686   { 0x500D, "HalftoneShape"}, 
00687   { 0x500E, "HalftoneMisc"}, 
00688   { 0x500F, "HalftoneScreen"}, 
00689   { 0x5010, "JPEGQuality"}, 
00690   { 0x5011, "GridSize"}, 
00691   { 0x5012, "ThumbnailFormat"}, 
00692   { 0x5013, "ThumbnailWidth"}, 
00693   { 0x5014, "ThumbnailHeight"}, 
00694   { 0x5015, "ThumbnailColorDepth"}, 
00695   { 0x5016, "ThumbnailPlanes"}, 
00696   { 0x5017, "ThumbnailRawBytes"}, 
00697   { 0x5018, "ThumbnailSize"}, 
00698   { 0x5019, "ThumbnailCompressedSize"}, 
00699   { 0x501A, "ColorTransferFunction"}, 
00700   { 0x501B, "ThumbnailData"}, 
00701   { 0x5020, "ThumbnailImageWidth"}, 
00702   { 0x5021, "ThumbnailImageHeight"}, 
00703   { 0x5022, "ThumbnailBitsPerSample"}, 
00704   { 0x5023, "ThumbnailCompression"}, 
00705   { 0x5024, "ThumbnailPhotometricInterp"}, 
00706   { 0x5025, "ThumbnailImageDescription"}, 
00707   { 0x5026, "ThumbnailEquipMake"}, 
00708   { 0x5027, "ThumbnailEquipModel"}, 
00709   { 0x5028, "ThumbnailStripOffsets"}, 
00710   { 0x5029, "ThumbnailOrientation"}, 
00711   { 0x502A, "ThumbnailSamplesPerPixel"}, 
00712   { 0x502B, "ThumbnailRowsPerStrip"}, 
00713   { 0x502C, "ThumbnailStripBytesCount"}, 
00714   { 0x502D, "ThumbnailResolutionX"}, 
00715   { 0x502E, "ThumbnailResolutionY"}, 
00716   { 0x502F, "ThumbnailPlanarConfig"}, 
00717   { 0x5030, "ThumbnailResolutionUnit"}, 
00718   { 0x5031, "ThumbnailTransferFunction"}, 
00719   { 0x5032, "ThumbnailSoftwareUsed"}, 
00720   { 0x5033, "ThumbnailDateTime"}, 
00721   { 0x5034, "ThumbnailArtist"}, 
00722   { 0x5035, "ThumbnailWhitePoint"}, 
00723   { 0x5036, "ThumbnailPrimaryChromaticities"}, 
00724   { 0x5037, "ThumbnailYCbCrCoefficients"}, 
00725   { 0x5038, "ThumbnailYCbCrSubsampling"}, 
00726   { 0x5039, "ThumbnailYCbCrPositioning"}, 
00727   { 0x503A, "ThumbnailRefBlackWhite"}, 
00728   { 0x503B, "ThumbnailCopyRight"}, 
00729   { 0x5090, "LuminanceTable"}, 
00730   { 0x5091, "ChrominanceTable"}, 
00731   { 0x5100, "FrameDelay"}, 
00732   { 0x5101, "LoopCount"}, 
00733   { 0x5110, "PixelUnit"}, 
00734   { 0x5111, "PixelPerUnitX"}, 
00735   { 0x5112, "PixelPerUnitY"}, 
00736   { 0x5113, "PaletteHistogram"}, 
00737   { 0x1000, "RelatedImageFileFormat"},
00738   { 0x800D, "ImageID"},
00739   { 0x80E3, "Matteing"},   /* obsoleted by ExtraSamples */
00740   { 0x80E4, "DataType"},   /* obsoleted by SampleFormat */
00741   { 0x80E5, "ImageDepth"},
00742   { 0x80E6, "TileDepth"},
00743   { 0x828D, "CFARepeatPatternDim"},
00744   { 0x828E, "CFAPattern"},
00745   { 0x828F, "BatteryLevel"},
00746   { 0x8298, "Copyright"},
00747   { 0x829A, "ExposureTime"},
00748   { 0x829D, "FNumber"},
00749   { 0x83BB, "IPTC/NAA"},
00750   { 0x84E3, "IT8RasterPadding"},
00751   { 0x84E5, "IT8ColorTable"},
00752   { 0x8649, "ImageResourceInformation"}, /* PhotoShop */
00753   { 0x8769, "Exif_IFD_Pointer"},
00754   { 0x8773, "ICC_Profile"},
00755   { 0x8822, "ExposureProgram"},
00756   { 0x8824, "SpectralSensity"},
00757   { 0x8828, "OECF"},
00758   { 0x8825, "GPS_IFD_Pointer"},
00759   { 0x8827, "ISOSpeedRatings"},
00760   { 0x8828, "OECF"},
00761   { 0x9000, "ExifVersion"},
00762   { 0x9003, "DateTimeOriginal"},
00763   { 0x9004, "DateTimeDigitized"},
00764   { 0x9101, "ComponentsConfiguration"},
00765   { 0x9102, "CompressedBitsPerPixel"},
00766   { 0x9201, "ShutterSpeedValue"},
00767   { 0x9202, "ApertureValue"},
00768   { 0x9203, "BrightnessValue"},
00769   { 0x9204, "ExposureBiasValue"},
00770   { 0x9205, "MaxApertureValue"},
00771   { 0x9206, "SubjectDistance"},
00772   { 0x9207, "MeteringMode"},
00773   { 0x9208, "LightSource"},
00774   { 0x9209, "Flash"},
00775   { 0x920A, "FocalLength"},
00776   { 0x920B, "FlashEnergy"},                 /* 0xA20B  in JPEG   */
00777   { 0x920C, "SpatialFrequencyResponse"},    /* 0xA20C    -  -    */
00778   { 0x920D, "Noise"},
00779   { 0x920E, "FocalPlaneXResolution"},       /* 0xA20E    -  -    */
00780   { 0x920F, "FocalPlaneYResolution"},       /* 0xA20F    -  -    */
00781   { 0x9210, "FocalPlaneResolutionUnit"},    /* 0xA210    -  -    */
00782   { 0x9211, "ImageNumber"},
00783   { 0x9212, "SecurityClassification"},
00784   { 0x9213, "ImageHistory"},
00785   { 0x9214, "SubjectLocation"},             /* 0xA214    -  -    */
00786   { 0x9215, "ExposureIndex"},               /* 0xA215    -  -    */
00787   { 0x9216, "TIFF/EPStandardID"},
00788   { 0x9217, "SensingMethod"},               /* 0xA217    -  -    */
00789   { 0x923F, "StoNits"},
00790   { 0x927C, "MakerNote"},
00791   { 0x9286, "UserComment"},
00792   { 0x9290, "SubSecTime"},
00793   { 0x9291, "SubSecTimeOriginal"},
00794   { 0x9292, "SubSecTimeDigitized"},
00795   { 0x935C, "ImageSourceData"},             /* "Adobe Photoshop Document Data Block": 8BIM... */
00796   { 0x9c9b, "Title" },                      /* Win XP specific, Unicode  */
00797   { 0x9c9c, "Comments" },                   /* Win XP specific, Unicode  */
00798   { 0x9c9d, "Author" },                     /* Win XP specific, Unicode  */
00799   { 0x9c9e, "Keywords" },                   /* Win XP specific, Unicode  */
00800   { 0x9c9f, "Subject" },                    /* Win XP specific, Unicode, not to be confused with SubjectDistance and SubjectLocation */
00801   { 0xA000, "FlashPixVersion"},
00802   { 0xA001, "ColorSpace"},
00803   { 0xA002, "ExifImageWidth"},
00804   { 0xA003, "ExifImageLength"},
00805   { 0xA004, "RelatedSoundFile"},
00806   { 0xA005, "InteroperabilityOffset"},
00807   { 0xA20B, "FlashEnergy"},                 /* 0x920B in TIFF/EP */
00808   { 0xA20C, "SpatialFrequencyResponse"},    /* 0x920C    -  -    */
00809   { 0xA20D, "Noise"},
00810   { 0xA20E, "FocalPlaneXResolution"},     /* 0x920E    -  -    */
00811   { 0xA20F, "FocalPlaneYResolution"},       /* 0x920F    -  -    */
00812   { 0xA210, "FocalPlaneResolutionUnit"},    /* 0x9210    -  -    */
00813   { 0xA211, "ImageNumber"},
00814   { 0xA212, "SecurityClassification"},
00815   { 0xA213, "ImageHistory"},
00816   { 0xA214, "SubjectLocation"},             /* 0x9214    -  -    */
00817   { 0xA215, "ExposureIndex"},               /* 0x9215    -  -    */
00818   { 0xA216, "TIFF/EPStandardID"},
00819   { 0xA217, "SensingMethod"},               /* 0x9217    -  -    */
00820   { 0xA300, "FileSource"},
00821   { 0xA301, "SceneType"},
00822   { 0xA302, "CFAPattern"},
00823   { 0xA401, "CustomRendered"},
00824   { 0xA402, "ExposureMode"},
00825   { 0xA403, "WhiteBalance"},
00826   { 0xA404, "DigitalZoomRatio"},
00827   { 0xA405, "FocalLengthIn35mmFilm"},
00828   { 0xA406, "SceneCaptureType"},
00829   { 0xA407, "GainControl"},
00830   { 0xA408, "Contrast"},
00831   { 0xA409, "Saturation"},
00832   { 0xA40A, "Sharpness"},
00833   { 0xA40B, "DeviceSettingDescription"},
00834   { 0xA40C, "SubjectDistanceRange"},
00835   { 0xA420, "ImageUniqueID"},
00836   TAG_TABLE_END
00837 } ;
00838 
00839 static tag_info_array tag_table_GPS = {
00840   { 0x0000, "GPSVersion"},
00841   { 0x0001, "GPSLatitudeRef"},
00842   { 0x0002, "GPSLatitude"},
00843   { 0x0003, "GPSLongitudeRef"},
00844   { 0x0004, "GPSLongitude"},
00845   { 0x0005, "GPSAltitudeRef"},
00846   { 0x0006, "GPSAltitude"},
00847   { 0x0007, "GPSTimeStamp"},
00848   { 0x0008, "GPSSatellites"},
00849   { 0x0009, "GPSStatus"},
00850   { 0x000A, "GPSMeasureMode"},
00851   { 0x000B, "GPSDOP"},
00852   { 0x000C, "GPSSpeedRef"},
00853   { 0x000D, "GPSSpeed"},
00854   { 0x000E, "GPSTrackRef"},
00855   { 0x000F, "GPSTrack"},
00856   { 0x0010, "GPSImgDirectionRef"},
00857   { 0x0011, "GPSImgDirection"},
00858   { 0x0012, "GPSMapDatum"},
00859   { 0x0013, "GPSDestLatitudeRef"},
00860   { 0x0014, "GPSDestLatitude"},
00861   { 0x0015, "GPSDestLongitudeRef"},
00862   { 0x0016, "GPSDestLongitude"},
00863   { 0x0017, "GPSDestBearingRef"},
00864   { 0x0018, "GPSDestBearing"},
00865   { 0x0019, "GPSDestDistanceRef"},
00866   { 0x001A, "GPSDestDistance"},
00867   { 0x001B, "GPSProcessingMode"},
00868   { 0x001C, "GPSAreaInformation"},
00869   { 0x001D, "GPSDateStamp"},
00870   { 0x001E, "GPSDifferential"},
00871   TAG_TABLE_END
00872 };
00873 
00874 static tag_info_array tag_table_IOP = {
00875   { 0x0001, "InterOperabilityIndex"}, /* should be 'R98' or 'THM' */
00876   { 0x0002, "InterOperabilityVersion"},
00877   { 0x1000, "RelatedFileFormat"},
00878   { 0x1001, "RelatedImageWidth"},
00879   { 0x1002, "RelatedImageHeight"},
00880   TAG_TABLE_END
00881 };
00882 
00883 static tag_info_array tag_table_VND_CANON = {
00884   { 0x0001, "ModeArray"}, /* guess */
00885   { 0x0004, "ImageInfo"}, /* guess */
00886   { 0x0006, "ImageType"},
00887   { 0x0007, "FirmwareVersion"},
00888   { 0x0008, "ImageNumber"},
00889   { 0x0009, "OwnerName"},
00890   { 0x000C, "Camera"},
00891   { 0x000F, "CustomFunctions"},
00892   TAG_TABLE_END
00893 };
00894 
00895 static tag_info_array tag_table_VND_CASIO = {
00896   { 0x0001, "RecordingMode"},
00897   { 0x0002, "Quality"},
00898   { 0x0003, "FocusingMode"},
00899   { 0x0004, "FlashMode"},
00900   { 0x0005, "FlashIntensity"},
00901   { 0x0006, "ObjectDistance"},
00902   { 0x0007, "WhiteBalance"},
00903   { 0x000A, "DigitalZoom"},
00904   { 0x000B, "Sharpness"},
00905   { 0x000C, "Contrast"},
00906   { 0x000D, "Saturation"},
00907   { 0x0014, "CCDSensitivity"},
00908   TAG_TABLE_END
00909 };
00910 
00911 static tag_info_array tag_table_VND_FUJI = {
00912   { 0x0000, "Version"},
00913   { 0x1000, "Quality"},
00914   { 0x1001, "Sharpness"},
00915   { 0x1002, "WhiteBalance"},
00916   { 0x1003, "Color"},
00917   { 0x1004, "Tone"},
00918   { 0x1010, "FlashMode"},
00919   { 0x1011, "FlashStrength"},
00920   { 0x1020, "Macro"},
00921   { 0x1021, "FocusMode"},
00922   { 0x1030, "SlowSync"},
00923   { 0x1031, "PictureMode"},
00924   { 0x1100, "ContTake"},
00925   { 0x1300, "BlurWarning"},
00926   { 0x1301, "FocusWarning"},
00927   { 0x1302, "AEWarning "},
00928   TAG_TABLE_END
00929 };
00930 
00931 static tag_info_array tag_table_VND_NIKON = {
00932   { 0x0003, "Quality"},
00933   { 0x0004, "ColorMode"},
00934   { 0x0005, "ImageAdjustment"},
00935   { 0x0006, "CCDSensitivity"},
00936   { 0x0007, "WhiteBalance"},
00937   { 0x0008, "Focus"},
00938   { 0x000a, "DigitalZoom"},
00939   { 0x000b, "Converter"},
00940   TAG_TABLE_END
00941 };
00942   
00943 static tag_info_array tag_table_VND_NIKON_990 = {
00944   { 0x0001, "Version"},
00945   { 0x0002, "ISOSetting"},
00946   { 0x0003, "ColorMode"},
00947   { 0x0004, "Quality"},
00948   { 0x0005, "WhiteBalance"},
00949   { 0x0006, "ImageSharpening"},
00950   { 0x0007, "FocusMode"},
00951   { 0x0008, "FlashSetting"},
00952   { 0x000F, "ISOSelection"},
00953   { 0x0080, "ImageAdjustment"},
00954   { 0x0082, "AuxiliaryLens"},
00955   { 0x0085, "ManualFocusDistance"},
00956   { 0x0086, "DigitalZoom"},
00957   { 0x0088, "AFFocusPosition"},
00958   { 0x0010, "DataDump"},
00959   TAG_TABLE_END
00960 };
00961   
00962 static tag_info_array tag_table_VND_OLYMPUS = {
00963   { 0x0200, "SpecialMode"},
00964   { 0x0201, "JPEGQuality"},
00965   { 0x0202, "Macro"},
00966   { 0x0204, "DigitalZoom"},
00967   { 0x0207, "SoftwareRelease"},
00968   { 0x0208, "PictureInfo"},
00969   { 0x0209, "CameraId"},
00970   { 0x0F00, "DataDump"},
00971   TAG_TABLE_END
00972 };
00973 
00974 typedef enum mn_byte_order_t {
00975        MN_ORDER_INTEL    = 0,
00976        MN_ORDER_MOTOROLA = 1,
00977        MN_ORDER_NORMAL
00978 } mn_byte_order_t;
00979 
00980 typedef enum mn_offset_mode_t {
00981        MN_OFFSET_NORMAL,
00982        MN_OFFSET_MAKER,
00983        MN_OFFSET_GUESS
00984 } mn_offset_mode_t;
00985 
00986 typedef struct {
00987        tag_table_type   tag_table;
00988        char *           make;
00989        char *           model;
00990        char *           id_string;
00991        int              id_string_len;
00992        int              offset;
00993        mn_byte_order_t  byte_order;
00994        mn_offset_mode_t offset_mode;
00995 } maker_note_type;
00996 
00997 static const maker_note_type maker_note_array[] = {
00998   { tag_table_VND_CANON,     "Canon",                   NULL,  NULL,                       0,  0,  MN_ORDER_INTEL,    MN_OFFSET_GUESS},
00999 /*  { tag_table_VND_CANON,     "Canon",                   NULL,  NULL,                       0,  0,  MN_ORDER_NORMAL,   MN_OFFSET_NORMAL},*/
01000   { tag_table_VND_CASIO,     "CASIO",                   NULL,  NULL,                       0,  0,  MN_ORDER_MOTOROLA, MN_OFFSET_NORMAL},
01001   { tag_table_VND_FUJI,      "FUJIFILM",                NULL,  "FUJIFILM\x0C\x00\x00\x00", 12, 12, MN_ORDER_INTEL,    MN_OFFSET_MAKER},
01002   { tag_table_VND_NIKON,     "NIKON",                   NULL,  "Nikon\x00\x01\x00",        8,  8,  MN_ORDER_NORMAL,   MN_OFFSET_NORMAL},
01003   { tag_table_VND_NIKON_990, "NIKON",                   NULL,  NULL,                       0,  0,  MN_ORDER_NORMAL,   MN_OFFSET_NORMAL},
01004   { tag_table_VND_OLYMPUS,   "OLYMPUS OPTICAL CO.,LTD", NULL,  "OLYMP\x00\x01\x00",        8,  8,  MN_ORDER_NORMAL,   MN_OFFSET_NORMAL},
01005 };
01006 /* }}} */
01007 
01008 /* {{{ exif_get_tagname
01009        Get headername for tag_num or NULL if not defined */
01010 static char * exif_get_tagname(int tag_num, char *ret, int len, tag_table_type tag_table TSRMLS_DC)
01011 {
01012        int i, t;
01013        char tmp[32];
01014 
01015        for (i = 0; (t = tag_table[i].Tag) != TAG_END_OF_LIST; i++) {
01016               if (t == tag_num) {
01017                      if (ret && len)  {
01018                             strlcpy(ret, tag_table[i].Desc, abs(len));
01019                             if (len < 0) {
01020                                    memset(ret + strlen(ret), ' ', -len - strlen(ret) - 1);
01021                                    ret[-len - 1] = '\0';
01022                             }
01023                             return ret;
01024                      }
01025                      return tag_table[i].Desc;
01026               }
01027        }
01028 
01029        if (ret && len) {
01030               snprintf(tmp, sizeof(tmp), "UndefinedTag:0x%04X", tag_num);
01031               strlcpy(ret, tmp, abs(len));
01032               if (len < 0) {
01033                      memset(ret + strlen(ret), ' ', -len - strlen(ret) - 1);
01034                      ret[-len - 1] = '\0';
01035               }
01036               return ret;
01037        }
01038        return "";
01039 }
01040 /* }}} */
01041 
01042 /* {{{ exif_char_dump
01043  * Do not use! This is a debug function... */
01044 #ifdef EXIF_DEBUG
01045 static unsigned char* exif_char_dump(unsigned char * addr, int len, int offset)
01046 {
01047        static unsigned char buf[4096+1];
01048        static unsigned char tmp[20];
01049        int c, i, p=0, n = 5+31;
01050 
01051        p += slprintf(buf+p, sizeof(buf)-p, "\nDump Len: %08X (%d)", len, len);
01052        if (len) {
01053               for(i=0; i<len+15 && p+n<=sizeof(buf); i++) {
01054                      if (i%16==0) {
01055                             p += slprintf(buf+p, sizeof(buf)-p, "\n%08X: ", i+offset);
01056                      }
01057                      if (i<len) {
01058                             c = *addr++;
01059                             p += slprintf(buf+p, sizeof(buf)-p, "%02X ", c);
01060                             tmp[i%16] = c>=32 ? c : '.';
01061                             tmp[(i%16)+1] = '\0';
01062                      } else {
01063                             p += slprintf(buf+p, sizeof(buf)-p, "   ");
01064                      }
01065                      if (i%16==15) {
01066                             p += slprintf(buf+p, sizeof(buf)-p, "    %s", tmp);
01067                             if (i>=len) {
01068                                    break;
01069                             }
01070                      }
01071               }
01072        }
01073        buf[sizeof(buf)-1] = '\0';
01074        return buf;
01075 }
01076 #endif
01077 /* }}} */
01078 
01079 /* {{{ php_jpg_get16
01080    Get 16 bits motorola order (always) for jpeg header stuff.
01081 */
01082 static int php_jpg_get16(void *value)
01083 {
01084        return (((uchar *)value)[0] << 8) | ((uchar *)value)[1];
01085 }
01086 /* }}} */
01087 
01088 /* {{{ php_ifd_get16u
01089  * Convert a 16 bit unsigned value from file's native byte order */
01090 static int php_ifd_get16u(void *value, int motorola_intel)
01091 {
01092        if (motorola_intel) {
01093               return (((uchar *)value)[0] << 8) | ((uchar *)value)[1];
01094        } else {
01095               return (((uchar *)value)[1] << 8) | ((uchar *)value)[0];
01096        }
01097 }
01098 /* }}} */
01099 
01100 /* {{{ php_ifd_get16s
01101  * Convert a 16 bit signed value from file's native byte order */
01102 static signed short php_ifd_get16s(void *value, int motorola_intel)
01103 {
01104        return (signed short)php_ifd_get16u(value, motorola_intel);
01105 }
01106 /* }}} */
01107 
01108 /* {{{ php_ifd_get32s
01109  * Convert a 32 bit signed value from file's native byte order */
01110 static int php_ifd_get32s(void *value, int motorola_intel)
01111 {
01112        if (motorola_intel) {
01113               return  (((char  *)value)[0] << 24)
01114                        | (((uchar *)value)[1] << 16)
01115                        | (((uchar *)value)[2] << 8 )
01116                        | (((uchar *)value)[3]      );
01117        } else {
01118               return  (((char  *)value)[3] << 24)
01119                        | (((uchar *)value)[2] << 16)
01120                        | (((uchar *)value)[1] << 8 )
01121                        | (((uchar *)value)[0]      );
01122        }
01123 }
01124 /* }}} */
01125 
01126 /* {{{ php_ifd_get32u
01127  * Write 32 bit unsigned value to data */
01128 static unsigned php_ifd_get32u(void *value, int motorola_intel)
01129 {
01130        return (unsigned)php_ifd_get32s(value, motorola_intel) & 0xffffffff;
01131 }
01132 /* }}} */
01133 
01134 /* {{{ php_ifd_set16u
01135  * Write 16 bit unsigned value to data */
01136 static void php_ifd_set16u(char *data, unsigned int value, int motorola_intel)
01137 {
01138        if (motorola_intel) {
01139               data[0] = (value & 0xFF00) >> 8;
01140               data[1] = (value & 0x00FF);
01141        } else {
01142               data[1] = (value & 0xFF00) >> 8;
01143               data[0] = (value & 0x00FF);
01144        }
01145 }
01146 /* }}} */
01147 
01148 /* {{{ php_ifd_set32u
01149  * Convert a 32 bit unsigned value from file's native byte order */
01150 static void php_ifd_set32u(char *data, size_t value, int motorola_intel)
01151 {
01152        if (motorola_intel) {
01153               data[0] = (value & 0xFF000000) >> 24;
01154               data[1] = (value & 0x00FF0000) >> 16;
01155               data[2] = (value & 0x0000FF00) >>  8;
01156               data[3] = (value & 0x000000FF);
01157        } else {
01158               data[3] = (value & 0xFF000000) >> 24;
01159               data[2] = (value & 0x00FF0000) >> 16;
01160               data[1] = (value & 0x0000FF00) >>  8;
01161               data[0] = (value & 0x000000FF);
01162        }
01163 }
01164 /* }}} */
01165 
01166 #ifdef EXIF_DEBUG
01167 char * exif_dump_data(int *dump_free, int format, int components, int length, int motorola_intel, char *value_ptr TSRMLS_DC) /* {{{ */
01168 {
01169        char *dump;
01170        int len;
01171 
01172        *dump_free = 0;
01173        if (format == TAG_FMT_STRING) {
01174               return value_ptr ? value_ptr : "<no data>";
01175        }
01176        if (format == TAG_FMT_UNDEFINED) {
01177               return "<undefined>\n";
01178        }
01179        if (format == TAG_FMT_IFD) {
01180               return "";
01181        }
01182        if (format == TAG_FMT_SINGLE || format == TAG_FMT_DOUBLE) {
01183               return "<not implemented>";
01184        }
01185        *dump_free = 1;
01186        if (components > 1) {
01187               len = spprintf(&dump, 0, "(%d,%d) {", components, length);
01188        } else {
01189               len = spprintf(&dump, 0, "{");
01190        }
01191        while(components > 0) {
01192               switch(format) {
01193                      case TAG_FMT_BYTE:
01194                      case TAG_FMT_UNDEFINED:
01195                      case TAG_FMT_STRING:
01196                      case TAG_FMT_SBYTE:
01197                             dump = erealloc(dump, len + 4 + 1);
01198                             snprintf(dump + len, 4 + 1, "0x%02X", *value_ptr);
01199                             len += 4;
01200                             value_ptr++;
01201                             break;
01202                      case TAG_FMT_USHORT:
01203                      case TAG_FMT_SSHORT:
01204                             dump = erealloc(dump, len + 6 + 1);
01205                             snprintf(dump + len, 6 + 1, "0x%04X", php_ifd_get16s(value_ptr, motorola_intel));
01206                             len += 6;
01207                             value_ptr += 2;
01208                             break;
01209                      case TAG_FMT_ULONG:
01210                      case TAG_FMT_SLONG:
01211                             dump = erealloc(dump, len + 6 + 1);
01212                             snprintf(dump + len, 6 + 1, "0x%04X", php_ifd_get32s(value_ptr, motorola_intel));
01213                             len += 6;
01214                             value_ptr += 4;
01215                             break;
01216                      case TAG_FMT_URATIONAL:
01217                      case TAG_FMT_SRATIONAL:
01218                             dump = erealloc(dump, len + 13 + 1);
01219                             snprintf(dump + len, 13 + 1, "0x%04X/0x%04X", php_ifd_get32s(value_ptr, motorola_intel), php_ifd_get32s(value_ptr+4, motorola_intel));
01220                             len += 13;
01221                             value_ptr += 8;
01222                             break;
01223               }
01224               if (components > 0) {
01225                      dump = erealloc(dump, len + 2 + 1);
01226                      snprintf(dump + len, 2 + 1, ", ");
01227                      len += 2;                   
01228                      components--;
01229               } else{
01230                      break;
01231               }
01232        }
01233        dump = erealloc(dump, len + 1 + 1);
01234        snprintf(dump + len, 1 + 1, "}");
01235        return dump;
01236 }
01237 /* }}} */
01238 #endif
01239 
01240 /* {{{ exif_convert_any_format
01241  * Evaluate number, be it int, rational, or float from directory. */
01242 static double exif_convert_any_format(void *value, int format, int motorola_intel TSRMLS_DC)
01243 {
01244        int           s_den;
01245        unsigned      u_den;
01246 
01247        switch(format) {
01248               case TAG_FMT_SBYTE:     return *(signed char *)value;
01249               case TAG_FMT_BYTE:      return *(uchar *)value;
01250 
01251               case TAG_FMT_USHORT:    return php_ifd_get16u(value, motorola_intel);
01252               case TAG_FMT_ULONG:     return php_ifd_get32u(value, motorola_intel);
01253 
01254               case TAG_FMT_URATIONAL:
01255                      u_den = php_ifd_get32u(4+(char *)value, motorola_intel);
01256                      if (u_den == 0) {
01257                             return 0;
01258                      } else {
01259                             return (double)php_ifd_get32u(value, motorola_intel) / u_den;
01260                      }
01261 
01262               case TAG_FMT_SRATIONAL:
01263                      s_den = php_ifd_get32s(4+(char *)value, motorola_intel);
01264                      if (s_den == 0) {
01265                             return 0;
01266                      } else {
01267                             return (double)php_ifd_get32s(value, motorola_intel) / s_den;
01268                      }
01269 
01270               case TAG_FMT_SSHORT:    return (signed short)php_ifd_get16u(value, motorola_intel);
01271               case TAG_FMT_SLONG:     return php_ifd_get32s(value, motorola_intel);
01272 
01273               /* Not sure if this is correct (never seen float used in Exif format) */
01274               case TAG_FMT_SINGLE:
01275 #ifdef EXIF_DEBUG
01276                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type single");
01277 #endif
01278                      return (double)*(float *)value;
01279               case TAG_FMT_DOUBLE:
01280 #ifdef EXIF_DEBUG
01281                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type double");
01282 #endif
01283                      return *(double *)value;
01284        }
01285        return 0;
01286 }
01287 /* }}} */
01288 
01289 /* {{{ exif_convert_any_to_int
01290  * Evaluate number, be it int, rational, or float from directory. */
01291 static size_t exif_convert_any_to_int(void *value, int format, int motorola_intel TSRMLS_DC)
01292 {
01293        int           s_den;
01294        unsigned      u_den;
01295 
01296        switch(format) {
01297               case TAG_FMT_SBYTE:     return *(signed char *)value;
01298               case TAG_FMT_BYTE:      return *(uchar *)value;
01299 
01300               case TAG_FMT_USHORT:    return php_ifd_get16u(value, motorola_intel);
01301               case TAG_FMT_ULONG:     return php_ifd_get32u(value, motorola_intel);
01302 
01303               case TAG_FMT_URATIONAL:
01304                      u_den = php_ifd_get32u(4+(char *)value, motorola_intel);
01305                      if (u_den == 0) {
01306                             return 0;
01307                      } else {
01308                             return php_ifd_get32u(value, motorola_intel) / u_den;
01309                      }
01310 
01311               case TAG_FMT_SRATIONAL:
01312                      s_den = php_ifd_get32s(4+(char *)value, motorola_intel);
01313                      if (s_den == 0) {
01314                             return 0;
01315                      } else {
01316                             return php_ifd_get32s(value, motorola_intel) / s_den;
01317                      }
01318 
01319               case TAG_FMT_SSHORT:    return php_ifd_get16u(value, motorola_intel);
01320               case TAG_FMT_SLONG:     return php_ifd_get32s(value, motorola_intel);
01321 
01322               /* Not sure if this is correct (never seen float used in Exif format) */
01323               case TAG_FMT_SINGLE:
01324 #ifdef EXIF_DEBUG
01325                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type single");
01326 #endif
01327                      return (size_t)*(float *)value;
01328               case TAG_FMT_DOUBLE:
01329 #ifdef EXIF_DEBUG
01330                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type double");
01331 #endif
01332                      return (size_t)*(double *)value;
01333        }
01334        return 0;
01335 }
01336 /* }}} */
01337 
01338 /* {{{ struct image_info_value, image_info_list
01339 */
01340 #ifndef WORD
01341 #define WORD unsigned short
01342 #endif
01343 #ifndef DWORD
01344 #define DWORD unsigned int
01345 #endif
01346 
01347 typedef struct {
01348        int             num;
01349        int             den;
01350 } signed_rational;
01351 
01352 typedef struct {
01353        unsigned int    num;
01354        unsigned int    den;
01355 } unsigned_rational;
01356 
01357 typedef union _image_info_value {
01358        char                        *s;
01359        unsigned            u;
01360        int                         i;
01361        float               f;
01362        double              d;
01363        signed_rational      sr;
01364        unsigned_rational    ur;
01365        union _image_info_value   *list;
01366 } image_info_value;
01367 
01368 typedef struct {
01369        WORD                tag;
01370        WORD                format;
01371        DWORD               length;
01372        DWORD               dummy;  /* value ptr of tiff directory entry */
01373        char                        *name;
01374        image_info_value    value;
01375 } image_info_data;
01376 
01377 typedef struct {
01378        int                 count;
01379        image_info_data      *list;
01380 } image_info_list;
01381 /* }}} */
01382 
01383 /* {{{ exif_get_sectionname
01384  Returns the name of a section
01385 */
01386 #define SECTION_FILE        0
01387 #define SECTION_COMPUTED    1
01388 #define SECTION_ANY_TAG     2
01389 #define SECTION_IFD0        3
01390 #define SECTION_THUMBNAIL   4
01391 #define SECTION_COMMENT     5
01392 #define SECTION_APP0        6
01393 #define SECTION_EXIF        7
01394 #define SECTION_FPIX        8
01395 #define SECTION_GPS         9
01396 #define SECTION_INTEROP     10
01397 #define SECTION_APP12       11
01398 #define SECTION_WINXP       12
01399 #define SECTION_MAKERNOTE   13
01400 #define SECTION_COUNT       14
01401 
01402 #define FOUND_FILE          (1<<SECTION_FILE)
01403 #define FOUND_COMPUTED      (1<<SECTION_COMPUTED)
01404 #define FOUND_ANY_TAG       (1<<SECTION_ANY_TAG)
01405 #define FOUND_IFD0          (1<<SECTION_IFD0)
01406 #define FOUND_THUMBNAIL     (1<<SECTION_THUMBNAIL)
01407 #define FOUND_COMMENT       (1<<SECTION_COMMENT)
01408 #define FOUND_APP0          (1<<SECTION_APP0)
01409 #define FOUND_EXIF          (1<<SECTION_EXIF)
01410 #define FOUND_FPIX          (1<<SECTION_FPIX)
01411 #define FOUND_GPS           (1<<SECTION_GPS)
01412 #define FOUND_INTEROP       (1<<SECTION_INTEROP)
01413 #define FOUND_APP12         (1<<SECTION_APP12)
01414 #define FOUND_WINXP         (1<<SECTION_WINXP)
01415 #define FOUND_MAKERNOTE     (1<<SECTION_MAKERNOTE)
01416 
01417 static char *exif_get_sectionname(int section)
01418 {
01419        switch(section) {
01420               case SECTION_FILE:      return "FILE";
01421               case SECTION_COMPUTED:  return "COMPUTED";
01422               case SECTION_ANY_TAG:   return "ANY_TAG";
01423               case SECTION_IFD0:      return "IFD0";
01424               case SECTION_THUMBNAIL: return "THUMBNAIL";
01425               case SECTION_COMMENT:   return "COMMENT";
01426               case SECTION_APP0:      return "APP0";
01427               case SECTION_EXIF:      return "EXIF";
01428               case SECTION_FPIX:      return "FPIX";
01429               case SECTION_GPS:       return "GPS";
01430               case SECTION_INTEROP:   return "INTEROP";
01431               case SECTION_APP12:     return "APP12";
01432               case SECTION_WINXP:     return "WINXP";
01433               case SECTION_MAKERNOTE: return "MAKERNOTE";
01434        }
01435        return "";
01436 }
01437 
01438 static tag_table_type exif_get_tag_table(int section)
01439 {
01440        switch(section) {
01441               case SECTION_FILE:      return &tag_table_IFD[0];
01442               case SECTION_COMPUTED:  return &tag_table_IFD[0];
01443               case SECTION_ANY_TAG:   return &tag_table_IFD[0];
01444               case SECTION_IFD0:      return &tag_table_IFD[0];
01445               case SECTION_THUMBNAIL: return &tag_table_IFD[0];
01446               case SECTION_COMMENT:   return &tag_table_IFD[0];
01447               case SECTION_APP0:      return &tag_table_IFD[0];
01448               case SECTION_EXIF:      return &tag_table_IFD[0];
01449               case SECTION_FPIX:      return &tag_table_IFD[0];
01450               case SECTION_GPS:       return &tag_table_GPS[0];
01451               case SECTION_INTEROP:   return &tag_table_IOP[0];
01452               case SECTION_APP12:     return &tag_table_IFD[0];
01453               case SECTION_WINXP:     return &tag_table_IFD[0];
01454        }
01455        return &tag_table_IFD[0];
01456 }
01457 /* }}} */
01458 
01459 /* {{{ exif_get_sectionlist
01460    Return list of sectionnames specified by sectionlist. Return value must be freed
01461 */
01462 static char *exif_get_sectionlist(int sectionlist TSRMLS_DC)
01463 {
01464        int i, len, ml = 0;
01465        char *sections;
01466 
01467        for(i=0; i<SECTION_COUNT; i++) {
01468               ml += strlen(exif_get_sectionname(i))+2;
01469        }
01470        sections = safe_emalloc(ml, 1, 1);
01471        sections[0] = '\0';
01472        len = 0;
01473        for(i=0; i<SECTION_COUNT; i++) {
01474               if (sectionlist&(1<<i)) {
01475                      snprintf(sections+len, ml-len, "%s, ", exif_get_sectionname(i));
01476                      len = strlen(sections);
01477               }
01478        }
01479        if (len>2)
01480               sections[len-2] = '\0';
01481        return sections;
01482 }
01483 /* }}} */
01484 
01485 /* {{{ struct image_info_type
01486    This structure stores Exif header image elements in a simple manner
01487    Used to store camera data as extracted from the various ways that it can be
01488    stored in a nexif header
01489 */
01490 
01491 typedef struct {
01492        int     type;
01493        size_t  size;
01494        uchar   *data;
01495 } file_section;
01496 
01497 typedef struct {
01498        int             count;
01499        file_section    *list;
01500 } file_section_list;
01501 
01502 typedef struct {
01503        image_filetype  filetype;
01504        size_t          width, height;
01505        size_t          size;
01506        size_t          offset;
01507        char           *data;
01508 } thumbnail_data;
01509 
01510 typedef struct {
01511        char                 *value;
01512        size_t               size;
01513        int                         tag;
01514 } xp_field_type;
01515 
01516 typedef struct {
01517        int             count;
01518        xp_field_type   *list;
01519 } xp_field_list;
01520 
01521 /* This structure is used to store a section of a Jpeg file. */
01522 typedef struct {
01523        php_stream      *infile;
01524        char            *FileName;
01525        time_t          FileDateTime;
01526        size_t          FileSize;
01527        image_filetype  FileType;
01528        int             Height, Width;
01529        int             IsColor;
01530 
01531        char            *make;
01532        char            *model;
01533 
01534        float           ApertureFNumber;
01535        float           ExposureTime;
01536        double          FocalplaneUnits;
01537        float           CCDWidth;
01538        double          FocalplaneXRes;
01539        size_t          ExifImageWidth;
01540        float           FocalLength;
01541        float           Distance;
01542 
01543        int             motorola_intel; /* 1 Motorola; 0 Intel */
01544 
01545        char            *UserComment;
01546        int             UserCommentLength;
01547        char            *UserCommentEncoding;
01548        char            *encode_unicode;
01549        char            *decode_unicode_be;
01550        char            *decode_unicode_le;
01551        char            *encode_jis;
01552        char            *decode_jis_be;
01553        char            *decode_jis_le;
01554        char            *Copyright;/* EXIF standard defines Copyright as "<Photographer> [ '\0' <Editor> ] ['\0']" */
01555        char            *CopyrightPhotographer;
01556        char            *CopyrightEditor;
01557 
01558        xp_field_list   xp_fields;
01559 
01560        thumbnail_data  Thumbnail;
01561        /* other */
01562        int             sections_found; /* FOUND_<marker> */
01563        image_info_list info_list[SECTION_COUNT];
01564        /* for parsing */
01565        int             read_thumbnail;
01566        int             read_all;
01567        int             ifd_nesting_level;
01568        /* internal */
01569        file_section_list    file;
01570 } image_info_type;
01571 /* }}} */
01572 
01573 /* {{{ exif_error_docref */
01574 static void exif_error_docref(const char *docref EXIFERR_DC, const image_info_type *ImageInfo, int type, const char *format, ...)
01575 {
01576        va_list args;
01577        
01578        va_start(args, format);
01579 #ifdef EXIF_DEBUG
01580        {
01581               char *buf;
01582 
01583               spprintf(&buf, 0, "%s(%d): %s", _file, _line, format);
01584               php_verror(docref, ImageInfo->FileName?ImageInfo->FileName:"", type, buf, args TSRMLS_CC);
01585               efree(buf);
01586        }
01587 #else
01588        php_verror(docref, ImageInfo->FileName?ImageInfo->FileName:"", type, format, args TSRMLS_CC);
01589 #endif
01590        va_end(args);
01591 }
01592 /* }}} */
01593 
01594 /* {{{ jpeg_sof_info
01595  */
01596 typedef struct {
01597        int     bits_per_sample;
01598        size_t  width;
01599        size_t  height;
01600        int     num_components;
01601 } jpeg_sof_info;
01602 /* }}} */
01603 
01604 /* {{{ exif_file_sections_add
01605  Add a file_section to image_info
01606  returns the used block or -1. if size>0 and data == NULL buffer of size is allocated
01607 */
01608 static int exif_file_sections_add(image_info_type *ImageInfo, int type, size_t size, uchar *data)
01609 {
01610        file_section    *tmp;
01611        int             count = ImageInfo->file.count;
01612 
01613        tmp = safe_erealloc(ImageInfo->file.list, (count+1), sizeof(file_section), 0);
01614        ImageInfo->file.list = tmp;
01615        ImageInfo->file.list[count].type = 0xFFFF;
01616        ImageInfo->file.list[count].data = NULL;
01617        ImageInfo->file.list[count].size = 0;
01618        ImageInfo->file.count = count+1;
01619        if (!size) {
01620               data = NULL;
01621        } else if (data == NULL) {
01622               data = safe_emalloc(size, 1, 0);
01623        }
01624        ImageInfo->file.list[count].type = type;
01625        ImageInfo->file.list[count].data = data;
01626        ImageInfo->file.list[count].size = size;
01627        return count;
01628 }
01629 /* }}} */
01630 
01631 /* {{{ exif_file_sections_realloc
01632  Reallocate a file section returns 0 on success and -1 on failure
01633 */
01634 static int exif_file_sections_realloc(image_info_type *ImageInfo, int section_index, size_t size TSRMLS_DC)
01635 {
01636        void *tmp;
01637 
01638        /* This is not a malloc/realloc check. It is a plausibility check for the
01639         * function parameters (requirements engineering).
01640         */
01641        if (section_index >= ImageInfo->file.count) {
01642               EXIF_ERRLOG_FSREALLOC(ImageInfo)
01643               return -1;
01644        }
01645        tmp = safe_erealloc(ImageInfo->file.list[section_index].data, 1, size, 0);
01646        ImageInfo->file.list[section_index].data = tmp;
01647        ImageInfo->file.list[section_index].size = size;
01648        return 0;
01649 }
01650 /* }}} */
01651 
01652 /* {{{ exif_file_section_free
01653    Discard all file_sections in ImageInfo
01654 */
01655 static int exif_file_sections_free(image_info_type *ImageInfo)
01656 {
01657        int i;
01658 
01659        if (ImageInfo->file.count) {
01660               for (i=0; i<ImageInfo->file.count; i++) {
01661                      EFREE_IF(ImageInfo->file.list[i].data);
01662               }
01663        }
01664        EFREE_IF(ImageInfo->file.list);
01665        ImageInfo->file.count = 0;
01666        return TRUE;
01667 }
01668 /* }}} */
01669 
01670 /* {{{ exif_iif_add_value
01671  Add a value to image_info
01672 */
01673 static void exif_iif_add_value(image_info_type *image_info, int section_index, char *name, int tag, int format, int length, void* value, int motorola_intel TSRMLS_DC)
01674 {
01675        size_t idex;
01676        void *vptr;
01677        image_info_value *info_value;
01678        image_info_data  *info_data;
01679        image_info_data  *list;
01680 
01681        if (length < 0) {
01682               return;
01683        }
01684 
01685        list = safe_erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1), sizeof(image_info_data), 0);
01686        image_info->info_list[section_index].list = list;
01687 
01688        info_data  = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
01689        memset(info_data, 0, sizeof(image_info_data));
01690        info_data->tag    = tag;
01691        info_data->format = format;
01692        info_data->length = length;
01693        info_data->name   = estrdup(name);
01694        info_value        = &info_data->value;
01695 
01696        switch (format) {
01697               case TAG_FMT_STRING:
01698                      if (value) {
01699                             length = php_strnlen(value, length);
01700                             if (PG(magic_quotes_runtime)) {
01701                                    info_value->s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
01702                             } else {
01703                                    info_value->s = estrndup(value, length);
01704                             }
01705                             info_data->length = length;
01706                      } else {
01707                             info_data->length = 0;
01708                             info_value->s = estrdup("");
01709                      }
01710                      break;
01711 
01712               default:
01713                      /* Standard says more types possible but skip them...
01714                       * but allow users to handle data if they know how to
01715                       * So not return but use type UNDEFINED
01716                       * return;
01717                       */
01718                      info_data->tag = TAG_FMT_UNDEFINED;/* otherwise not freed from memory */
01719               case TAG_FMT_SBYTE:
01720               case TAG_FMT_BYTE:
01721               /* in contrast to strings bytes do not need to allocate buffer for NULL if length==0 */
01722                      if (!length)
01723                             break;
01724               case TAG_FMT_UNDEFINED:
01725                      if (value) {
01726                             /* do not recompute length here */
01727                             if (PG(magic_quotes_runtime)) {
01728                                    info_value->s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
01729                             } else {
01730                                    info_value->s = estrndup(value, length);
01731                             }
01732                             info_data->length = length;
01733                      } else {
01734                             info_data->length = 0;
01735                             info_value->s = estrdup("");
01736                      }
01737                      break;
01738 
01739               case TAG_FMT_USHORT:
01740               case TAG_FMT_ULONG:
01741               case TAG_FMT_URATIONAL:
01742               case TAG_FMT_SSHORT:
01743               case TAG_FMT_SLONG:
01744               case TAG_FMT_SRATIONAL:
01745               case TAG_FMT_SINGLE:
01746               case TAG_FMT_DOUBLE:
01747                      if (length==0) {
01748                             break;
01749                      } else
01750                      if (length>1) {
01751                             info_value->list = safe_emalloc(length, sizeof(image_info_value), 0);
01752                      } else {
01753                             info_value = &info_data->value;
01754                      }
01755                      for (idex=0,vptr=value; idex<(size_t)length; idex++,vptr=(char *) vptr + php_tiff_bytes_per_format[format]) {
01756                             if (length>1) {
01757                                    info_value = &info_data->value.list[idex];
01758                             }
01759                             switch (format) {
01760                                    case TAG_FMT_USHORT:
01761                                           info_value->u = php_ifd_get16u(vptr, motorola_intel);
01762                                           break;
01763 
01764                                    case TAG_FMT_ULONG:
01765                                           info_value->u = php_ifd_get32u(vptr, motorola_intel);
01766                                           break;
01767 
01768                                    case TAG_FMT_URATIONAL:
01769                                           info_value->ur.num = php_ifd_get32u(vptr, motorola_intel);
01770                                           info_value->ur.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
01771                                           break;
01772 
01773                                    case TAG_FMT_SSHORT:
01774                                           info_value->i = php_ifd_get16s(vptr, motorola_intel);
01775                                           break;
01776 
01777                                    case TAG_FMT_SLONG:
01778                                           info_value->i = php_ifd_get32s(vptr, motorola_intel);
01779                                           break;
01780 
01781                                    case TAG_FMT_SRATIONAL:
01782                                           info_value->sr.num = php_ifd_get32u(vptr, motorola_intel);
01783                                           info_value->sr.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
01784                                           break;
01785 
01786                                    case TAG_FMT_SINGLE:
01787 #ifdef EXIF_DEBUG
01788                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Found value of type single");
01789 #endif
01790                                           info_value->f = *(float *)value;
01791 
01792                                    case TAG_FMT_DOUBLE:
01793 #ifdef EXIF_DEBUG
01794                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Found value of type double");
01795 #endif
01796                                           info_value->d = *(double *)value;
01797                                           break;
01798                             }
01799                      }
01800        }
01801        image_info->sections_found |= 1<<section_index;
01802        image_info->info_list[section_index].count++;
01803 }
01804 /* }}} */
01805 
01806 /* {{{ exif_iif_add_tag
01807  Add a tag from IFD to image_info
01808 */
01809 static void exif_iif_add_tag(image_info_type *image_info, int section_index, char *name, int tag, int format, size_t length, void* value TSRMLS_DC)
01810 {
01811        exif_iif_add_value(image_info, section_index, name, tag, format, (int)length, value, image_info->motorola_intel TSRMLS_CC);
01812 }
01813 /* }}} */
01814 
01815 /* {{{ exif_iif_add_int
01816  Add an int value to image_info
01817 */
01818 static void exif_iif_add_int(image_info_type *image_info, int section_index, char *name, int value TSRMLS_DC)
01819 {
01820        image_info_data  *info_data;
01821        image_info_data  *list;
01822 
01823        list = safe_erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1), sizeof(image_info_data), 0);
01824        image_info->info_list[section_index].list = list;
01825 
01826        info_data  = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
01827        info_data->tag    = TAG_NONE;
01828        info_data->format = TAG_FMT_SLONG;
01829        info_data->length = 1;
01830        info_data->name   = estrdup(name);
01831        info_data->value.i = value;
01832        image_info->sections_found |= 1<<section_index;
01833        image_info->info_list[section_index].count++;
01834 }
01835 /* }}} */
01836 
01837 /* {{{ exif_iif_add_str
01838  Add a string value to image_info MUST BE NUL TERMINATED
01839 */
01840 static void exif_iif_add_str(image_info_type *image_info, int section_index, char *name, char *value TSRMLS_DC)
01841 {
01842        image_info_data  *info_data;
01843        image_info_data  *list;
01844 
01845        if (value) {
01846               list = safe_erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1), sizeof(image_info_data), 0);
01847               image_info->info_list[section_index].list = list;
01848               info_data  = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
01849               info_data->tag    = TAG_NONE;
01850               info_data->format = TAG_FMT_STRING;
01851               info_data->length = 1;
01852               info_data->name   = estrdup(name);
01853               if (PG(magic_quotes_runtime)) {
01854                      info_data->value.s = php_addslashes(value, strlen(value), NULL, 0 TSRMLS_CC);
01855               } else {
01856                      info_data->value.s = estrdup(value);
01857               }
01858               image_info->sections_found |= 1<<section_index;
01859               image_info->info_list[section_index].count++;
01860        }
01861 }
01862 /* }}} */
01863 
01864 /* {{{ exif_iif_add_fmt
01865  Add a format string value to image_info MUST BE NUL TERMINATED
01866 */
01867 static void exif_iif_add_fmt(image_info_type *image_info, int section_index, char *name TSRMLS_DC, char *value, ...)
01868 {
01869        char             *tmp;
01870        va_list               arglist;
01871 
01872        va_start(arglist, value);
01873        if (value) {
01874               vspprintf(&tmp, 0, value, arglist);
01875               exif_iif_add_str(image_info, section_index, name, tmp TSRMLS_CC);
01876               efree(tmp);
01877        }
01878        va_end(arglist);
01879 }
01880 /* }}} */
01881 
01882 /* {{{ exif_iif_add_str
01883  Add a string value to image_info MUST BE NUL TERMINATED
01884 */
01885 static void exif_iif_add_buffer(image_info_type *image_info, int section_index, char *name, int length, char *value TSRMLS_DC)
01886 {
01887        image_info_data  *info_data;
01888        image_info_data  *list;
01889 
01890        if (value) {
01891               list = safe_erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1), sizeof(image_info_data), 0);
01892               image_info->info_list[section_index].list = list;
01893               info_data  = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
01894               info_data->tag    = TAG_NONE;
01895               info_data->format = TAG_FMT_UNDEFINED;
01896               info_data->length = length;
01897               info_data->name   = estrdup(name);
01898               if (PG(magic_quotes_runtime)) {
01899 #ifdef EXIF_DEBUG
01900                      exif_error_docref(NULL EXIFERR_CC, image_info, E_NOTICE, "Adding %s as buffer%s", name, exif_char_dump(value, length, 0));
01901 #endif
01902                      info_data->value.s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
01903                      info_data->length = length;
01904               } else {
01905                      info_data->value.s = safe_emalloc(length, 1, 1);
01906                      memcpy(info_data->value.s, value, length);
01907                      info_data->value.s[length] = 0;
01908               }
01909               image_info->sections_found |= 1<<section_index;
01910               image_info->info_list[section_index].count++;
01911        }
01912 }
01913 /* }}} */
01914 
01915 /* {{{ exif_iif_free
01916  Free memory allocated for image_info
01917 */
01918 static void exif_iif_free(image_info_type *image_info, int section_index) {
01919        int  i;
01920        void *f; /* faster */
01921 
01922        if (image_info->info_list[section_index].count) {
01923               for (i=0; i < image_info->info_list[section_index].count; i++) {
01924                      if ((f=image_info->info_list[section_index].list[i].name) != NULL) {
01925                             efree(f);
01926                      }
01927                      switch(image_info->info_list[section_index].list[i].format) {
01928                             case TAG_FMT_SBYTE:
01929                             case TAG_FMT_BYTE:
01930                                    /* in contrast to strings bytes do not need to allocate buffer for NULL if length==0 */
01931                                    if (image_info->info_list[section_index].list[i].length<1)
01932                                           break;
01933                             default:
01934                             case TAG_FMT_UNDEFINED:
01935                             case TAG_FMT_STRING:
01936                                    if ((f=image_info->info_list[section_index].list[i].value.s) != NULL) {
01937                                           efree(f);
01938                                    }
01939                                    break;
01940 
01941                             case TAG_FMT_USHORT:
01942                             case TAG_FMT_ULONG:
01943                             case TAG_FMT_URATIONAL:
01944                             case TAG_FMT_SSHORT:
01945                             case TAG_FMT_SLONG:
01946                             case TAG_FMT_SRATIONAL:
01947                             case TAG_FMT_SINGLE:
01948                             case TAG_FMT_DOUBLE:
01949                                    /* nothing to do here */
01950                                    if (image_info->info_list[section_index].list[i].length > 1) {
01951                                           if ((f=image_info->info_list[section_index].list[i].value.list) != NULL) {
01952                                                  efree(f);
01953                                           }
01954                                    }
01955                                    break;
01956                      }
01957               }
01958        }
01959        EFREE_IF(image_info->info_list[section_index].list);
01960 }
01961 /* }}} */
01962 
01963 /* {{{ add_assoc_image_info
01964  * Add image_info to associative array value. */
01965 static void add_assoc_image_info(zval *value, int sub_array, image_info_type *image_info, int section_index TSRMLS_DC)
01966 {
01967        char    buffer[64], *val, *name, uname[64];
01968        int     i, ap, l, b, idx=0, unknown=0;
01969 #ifdef EXIF_DEBUG
01970        int     info_tag;
01971 #endif
01972        image_info_value *info_value;
01973        image_info_data  *info_data;
01974        zval                  *tmpi, *array = NULL;
01975 
01976 #ifdef EXIF_DEBUG
01977 /*            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Adding %d infos from section %s", image_info->info_list[section_index].count, exif_get_sectionname(section_index));*/
01978 #endif
01979        if (image_info->info_list[section_index].count) {
01980               if (sub_array) {
01981                      MAKE_STD_ZVAL(tmpi);
01982                      array_init(tmpi);
01983               } else {
01984                      tmpi = value;
01985               }
01986 
01987               for(i=0; i<image_info->info_list[section_index].count; i++) {
01988                      info_data  = &image_info->info_list[section_index].list[i];
01989 #ifdef EXIF_DEBUG
01990                      info_tag   = info_data->tag; /* conversion */
01991 #endif
01992                      info_value = &info_data->value;
01993                      if (!(name = info_data->name)) {
01994                             snprintf(uname, sizeof(uname), "%d", unknown++);
01995                             name = uname;
01996                      }
01997 #ifdef EXIF_DEBUG
01998 /*            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Adding infos: tag(0x%04X,%12s,L=0x%04X): %s", info_tag, exif_get_tagname(info_tag, buffer, -12, exif_get_tag_table(section_index) TSRMLS_CC), info_data->length, info_data->format==TAG_FMT_STRING?(info_value&&info_value->s?info_value->s:"<no data>"):exif_get_tagformat(info_data->format));*/
01999 #endif
02000                      if (info_data->length==0) {
02001                             add_assoc_null(tmpi, name);
02002                      } else {
02003                             switch (info_data->format) {
02004                                    default:
02005                                           /* Standard says more types possible but skip them...
02006                                            * but allow users to handle data if they know how to
02007                                            * So not return but use type UNDEFINED
02008                                            * return;
02009                                            */
02010                                    case TAG_FMT_BYTE:
02011                                    case TAG_FMT_SBYTE:
02012                                    case TAG_FMT_UNDEFINED:
02013                                           if (!info_value->s) {
02014                                                  add_assoc_stringl(tmpi, name, "", 0, 1);
02015                                           } else {
02016                                                  add_assoc_stringl(tmpi, name, info_value->s, info_data->length, 1);
02017                                           }
02018                                           break;
02019 
02020                                    case TAG_FMT_STRING:
02021                                           if (!(val = info_value->s)) {
02022                                                  val = "";
02023                                           }
02024                                           if (section_index==SECTION_COMMENT) {
02025                                                  add_index_string(tmpi, idx++, val, 1);
02026                                           } else {
02027                                                  add_assoc_string(tmpi, name, val, 1);
02028                                           }
02029                                           break;
02030 
02031                                    case TAG_FMT_URATIONAL:
02032                                    case TAG_FMT_SRATIONAL:
02033                                    /*case TAG_FMT_BYTE:
02034                                    case TAG_FMT_SBYTE:*/
02035                                    case TAG_FMT_USHORT:
02036                                    case TAG_FMT_SSHORT:
02037                                    case TAG_FMT_SINGLE:
02038                                    case TAG_FMT_DOUBLE:
02039                                    case TAG_FMT_ULONG:
02040                                    case TAG_FMT_SLONG:
02041                                           /* now the rest, first see if it becomes an array */
02042                                           if ((l = info_data->length) > 1) {
02043                                                  array = NULL;
02044                                                  MAKE_STD_ZVAL(array);
02045                                                  array_init(array);
02046                                           }
02047                                           for(ap=0; ap<l; ap++) {
02048                                                  if (l>1) {
02049                                                         info_value = &info_data->value.list[ap];
02050                                                  }
02051                                                  switch (info_data->format) {
02052                                                         case TAG_FMT_BYTE:
02053                                                                if (l>1) {
02054                                                                       info_value = &info_data->value;
02055                                                                       for (b=0;b<l;b++) {
02056                                                                              add_index_long(array, b, (int)(info_value->s[b]));
02057                                                                       }
02058                                                                       break;
02059                                                                }
02060                                                         case TAG_FMT_USHORT:
02061                                                         case TAG_FMT_ULONG:
02062                                                                if (l==1) {
02063                                                                       add_assoc_long(tmpi, name, (int)info_value->u);
02064                                                                } else {
02065                                                                       add_index_long(array, ap, (int)info_value->u);
02066                                                                }
02067                                                                break;
02068 
02069                                                         case TAG_FMT_URATIONAL:
02070                                                                snprintf(buffer, sizeof(buffer), "%i/%i", info_value->ur.num, info_value->ur.den);
02071                                                                if (l==1) {
02072                                                                       add_assoc_string(tmpi, name, buffer, 1);
02073                                                                } else {
02074                                                                       add_index_string(array, ap, buffer, 1);
02075                                                                }
02076                                                                break;
02077 
02078                                                         case TAG_FMT_SBYTE:
02079                                                                if (l>1) {
02080                                                                       info_value = &info_data->value;
02081                                                                       for (b=0;b<l;b++) {
02082                                                                              add_index_long(array, ap, (int)info_value->s[b]);
02083                                                                       }
02084                                                                       break;
02085                                                                }
02086                                                         case TAG_FMT_SSHORT:
02087                                                         case TAG_FMT_SLONG:
02088                                                                if (l==1) {
02089                                                                       add_assoc_long(tmpi, name, info_value->i);
02090                                                                } else {
02091                                                                       add_index_long(array, ap, info_value->i);
02092                                                                }
02093                                                                break;
02094 
02095                                                         case TAG_FMT_SRATIONAL:
02096                                                                snprintf(buffer, sizeof(buffer), "%i/%i", info_value->sr.num, info_value->sr.den);
02097                                                                if (l==1) {
02098                                                                       add_assoc_string(tmpi, name, buffer, 1);
02099                                                                } else {
02100                                                                       add_index_string(array, ap, buffer, 1);
02101                                                                }
02102                                                                break;
02103 
02104                                                         case TAG_FMT_SINGLE:
02105                                                                if (l==1) {
02106                                                                       add_assoc_double(tmpi, name, info_value->f);
02107                                                                } else {
02108                                                                       add_index_double(array, ap, info_value->f);
02109                                                                }
02110                                                                break;
02111 
02112                                                         case TAG_FMT_DOUBLE:
02113                                                                if (l==1) {
02114                                                                       add_assoc_double(tmpi, name, info_value->d);
02115                                                                } else {
02116                                                                       add_index_double(array, ap, info_value->d);
02117                                                                }
02118                                                                break;
02119                                                  }
02120                                                  info_value = &info_data->value.list[ap];
02121                                           }
02122                                           if (l>1) {
02123                                                  add_assoc_zval(tmpi, name, array);
02124                                           }
02125                                           break;
02126                             }
02127                      }
02128               }
02129               if (sub_array) {
02130                      add_assoc_zval(value, exif_get_sectionname(section_index), tmpi);
02131               }
02132        }
02133 }
02134 /* }}} */
02135 
02136 /* {{{ Markers
02137    JPEG markers consist of one or more 0xFF bytes, followed by a marker
02138    code byte (which is not an FF).  Here are the marker codes of interest
02139    in this program.  (See jdmarker.c for a more complete list.)
02140 */
02141 
02142 #define M_TEM   0x01    /* temp for arithmetic coding              */
02143 #define M_RES   0x02    /* reserved                                */
02144 #define M_SOF0  0xC0    /* Start Of Frame N                        */
02145 #define M_SOF1  0xC1    /* N indicates which compression process   */
02146 #define M_SOF2  0xC2    /* Only SOF0-SOF2 are now in common use    */
02147 #define M_SOF3  0xC3
02148 #define M_DHT   0xC4
02149 #define M_SOF5  0xC5    /* NB: codes C4 and CC are NOT SOF markers */
02150 #define M_SOF6  0xC6
02151 #define M_SOF7  0xC7
02152 #define M_JPEG  0x08    /* reserved for extensions                 */
02153 #define M_SOF9  0xC9
02154 #define M_SOF10 0xCA
02155 #define M_SOF11 0xCB
02156 #define M_DAC   0xCC    /* arithmetic table                         */
02157 #define M_SOF13 0xCD
02158 #define M_SOF14 0xCE
02159 #define M_SOF15 0xCF
02160 #define M_RST0  0xD0    /* restart segment                          */
02161 #define M_RST1  0xD1
02162 #define M_RST2  0xD2
02163 #define M_RST3  0xD3
02164 #define M_RST4  0xD4
02165 #define M_RST5  0xD5
02166 #define M_RST6  0xD6
02167 #define M_RST7  0xD7
02168 #define M_SOI   0xD8    /* Start Of Image (beginning of datastream) */
02169 #define M_EOI   0xD9    /* End Of Image (end of datastream)         */
02170 #define M_SOS   0xDA    /* Start Of Scan (begins compressed data)   */
02171 #define M_DQT   0xDB
02172 #define M_DNL   0xDC
02173 #define M_DRI   0xDD
02174 #define M_DHP   0xDE
02175 #define M_EXP   0xDF
02176 #define M_APP0  0xE0    /* JPEG: 'JFIFF' AND (additional 'JFXX')    */
02177 #define M_EXIF  0xE1    /* Exif Attribute Information               */
02178 #define M_APP2  0xE2    /* Flash Pix Extension Data?                */
02179 #define M_APP3  0xE3
02180 #define M_APP4  0xE4
02181 #define M_APP5  0xE5
02182 #define M_APP6  0xE6
02183 #define M_APP7  0xE7
02184 #define M_APP8  0xE8
02185 #define M_APP9  0xE9
02186 #define M_APP10 0xEA
02187 #define M_APP11 0xEB
02188 #define M_APP12 0xEC
02189 #define M_APP13 0xED    /* IPTC International Press Telecommunications Council */
02190 #define M_APP14 0xEE    /* Software, Copyright?                     */
02191 #define M_APP15 0xEF
02192 #define M_JPG0  0xF0
02193 #define M_JPG1  0xF1
02194 #define M_JPG2  0xF2
02195 #define M_JPG3  0xF3
02196 #define M_JPG4  0xF4
02197 #define M_JPG5  0xF5
02198 #define M_JPG6  0xF6
02199 #define M_JPG7  0xF7
02200 #define M_JPG8  0xF8
02201 #define M_JPG9  0xF9
02202 #define M_JPG10 0xFA
02203 #define M_JPG11 0xFB
02204 #define M_JPG12 0xFC
02205 #define M_JPG13 0xFD
02206 #define M_COM   0xFE    /* COMment                                  */
02207 
02208 #define M_PSEUDO 0x123      /* Extra value.                             */
02209 
02210 /* }}} */
02211 
02212 /* {{{ jpeg2000 markers
02213  */
02214 /* Markers x30 - x3F do not have a segment */
02215 /* Markers x00, x01, xFE, xC0 - xDF ISO/IEC 10918-1 -> M_<xx> */
02216 /* Markers xF0 - xF7 ISO/IEC 10918-3 */
02217 /* Markers xF7 - xF8 ISO/IEC 14495-1 */
02218 /* XY=Main/Tile-header:(R:required, N:not_allowed, O:optional, L:last_marker) */
02219 #define JC_SOC   0x4F   /* NN, Start of codestream                          */
02220 #define JC_SIZ   0x51   /* RN, Image and tile size                          */
02221 #define JC_COD   0x52   /* RO, Codeing style defaulte                       */
02222 #define JC_COC   0x53   /* OO, Coding style component                       */
02223 #define JC_TLM   0x55   /* ON, Tile part length main header                 */
02224 #define JC_PLM   0x57   /* ON, Packet length main header                    */
02225 #define JC_PLT   0x58   /* NO, Packet length tile part header               */
02226 #define JC_QCD   0x5C   /* RO, Quantization default                         */
02227 #define JC_QCC   0x5D   /* OO, Quantization component                       */
02228 #define JC_RGN   0x5E   /* OO, Region of interest                           */
02229 #define JC_POD   0x5F   /* OO, Progression order default                    */
02230 #define JC_PPM   0x60   /* ON, Packed packet headers main header            */
02231 #define JC_PPT   0x61   /* NO, Packet packet headers tile part header       */
02232 #define JC_CME   0x64   /* OO, Comment: "LL E <text>" E=0:binary, E=1:ascii */
02233 #define JC_SOT   0x90   /* NR, Start of tile                                */
02234 #define JC_SOP   0x91   /* NO, Start of packeter default                    */
02235 #define JC_EPH   0x92   /* NO, End of packet header                         */
02236 #define JC_SOD   0x93   /* NL, Start of data                                */
02237 #define JC_EOC   0xD9   /* NN, End of codestream                            */
02238 /* }}} */
02239 
02240 /* {{{ exif_process_COM
02241    Process a COM marker.
02242    We want to print out the marker contents as legible text;
02243    we must guard against random junk and varying newline representations.
02244 */
02245 static void exif_process_COM (image_info_type *image_info, char *value, size_t length TSRMLS_DC)
02246 {
02247        exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_STRING, length-2, value+2 TSRMLS_CC);
02248 }
02249 /* }}} */
02250 
02251 /* {{{ exif_process_CME
02252    Process a CME marker.
02253    We want to print out the marker contents as legible text;
02254    we must guard against random junk and varying newline representations.
02255 */
02256 #ifdef EXIF_JPEG2000
02257 static void exif_process_CME (image_info_type *image_info, char *value, size_t length TSRMLS_DC)
02258 {
02259        if (length>3) {
02260               switch(value[2]) {
02261                      case 0:
02262                             exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_UNDEFINED, length, value TSRMLS_CC);
02263                             break;
02264                      case 1:
02265                             exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_STRING, length, value);
02266                             break;
02267                      default:
02268                             php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined JPEG2000 comment encoding");
02269                             break;
02270               }
02271        } else {
02272               exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_UNDEFINED, 0, NULL);
02273               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "JPEG2000 comment section too small");
02274        }
02275 }
02276 #endif
02277 /* }}} */
02278 
02279 /* {{{ exif_process_SOFn
02280  * Process a SOFn marker.  This is useful for the image dimensions */
02281 static void exif_process_SOFn (uchar *Data, int marker, jpeg_sof_info *result)
02282 {
02283 /* 0xFF SOSn SectLen(2) Bits(1) Height(2) Width(2) Channels(1)  3*Channels (1)  */
02284        result->bits_per_sample = Data[2];
02285        result->height          = php_jpg_get16(Data+3);
02286        result->width           = php_jpg_get16(Data+5);
02287        result->num_components  = Data[7];
02288 
02289 /*     switch (marker) {
02290               case M_SOF0:  process = "Baseline";  break;
02291               case M_SOF1:  process = "Extended sequential";  break;
02292               case M_SOF2:  process = "Progressive";  break;
02293               case M_SOF3:  process = "Lossless";  break;
02294               case M_SOF5:  process = "Differential sequential";  break;
02295               case M_SOF6:  process = "Differential progressive";  break;
02296               case M_SOF7:  process = "Differential lossless";  break;
02297               case M_SOF9:  process = "Extended sequential, arithmetic coding";  break;
02298               case M_SOF10: process = "Progressive, arithmetic coding";  break;
02299               case M_SOF11: process = "Lossless, arithmetic coding";  break;
02300               case M_SOF13: process = "Differential sequential, arithmetic coding";  break;
02301               case M_SOF14: process = "Differential progressive, arithmetic coding"; break;
02302               case M_SOF15: process = "Differential lossless, arithmetic coding";  break;
02303               default:      process = "Unknown";  break;
02304        }*/
02305 }
02306 /* }}} */
02307 
02308 /* forward declarations */
02309 static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start, char *offset_base, size_t IFDlength, size_t displacement, int section_index TSRMLS_DC);
02310 static int exif_process_IFD_TAG(    image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table TSRMLS_DC);
02311 
02312 /* {{{ exif_get_markername
02313        Get name of marker */
02314 #ifdef EXIF_DEBUG
02315 static char * exif_get_markername(int marker)
02316 {
02317        switch(marker) {
02318               case 0xC0: return "SOF0";
02319               case 0xC1: return "SOF1";
02320               case 0xC2: return "SOF2";
02321               case 0xC3: return "SOF3";
02322               case 0xC4: return "DHT";
02323               case 0xC5: return "SOF5";
02324               case 0xC6: return "SOF6";
02325               case 0xC7: return "SOF7";
02326               case 0xC9: return "SOF9";
02327               case 0xCA: return "SOF10";
02328               case 0xCB: return "SOF11";
02329               case 0xCD: return "SOF13";
02330               case 0xCE: return "SOF14";
02331               case 0xCF: return "SOF15";
02332               case 0xD8: return "SOI";
02333               case 0xD9: return "EOI";
02334               case 0xDA: return "SOS";
02335               case 0xDB: return "DQT";
02336               case 0xDC: return "DNL";
02337               case 0xDD: return "DRI";
02338               case 0xDE: return "DHP";
02339               case 0xDF: return "EXP";
02340               case 0xE0: return "APP0";
02341               case 0xE1: return "EXIF";
02342               case 0xE2: return "FPIX";
02343               case 0xE3: return "APP3";
02344               case 0xE4: return "APP4";
02345               case 0xE5: return "APP5";
02346               case 0xE6: return "APP6";
02347               case 0xE7: return "APP7";
02348               case 0xE8: return "APP8";
02349               case 0xE9: return "APP9";
02350               case 0xEA: return "APP10";
02351               case 0xEB: return "APP11";
02352               case 0xEC: return "APP12";
02353               case 0xED: return "APP13";
02354               case 0xEE: return "APP14";
02355               case 0xEF: return "APP15";
02356               case 0xF0: return "JPG0";
02357               case 0xFD: return "JPG13";
02358               case 0xFE: return "COM";
02359               case 0x01: return "TEM";
02360        }
02361        return "Unknown";
02362 }
02363 #endif
02364 /* }}} */
02365 
02366 /* {{{ proto string exif_tagname(index)
02367        Get headername for index or false if not defined */
02368 PHP_FUNCTION(exif_tagname)
02369 {
02370        long tag;
02371        char *szTemp;
02372 
02373        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE) {
02374               return;
02375        }
02376 
02377        szTemp = exif_get_tagname(tag, NULL, 0, tag_table_IFD TSRMLS_CC);
02378 
02379        if (tag < 0 || !szTemp || !szTemp[0]) {
02380               RETURN_FALSE;
02381        }
02382 
02383        RETURN_STRING(szTemp, 1)
02384 }
02385 /* }}} */
02386 
02387 /* {{{ exif_ifd_make_value
02388  * Create a value for an ifd from an info_data pointer */
02389 static void* exif_ifd_make_value(image_info_data *info_data, int motorola_intel TSRMLS_DC) {
02390        size_t  byte_count;
02391        char    *value_ptr, *data_ptr;
02392        size_t  i;
02393 
02394        image_info_value  *info_value;
02395 
02396        byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
02397        value_ptr = safe_emalloc(max(byte_count, 4), 1, 0);
02398        memset(value_ptr, 0, 4);
02399        if (!info_data->length) {
02400               return value_ptr;
02401        }
02402        if (info_data->format == TAG_FMT_UNDEFINED || info_data->format == TAG_FMT_STRING
02403          || (byte_count>1 && (info_data->format == TAG_FMT_BYTE || info_data->format == TAG_FMT_SBYTE))
02404        ) {
02405               memmove(value_ptr, info_data->value.s, byte_count);
02406               return value_ptr;
02407        } else if (info_data->format == TAG_FMT_BYTE) {
02408               *value_ptr = info_data->value.u;
02409               return value_ptr;
02410        } else if (info_data->format == TAG_FMT_SBYTE) {
02411               *value_ptr = info_data->value.i;
02412               return value_ptr;
02413        } else {
02414               data_ptr = value_ptr;
02415               for(i=0; i<info_data->length; i++) {
02416                      if (info_data->length==1) {
02417                             info_value = &info_data->value;
02418                      } else {
02419                             info_value = &info_data->value.list[i];
02420                      }
02421                      switch(info_data->format) {
02422                             case TAG_FMT_USHORT:
02423                                    php_ifd_set16u(data_ptr, info_value->u, motorola_intel);
02424                                    data_ptr += 2;
02425                                    break;
02426                             case TAG_FMT_ULONG:
02427                                    php_ifd_set32u(data_ptr, info_value->u, motorola_intel);
02428                                    data_ptr += 4;
02429                                    break;
02430                             case TAG_FMT_SSHORT:
02431                                    php_ifd_set16u(data_ptr, info_value->i, motorola_intel);
02432                                    data_ptr += 2;
02433                                    break;
02434                             case TAG_FMT_SLONG:
02435                                    php_ifd_set32u(data_ptr, info_value->i, motorola_intel);
02436                                    data_ptr += 4;
02437                                    break;
02438                             case TAG_FMT_URATIONAL:
02439                                    php_ifd_set32u(data_ptr,   info_value->sr.num, motorola_intel);
02440                                    php_ifd_set32u(data_ptr+4, info_value->sr.den, motorola_intel);
02441                                    data_ptr += 8;
02442                                    break;
02443                             case TAG_FMT_SRATIONAL:
02444                                    php_ifd_set32u(data_ptr,   info_value->ur.num, motorola_intel);
02445                                    php_ifd_set32u(data_ptr+4, info_value->ur.den, motorola_intel);
02446                                    data_ptr += 8;
02447                                    break;
02448                             case TAG_FMT_SINGLE:
02449                                    memmove(data_ptr, &info_data->value.f, byte_count);
02450                                    data_ptr += 4;
02451                                    break;
02452                             case TAG_FMT_DOUBLE:
02453                                    memmove(data_ptr, &info_data->value.d, byte_count);
02454                                    data_ptr += 8;
02455                                    break;
02456                      }
02457               }
02458        }
02459        return value_ptr;
02460 }
02461 /* }}} */
02462 
02463 /* {{{ exif_thumbnail_build
02464  * Check and build thumbnail */
02465 static void exif_thumbnail_build(image_info_type *ImageInfo TSRMLS_DC) {
02466        size_t            new_size, new_move, new_value;
02467        char              *new_data;
02468        void              *value_ptr;
02469        int               i, byte_count;
02470        image_info_list   *info_list;
02471        image_info_data   *info_data;
02472 #ifdef EXIF_DEBUG
02473        char              tagname[64];
02474 #endif
02475 
02476        if (!ImageInfo->read_thumbnail || !ImageInfo->Thumbnail.offset || !ImageInfo->Thumbnail.size) {
02477               return; /* ignore this call */
02478        }
02479 #ifdef EXIF_DEBUG
02480        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: filetype = %d", ImageInfo->Thumbnail.filetype);
02481 #endif
02482        switch(ImageInfo->Thumbnail.filetype) {
02483               default:
02484               case IMAGE_FILETYPE_JPEG:
02485                      /* done */
02486                      break;
02487               case IMAGE_FILETYPE_TIFF_II:
02488               case IMAGE_FILETYPE_TIFF_MM:
02489                      info_list = &ImageInfo->info_list[SECTION_THUMBNAIL];
02490                      new_size  = 8 + 2 + info_list->count * 12 + 4;
02491 #ifdef EXIF_DEBUG
02492                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: size of signature + directory(%d): 0x%02X", info_list->count, new_size);
02493 #endif
02494                      new_value= new_size; /* offset for ifd values outside ifd directory */
02495                      for (i=0; i<info_list->count; i++) {
02496                             info_data  = &info_list->list[i];
02497                             byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
02498                             if (byte_count > 4) {
02499                                    new_size += byte_count;
02500                             }
02501                      }
02502                      new_move = new_size;
02503                      new_data = safe_erealloc(ImageInfo->Thumbnail.data, 1, ImageInfo->Thumbnail.size, new_size);
02504                      ImageInfo->Thumbnail.data = new_data;
02505                      memmove(ImageInfo->Thumbnail.data + new_move, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
02506                      ImageInfo->Thumbnail.size += new_size;
02507                      /* fill in data */
02508                      if (ImageInfo->motorola_intel) {
02509                             memmove(new_data, "MM\x00\x2a\x00\x00\x00\x08", 8);
02510                      } else {
02511                             memmove(new_data, "II\x2a\x00\x08\x00\x00\x00", 8);
02512                      }
02513                      new_data += 8;
02514                      php_ifd_set16u(new_data, info_list->count, ImageInfo->motorola_intel);
02515                      new_data += 2;
02516                      for (i=0; i<info_list->count; i++) {
02517                             info_data  = &info_list->list[i];
02518                             byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
02519 #ifdef EXIF_DEBUG
02520                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: process tag(x%04X=%s): %s%s (%d bytes)", info_data->tag, exif_get_tagname(info_data->tag, tagname, -12, tag_table_IFD TSRMLS_CC), (info_data->length>1)&&info_data->format!=TAG_FMT_UNDEFINED&&info_data->format!=TAG_FMT_STRING?"ARRAY OF ":"", exif_get_tagformat(info_data->format), byte_count);
02521 #endif
02522                             if (info_data->tag==TAG_STRIP_OFFSETS || info_data->tag==TAG_JPEG_INTERCHANGE_FORMAT) {
02523                                    php_ifd_set16u(new_data + 0, info_data->tag,    ImageInfo->motorola_intel);
02524                                    php_ifd_set16u(new_data + 2, TAG_FMT_ULONG,     ImageInfo->motorola_intel);
02525                                    php_ifd_set32u(new_data + 4, 1,                 ImageInfo->motorola_intel);
02526                                    php_ifd_set32u(new_data + 8, new_move,          ImageInfo->motorola_intel);
02527                             } else {
02528                                    php_ifd_set16u(new_data + 0, info_data->tag,    ImageInfo->motorola_intel);
02529                                    php_ifd_set16u(new_data + 2, info_data->format, ImageInfo->motorola_intel);
02530                                    php_ifd_set32u(new_data + 4, info_data->length, ImageInfo->motorola_intel);
02531                                    value_ptr  = exif_ifd_make_value(info_data, ImageInfo->motorola_intel TSRMLS_CC);
02532                                    if (byte_count <= 4) {
02533                                           memmove(new_data+8, value_ptr, 4);
02534                                    } else {
02535                                           php_ifd_set32u(new_data+8, new_value, ImageInfo->motorola_intel);
02536 #ifdef EXIF_DEBUG
02537                                           exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: writing with value offset: 0x%04X + 0x%02X", new_value, byte_count);
02538 #endif
02539                                           memmove(ImageInfo->Thumbnail.data+new_value, value_ptr, byte_count);
02540                                           new_value += byte_count;
02541                                    }
02542                                    efree(value_ptr);
02543                             }
02544                             new_data += 12;
02545                      }
02546                      memset(new_data, 0, 4); /* next ifd pointer */
02547 #ifdef EXIF_DEBUG
02548                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: created");
02549 #endif
02550                      break;
02551        }
02552 }
02553 /* }}} */
02554 
02555 /* {{{ exif_thumbnail_extract
02556  * Grab the thumbnail, corrected */
02557 static void exif_thumbnail_extract(image_info_type *ImageInfo, char *offset, size_t length TSRMLS_DC) {
02558        if (ImageInfo->Thumbnail.data) {
02559               exif_error_docref("exif_read_data#error_mult_thumb" EXIFERR_CC, ImageInfo, E_WARNING, "Multiple possible thumbnails");
02560               return; /* Should not happen */
02561        }
02562        if (!ImageInfo->read_thumbnail)    {
02563               return; /* ignore this call */
02564        }
02565        /* according to exif2.1, the thumbnail is not supposed to be greater than 64K */
02566        if (ImageInfo->Thumbnail.size >= 65536
02567         || ImageInfo->Thumbnail.size <= 0
02568         || ImageInfo->Thumbnail.offset <= 0
02569        ) {
02570               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Illegal thumbnail size/offset");
02571               return;
02572        }
02573        /* Check to make sure we are not going to go past the ExifLength */
02574        if ((ImageInfo->Thumbnail.offset + ImageInfo->Thumbnail.size) > length) {
02575               EXIF_ERRLOG_THUMBEOF(ImageInfo)
02576               return;
02577        }
02578        ImageInfo->Thumbnail.data = estrndup(offset + ImageInfo->Thumbnail.offset, ImageInfo->Thumbnail.size);
02579        exif_thumbnail_build(ImageInfo TSRMLS_CC);
02580 }
02581 /* }}} */
02582 
02583 /* {{{ exif_process_undefined
02584  * Copy a string/buffer in Exif header to a character string and return length of allocated buffer if any. */
02585 static int exif_process_undefined(char **result, char *value, size_t byte_count TSRMLS_DC) {
02586        /* we cannot use strlcpy - here the problem is that we have to copy NUL
02587         * chars up to byte_count, we also have to add a single NUL character to
02588         * force end of string.
02589         * estrndup does not return length
02590         */
02591        if (byte_count) {
02592               (*result) = estrndup(value, byte_count); /* NULL @ byte_count!!! */
02593               return byte_count+1;
02594        }
02595        return 0;
02596 }
02597 /* }}} */
02598 
02599 /* {{{ exif_process_string_raw
02600  * Copy a string in Exif header to a character string returns length of allocated buffer if any. */
02601 #if !EXIF_USE_MBSTRING
02602 static int exif_process_string_raw(char **result, char *value, size_t byte_count) {
02603        /* we cannot use strlcpy - here the problem is that we have to copy NUL
02604         * chars up to byte_count, we also have to add a single NUL character to
02605         * force end of string.
02606         */
02607        if (byte_count) {
02608               (*result) = safe_emalloc(byte_count, 1, 1);
02609               memcpy(*result, value, byte_count);
02610               (*result)[byte_count] = '\0';
02611               return byte_count+1;
02612        }
02613        return 0;
02614 }
02615 #endif
02616 /* }}} */
02617 
02618 /* {{{ exif_process_string
02619  * Copy a string in Exif header to a character string and return length of allocated buffer if any.
02620  * In contrast to exif_process_string this function does allways return a string buffer */
02621 static int exif_process_string(char **result, char *value, size_t byte_count TSRMLS_DC) {
02622        /* we cannot use strlcpy - here the problem is that we cannot use strlen to
02623         * determin length of string and we cannot use strlcpy with len=byte_count+1
02624         * because then we might get into an EXCEPTION if we exceed an allocated
02625         * memory page...so we use php_strnlen in conjunction with memcpy and add the NUL
02626         * char.
02627         * estrdup would sometimes allocate more memory and does not return length
02628         */
02629        if ((byte_count=php_strnlen(value, byte_count)) > 0) {
02630               return exif_process_undefined(result, value, byte_count TSRMLS_CC);
02631        }
02632        (*result) = estrndup("", 1); /* force empty string */
02633        return byte_count+1;
02634 }
02635 /* }}} */
02636 
02637 /* {{{ exif_process_user_comment
02638  * Process UserComment in IFD. */
02639 static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoPtr, char **pszEncoding, char *szValuePtr, int ByteCount TSRMLS_DC)
02640 {
02641        int   a;
02642 
02643 #if EXIF_USE_MBSTRING
02644        char  *decode;
02645        size_t len;;
02646 #endif
02647 
02648        *pszEncoding = NULL;
02649        /* Copy the comment */
02650        if (ByteCount>=8) {
02651               if (!memcmp(szValuePtr, "UNICODE\0", 8)) {
02652                      *pszEncoding = estrdup((const char*)szValuePtr);
02653                      szValuePtr = szValuePtr+8;
02654                      ByteCount -= 8;
02655 #if EXIF_USE_MBSTRING
02656                      /* First try to detect BOM: ZERO WIDTH NOBREAK SPACE (FEFF 16) 
02657                       * since we have no encoding support for the BOM yet we skip that.
02658                       */
02659                      if (!memcmp(szValuePtr, "\xFE\xFF", 2)) {
02660                             decode = "UCS-2BE";
02661                             szValuePtr = szValuePtr+2;
02662                             ByteCount -= 2;
02663                      } else if (!memcmp(szValuePtr, "\xFF\xFE", 2)) {
02664                             decode = "UCS-2LE";
02665                             szValuePtr = szValuePtr+2;
02666                             ByteCount -= 2;
02667                      } else if (ImageInfo->motorola_intel) {
02668                             decode = ImageInfo->decode_unicode_be;
02669                      } else {
02670                             decode = ImageInfo->decode_unicode_le;
02671                      }
02672                      *pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, decode, &len TSRMLS_CC);
02673                      return len;
02674 #else
02675                      return exif_process_string_raw(pszInfoPtr, szValuePtr, ByteCount);
02676 #endif
02677               } else
02678               if (!memcmp(szValuePtr, "ASCII\0\0\0", 8)) {
02679                      *pszEncoding = estrdup((const char*)szValuePtr);
02680                      szValuePtr = szValuePtr+8;
02681                      ByteCount -= 8;
02682               } else
02683               if (!memcmp(szValuePtr, "JIS\0\0\0\0\0", 8)) {
02684                      /* JIS should be tanslated to MB or we leave it to the user - leave it to the user */
02685                      *pszEncoding = estrdup((const char*)szValuePtr);
02686                      szValuePtr = szValuePtr+8;
02687                      ByteCount -= 8;
02688 #if EXIF_USE_MBSTRING
02689                      if (ImageInfo->motorola_intel) {
02690                             *pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_jis, ImageInfo->decode_jis_be, &len TSRMLS_CC);
02691                      } else {
02692                             *pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_jis, ImageInfo->decode_jis_le, &len TSRMLS_CC);
02693                      }
02694                      return len;
02695 #else
02696                      return exif_process_string_raw(pszInfoPtr, szValuePtr, ByteCount);
02697 #endif
02698               } else
02699               if (!memcmp(szValuePtr, "\0\0\0\0\0\0\0\0", 8)) {
02700                      /* 8 NULL means undefined and should be ASCII... */
02701                      *pszEncoding = estrdup("UNDEFINED");
02702                      szValuePtr = szValuePtr+8;
02703                      ByteCount -= 8;
02704               }
02705        }
02706 
02707        /* Olympus has this padded with trailing spaces.  Remove these first. */
02708        if (ByteCount>0) {
02709               for (a=ByteCount-1;a && szValuePtr[a]==' ';a--) {
02710                      (szValuePtr)[a] = '\0';
02711               }
02712        }
02713 
02714        /* normal text without encoding */
02715        exif_process_string(pszInfoPtr, szValuePtr, ByteCount TSRMLS_CC);
02716        return strlen(*pszInfoPtr);
02717 }
02718 /* }}} */
02719 
02720 /* {{{ exif_process_unicode
02721  * Process unicode field in IFD. */
02722 static int exif_process_unicode(image_info_type *ImageInfo, xp_field_type *xp_field, int tag, char *szValuePtr, int ByteCount TSRMLS_DC)
02723 {
02724        xp_field->tag = tag; 
02725 
02726        /* Copy the comment */
02727 #if EXIF_USE_MBSTRING
02728 /*  What if MS supports big-endian with XP? */
02729 /*     if (ImageInfo->motorola_intel) {
02730               xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_be, &xp_field->size TSRMLS_CC);
02731        } else {
02732               xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_le, &xp_field->size TSRMLS_CC);
02733        }*/
02734        xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_le, &xp_field->size TSRMLS_CC);
02735        return xp_field->size;
02736 #else
02737        xp_field->size = exif_process_string_raw(&xp_field->value, szValuePtr, ByteCount);
02738        return xp_field->size;
02739 #endif
02740 }
02741 /* }}} */
02742 
02743 /* {{{ exif_process_IFD_in_MAKERNOTE
02744  * Process nested IFDs directories in Maker Note. */
02745 static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * value_ptr, int value_len, char *offset_base, size_t IFDlength, size_t displacement TSRMLS_DC)
02746 {
02747        int de, i=0, section_index = SECTION_MAKERNOTE;
02748        int NumDirEntries, old_motorola_intel, offset_diff;
02749        const maker_note_type *maker_note;
02750        char *dir_start;
02751 
02752        for (i=0; i<=sizeof(maker_note_array)/sizeof(maker_note_type); i++) {
02753               if (i==sizeof(maker_note_array)/sizeof(maker_note_type))
02754                      return FALSE;
02755               maker_note = maker_note_array+i;
02756               
02757               /*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "check (%s,%s)", maker_note->make?maker_note->make:"", maker_note->model?maker_note->model:"");*/
02758               if (maker_note->make && (!ImageInfo->make || strcmp(maker_note->make, ImageInfo->make)))
02759                      continue;
02760               if (maker_note->model && (!ImageInfo->model || strcmp(maker_note->model, ImageInfo->model)))
02761                      continue;
02762               if (maker_note->id_string && strncmp(maker_note->id_string, value_ptr, maker_note->id_string_len))
02763                      continue;
02764               break;
02765        }
02766 
02767        dir_start = value_ptr + maker_note->offset;
02768 
02769 #ifdef EXIF_DEBUG
02770        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process %s @x%04X + 0x%04X=%d: %s", exif_get_sectionname(section_index), (int)dir_start-(int)offset_base+maker_note->offset+displacement, value_len, value_len, exif_char_dump(value_ptr, value_len, (int)dir_start-(int)offset_base+maker_note->offset+displacement));
02771 #endif
02772 
02773        ImageInfo->sections_found |= FOUND_MAKERNOTE;
02774 
02775        old_motorola_intel = ImageInfo->motorola_intel;
02776        switch (maker_note->byte_order) {
02777               case MN_ORDER_INTEL:
02778                      ImageInfo->motorola_intel = 0;
02779                      break;
02780               case MN_ORDER_MOTOROLA:
02781                      ImageInfo->motorola_intel = 1;
02782                      break;
02783               default:
02784               case MN_ORDER_NORMAL:
02785                      break;
02786        }
02787 
02788        NumDirEntries = php_ifd_get16u(dir_start, ImageInfo->motorola_intel);
02789 
02790        switch (maker_note->offset_mode) {
02791               case MN_OFFSET_MAKER:
02792                      offset_base = value_ptr;
02793                      break;
02794               case MN_OFFSET_GUESS:
02795                      offset_diff = 2 + NumDirEntries*12 + 4 - php_ifd_get32u(dir_start+10, ImageInfo->motorola_intel);
02796 #ifdef EXIF_DEBUG
02797                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Using automatic offset correction: 0x%04X", ((int)dir_start-(int)offset_base+maker_note->offset+displacement) + offset_diff);
02798 #endif
02799                      offset_base = value_ptr + offset_diff;
02800                      break;
02801               default:
02802               case MN_OFFSET_NORMAL:
02803                      break;
02804        }
02805 
02806        if ((2+NumDirEntries*12) > value_len) {
02807               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD size: 2 + x%04X*12 = x%04X > x%04X", NumDirEntries, 2+NumDirEntries*12, value_len);
02808               return FALSE;
02809        }
02810 
02811        for (de=0;de<NumDirEntries;de++) {
02812               if (!exif_process_IFD_TAG(ImageInfo, dir_start + 2 + 12 * de,
02813                                                           offset_base, IFDlength, displacement, section_index, 0, maker_note->tag_table TSRMLS_CC)) {
02814                      return FALSE;
02815               }
02816        }
02817        ImageInfo->motorola_intel = old_motorola_intel;
02818 /*     NextDirOffset (must be NULL) = php_ifd_get32u(dir_start+2+12*de, ImageInfo->motorola_intel);*/
02819 #ifdef EXIF_DEBUG
02820        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Subsection %s done", exif_get_sectionname(SECTION_MAKERNOTE));
02821 #endif
02822        return TRUE;
02823 }
02824 /* }}} */
02825 
02826 /* {{{ exif_process_IFD_TAG
02827  * Process one of the nested IFDs directories. */
02828 static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table TSRMLS_DC)
02829 {
02830        size_t length;
02831        int tag, format, components;
02832        char *value_ptr, tagname[64], cbuf[32], *outside=NULL;
02833        size_t byte_count, offset_val, fpos, fgot;
02834        int64_t byte_count_signed;
02835        xp_field_type *tmp_xp;
02836 #ifdef EXIF_DEBUG
02837        char *dump_data;
02838        int dump_free;
02839 #endif /* EXIF_DEBUG */
02840 
02841        /* Protect against corrupt headers */
02842        if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
02843               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached");
02844               return FALSE;
02845        }
02846        ImageInfo->ifd_nesting_level++;
02847 
02848        tag = php_ifd_get16u(dir_entry, ImageInfo->motorola_intel);
02849        format = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
02850        components = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel);
02851 
02852        if (!format || format > NUM_FORMATS) {
02853               /* (-1) catches illegal zero case as unsigned underflows to positive large. */
02854               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal format code 0x%04X, suppose BYTE", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), format);
02855               format = TAG_FMT_BYTE;
02856               /*return TRUE;*/
02857        }
02858 
02859        if (components < 0) {
02860               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal components(%ld)", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), components);
02861               return FALSE;
02862        }
02863 
02864        byte_count_signed = (int64_t)components * php_tiff_bytes_per_format[format];
02865 
02866        if (byte_count_signed < 0 || (byte_count_signed > INT32_MAX)) {
02867               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal byte_count", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC));
02868               return FALSE;
02869        }
02870 
02871        byte_count = (size_t)byte_count_signed;
02872 
02873        if (byte_count > 4) {
02874               offset_val = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
02875               /* If its bigger than 4 bytes, the dir entry contains an offset. */
02876               value_ptr = offset_base+offset_val;
02877               if (byte_count > IFDlength || offset_val > IFDlength-byte_count || value_ptr < dir_entry) {
02878                      /* It is important to check for IMAGE_FILETYPE_TIFF
02879                       * JPEG does not use absolute pointers instead its pointers are
02880                       * relative to the start of the TIFF header in APP1 section. */
02881                      if (byte_count > ImageInfo->FileSize || offset_val>ImageInfo->FileSize-byte_count || (ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_II && ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_MM && ImageInfo->FileType!=IMAGE_FILETYPE_JPEG)) {
02882                             if (value_ptr < dir_entry) {
02883                                    /* we can read this if offset_val > 0 */
02884                                    /* some files have their values in other parts of the file */
02885                                    exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal pointer offset(x%04X < x%04X)", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), offset_val, dir_entry);
02886                             } else {
02887                                    /* this is for sure not allowed */
02888                                    /* exception are IFD pointers */
02889                                    exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal pointer offset(x%04X + x%04X = x%04X > x%04X)", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), offset_val, byte_count, offset_val+byte_count, IFDlength);
02890                             }
02891                             return FALSE;
02892                      }
02893                      if (byte_count>sizeof(cbuf)) {
02894                             /* mark as outside range and get buffer */
02895                             value_ptr = safe_emalloc(byte_count, 1, 0);
02896                             outside = value_ptr;
02897                      } else {
02898                             /* In most cases we only access a small range so
02899                              * it is faster to use a static buffer there
02900                              * BUT it offers also the possibility to have
02901                              * pointers read without the need to free them
02902                              * explicitley before returning. */
02903                             memset(&cbuf, 0, sizeof(cbuf));
02904                             value_ptr = cbuf;
02905                      }
02906 
02907                      fpos = php_stream_tell(ImageInfo->infile);
02908                      php_stream_seek(ImageInfo->infile, offset_val, SEEK_SET);
02909                      fgot = php_stream_tell(ImageInfo->infile);
02910                      if (fgot!=offset_val) {
02911                             EFREE_IF(outside);
02912                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x%08X", fgot, offset_val);
02913                             return FALSE;
02914                      }
02915                      fgot = php_stream_read(ImageInfo->infile, value_ptr, byte_count);
02916                      php_stream_seek(ImageInfo->infile, fpos, SEEK_SET);
02917                      if (fgot<byte_count) {
02918                             EFREE_IF(outside);
02919                             EXIF_ERRLOG_FILEEOF(ImageInfo)
02920                             return FALSE;
02921                      }
02922               }
02923        } else {
02924               /* 4 bytes or less and value is in the dir entry itself */
02925               value_ptr = dir_entry+8;
02926               offset_val= value_ptr-offset_base;
02927        }
02928 
02929        ImageInfo->sections_found |= FOUND_ANY_TAG;
02930 #ifdef EXIF_DEBUG
02931        dump_data = exif_dump_data(&dump_free, format, components, length, ImageInfo->motorola_intel, value_ptr TSRMLS_CC);
02932        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process tag(x%04X=%s,@x%04X + x%04X(=%d)): %s%s %s", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), offset_val+displacement, byte_count, byte_count, (components>1)&&format!=TAG_FMT_UNDEFINED&&format!=TAG_FMT_STRING?"ARRAY OF ":"", exif_get_tagformat(format), dump_data);
02933        if (dump_free) {
02934               efree(dump_data);
02935        }
02936 #endif
02937 
02938        if (section_index==SECTION_THUMBNAIL) {
02939               if (!ImageInfo->Thumbnail.data) {
02940                      switch(tag) {
02941                             case TAG_IMAGEWIDTH:
02942                             case TAG_COMP_IMAGE_WIDTH:
02943                                    ImageInfo->Thumbnail.width = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
02944                                    break;
02945        
02946                             case TAG_IMAGEHEIGHT:
02947                             case TAG_COMP_IMAGE_HEIGHT:
02948                                    ImageInfo->Thumbnail.height = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
02949                                    break;
02950        
02951                             case TAG_STRIP_OFFSETS:
02952                             case TAG_JPEG_INTERCHANGE_FORMAT:
02953                                    /* accept both formats */
02954                                    ImageInfo->Thumbnail.offset = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
02955                                    break;
02956        
02957                             case TAG_STRIP_BYTE_COUNTS:
02958                                    if (ImageInfo->FileType == IMAGE_FILETYPE_TIFF_II || ImageInfo->FileType == IMAGE_FILETYPE_TIFF_MM) {
02959                                           ImageInfo->Thumbnail.filetype = ImageInfo->FileType;
02960                                    } else {
02961                                           /* motorola is easier to read */
02962                                           ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_TIFF_MM;
02963                                    }
02964                                    ImageInfo->Thumbnail.size = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
02965                                    break;
02966        
02967                             case TAG_JPEG_INTERCHANGE_FORMAT_LEN:
02968                                    if (ImageInfo->Thumbnail.filetype == IMAGE_FILETYPE_UNKNOWN) {
02969                                           ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_JPEG;
02970                                           ImageInfo->Thumbnail.size = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
02971                                    }
02972                                    break;
02973                      }
02974               }
02975        } else {
02976               if (section_index==SECTION_IFD0 || section_index==SECTION_EXIF)
02977               switch(tag) {
02978                      case TAG_COPYRIGHT:
02979                             /* check for "<photographer> NUL <editor> NUL" */
02980                             if (byte_count>1 && (length=php_strnlen(value_ptr, byte_count)) > 0) {
02981                                    if (length<byte_count-1) {
02982                                           /* When there are any characters after the first NUL */
02983                                           ImageInfo->CopyrightPhotographer  = estrdup(value_ptr);
02984                                           ImageInfo->CopyrightEditor        = estrdup(value_ptr+length+1);
02985                                           spprintf(&ImageInfo->Copyright, 0, "%s, %s", value_ptr, value_ptr+length+1);
02986                                           /* format = TAG_FMT_UNDEFINED; this musn't be ASCII         */
02987                                           /* but we are not supposed to change this                   */
02988                                           /* keep in mind that image_info does not store editor value */
02989                                    } else {
02990                                           ImageInfo->Copyright = estrdup(value_ptr);
02991                                    }
02992                             }
02993                             break;   
02994 
02995                      case TAG_USERCOMMENT:
02996                             ImageInfo->UserCommentLength = exif_process_user_comment(ImageInfo, &(ImageInfo->UserComment), &(ImageInfo->UserCommentEncoding), value_ptr, byte_count TSRMLS_CC);
02997                             break;
02998 
02999                      case TAG_XP_TITLE:
03000                      case TAG_XP_COMMENTS:
03001                      case TAG_XP_AUTHOR:
03002                      case TAG_XP_KEYWORDS:
03003                      case TAG_XP_SUBJECT:
03004                             tmp_xp = (xp_field_type*)safe_erealloc(ImageInfo->xp_fields.list, (ImageInfo->xp_fields.count+1), sizeof(xp_field_type), 0);
03005                             ImageInfo->sections_found |= FOUND_WINXP;
03006                             ImageInfo->xp_fields.list = tmp_xp;
03007                             ImageInfo->xp_fields.count++;
03008                             exif_process_unicode(ImageInfo, &(ImageInfo->xp_fields.list[ImageInfo->xp_fields.count-1]), tag, value_ptr, byte_count TSRMLS_CC);
03009                             break;
03010 
03011                      case TAG_FNUMBER:
03012                             /* Simplest way of expressing aperture, so I trust it the most.
03013                                (overwrite previously computed value if there is one) */
03014                             ImageInfo->ApertureFNumber = (float)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
03015                             break;
03016 
03017                      case TAG_APERTURE:
03018                      case TAG_MAX_APERTURE:
03019                             /* More relevant info always comes earlier, so only use this field if we don't
03020                                have appropriate aperture information yet. */
03021                             if (ImageInfo->ApertureFNumber == 0) {
03022                                    ImageInfo->ApertureFNumber
03023                                           = (float)exp(exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)*log(2)*0.5);
03024                             }
03025                             break;
03026 
03027                      case TAG_SHUTTERSPEED:
03028                             /* More complicated way of expressing exposure time, so only use
03029                                this value if we don't already have it from somewhere else.
03030                                SHUTTERSPEED comes after EXPOSURE TIME
03031                               */
03032                             if (ImageInfo->ExposureTime == 0) {
03033                                    ImageInfo->ExposureTime
03034                                           = (float)(1/exp(exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)*log(2)));
03035                             }
03036                             break;
03037                      case TAG_EXPOSURETIME:
03038                             ImageInfo->ExposureTime = -1;
03039                             break;
03040 
03041                      case TAG_COMP_IMAGE_WIDTH:
03042                             ImageInfo->ExifImageWidth = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
03043                             break;
03044 
03045                      case TAG_FOCALPLANE_X_RES:
03046                             ImageInfo->FocalplaneXRes = exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
03047                             break;
03048 
03049                      case TAG_SUBJECT_DISTANCE:
03050                             /* Inidcates the distacne the autofocus camera is focused to.
03051                                Tends to be less accurate as distance increases. */
03052                             ImageInfo->Distance = (float)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
03053                             break;
03054 
03055                      case TAG_FOCALPLANE_RESOLUTION_UNIT:
03056                             switch((int)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)) {
03057                                    case 1: ImageInfo->FocalplaneUnits = 25.4; break; /* inch */
03058                                    case 2:
03059                                           /* According to the information I was using, 2 measn meters.
03060                                              But looking at the Cannon powershot's files, inches is the only
03061                                              sensible value. */
03062                                           ImageInfo->FocalplaneUnits = 25.4;
03063                                           break;
03064 
03065                                    case 3: ImageInfo->FocalplaneUnits = 10;   break;  /* centimeter */
03066                                    case 4: ImageInfo->FocalplaneUnits = 1;    break;  /* milimeter  */
03067                                    case 5: ImageInfo->FocalplaneUnits = .001; break;  /* micrometer */
03068                             }
03069                             break;
03070 
03071                      case TAG_SUB_IFD:
03072                             if (format==TAG_FMT_IFD) {
03073                                    /* If this is called we are either in a TIFFs thumbnail or a JPEG where we cannot handle it */
03074                                    /* TIFF thumbnail: our data structure cannot store a thumbnail of a thumbnail */
03075                                    /* JPEG do we have the data area and what to do with it */
03076                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Skip SUB IFD");
03077                             }
03078                             break;
03079 
03080                      case TAG_MAKE:
03081                             ImageInfo->make = estrdup(value_ptr);
03082                             break;
03083                      case TAG_MODEL:
03084                             ImageInfo->model = estrdup(value_ptr);
03085                             break;
03086 
03087                      case TAG_MAKER_NOTE:
03088                             exif_process_IFD_in_MAKERNOTE(ImageInfo, value_ptr, byte_count, offset_base, IFDlength, displacement TSRMLS_CC);
03089                             break;
03090 
03091                      case TAG_EXIF_IFD_POINTER:
03092                      case TAG_GPS_IFD_POINTER:
03093                      case TAG_INTEROP_IFD_POINTER:
03094                             if (ReadNextIFD) {
03095                                    char *Subdir_start;
03096                                    int sub_section_index = 0;
03097                                    switch(tag) {
03098                                           case TAG_EXIF_IFD_POINTER:
03099 #ifdef EXIF_DEBUG
03100                                                  exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found EXIF");
03101 #endif
03102                                                  ImageInfo->sections_found |= FOUND_EXIF;
03103                                                  sub_section_index = SECTION_EXIF;
03104                                                  break;
03105                                           case TAG_GPS_IFD_POINTER:
03106 #ifdef EXIF_DEBUG
03107                                                  exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found GPS");
03108 #endif
03109                                                  ImageInfo->sections_found |= FOUND_GPS;
03110                                                  sub_section_index = SECTION_GPS;
03111                                                  break;
03112                                           case TAG_INTEROP_IFD_POINTER:
03113 #ifdef EXIF_DEBUG
03114                                                  exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found INTEROPERABILITY");
03115 #endif
03116                                                  ImageInfo->sections_found |= FOUND_INTEROP;
03117                                                  sub_section_index = SECTION_INTEROP;
03118                                                  break;
03119                                    }
03120                                    Subdir_start = offset_base + php_ifd_get32u(value_ptr, ImageInfo->motorola_intel);
03121                                    if (Subdir_start < offset_base || Subdir_start > offset_base+IFDlength) {
03122                                           exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD Pointer");
03123                                           return FALSE;
03124                                    }
03125                                    if (!exif_process_IFD_in_JPEG(ImageInfo, Subdir_start, offset_base, IFDlength, displacement, sub_section_index TSRMLS_CC)) {
03126                                           return FALSE;
03127                                    }
03128 #ifdef EXIF_DEBUG
03129                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Subsection %s done", exif_get_sectionname(sub_section_index));
03130 #endif
03131                             }
03132               }
03133        }
03134        exif_iif_add_tag(ImageInfo, section_index, exif_get_tagname(tag, tagname, sizeof(tagname), tag_table TSRMLS_CC), tag, format, components, value_ptr TSRMLS_CC);
03135        EFREE_IF(outside);
03136        return TRUE;
03137 }
03138 /* }}} */
03139 
03140 /* {{{ exif_process_IFD_in_JPEG
03141  * Process one of the nested IFDs directories. */
03142 static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start, char *offset_base, size_t IFDlength, size_t displacement, int section_index TSRMLS_DC)
03143 {
03144        int de;
03145        int NumDirEntries;
03146        int NextDirOffset;
03147 
03148 #ifdef EXIF_DEBUG
03149        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process %s (x%04X(=%d))", exif_get_sectionname(section_index), IFDlength, IFDlength);
03150 #endif
03151 
03152        ImageInfo->sections_found |= FOUND_IFD0;
03153 
03154        NumDirEntries = php_ifd_get16u(dir_start, ImageInfo->motorola_intel);
03155 
03156        if ((dir_start+2+NumDirEntries*12) > (offset_base+IFDlength)) {
03157               exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD size: x%04X + 2 + x%04X*12 = x%04X > x%04X", (int)((size_t)dir_start+2-(size_t)offset_base), NumDirEntries, (int)((size_t)dir_start+2+NumDirEntries*12-(size_t)offset_base), IFDlength);
03158               return FALSE;
03159        }
03160 
03161        for (de=0;de<NumDirEntries;de++) {
03162               if (!exif_process_IFD_TAG(ImageInfo, dir_start + 2 + 12 * de,
03163                                                           offset_base, IFDlength, displacement, section_index, 1, exif_get_tag_table(section_index) TSRMLS_CC)) {
03164                      return FALSE;
03165               }
03166        }
03167        /*
03168         * Ignore IFD2 if it purportedly exists
03169         */
03170        if (section_index == SECTION_THUMBNAIL) {
03171               return TRUE;
03172        }
03173        /*
03174         * Hack to make it process IDF1 I hope
03175         * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail
03176         */
03177        NextDirOffset = php_ifd_get32u(dir_start+2+12*de, ImageInfo->motorola_intel);
03178        if (NextDirOffset) {
03179               /* the next line seems false but here IFDlength means length of all IFDs */
03180               if (offset_base + NextDirOffset < offset_base || offset_base + NextDirOffset > offset_base+IFDlength) {
03181                      exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD offset");
03182                      return FALSE;
03183               }
03184               /* That is the IFD for the first thumbnail */
03185 #ifdef EXIF_DEBUG
03186               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Expect next IFD to be thumbnail");
03187 #endif
03188               if (exif_process_IFD_in_JPEG(ImageInfo, offset_base + NextDirOffset, offset_base, IFDlength, displacement, SECTION_THUMBNAIL TSRMLS_CC)) {
03189 #ifdef EXIF_DEBUG
03190                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail size: 0x%04X", ImageInfo->Thumbnail.size);
03191 #endif
03192                      if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN
03193                      &&  ImageInfo->Thumbnail.size
03194                      &&  ImageInfo->Thumbnail.offset
03195                      &&  ImageInfo->read_thumbnail
03196                      ) {
03197                             exif_thumbnail_extract(ImageInfo, offset_base, IFDlength TSRMLS_CC);
03198                      }
03199                      return TRUE;
03200               } else {
03201                      return FALSE;
03202               }
03203        }
03204        return TRUE;
03205 }
03206 /* }}} */
03207 
03208 /* {{{ exif_process_TIFF_in_JPEG
03209    Process a TIFF header in a JPEG file
03210 */
03211 static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo, char *CharBuf, size_t length, size_t displacement TSRMLS_DC)
03212 {
03213        unsigned exif_value_2a, offset_of_ifd;
03214 
03215        /* set the thumbnail stuff to nothing so we can test to see if they get set up */
03216        if (memcmp(CharBuf, "II", 2) == 0) {
03217               ImageInfo->motorola_intel = 0;
03218        } else if (memcmp(CharBuf, "MM", 2) == 0) {
03219               ImageInfo->motorola_intel = 1;
03220        } else {
03221               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF alignment marker");
03222               return;
03223        }
03224 
03225        /* Check the next two values for correctness. */
03226        exif_value_2a = php_ifd_get16u(CharBuf+2, ImageInfo->motorola_intel);
03227        offset_of_ifd = php_ifd_get32u(CharBuf+4, ImageInfo->motorola_intel);
03228        if ( exif_value_2a != 0x2a || offset_of_ifd < 0x08) {
03229               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF start (1)");
03230               return;
03231        }
03232        if (offset_of_ifd > length) {
03233               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid IFD start");
03234               return;
03235        }
03236 
03237        ImageInfo->sections_found |= FOUND_IFD0;
03238        /* First directory starts at offset 8. Offsets starts at 0. */
03239        exif_process_IFD_in_JPEG(ImageInfo, CharBuf+offset_of_ifd, CharBuf, length/*-14*/, displacement, SECTION_IFD0 TSRMLS_CC);
03240 
03241 #ifdef EXIF_DEBUG
03242        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process TIFF in JPEG done");
03243 #endif
03244 
03245        /* Compute the CCD width, in milimeters. */
03246        if (ImageInfo->FocalplaneXRes != 0) {
03247               ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth * ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes);
03248        }
03249 }
03250 /* }}} */
03251 
03252 /* {{{ exif_process_APP1
03253    Process an JPEG APP1 block marker
03254    Describes all the drivel that most digital cameras include...
03255 */
03256 static void exif_process_APP1(image_info_type *ImageInfo, char *CharBuf, size_t length, size_t displacement TSRMLS_DC)
03257 {
03258        /* Check the APP1 for Exif Identifier Code */
03259        static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
03260        if (length <= 8 || memcmp(CharBuf+2, ExifHeader, 6)) {
03261               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Incorrect APP1 Exif Identifier Code");
03262               return;
03263        }
03264        exif_process_TIFF_in_JPEG(ImageInfo, CharBuf + 8, length - 8, displacement+8 TSRMLS_CC);
03265 #ifdef EXIF_DEBUG
03266        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process APP1/EXIF done");
03267 #endif
03268 }
03269 /* }}} */
03270 
03271 /* {{{ exif_process_APP12
03272    Process an JPEG APP12 block marker used by OLYMPUS
03273 */
03274 static void exif_process_APP12(image_info_type *ImageInfo, char *buffer, size_t length TSRMLS_DC)
03275 {
03276        size_t l1, l2=0;
03277 
03278        if ((l1 = php_strnlen(buffer+2, length-2)) > 0) {
03279               exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company", TAG_NONE, TAG_FMT_STRING, l1, buffer+2 TSRMLS_CC);
03280               if (length > 2+l1+1) {
03281                      l2 = php_strnlen(buffer+2+l1+1, length-2-l1+1);
03282                      exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info", TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1 TSRMLS_CC);
03283               }
03284        }
03285 #ifdef EXIF_DEBUG
03286        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process section APP12 with l1=%d, l2=%d done", l1, l2);
03287 #endif
03288 }
03289 /* }}} */
03290 
03291 /* {{{ exif_scan_JPEG_header
03292  * Parse the marker stream until SOS or EOI is seen; */
03293 static int exif_scan_JPEG_header(image_info_type *ImageInfo TSRMLS_DC)
03294 {
03295        int section, sn;
03296        int marker = 0, last_marker = M_PSEUDO, comment_correction=1;
03297        unsigned int ll, lh;
03298        uchar *Data;
03299        size_t fpos, size, got, itemlen;
03300        jpeg_sof_info  sof_info;
03301 
03302        for(section=0;;section++) {
03303 #ifdef EXIF_DEBUG
03304               fpos = php_stream_tell(ImageInfo->infile);
03305               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Needing section %d @ 0x%08X", ImageInfo->file.count, fpos);
03306 #endif
03307 
03308               /* get marker byte, swallowing possible padding                           */
03309               /* some software does not count the length bytes of COM section           */
03310               /* one company doing so is very much envolved in JPEG... so we accept too */
03311               if (last_marker==M_COM && comment_correction) {
03312                      comment_correction = 2;
03313               }
03314               do {
03315                      if ((marker = php_stream_getc(ImageInfo->infile)) == EOF) {
03316                             EXIF_ERRLOG_CORRUPT(ImageInfo)
03317                             return FALSE;
03318                      }
03319                      if (last_marker==M_COM && comment_correction>0) {
03320                             if (marker!=0xFF) {
03321                                    marker = 0xff;
03322                                    comment_correction--;
03323                             } else  {
03324                                    last_marker = M_PSEUDO; /* stop skipping 0 for M_COM */
03325                             }
03326                      }
03327               } while (marker == 0xff);
03328               if (last_marker==M_COM && !comment_correction) {
03329                      exif_error_docref("exif_read_data#error_mcom" EXIFERR_CC, ImageInfo, E_NOTICE, "Image has corrupt COM section: some software set wrong length information");
03330               }
03331               if (last_marker==M_COM && comment_correction)
03332                      return M_EOI; /* ah illegal: char after COM section not 0xFF */
03333 
03334               fpos = php_stream_tell(ImageInfo->infile);
03335 
03336               if (marker == 0xff) {
03337                      /* 0xff is legal padding, but if we get that many, something's wrong. */
03338                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "To many padding bytes");
03339                      return FALSE;
03340               }
03341 
03342               /* Read the length of the section. */
03343               if ((lh = php_stream_getc(ImageInfo->infile)) == EOF) {
03344                      EXIF_ERRLOG_CORRUPT(ImageInfo)
03345                      return FALSE;
03346               }
03347               if ((ll = php_stream_getc(ImageInfo->infile)) == EOF) {
03348                      EXIF_ERRLOG_CORRUPT(ImageInfo)
03349                      return FALSE;
03350               }
03351 
03352               itemlen = (lh << 8) | ll;
03353 
03354               if (itemlen < 2) {
03355 #ifdef EXIF_DEBUG
03356                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s, Section length: 0x%02X%02X", EXIF_ERROR_CORRUPT, lh, ll);
03357 #else
03358                      EXIF_ERRLOG_CORRUPT(ImageInfo)
03359 #endif
03360                      return FALSE;
03361               }
03362 
03363               sn = exif_file_sections_add(ImageInfo, marker, itemlen+1, NULL);
03364               Data = ImageInfo->file.list[sn].data;
03365 
03366               /* Store first two pre-read bytes. */
03367               Data[0] = (uchar)lh;
03368               Data[1] = (uchar)ll;
03369 
03370               got = php_stream_read(ImageInfo->infile, (char*)(Data+2), itemlen-2); /* Read the whole section. */
03371               if (got != itemlen-2) {
03372                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error reading from file: got=x%04X(=%d) != itemlen-2=x%04X(=%d)", got, got, itemlen-2, itemlen-2);
03373                      return FALSE;
03374               }
03375 
03376 #ifdef EXIF_DEBUG
03377               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process section(x%02X=%s) @ x%04X + x%04X(=%d)", marker, exif_get_markername(marker), fpos, itemlen, itemlen);
03378 #endif
03379               switch(marker) {
03380                      case M_SOS:   /* stop before hitting compressed data  */
03381                             /* If reading entire image is requested, read the rest of the data. */
03382                             if (ImageInfo->read_all) {
03383                                    /* Determine how much file is left. */
03384                                    fpos = php_stream_tell(ImageInfo->infile);
03385                                    size = ImageInfo->FileSize - fpos;
03386                                    sn = exif_file_sections_add(ImageInfo, M_PSEUDO, size, NULL);
03387                                    Data = ImageInfo->file.list[sn].data;
03388                                    got = php_stream_read(ImageInfo->infile, (char*)Data, size);
03389                                    if (got != size) {
03390                                           EXIF_ERRLOG_FILEEOF(ImageInfo)
03391                                           return FALSE;
03392                                    }
03393                             }
03394                             return TRUE;
03395 
03396                      case M_EOI:   /* in case it's a tables-only JPEG stream */
03397                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "No image in jpeg!");
03398                             return (ImageInfo->sections_found&(~FOUND_COMPUTED)) ? TRUE : FALSE;
03399 
03400                      case M_COM: /* Comment section */
03401                             exif_process_COM(ImageInfo, (char *)Data, itemlen TSRMLS_CC);
03402                             break;
03403 
03404                      case M_EXIF:
03405                             if (!(ImageInfo->sections_found&FOUND_IFD0)) {
03406                                    /*ImageInfo->sections_found |= FOUND_EXIF;*/
03407                                    /* Seen files from some 'U-lead' software with Vivitar scanner
03408                                       that uses marker 31 later in the file (no clue what for!) */
03409                                    exif_process_APP1(ImageInfo, (char *)Data, itemlen, fpos TSRMLS_CC);
03410                             }
03411                             break;
03412 
03413                      case M_APP12:
03414                             exif_process_APP12(ImageInfo, (char *)Data, itemlen TSRMLS_CC);
03415                             break;
03416 
03417 
03418                      case M_SOF0:
03419                      case M_SOF1:
03420                      case M_SOF2:
03421                      case M_SOF3:
03422                      case M_SOF5:
03423                      case M_SOF6:
03424                      case M_SOF7:
03425                      case M_SOF9:
03426                      case M_SOF10:
03427                      case M_SOF11:
03428                      case M_SOF13:
03429                      case M_SOF14:
03430                      case M_SOF15:
03431                             exif_process_SOFn(Data, marker, &sof_info);
03432                             ImageInfo->Width  = sof_info.width;
03433                             ImageInfo->Height = sof_info.height;
03434                             if (sof_info.num_components == 3) {
03435                                    ImageInfo->IsColor = 1;
03436                             } else {
03437                                    ImageInfo->IsColor = 0;
03438                             }
03439                             break;
03440                      default:
03441                             /* skip any other marker silently. */
03442                             break;
03443               }
03444 
03445               /* keep track of last marker */
03446               last_marker = marker;
03447        }
03448 #ifdef EXIF_DEBUG
03449        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Done");
03450 #endif
03451        return TRUE;
03452 }
03453 /* }}} */
03454 
03455 /* {{{ exif_scan_thumbnail
03456  * scan JPEG in thumbnail (memory) */
03457 static int exif_scan_thumbnail(image_info_type *ImageInfo TSRMLS_DC)
03458 {
03459        uchar           c, *data = (uchar*)ImageInfo->Thumbnail.data;
03460        int             n, marker;
03461        size_t          length=2, pos=0;
03462        jpeg_sof_info   sof_info;
03463 
03464        if (!data) {
03465               return FALSE; /* nothing to do here */
03466        }
03467        if (memcmp(data, "\xFF\xD8\xFF", 3)) {
03468               if (!ImageInfo->Thumbnail.width && !ImageInfo->Thumbnail.height) {
03469                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Thumbnail is not a JPEG image");
03470               }
03471               return FALSE;
03472        }
03473        for (;;) {
03474               pos += length;
03475               if (pos>=ImageInfo->Thumbnail.size) 
03476                      return FALSE;
03477               c = data[pos++];
03478               if (pos>=ImageInfo->Thumbnail.size) 
03479                      return FALSE;
03480               if (c != 0xFF) {
03481                      return FALSE;
03482               }
03483               n = 8;
03484               while ((c = data[pos++]) == 0xFF && n--) {
03485                      if (pos+3>=ImageInfo->Thumbnail.size) 
03486                             return FALSE;
03487                      /* +3 = pos++ of next check when reaching marker + 2 bytes for length */
03488               }
03489               if (c == 0xFF) 
03490                      return FALSE;
03491               marker = c;
03492               length = php_jpg_get16(data+pos);
03493               if (pos+length>=ImageInfo->Thumbnail.size) {
03494                      return FALSE;
03495               }
03496 #ifdef EXIF_DEBUG
03497               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: process section(x%02X=%s) @ x%04X + x%04X", marker, exif_get_markername(marker), pos, length);
03498 #endif
03499               switch (marker) {
03500                      case M_SOF0:
03501                      case M_SOF1:
03502                      case M_SOF2:
03503                      case M_SOF3:
03504                      case M_SOF5:
03505                      case M_SOF6:
03506                      case M_SOF7:
03507                      case M_SOF9:
03508                      case M_SOF10:
03509                      case M_SOF11:
03510                      case M_SOF13:
03511                      case M_SOF14:
03512                      case M_SOF15:
03513                             /* handle SOFn block */
03514                             exif_process_SOFn(data+pos, marker, &sof_info);
03515                             ImageInfo->Thumbnail.height   = sof_info.height;
03516                             ImageInfo->Thumbnail.width    = sof_info.width;
03517 #ifdef EXIF_DEBUG
03518                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: size: %d * %d", sof_info.width, sof_info.height);
03519 #endif
03520                             return TRUE;
03521 
03522                      case M_SOS:
03523                      case M_EOI:
03524                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Could not compute size of thumbnail");
03525                             return FALSE;
03526                             break;
03527 
03528                      default:
03529                             /* just skip */
03530                             break;
03531               }
03532        }
03533 
03534        exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Could not compute size of thumbnail");
03535        return FALSE;
03536 }
03537 /* }}} */
03538 
03539 /* {{{ exif_process_IFD_in_TIFF
03540  * Parse the TIFF header; */
03541 static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index TSRMLS_DC)
03542 {
03543        int i, sn, num_entries, sub_section_index = 0;
03544        unsigned char *dir_entry;
03545        char tagname[64];
03546        size_t ifd_size, dir_size, entry_offset, next_offset, entry_length, entry_value=0, fgot;
03547        int entry_tag , entry_type;
03548        tag_table_type tag_table = exif_get_tag_table(section_index);
03549 
03550        if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
03551                 return FALSE;
03552         }
03553 
03554        if (ImageInfo->FileSize >= dir_offset+2) {
03555               sn = exif_file_sections_add(ImageInfo, M_PSEUDO, 2, NULL);
03556 #ifdef EXIF_DEBUG
03557               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF: filesize(x%04X), IFD dir(x%04X + x%04X)", ImageInfo->FileSize, dir_offset, 2);
03558 #endif
03559               php_stream_seek(ImageInfo->infile, dir_offset, SEEK_SET); /* we do not know the order of sections */
03560               php_stream_read(ImageInfo->infile, (char*)ImageInfo->file.list[sn].data, 2);
03561               num_entries = php_ifd_get16u(ImageInfo->file.list[sn].data, ImageInfo->motorola_intel);
03562               dir_size = 2/*num dir entries*/ +12/*length of entry*/*num_entries +4/* offset to next ifd (points to thumbnail or NULL)*/;
03563               if (ImageInfo->FileSize >= dir_offset+dir_size) {
03564 #ifdef EXIF_DEBUG
03565                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF: filesize(x%04X), IFD dir(x%04X + x%04X), IFD entries(%d)", ImageInfo->FileSize, dir_offset+2, dir_size-2, num_entries);
03566 #endif
03567                      if (exif_file_sections_realloc(ImageInfo, sn, dir_size TSRMLS_CC)) {
03568                             return FALSE;
03569                      }
03570                      php_stream_read(ImageInfo->infile, (char*)(ImageInfo->file.list[sn].data+2), dir_size-2);
03571                      /*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Dump: %s", exif_char_dump(ImageInfo->file.list[sn].data, dir_size, 0));*/
03572                      next_offset = php_ifd_get32u(ImageInfo->file.list[sn].data + dir_size - 4, ImageInfo->motorola_intel);
03573 #ifdef EXIF_DEBUG
03574                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF done, next offset x%04X", next_offset);
03575 #endif
03576                      /* now we have the directory we can look how long it should be */
03577                      ifd_size = dir_size;
03578                      for(i=0;i<num_entries;i++) {
03579                             dir_entry      = ImageInfo->file.list[sn].data+2+i*12;
03580                             entry_tag    = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
03581                             entry_type   = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
03582                             if (entry_type > NUM_FORMATS) {
03583                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF: tag(0x%04X,%12s): Illegal format code 0x%04X, switching to BYTE", entry_tag, exif_get_tagname(entry_tag, tagname, -12, tag_table TSRMLS_CC), entry_type);
03584                                    /* Since this is repeated in exif_process_IFD_TAG make it a notice here */
03585                                    /* and make it a warning in the exif_process_IFD_TAG which is called    */
03586                                    /* elsewhere. */
03587                                    entry_type = TAG_FMT_BYTE;
03588                                    /*The next line would break the image on writeback: */
03589                                    /* php_ifd_set16u(dir_entry+2, entry_type, ImageInfo->motorola_intel);*/
03590                             }
03591                             entry_length = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel) * php_tiff_bytes_per_format[entry_type];
03592                             if (entry_length <= 4) {
03593                                    switch(entry_type) {
03594                                           case TAG_FMT_USHORT:
03595                                                  entry_value  = php_ifd_get16u(dir_entry+8, ImageInfo->motorola_intel);
03596                                                  break;
03597                                           case TAG_FMT_SSHORT:
03598                                                  entry_value  = php_ifd_get16s(dir_entry+8, ImageInfo->motorola_intel);
03599                                                  break;
03600                                           case TAG_FMT_ULONG:
03601                                                  entry_value  = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
03602                                                  break;
03603                                           case TAG_FMT_SLONG:
03604                                                  entry_value  = php_ifd_get32s(dir_entry+8, ImageInfo->motorola_intel);
03605                                                  break;
03606                                    }
03607                                    switch(entry_tag) {
03608                                           case TAG_IMAGEWIDTH:
03609                                           case TAG_COMP_IMAGE_WIDTH:
03610                                                  ImageInfo->Width  = entry_value;
03611                                                  break;
03612                                           case TAG_IMAGEHEIGHT:
03613                                           case TAG_COMP_IMAGE_HEIGHT:
03614                                                  ImageInfo->Height = entry_value;
03615                                                  break;
03616                                           case TAG_PHOTOMETRIC_INTERPRETATION:
03617                                                  switch (entry_value) {
03618                                                         case PMI_BLACK_IS_ZERO:
03619                                                         case PMI_WHITE_IS_ZERO:
03620                                                         case PMI_TRANSPARENCY_MASK:
03621                                                                ImageInfo->IsColor = 0;
03622                                                                break;
03623                                                         case PMI_RGB:
03624                                                         case PMI_PALETTE_COLOR:
03625                                                         case PMI_SEPARATED:
03626                                                         case PMI_YCBCR:
03627                                                         case PMI_CIELAB:
03628                                                                ImageInfo->IsColor = 1;
03629                                                                break;
03630                                                  }
03631                                                  break;
03632                                    }
03633                             } else {
03634                                    entry_offset = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
03635                                    /* if entry needs expading ifd cache and entry is at end of current ifd cache. */
03636                                    /* otherwise there may be huge holes between two entries */
03637                                    if (entry_offset + entry_length > dir_offset + ifd_size
03638                                      && entry_offset == dir_offset + ifd_size) {
03639                                           ifd_size = entry_offset + entry_length - dir_offset;
03640 #ifdef EXIF_DEBUG
03641                                           exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Resize struct: x%04X + x%04X - x%04X = x%04X", entry_offset, entry_length, dir_offset, ifd_size);
03642 #endif
03643                                    }
03644                             }
03645                      }
03646                      if (ImageInfo->FileSize >= dir_offset + ImageInfo->file.list[sn].size) {
03647                             if (ifd_size > dir_size) {
03648                                    if (dir_offset + ifd_size > ImageInfo->FileSize) {
03649                                           exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD(x%04X + x%04X)", ImageInfo->FileSize, dir_offset, ifd_size);
03650                                           return FALSE;
03651                                    }
03652                                    if (exif_file_sections_realloc(ImageInfo, sn, ifd_size TSRMLS_CC)) {
03653                                           return FALSE;
03654                                    }
03655                                    /* read values not stored in directory itself */
03656 #ifdef EXIF_DEBUG
03657                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF: filesize(x%04X), IFD(x%04X + x%04X)", ImageInfo->FileSize, dir_offset, ifd_size);
03658 #endif
03659                                    php_stream_read(ImageInfo->infile, (char*)(ImageInfo->file.list[sn].data+dir_size), ifd_size-dir_size);
03660 #ifdef EXIF_DEBUG
03661                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF, done");
03662 #endif
03663                             }
03664                             /* now process the tags */
03665                             for(i=0;i<num_entries;i++) {
03666                                    dir_entry      = ImageInfo->file.list[sn].data+2+i*12;
03667                                    entry_tag    = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
03668                                    entry_type   = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
03669                                    /*entry_length = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel);*/
03670                                    if (entry_tag == TAG_EXIF_IFD_POINTER ||
03671                                           entry_tag == TAG_INTEROP_IFD_POINTER ||
03672                                           entry_tag == TAG_GPS_IFD_POINTER ||
03673                                           entry_tag == TAG_SUB_IFD
03674                                    ) {
03675                                           switch(entry_tag) {
03676                                                  case TAG_EXIF_IFD_POINTER:
03677                                                         ImageInfo->sections_found |= FOUND_EXIF;
03678                                                         sub_section_index = SECTION_EXIF;
03679                                                         break;
03680                                                  case TAG_GPS_IFD_POINTER:
03681                                                         ImageInfo->sections_found |= FOUND_GPS;
03682                                                         sub_section_index = SECTION_GPS;
03683                                                         break;
03684                                                  case TAG_INTEROP_IFD_POINTER:
03685                                                         ImageInfo->sections_found |= FOUND_INTEROP;
03686                                                         sub_section_index = SECTION_INTEROP;
03687                                                         break;
03688                                                  case TAG_SUB_IFD:
03689                                                         ImageInfo->sections_found |= FOUND_THUMBNAIL;
03690                                                         sub_section_index = SECTION_THUMBNAIL;
03691                                                         break;
03692                                           }
03693                                           entry_offset = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
03694 #ifdef EXIF_DEBUG
03695                                           exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @x%04X", exif_get_sectionname(sub_section_index), entry_offset);
03696 #endif
03697                                           ImageInfo->ifd_nesting_level++;
03698                                           exif_process_IFD_in_TIFF(ImageInfo, entry_offset, sub_section_index TSRMLS_CC);
03699                                           if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) {
03700                                                  if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN
03701                                                  &&  ImageInfo->Thumbnail.size
03702                                                  &&  ImageInfo->Thumbnail.offset
03703                                                  &&  ImageInfo->read_thumbnail
03704                                                  ) {
03705 #ifdef EXIF_DEBUG
03706                                                         exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "%s THUMBNAIL @0x%04X + 0x%04X", ImageInfo->Thumbnail.data ? "Ignore" : "Read", ImageInfo->Thumbnail.offset, ImageInfo->Thumbnail.size);
03707 #endif
03708                                                         if (!ImageInfo->Thumbnail.data) {
03709                                                                ImageInfo->Thumbnail.data = safe_emalloc(ImageInfo->Thumbnail.size, 1, 0);
03710                                                                php_stream_seek(ImageInfo->infile, ImageInfo->Thumbnail.offset, SEEK_SET);
03711                                                                fgot = php_stream_read(ImageInfo->infile, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
03712                                                                if (fgot < ImageInfo->Thumbnail.size) {
03713                                                                       EXIF_ERRLOG_THUMBEOF(ImageInfo)
03714                                                                }
03715                                                                exif_thumbnail_build(ImageInfo TSRMLS_CC);
03716                                                         }
03717                                                  }
03718                                           }
03719 #ifdef EXIF_DEBUG
03720                                           exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s done", exif_get_sectionname(sub_section_index));
03721 #endif
03722                                    } else {
03723                                           if (!exif_process_IFD_TAG(ImageInfo, (char*)dir_entry,
03724                                                                                       (char*)(ImageInfo->file.list[sn].data-dir_offset),
03725                                                                                       ifd_size, 0, section_index, 0, tag_table TSRMLS_CC)) {
03726                                                  return FALSE;
03727                                           }
03728                                    }
03729                             }
03730                             /* If we had a thumbnail in a SUB_IFD we have ANOTHER image in NEXT IFD */
03731                             if (next_offset && section_index != SECTION_THUMBNAIL) {
03732                                    /* this should be a thumbnail IFD */
03733                                    /* the thumbnail itself is stored at Tag=StripOffsets */
03734 #ifdef EXIF_DEBUG
03735                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) at x%04X", next_offset);
03736 #endif
03737                                    ImageInfo->ifd_nesting_level++;
03738                                    exif_process_IFD_in_TIFF(ImageInfo, next_offset, SECTION_THUMBNAIL TSRMLS_CC);
03739 #ifdef EXIF_DEBUG
03740                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "%s THUMBNAIL @0x%04X + 0x%04X", ImageInfo->Thumbnail.data ? "Ignore" : "Read", ImageInfo->Thumbnail.offset, ImageInfo->Thumbnail.size);
03741 #endif
03742                                    if (!ImageInfo->Thumbnail.data && ImageInfo->Thumbnail.offset && ImageInfo->Thumbnail.size && ImageInfo->read_thumbnail) {
03743                                           ImageInfo->Thumbnail.data = safe_emalloc(ImageInfo->Thumbnail.size, 1, 0);
03744                                           php_stream_seek(ImageInfo->infile, ImageInfo->Thumbnail.offset, SEEK_SET);
03745                                           fgot = php_stream_read(ImageInfo->infile, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
03746                                           if (fgot < ImageInfo->Thumbnail.size) {
03747                                                  EXIF_ERRLOG_THUMBEOF(ImageInfo)
03748                                           }
03749                                           exif_thumbnail_build(ImageInfo TSRMLS_CC);
03750                                    }
03751 #ifdef EXIF_DEBUG
03752                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) done");
03753 #endif
03754                             }
03755                             return TRUE;
03756                      } else {
03757                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD(x%04X)", ImageInfo->FileSize, dir_offset+ImageInfo->file.list[sn].size);
03758                             return FALSE;
03759                      }
03760               } else {
03761                      exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD dir(x%04X)", ImageInfo->FileSize, dir_offset+dir_size);
03762                      return FALSE;
03763               }
03764        } else {
03765               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than start of IFD dir(x%04X)", ImageInfo->FileSize, dir_offset+2);
03766               return FALSE;
03767        }
03768 }
03769 /* }}} */
03770 
03771 /* {{{ exif_scan_FILE_header
03772  * Parse the marker stream until SOS or EOI is seen; */
03773 static int exif_scan_FILE_header(image_info_type *ImageInfo TSRMLS_DC)
03774 {
03775        unsigned char file_header[8];
03776        int ret = FALSE;
03777 
03778        ImageInfo->FileType = IMAGE_FILETYPE_UNKNOWN;
03779 
03780        if (ImageInfo->FileSize >= 2) {
03781               php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
03782               if (php_stream_read(ImageInfo->infile, (char*)file_header, 2) != 2) {
03783                      return FALSE;
03784               }
03785               if ((file_header[0]==0xff) && (file_header[1]==M_SOI)) {
03786                      ImageInfo->FileType = IMAGE_FILETYPE_JPEG;
03787                      if (exif_scan_JPEG_header(ImageInfo TSRMLS_CC)) {
03788                             ret = TRUE;
03789                      } else {
03790                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid JPEG file");
03791                      }
03792               } else if (ImageInfo->FileSize >= 8) {
03793                      if (php_stream_read(ImageInfo->infile, (char*)(file_header+2), 6) != 6) {
03794                             return FALSE;
03795                      }
03796                      if (!memcmp(file_header, "II\x2A\x00", 4)) {
03797                             ImageInfo->FileType = IMAGE_FILETYPE_TIFF_II;
03798                             ImageInfo->motorola_intel = 0;
03799 #ifdef EXIF_DEBUG
03800                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/II format");
03801 #endif
03802                             ImageInfo->sections_found |= FOUND_IFD0;
03803                             if (exif_process_IFD_in_TIFF(ImageInfo, 
03804                                                                               php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
03805                                                                               SECTION_IFD0 TSRMLS_CC)) {
03806                                    ret = TRUE;
03807                             } else {
03808                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
03809                             }
03810                      } else if (!memcmp(file_header, "MM\x00\x2a", 4)) {
03811                             ImageInfo->FileType = IMAGE_FILETYPE_TIFF_MM;
03812                             ImageInfo->motorola_intel = 1;
03813 #ifdef EXIF_DEBUG
03814                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/MM format");
03815 #endif
03816                             ImageInfo->sections_found |= FOUND_IFD0;
03817                             if (exif_process_IFD_in_TIFF(ImageInfo,
03818                                                                               php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
03819                                                                               SECTION_IFD0 TSRMLS_CC)) {
03820                                    ret = TRUE;
03821                             } else {
03822                                    exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
03823                             }
03824                      } else {
03825                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File not supported");
03826                             return FALSE;
03827                      }
03828               }
03829        } else {
03830               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File too small (%d)", ImageInfo->FileSize);
03831        }
03832        return ret;
03833 }
03834 /* }}} */
03835 
03836 /* {{{ exif_discard_imageinfo
03837    Discard data scanned by exif_read_file.
03838 */
03839 static int exif_discard_imageinfo(image_info_type *ImageInfo)
03840 {
03841        int i;
03842 
03843        EFREE_IF(ImageInfo->FileName);
03844        EFREE_IF(ImageInfo->UserComment);
03845        EFREE_IF(ImageInfo->UserCommentEncoding);
03846        EFREE_IF(ImageInfo->Copyright);
03847        EFREE_IF(ImageInfo->CopyrightPhotographer);
03848        EFREE_IF(ImageInfo->CopyrightEditor);
03849        EFREE_IF(ImageInfo->Thumbnail.data);
03850        EFREE_IF(ImageInfo->encode_unicode);
03851        EFREE_IF(ImageInfo->decode_unicode_be);
03852        EFREE_IF(ImageInfo->decode_unicode_le);
03853        EFREE_IF(ImageInfo->encode_jis);
03854        EFREE_IF(ImageInfo->decode_jis_be);
03855        EFREE_IF(ImageInfo->decode_jis_le);
03856        EFREE_IF(ImageInfo->make);
03857        EFREE_IF(ImageInfo->model);
03858        for (i=0; i<ImageInfo->xp_fields.count; i++) {
03859               EFREE_IF(ImageInfo->xp_fields.list[i].value);
03860        }
03861        EFREE_IF(ImageInfo->xp_fields.list);
03862        for (i=0; i<SECTION_COUNT; i++) {
03863               exif_iif_free(ImageInfo, i);
03864        }
03865        exif_file_sections_free(ImageInfo);
03866        memset(ImageInfo, 0, sizeof(*ImageInfo));
03867        return TRUE;
03868 }
03869 /* }}} */
03870 
03871 /* {{{ exif_read_file
03872  */
03873 static int exif_read_file(image_info_type *ImageInfo, char *FileName, int read_thumbnail, int read_all TSRMLS_DC)
03874 {
03875        int ret;
03876        struct stat st;
03877 
03878        /* Start with an empty image information structure. */
03879        memset(ImageInfo, 0, sizeof(*ImageInfo));
03880 
03881        ImageInfo->motorola_intel = -1; /* flag as unknown */
03882 
03883        ImageInfo->infile = php_stream_open_wrapper(FileName, "rb", STREAM_MUST_SEEK|IGNORE_PATH|ENFORCE_SAFE_MODE, NULL);
03884        if (!ImageInfo->infile) {
03885               exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Unable to open file");
03886               return FALSE;
03887        }
03888 
03889        if (php_stream_is(ImageInfo->infile, PHP_STREAM_IS_STDIO)) {
03890               if (VCWD_STAT(FileName, &st) >= 0) {
03891                      if ((st.st_mode & S_IFMT) != S_IFREG) {
03892                             exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Not a file");
03893                             php_stream_close(ImageInfo->infile);
03894                             return FALSE;
03895                      }
03896 
03897                      /* Store file date/time. */
03898                      ImageInfo->FileDateTime = st.st_mtime;
03899                      ImageInfo->FileSize = st.st_size;
03900                      /*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Opened stream is file: %d", ImageInfo->FileSize);*/
03901               }
03902        } else {
03903               if (!ImageInfo->FileSize) {
03904                      php_stream_seek(ImageInfo->infile, 0, SEEK_END);
03905                      ImageInfo->FileSize = php_stream_tell(ImageInfo->infile);
03906                      php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
03907               }
03908        }
03909 
03910        php_basename(FileName, strlen(FileName), NULL, 0, &(ImageInfo->FileName), NULL TSRMLS_CC);
03911        ImageInfo->read_thumbnail = read_thumbnail;
03912        ImageInfo->read_all = read_all;
03913        ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_UNKNOWN;
03914 
03915        ImageInfo->encode_unicode    = safe_estrdup(EXIF_G(encode_unicode));
03916        ImageInfo->decode_unicode_be = safe_estrdup(EXIF_G(decode_unicode_be));
03917        ImageInfo->decode_unicode_le = safe_estrdup(EXIF_G(decode_unicode_le));
03918        ImageInfo->encode_jis        = safe_estrdup(EXIF_G(encode_jis));
03919        ImageInfo->decode_jis_be     = safe_estrdup(EXIF_G(decode_jis_be));
03920        ImageInfo->decode_jis_le     = safe_estrdup(EXIF_G(decode_jis_le));
03921 
03922 
03923        ImageInfo->ifd_nesting_level = 0;
03924 
03925        /* Scan the JPEG headers. */
03926        ret = exif_scan_FILE_header(ImageInfo TSRMLS_CC);
03927 
03928        php_stream_close(ImageInfo->infile);
03929        return ret;
03930 }
03931 /* }}} */
03932 
03933 /* {{{ proto array exif_read_data(string filename [, sections_needed [, sub_arrays[, read_thumbnail]]])
03934    Reads header data from the JPEG/TIFF image filename and optionally reads the internal thumbnails */
03935 PHP_FUNCTION(exif_read_data)
03936 {
03937        char *p_name, *p_sections_needed = NULL;
03938        int p_name_len, p_sections_needed_len = 0;
03939        zend_bool sub_arrays=0, read_thumbnail=0, read_all=0;
03940 
03941        int i, ret, sections_needed=0;
03942        image_info_type ImageInfo;
03943        char tmp[64], *sections_str, *s;
03944 
03945        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sbb", &p_name, &p_name_len, &p_sections_needed, &p_sections_needed_len, &sub_arrays, &read_thumbnail) == FAILURE) {
03946               return;
03947        }
03948 
03949        memset(&ImageInfo, 0, sizeof(ImageInfo));
03950 
03951        if (p_sections_needed) {
03952               spprintf(&sections_str, 0, ",%s,", p_sections_needed);
03953               /* sections_str DOES start with , and SPACES are NOT allowed in names */
03954               s = sections_str;
03955               while (*++s) {
03956                      if (*s == ' ') {
03957                             *s = ',';
03958                      }
03959               }
03960 
03961               for (i = 0; i < SECTION_COUNT; i++) {
03962                      snprintf(tmp, sizeof(tmp), ",%s,", exif_get_sectionname(i));
03963                      if (strstr(sections_str, tmp)) {
03964                             sections_needed |= 1<<i;
03965                      }
03966               }
03967               EFREE_IF(sections_str);
03968               /* now see what we need */
03969 #ifdef EXIF_DEBUG
03970               sections_str = exif_get_sectionlist(sections_needed TSRMLS_CC);
03971               if (!sections_str) {
03972                      RETURN_FALSE;
03973               }
03974               exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Sections needed: %s", sections_str[0] ? sections_str : "None");
03975               EFREE_IF(sections_str);
03976 #endif
03977        }
03978 
03979        ret = exif_read_file(&ImageInfo, p_name, read_thumbnail, read_all TSRMLS_CC);
03980        sections_str = exif_get_sectionlist(ImageInfo.sections_found TSRMLS_CC);
03981 
03982 #ifdef EXIF_DEBUG
03983        if (sections_str) 
03984               exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Sections found: %s", sections_str[0] ? sections_str : "None");
03985 #endif
03986 
03987        ImageInfo.sections_found |= FOUND_COMPUTED|FOUND_FILE;/* do not inform about in debug*/
03988 
03989        if (ret == FALSE || (sections_needed && !(sections_needed&ImageInfo.sections_found))) {
03990               /* array_init must be checked at last! otherwise the array must be freed if a later test fails. */
03991               exif_discard_imageinfo(&ImageInfo);
03992               EFREE_IF(sections_str);
03993               RETURN_FALSE;
03994        }
03995 
03996        array_init(return_value);
03997 
03998 #ifdef EXIF_DEBUG
03999        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Generate section FILE");
04000 #endif
04001 
04002        /* now we can add our information */
04003        exif_iif_add_str(&ImageInfo, SECTION_FILE, "FileName",      ImageInfo.FileName TSRMLS_CC);
04004        exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileDateTime",  ImageInfo.FileDateTime TSRMLS_CC);
04005        exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileSize",      ImageInfo.FileSize TSRMLS_CC);
04006        exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileType",      ImageInfo.FileType TSRMLS_CC);
04007        exif_iif_add_str(&ImageInfo, SECTION_FILE, "MimeType",      (char*)php_image_type_to_mime_type(ImageInfo.FileType) TSRMLS_CC);
04008        exif_iif_add_str(&ImageInfo, SECTION_FILE, "SectionsFound", sections_str ? sections_str : "NONE" TSRMLS_CC);
04009 
04010 #ifdef EXIF_DEBUG
04011        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Generate section COMPUTED");
04012 #endif
04013 
04014        if (ImageInfo.Width>0 &&  ImageInfo.Height>0) {
04015               exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "html"    TSRMLS_CC, "width=\"%d\" height=\"%d\"", ImageInfo.Width, ImageInfo.Height);
04016               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Height", ImageInfo.Height TSRMLS_CC);
04017               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Width",  ImageInfo.Width TSRMLS_CC);
04018        }
04019        exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "IsColor", ImageInfo.IsColor TSRMLS_CC);
04020        if (ImageInfo.motorola_intel != -1) {
04021               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "ByteOrderMotorola", ImageInfo.motorola_intel TSRMLS_CC);
04022        }
04023        if (ImageInfo.FocalLength) {
04024               exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "FocalLength" TSRMLS_CC, "%4.1Fmm", ImageInfo.FocalLength);
04025               if(ImageInfo.CCDWidth) {
04026                      exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "35mmFocalLength" TSRMLS_CC, "%dmm", (int)(ImageInfo.FocalLength/ImageInfo.CCDWidth*35+0.5));
04027               }
04028        }
04029        if(ImageInfo.CCDWidth) {
04030               exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "CCDWidth" TSRMLS_CC, "%dmm", (int)ImageInfo.CCDWidth);
04031        }
04032        if(ImageInfo.ExposureTime>0) {
04033               if(ImageInfo.ExposureTime <= 0.5) {
04034                      exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ExposureTime" TSRMLS_CC, "%0.3F s (1/%d)", ImageInfo.ExposureTime, (int)(0.5 + 1/ImageInfo.ExposureTime));
04035               } else {
04036                      exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ExposureTime" TSRMLS_CC, "%0.3F s", ImageInfo.ExposureTime);
04037               }
04038        }
04039        if(ImageInfo.ApertureFNumber) {
04040               exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ApertureFNumber" TSRMLS_CC, "f/%.1F", ImageInfo.ApertureFNumber);
04041        }
04042        if(ImageInfo.Distance) {
04043               if(ImageInfo.Distance<0) {
04044                      exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "FocusDistance", "Infinite" TSRMLS_CC);
04045               } else {
04046                      exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "FocusDistance" TSRMLS_CC, "%0.2Fm", ImageInfo.Distance);
04047               }
04048        }
04049        if (ImageInfo.UserComment) {
04050               exif_iif_add_buffer(&ImageInfo, SECTION_COMPUTED, "UserComment", ImageInfo.UserCommentLength, ImageInfo.UserComment TSRMLS_CC);
04051               if (ImageInfo.UserCommentEncoding && strlen(ImageInfo.UserCommentEncoding)) {
04052                      exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "UserCommentEncoding", ImageInfo.UserCommentEncoding TSRMLS_CC);
04053               }
04054        }
04055 
04056        exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright",              ImageInfo.Copyright TSRMLS_CC);
04057        exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright.Photographer", ImageInfo.CopyrightPhotographer TSRMLS_CC);
04058        exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright.Editor",       ImageInfo.CopyrightEditor TSRMLS_CC);
04059 
04060        for (i=0; i<ImageInfo.xp_fields.count; i++) {
04061               exif_iif_add_str(&ImageInfo, SECTION_WINXP, exif_get_tagname(ImageInfo.xp_fields.list[i].tag, NULL, 0, exif_get_tag_table(SECTION_WINXP) TSRMLS_CC), ImageInfo.xp_fields.list[i].value TSRMLS_CC);
04062        }
04063        if (ImageInfo.Thumbnail.size) {
04064               if (read_thumbnail) {
04065                      /* not exif_iif_add_str : this is a buffer */
04066                      exif_iif_add_tag(&ImageInfo, SECTION_THUMBNAIL, "THUMBNAIL", TAG_NONE, TAG_FMT_UNDEFINED, ImageInfo.Thumbnail.size, ImageInfo.Thumbnail.data TSRMLS_CC);
04067               }
04068               if (!ImageInfo.Thumbnail.width || !ImageInfo.Thumbnail.height) {
04069                      /* try to evaluate if thumbnail data is present */
04070                      exif_scan_thumbnail(&ImageInfo TSRMLS_CC);
04071               }
04072               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.FileType", ImageInfo.Thumbnail.filetype TSRMLS_CC);
04073               exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Thumbnail.MimeType", (char*)php_image_type_to_mime_type(ImageInfo.Thumbnail.filetype) TSRMLS_CC);
04074        }
04075        if (ImageInfo.Thumbnail.width && ImageInfo.Thumbnail.height) {
04076               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.Height", ImageInfo.Thumbnail.height TSRMLS_CC);
04077               exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.Width",  ImageInfo.Thumbnail.width TSRMLS_CC);
04078        }
04079        EFREE_IF(sections_str);
04080 
04081 #ifdef EXIF_DEBUG
04082        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Adding image infos");
04083 #endif
04084 
04085        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_FILE       TSRMLS_CC);
04086        add_assoc_image_info(return_value, 1,          &ImageInfo, SECTION_COMPUTED   TSRMLS_CC);
04087        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_ANY_TAG    TSRMLS_CC);
04088        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_IFD0       TSRMLS_CC);
04089        add_assoc_image_info(return_value, 1,          &ImageInfo, SECTION_THUMBNAIL  TSRMLS_CC);
04090        add_assoc_image_info(return_value, 1,          &ImageInfo, SECTION_COMMENT    TSRMLS_CC);
04091        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_EXIF       TSRMLS_CC);
04092        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_GPS        TSRMLS_CC);
04093        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_INTEROP    TSRMLS_CC);
04094        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_FPIX       TSRMLS_CC);
04095        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_APP12      TSRMLS_CC);
04096        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_WINXP      TSRMLS_CC);
04097        add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_MAKERNOTE  TSRMLS_CC);
04098 
04099 #ifdef EXIF_DEBUG
04100        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Discarding info");
04101 #endif
04102 
04103        exif_discard_imageinfo(&ImageInfo);
04104 
04105 #ifdef EXIF_DEBUG
04106        php_error_docref1(NULL TSRMLS_CC, Z_STRVAL_PP(p_name), E_NOTICE, "done");
04107 #endif
04108 }
04109 /* }}} */
04110 
04111 /* {{{ proto string exif_thumbnail(string filename [, &width, &height [, &imagetype]])
04112    Reads the embedded thumbnail */
04113 PHP_FUNCTION(exif_thumbnail)
04114 {
04115        zval *p_width = 0, *p_height = 0, *p_imagetype = 0;
04116        char *p_name;
04117        int p_name_len, ret, arg_c = ZEND_NUM_ARGS();
04118        image_info_type ImageInfo;
04119 
04120        memset(&ImageInfo, 0, sizeof(ImageInfo));
04121 
04122        if (arg_c!=1 && arg_c!=3 && arg_c!=4) {
04123               WRONG_PARAM_COUNT;
04124        }
04125 
04126        if (zend_parse_parameters(arg_c TSRMLS_CC, "s|z/z/z/", &p_name, &p_name_len, &p_width, &p_height, &p_imagetype) == FAILURE) {
04127               return;
04128        }
04129 
04130        ret = exif_read_file(&ImageInfo, p_name, 1, 0 TSRMLS_CC);
04131        if (ret==FALSE) {
04132               exif_discard_imageinfo(&ImageInfo);
04133               RETURN_FALSE;
04134        }
04135 
04136 #ifdef EXIF_DEBUG
04137        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Thumbnail data %d %d %d, %d x %d", ImageInfo.Thumbnail.data, ImageInfo.Thumbnail.size, ImageInfo.Thumbnail.filetype, ImageInfo.Thumbnail.width, ImageInfo.Thumbnail.height);
04138 #endif
04139        if (!ImageInfo.Thumbnail.data || !ImageInfo.Thumbnail.size) {
04140               exif_discard_imageinfo(&ImageInfo);
04141               RETURN_FALSE;
04142        }
04143 
04144 #ifdef EXIF_DEBUG
04145        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Returning thumbnail(%d)", ImageInfo.Thumbnail.size);
04146 #endif
04147 
04148        ZVAL_STRINGL(return_value, ImageInfo.Thumbnail.data, ImageInfo.Thumbnail.size, 1);
04149        if (arg_c >= 3) {
04150               if (!ImageInfo.Thumbnail.width || !ImageInfo.Thumbnail.height) {
04151                      exif_scan_thumbnail(&ImageInfo TSRMLS_CC);
04152               }
04153               zval_dtor(p_width);
04154               zval_dtor(p_height);
04155               ZVAL_LONG(p_width,  ImageInfo.Thumbnail.width);
04156               ZVAL_LONG(p_height, ImageInfo.Thumbnail.height);
04157        }
04158        if (arg_c >= 4)      {
04159               zval_dtor(p_imagetype);
04160               ZVAL_LONG(p_imagetype, ImageInfo.Thumbnail.filetype);
04161        }
04162 
04163 #ifdef EXIF_DEBUG
04164        exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Discarding info");
04165 #endif
04166 
04167        exif_discard_imageinfo(&ImageInfo);
04168 
04169 #ifdef EXIF_DEBUG
04170        php_error_docref1(NULL TSRMLS_CC, p_name, E_NOTICE, "Done");
04171 #endif
04172 }
04173 /* }}} */
04174 
04175 /* {{{ proto int exif_imagetype(string imagefile)
04176    Get the type of an image */
04177 PHP_FUNCTION(exif_imagetype)
04178 {
04179        char *imagefile;
04180        int imagefile_len;
04181        php_stream * stream;
04182        int itype = 0;
04183 
04184        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &imagefile, &imagefile_len) == FAILURE) {
04185               return;
04186        }
04187 
04188        stream = php_stream_open_wrapper(imagefile, "rb", IGNORE_PATH|ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL);
04189 
04190        if (stream == NULL) {
04191               RETURN_FALSE;
04192        }
04193 
04194        itype = php_getimagetype(stream, NULL TSRMLS_CC);
04195 
04196        php_stream_close(stream);
04197 
04198        if (itype == IMAGE_FILETYPE_UNKNOWN) {
04199               RETURN_FALSE;
04200        } else {
04201               ZVAL_LONG(return_value, itype);
04202        }
04203 }
04204 /* }}} */
04205 
04206 #endif
04207 
04208 /*
04209  * Local variables:
04210  * tab-width: 4
04211  * c-basic-offset: 4
04212  * End:
04213  * vim600: sw=4 ts=4 tw=78 fdm=marker
04214  * vim<600: sw=4 ts=4 tw=78
04215  */