2
+----------------------------------------------------------------------+
4
+----------------------------------------------------------------------+
5
| Copyright (c) 1997-2004 The PHP Group |
6
+----------------------------------------------------------------------+
7
| This source file is subject to version 3.0 of the PHP license, |
8
| that is bundled with this package in the file LICENSE, and is |
9
| available through the world-wide-web at the following url: |
10
| http://www.php.net/license/3_0.txt. |
11
| If you did not receive a copy of the PHP license and are unable to |
12
| obtain it through the world-wide-web, please send a note to |
13
| license@php.net so we can mail you a copy immediately. |
14
+----------------------------------------------------------------------+
15
| Authors: Rasmus Lerdorf <rasmus@php.net> |
16
| Marcus Boerger <helly@php.net> |
17
+----------------------------------------------------------------------+
20
/* $Id: exif.c,v 1.162.2.9 2005/08/05 14:00:47 hyanantha Exp $ */
24
* See if example images from http://www.exif.org have illegal
25
* thumbnail sizes or if code is corrupt.
26
* Create/Update exif headers.
27
* Create/Remove/Update image thumbnails.
32
* At current time i do not see any security problems but a potential
33
* attacker could generate an image with recursive ifd pointers...(Marcus)
41
#include "ext/standard/file.h"
45
/* When EXIF_DEBUG is defined the module generates a lot of debug messages
46
* that help understanding what is going on. This can and should be used
47
* while extending the module as it shows if you are at the right position.
48
* You are always considered to have a copy of TIFF6.0 and EXIF2.10 standard.
53
#define EXIFERR_DC , const char *_file, size_t _line TSRMLS_DC
54
#define EXIFERR_CC , __FILE__, __LINE__ TSRMLS_CC
56
#define EXIFERR_DC TSRMLS_DC
57
#define EXIFERR_CC TSRMLS_CC
65
#include "ext/standard/php_string.h"
66
#include "ext/standard/php_image.h"
67
#include "ext/standard/info.h"
69
#if defined(PHP_WIN32) || (HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING))
70
#define EXIF_USE_MBSTRING 1
72
#define EXIF_USE_MBSTRING 0
76
#include "ext/mbstring/mbstring.h"
79
/* needed for ssize_t definition */
80
#include <sys/types.h>
82
typedef unsigned char uchar;
85
# define safe_emalloc(a,b,c) emalloc((a)*(b)+(c))
94
# define max(a,b) ((a)>(b) ? (a) : (b))
97
#define EFREE_IF(ptr) if (ptr) efree(ptr)
99
#define MAX_IFD_NESTING_LEVEL 100
102
ZEND_BEGIN_ARG_INFO(exif_thumbnail_force_ref, 1)
103
ZEND_ARG_PASS_INFO(0)
106
/* {{{ exif_functions[]
108
function_entry exif_functions[] = {
109
PHP_FE(exif_read_data, NULL)
110
PHP_FALIAS(read_exif_data, exif_read_data, NULL)
111
PHP_FE(exif_tagname, NULL)
112
PHP_FE(exif_thumbnail, exif_thumbnail_force_ref)
113
PHP_FE(exif_imagetype, NULL)
118
#define EXIF_VERSION "1.4 $Id: exif.c,v 1.162.2.9 2005/08/05 14:00:47 hyanantha Exp $"
120
/* {{{ PHP_MINFO_FUNCTION
122
PHP_MINFO_FUNCTION(exif)
124
php_info_print_table_start();
125
php_info_print_table_row(2, "EXIF Support", "enabled");
126
php_info_print_table_row(2, "EXIF Version", EXIF_VERSION);
127
php_info_print_table_row(2, "Supported EXIF Version", "0220");
128
php_info_print_table_row(2, "Supported filetypes", "JPEG,TIFF");
129
php_info_print_table_end();
133
ZEND_BEGIN_MODULE_GLOBALS(exif)
134
char * encode_unicode;
135
char * decode_unicode_be;
136
char * decode_unicode_le;
138
char * decode_jis_be;
139
char * decode_jis_le;
140
ZEND_END_MODULE_GLOBALS(exif)
142
ZEND_DECLARE_MODULE_GLOBALS(exif)
145
#define EXIF_G(v) TSRMG(exif_globals_id, zend_exif_globals *, v)
147
#define EXIF_G(v) (exif_globals.v)
153
ZEND_INI_MH(OnUpdateEncode)
155
#if EXIF_USE_MBSTRING
156
if (new_value && strlen(new_value) && !php_mb_check_encoding_list(new_value TSRMLS_CC)) {
157
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Illegal encoding ignored: '%s'", new_value);
161
return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
164
ZEND_INI_MH(OnUpdateDecode)
166
#if EXIF_USE_MBSTRING
167
if (!php_mb_check_encoding_list(new_value TSRMLS_CC)) {
168
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Illegal encoding ignored: '%s'", new_value);
172
return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
176
STD_PHP_INI_ENTRY("exif.encode_unicode", "ISO-8859-15", PHP_INI_ALL, OnUpdateEncode, encode_unicode, zend_exif_globals, exif_globals)
177
STD_PHP_INI_ENTRY("exif.decode_unicode_motorola", "UCS-2BE", PHP_INI_ALL, OnUpdateDecode, decode_unicode_be, zend_exif_globals, exif_globals)
178
STD_PHP_INI_ENTRY("exif.decode_unicode_intel", "UCS-2LE", PHP_INI_ALL, OnUpdateDecode, decode_unicode_le, zend_exif_globals, exif_globals)
179
STD_PHP_INI_ENTRY("exif.encode_jis", "", PHP_INI_ALL, OnUpdateEncode, encode_jis, zend_exif_globals, exif_globals)
180
STD_PHP_INI_ENTRY("exif.decode_jis_motorola", "JIS", PHP_INI_ALL, OnUpdateDecode, decode_jis_be, zend_exif_globals, exif_globals)
181
STD_PHP_INI_ENTRY("exif.decode_jis_intel", "JIS", PHP_INI_ALL, OnUpdateDecode, decode_jis_le, zend_exif_globals, exif_globals)
185
/* {{{ php_extname_init_globals
187
static void php_exif_init_globals(zend_exif_globals *exif_globals)
189
exif_globals->encode_unicode = NULL;
190
exif_globals->decode_unicode_be = NULL;
191
exif_globals->decode_unicode_le = NULL;
192
exif_globals->encode_jis = NULL;
193
exif_globals->decode_jis_be = NULL;
194
exif_globals->decode_jis_le = NULL;
198
/* {{{ PHP_MINIT_FUNCTION(exif)
199
Get the size of an image as 4-element array */
200
PHP_MINIT_FUNCTION(exif)
202
ZEND_INIT_MODULE_GLOBALS(exif, php_exif_init_globals, NULL);
203
REGISTER_INI_ENTRIES();
204
REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", EXIF_USE_MBSTRING, CONST_CS | CONST_PERSISTENT);
209
/* {{{ PHP_MSHUTDOWN_FUNCTION
211
PHP_MSHUTDOWN_FUNCTION(exif)
213
UNREGISTER_INI_ENTRIES();
218
/* {{{ exif_module_entry
220
zend_module_entry exif_module_entry = {
221
#if ZEND_MODULE_API_NO >= 20010901
222
STANDARD_MODULE_HEADER,
230
#if ZEND_MODULE_API_NO >= 20010901
233
STANDARD_MODULE_PROPERTIES
237
#ifdef COMPILE_DL_EXIF
238
ZEND_GET_MODULE(exif)
242
* get length of string if buffer if less than buffer size or buffer size */
243
static size_t php_strnlen(char* str, size_t maxlen) {
246
if (str && maxlen && *str) {
249
} while (--maxlen && *(++str));
255
/* {{{ error messages
257
static const char * EXIF_ERROR_FILEEOF = "Unexpected end of file reached";
258
static const char * EXIF_ERROR_CORRUPT = "File structure corrupted";
259
static const char * EXIF_ERROR_THUMBEOF = "Thumbnail goes IFD boundary or end of file reached";
260
static const char * EXIF_ERROR_FSREALLOC = "Illegal reallocating of undefined file section";
262
#define EXIF_ERRLOG_FILEEOF(ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_FILEEOF);
263
#define EXIF_ERRLOG_CORRUPT(ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_CORRUPT);
264
#define EXIF_ERRLOG_THUMBEOF(ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_THUMBEOF);
265
#define EXIF_ERRLOG_FSREALLOC(ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s", EXIF_ERROR_FSREALLOC);
268
/* {{{ format description defines
269
Describes format descriptor
271
static int php_tiff_bytes_per_format[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1};
272
#define NUM_FORMATS 13
274
#define TAG_FMT_BYTE 1
275
#define TAG_FMT_STRING 2
276
#define TAG_FMT_USHORT 3
277
#define TAG_FMT_ULONG 4
278
#define TAG_FMT_URATIONAL 5
279
#define TAG_FMT_SBYTE 6
280
#define TAG_FMT_UNDEFINED 7
281
#define TAG_FMT_SSHORT 8
282
#define TAG_FMT_SLONG 9
283
#define TAG_FMT_SRATIONAL 10
284
#define TAG_FMT_SINGLE 11
285
#define TAG_FMT_DOUBLE 12
286
#define TAG_FMT_IFD 13
289
static char *exif_get_tagformat(int format)
292
case TAG_FMT_BYTE: return "BYTE";
293
case TAG_FMT_STRING: return "STRING";
294
case TAG_FMT_USHORT: return "USHORT";
295
case TAG_FMT_ULONG: return "ULONG";
296
case TAG_FMT_URATIONAL: return "URATIONAL";
297
case TAG_FMT_SBYTE: return "SBYTE";
298
case TAG_FMT_UNDEFINED: return "UNDEFINED";
299
case TAG_FMT_SSHORT: return "SSHORT";
300
case TAG_FMT_SLONG: return "SLONG";
301
case TAG_FMT_SRATIONAL: return "SRATIONAL";
302
case TAG_FMT_SINGLE: return "SINGLE";
303
case TAG_FMT_DOUBLE: return "DOUBLE";
304
case TAG_FMT_IFD: return "IFD";
310
/* Describes tag values */
311
#define TAG_GPS_VERSION_ID 0x0000
312
#define TAG_GPS_LATITUDE_REF 0x0001
313
#define TAG_GPS_LATITUDE 0x0002
314
#define TAG_GPS_LONGITUDE_REF 0x0003
315
#define TAG_GPS_LONGITUDE 0x0004
316
#define TAG_GPS_ALTITUDE_REF 0x0005
317
#define TAG_GPS_ALTITUDE 0x0006
318
#define TAG_GPS_TIME_STAMP 0x0007
319
#define TAG_GPS_SATELLITES 0x0008
320
#define TAG_GPS_STATUS 0x0009
321
#define TAG_GPS_MEASURE_MODE 0x000A
322
#define TAG_GPS_DOP 0x000B
323
#define TAG_GPS_SPEED_REF 0x000C
324
#define TAG_GPS_SPEED 0x000D
325
#define TAG_GPS_TRACK_REF 0x000E
326
#define TAG_GPS_TRACK 0x000F
327
#define TAG_GPS_IMG_DIRECTION_REF 0x0010
328
#define TAG_GPS_IMG_DIRECTION 0x0011
329
#define TAG_GPS_MAP_DATUM 0x0012
330
#define TAG_GPS_DEST_LATITUDE_REF 0x0013
331
#define TAG_GPS_DEST_LATITUDE 0x0014
332
#define TAG_GPS_DEST_LONGITUDE_REF 0x0015
333
#define TAG_GPS_DEST_LONGITUDE 0x0016
334
#define TAG_GPS_DEST_BEARING_REF 0x0017
335
#define TAG_GPS_DEST_BEARING 0x0018
336
#define TAG_GPS_DEST_DISTANCE_REF 0x0019
337
#define TAG_GPS_DEST_DISTANCE 0x001A
338
#define TAG_GPS_PROCESSING_METHOD 0x001B
339
#define TAG_GPS_AREA_INFORMATION 0x001C
340
#define TAG_GPS_DATE_STAMP 0x001D
341
#define TAG_GPS_DIFFERENTIAL 0x001E
342
#define TAG_TIFF_COMMENT 0x00FE /* SHOUDLNT HAPPEN */
343
#define TAG_NEW_SUBFILE 0x00FE /* New version of subfile tag */
344
#define TAG_SUBFILE_TYPE 0x00FF /* Old version of subfile tag */
345
#define TAG_IMAGEWIDTH 0x0100
346
#define TAG_IMAGEHEIGHT 0x0101
347
#define TAG_BITS_PER_SAMPLE 0x0102
348
#define TAG_COMPRESSION 0x0103
349
#define TAG_PHOTOMETRIC_INTERPRETATION 0x0106
350
#define TAG_TRESHHOLDING 0x0107
351
#define TAG_CELL_WIDTH 0x0108
352
#define TAG_CELL_HEIGHT 0x0109
353
#define TAG_FILL_ORDER 0x010A
354
#define TAG_DOCUMENT_NAME 0x010D
355
#define TAG_IMAGE_DESCRIPTION 0x010E
356
#define TAG_MAKE 0x010F
357
#define TAG_MODEL 0x0110
358
#define TAG_STRIP_OFFSETS 0x0111
359
#define TAG_ORIENTATION 0x0112
360
#define TAG_SAMPLES_PER_PIXEL 0x0115
361
#define TAG_ROWS_PER_STRIP 0x0116
362
#define TAG_STRIP_BYTE_COUNTS 0x0117
363
#define TAG_MIN_SAMPPLE_VALUE 0x0118
364
#define TAG_MAX_SAMPLE_VALUE 0x0119
365
#define TAG_X_RESOLUTION 0x011A
366
#define TAG_Y_RESOLUTION 0x011B
367
#define TAG_PLANAR_CONFIGURATION 0x011C
368
#define TAG_PAGE_NAME 0x011D
369
#define TAG_X_POSITION 0x011E
370
#define TAG_Y_POSITION 0x011F
371
#define TAG_FREE_OFFSETS 0x0120
372
#define TAG_FREE_BYTE_COUNTS 0x0121
373
#define TAG_GRAY_RESPONSE_UNIT 0x0122
374
#define TAG_GRAY_RESPONSE_CURVE 0x0123
375
#define TAG_RESOLUTION_UNIT 0x0128
376
#define TAG_PAGE_NUMBER 0x0129
377
#define TAG_TRANSFER_FUNCTION 0x012D
378
#define TAG_SOFTWARE 0x0131
379
#define TAG_DATETIME 0x0132
380
#define TAG_ARTIST 0x013B
381
#define TAG_HOST_COMPUTER 0x013C
382
#define TAG_PREDICTOR 0x013D
383
#define TAG_WHITE_POINT 0x013E
384
#define TAG_PRIMARY_CHROMATICITIES 0x013F
385
#define TAG_COLOR_MAP 0x0140
386
#define TAG_HALFTONE_HINTS 0x0141
387
#define TAG_TILE_WIDTH 0x0142
388
#define TAG_TILE_LENGTH 0x0143
389
#define TAG_TILE_OFFSETS 0x0144
390
#define TAG_TILE_BYTE_COUNTS 0x0145
391
#define TAG_SUB_IFD 0x014A
392
#define TAG_INK_SETMPUTER 0x014C
393
#define TAG_INK_NAMES 0x014D
394
#define TAG_NUMBER_OF_INKS 0x014E
395
#define TAG_DOT_RANGE 0x0150
396
#define TAG_TARGET_PRINTER 0x0151
397
#define TAG_EXTRA_SAMPLE 0x0152
398
#define TAG_SAMPLE_FORMAT 0x0153
399
#define TAG_S_MIN_SAMPLE_VALUE 0x0154
400
#define TAG_S_MAX_SAMPLE_VALUE 0x0155
401
#define TAG_TRANSFER_RANGE 0x0156
402
#define TAG_JPEG_TABLES 0x015B
403
#define TAG_JPEG_PROC 0x0200
404
#define TAG_JPEG_INTERCHANGE_FORMAT 0x0201
405
#define TAG_JPEG_INTERCHANGE_FORMAT_LEN 0x0202
406
#define TAG_JPEG_RESTART_INTERVAL 0x0203
407
#define TAG_JPEG_LOSSLESS_PREDICTOR 0x0205
408
#define TAG_JPEG_POINT_TRANSFORMS 0x0206
409
#define TAG_JPEG_Q_TABLES 0x0207
410
#define TAG_JPEG_DC_TABLES 0x0208
411
#define TAG_JPEG_AC_TABLES 0x0209
412
#define TAG_YCC_COEFFICIENTS 0x0211
413
#define TAG_YCC_SUB_SAMPLING 0x0212
414
#define TAG_YCC_POSITIONING 0x0213
415
#define TAG_REFERENCE_BLACK_WHITE 0x0214
416
/* 0x0301 - 0x0302 */
419
/* 0x5001 - 0x501B */
420
/* 0x5021 - 0x503B */
421
/* 0x5090 - 0x5091 */
422
/* 0x5100 - 0x5101 */
423
/* 0x5110 - 0x5113 */
424
/* 0x80E3 - 0x80E6 */
425
/* 0x828d - 0x828F */
426
#define TAG_COPYRIGHT 0x8298
427
#define TAG_EXPOSURETIME 0x829A
428
#define TAG_FNUMBER 0x829D
429
#define TAG_EXIF_IFD_POINTER 0x8769
430
#define TAG_ICC_PROFILE 0x8773
431
#define TAG_EXPOSURE_PROGRAM 0x8822
432
#define TAG_SPECTRAL_SENSITY 0x8824
433
#define TAG_GPS_IFD_POINTER 0x8825
434
#define TAG_ISOSPEED 0x8827
435
#define TAG_OPTOELECTRIC_CONVERSION_F 0x8828
436
/* 0x8829 - 0x882b */
437
#define TAG_EXIFVERSION 0x9000
438
#define TAG_DATE_TIME_ORIGINAL 0x9003
439
#define TAG_DATE_TIME_DIGITIZED 0x9004
440
#define TAG_COMPONENT_CONFIG 0x9101
441
#define TAG_COMPRESSED_BITS_PER_PIXEL 0x9102
442
#define TAG_SHUTTERSPEED 0x9201
443
#define TAG_APERTURE 0x9202
444
#define TAG_BRIGHTNESS_VALUE 0x9203
445
#define TAG_EXPOSURE_BIAS_VALUE 0x9204
446
#define TAG_MAX_APERTURE 0x9205
447
#define TAG_SUBJECT_DISTANCE 0x9206
448
#define TAG_METRIC_MODULE 0x9207
449
#define TAG_LIGHT_SOURCE 0x9208
450
#define TAG_FLASH 0x9209
451
#define TAG_FOCAL_LENGTH 0x920A
452
/* 0x920B - 0x920D */
453
/* 0x9211 - 0x9216 */
454
#define TAG_SUBJECT_AREA 0x9214
455
#define TAG_MAKER_NOTE 0x927C
456
#define TAG_USERCOMMENT 0x9286
457
#define TAG_SUB_SEC_TIME 0x9290
458
#define TAG_SUB_SEC_TIME_ORIGINAL 0x9291
459
#define TAG_SUB_SEC_TIME_DIGITIZED 0x9292
462
#define TAG_XP_TITLE 0x9C9B
463
#define TAG_XP_COMMENTS 0x9C9C
464
#define TAG_XP_AUTHOR 0x9C9D
465
#define TAG_XP_KEYWORDS 0x9C9E
466
#define TAG_XP_SUBJECT 0x9C9F
467
#define TAG_FLASH_PIX_VERSION 0xA000
468
#define TAG_COLOR_SPACE 0xA001
469
#define TAG_COMP_IMAGE_WIDTH 0xA002 /* compressed images only */
470
#define TAG_COMP_IMAGE_HEIGHT 0xA003
471
#define TAG_RELATED_SOUND_FILE 0xA004
472
#define TAG_INTEROP_IFD_POINTER 0xA005 /* IFD pointer */
473
#define TAG_FLASH_ENERGY 0xA20B
474
#define TAG_SPATIAL_FREQUENCY_RESPONSE 0xA20C
475
#define TAG_FOCALPLANE_X_RES 0xA20E
476
#define TAG_FOCALPLANE_Y_RES 0xA20F
477
#define TAG_FOCALPLANE_RESOLUTION_UNIT 0xA210
478
#define TAG_SUBJECT_LOCATION 0xA214
479
#define TAG_EXPOSURE_INDEX 0xA215
480
#define TAG_SENSING_METHOD 0xA217
481
#define TAG_FILE_SOURCE 0xA300
482
#define TAG_SCENE_TYPE 0xA301
483
#define TAG_CFA_PATTERN 0xA302
484
#define TAG_CUSTOM_RENDERED 0xA401
485
#define TAG_EXPOSURE_MODE 0xA402
486
#define TAG_WHITE_BALANCE 0xA403
487
#define TAG_DIGITAL_ZOOM_RATIO 0xA404
488
#define TAG_FOCAL_LENGTH_IN_35_MM_FILM 0xA405
489
#define TAG_SCENE_CAPTURE_TYPE 0xA406
490
#define TAG_GAIN_CONTROL 0xA407
491
#define TAG_CONTRAST 0xA408
492
#define TAG_SATURATION 0xA409
493
#define TAG_SHARPNESS 0xA40A
494
#define TAG_DEVICE_SETTING_DESCRIPTION 0xA40B
495
#define TAG_SUBJECT_DISTANCE_RANGE 0xA40C
496
#define TAG_IMAGE_UNIQUE_ID 0xA420
498
/* Olympus specific tags */
499
#define TAG_OLYMPUS_SPECIALMODE 0x0200
500
#define TAG_OLYMPUS_JPEGQUAL 0x0201
501
#define TAG_OLYMPUS_MACRO 0x0202
502
#define TAG_OLYMPUS_DIGIZOOM 0x0204
503
#define TAG_OLYMPUS_SOFTWARERELEASE 0x0207
504
#define TAG_OLYMPUS_PICTINFO 0x0208
505
#define TAG_OLYMPUS_CAMERAID 0x0209
506
/* end Olympus specific tags */
509
#define TAG_NONE -1 /* note that -1 <> 0xFFFF */
510
#define TAG_COMPUTED_VALUE -2
511
#define TAG_END_OF_LIST 0xFFFD
513
/* Values for TAG_PHOTOMETRIC_INTERPRETATION */
514
#define PMI_BLACK_IS_ZERO 0
515
#define PMI_WHITE_IS_ZERO 1
517
#define PMI_PALETTE_COLOR 3
518
#define PMI_TRANSPARENCY_MASK 4
519
#define PMI_SEPARATED 5
527
typedef const struct {
532
typedef tag_info_type tag_info_array[];
533
typedef tag_info_type *tag_table_type;
535
#define TAG_TABLE_END \
536
{TAG_NONE, "No tag value"},\
537
{TAG_COMPUTED_VALUE, "Computed value"},\
538
{TAG_END_OF_LIST, ""} /* Important for exif_get_tagname() IF value != "" functionresult is != false */
540
static tag_info_array tag_table_IFD = {
541
{ 0x000B, "ACDComment"},
542
{ 0x00FE, "NewSubFile"}, /* better name it 'ImageType' ? */
543
{ 0x00FF, "SubFile"},
544
{ 0x0100, "ImageWidth"},
545
{ 0x0101, "ImageLength"},
546
{ 0x0102, "BitsPerSample"},
547
{ 0x0103, "Compression"},
548
{ 0x0106, "PhotometricInterpretation"},
549
{ 0x010A, "FillOrder"},
550
{ 0x010D, "DocumentName"},
551
{ 0x010E, "ImageDescription"},
554
{ 0x0111, "StripOffsets"},
555
{ 0x0112, "Orientation"},
556
{ 0x0115, "SamplesPerPixel"},
557
{ 0x0116, "RowsPerStrip"},
558
{ 0x0117, "StripByteCounts"},
559
{ 0x0118, "MinSampleValue"},
560
{ 0x0119, "MaxSampleValue"},
561
{ 0x011A, "XResolution"},
562
{ 0x011B, "YResolution"},
563
{ 0x011C, "PlanarConfiguration"},
564
{ 0x011D, "PageName"},
565
{ 0x011E, "XPosition"},
566
{ 0x011F, "YPosition"},
567
{ 0x0120, "FreeOffsets"},
568
{ 0x0121, "FreeByteCounts"},
569
{ 0x0122, "GrayResponseUnit"},
570
{ 0x0123, "GrayResponseCurve"},
571
{ 0x0124, "T4Options"},
572
{ 0x0125, "T6Options"},
573
{ 0x0128, "ResolutionUnit"},
574
{ 0x0129, "PageNumber"},
575
{ 0x012D, "TransferFunction"},
576
{ 0x0131, "Software"},
577
{ 0x0132, "DateTime"},
579
{ 0x013C, "HostComputer"},
580
{ 0x013D, "Predictor"},
581
{ 0x013E, "WhitePoint"},
582
{ 0x013F, "PrimaryChromaticities"},
583
{ 0x0140, "ColorMap"},
584
{ 0x0141, "HalfToneHints"},
585
{ 0x0142, "TileWidth"},
586
{ 0x0143, "TileLength"},
587
{ 0x0144, "TileOffsets"},
588
{ 0x0145, "TileByteCounts"},
591
{ 0x014D, "InkNames"},
592
{ 0x014E, "NumberOfInks"},
593
{ 0x0150, "DotRange"},
594
{ 0x0151, "TargetPrinter"},
595
{ 0x0152, "ExtraSample"},
596
{ 0x0153, "SampleFormat"},
597
{ 0x0154, "SMinSampleValue"},
598
{ 0x0155, "SMaxSampleValue"},
599
{ 0x0156, "TransferRange"},
600
{ 0x0157, "ClipPath"},
601
{ 0x0158, "XClipPathUnits"},
602
{ 0x0159, "YClipPathUnits"},
603
{ 0x015A, "Indexed"},
604
{ 0x015B, "JPEGTables"},
605
{ 0x015F, "OPIProxy"},
606
{ 0x0200, "JPEGProc"},
607
{ 0x0201, "JPEGInterchangeFormat"},
608
{ 0x0202, "JPEGInterchangeFormatLength"},
609
{ 0x0203, "JPEGRestartInterval"},
610
{ 0x0205, "JPEGLosslessPredictors"},
611
{ 0x0206, "JPEGPointTransforms"},
612
{ 0x0207, "JPEGQTables"},
613
{ 0x0208, "JPEGDCTables"},
614
{ 0x0209, "JPEGACTables"},
615
{ 0x0211, "YCbCrCoefficients"},
616
{ 0x0212, "YCbCrSubSampling"},
617
{ 0x0213, "YCbCrPositioning"},
618
{ 0x0214, "ReferenceBlackWhite"},
619
{ 0x02BC, "ExtensibleMetadataPlatform"}, /* XAP: Extensible Authoring Publishing, obsoleted by XMP: Extensible Metadata Platform */
621
{ 0x0302, "ICCProfileDescriptor"},
622
{ 0x0303, "SRGBRenderingIntent"},
623
{ 0x0320, "ImageTitle"},
624
{ 0x5001, "ResolutionXUnit"},
625
{ 0x5002, "ResolutionYUnit"},
626
{ 0x5003, "ResolutionXLengthUnit"},
627
{ 0x5004, "ResolutionYLengthUnit"},
628
{ 0x5005, "PrintFlags"},
629
{ 0x5006, "PrintFlagsVersion"},
630
{ 0x5007, "PrintFlagsCrop"},
631
{ 0x5008, "PrintFlagsBleedWidth"},
632
{ 0x5009, "PrintFlagsBleedWidthScale"},
633
{ 0x500A, "HalftoneLPI"},
634
{ 0x500B, "HalftoneLPIUnit"},
635
{ 0x500C, "HalftoneDegree"},
636
{ 0x500D, "HalftoneShape"},
637
{ 0x500E, "HalftoneMisc"},
638
{ 0x500F, "HalftoneScreen"},
639
{ 0x5010, "JPEGQuality"},
640
{ 0x5011, "GridSize"},
641
{ 0x5012, "ThumbnailFormat"},
642
{ 0x5013, "ThumbnailWidth"},
643
{ 0x5014, "ThumbnailHeight"},
644
{ 0x5015, "ThumbnailColorDepth"},
645
{ 0x5016, "ThumbnailPlanes"},
646
{ 0x5017, "ThumbnailRawBytes"},
647
{ 0x5018, "ThumbnailSize"},
648
{ 0x5019, "ThumbnailCompressedSize"},
649
{ 0x501A, "ColorTransferFunction"},
650
{ 0x501B, "ThumbnailData"},
651
{ 0x5020, "ThumbnailImageWidth"},
652
{ 0x5021, "ThumbnailImageHeight"},
653
{ 0x5022, "ThumbnailBitsPerSample"},
654
{ 0x5023, "ThumbnailCompression"},
655
{ 0x5024, "ThumbnailPhotometricInterp"},
656
{ 0x5025, "ThumbnailImageDescription"},
657
{ 0x5026, "ThumbnailEquipMake"},
658
{ 0x5027, "ThumbnailEquipModel"},
659
{ 0x5028, "ThumbnailStripOffsets"},
660
{ 0x5029, "ThumbnailOrientation"},
661
{ 0x502A, "ThumbnailSamplesPerPixel"},
662
{ 0x502B, "ThumbnailRowsPerStrip"},
663
{ 0x502C, "ThumbnailStripBytesCount"},
664
{ 0x502D, "ThumbnailResolutionX"},
665
{ 0x502E, "ThumbnailResolutionY"},
666
{ 0x502F, "ThumbnailPlanarConfig"},
667
{ 0x5030, "ThumbnailResolutionUnit"},
668
{ 0x5031, "ThumbnailTransferFunction"},
669
{ 0x5032, "ThumbnailSoftwareUsed"},
670
{ 0x5033, "ThumbnailDateTime"},
671
{ 0x5034, "ThumbnailArtist"},
672
{ 0x5035, "ThumbnailWhitePoint"},
673
{ 0x5036, "ThumbnailPrimaryChromaticities"},
674
{ 0x5037, "ThumbnailYCbCrCoefficients"},
675
{ 0x5038, "ThumbnailYCbCrSubsampling"},
676
{ 0x5039, "ThumbnailYCbCrPositioning"},
677
{ 0x503A, "ThumbnailRefBlackWhite"},
678
{ 0x503B, "ThumbnailCopyRight"},
679
{ 0x5090, "LuminanceTable"},
680
{ 0x5091, "ChrominanceTable"},
681
{ 0x5100, "FrameDelay"},
682
{ 0x5101, "LoopCount"},
683
{ 0x5110, "PixelUnit"},
684
{ 0x5111, "PixelPerUnitX"},
685
{ 0x5112, "PixelPerUnitY"},
686
{ 0x5113, "PaletteHistogram"},
687
{ 0x1000, "RelatedImageFileFormat"},
688
{ 0x800D, "ImageID"},
689
{ 0x80E3, "Matteing"}, /* obsoleted by ExtraSamples */
690
{ 0x80E4, "DataType"}, /* obsoleted by SampleFormat */
691
{ 0x80E5, "ImageDepth"},
692
{ 0x80E6, "TileDepth"},
693
{ 0x828D, "CFARepeatPatternDim"},
694
{ 0x828E, "CFAPattern"},
695
{ 0x828F, "BatteryLevel"},
696
{ 0x8298, "Copyright"},
697
{ 0x829A, "ExposureTime"},
698
{ 0x829D, "FNumber"},
699
{ 0x83BB, "IPTC/NAA"},
700
{ 0x84E3, "IT8RasterPadding"},
701
{ 0x84E5, "IT8ColorTable"},
702
{ 0x8649, "ImageResourceInformation"}, /* PhotoShop */
703
{ 0x8769, "Exif_IFD_Pointer"},
704
{ 0x8773, "ICC_Profile"},
705
{ 0x8822, "ExposureProgram"},
706
{ 0x8824, "SpectralSensity"},
708
{ 0x8825, "GPS_IFD_Pointer"},
709
{ 0x8827, "ISOSpeedRatings"},
711
{ 0x9000, "ExifVersion"},
712
{ 0x9003, "DateTimeOriginal"},
713
{ 0x9004, "DateTimeDigitized"},
714
{ 0x9101, "ComponentsConfiguration"},
715
{ 0x9102, "CompressedBitsPerPixel"},
716
{ 0x9201, "ShutterSpeedValue"},
717
{ 0x9202, "ApertureValue"},
718
{ 0x9203, "BrightnessValue"},
719
{ 0x9204, "ExposureBiasValue"},
720
{ 0x9205, "MaxApertureValue"},
721
{ 0x9206, "SubjectDistance"},
722
{ 0x9207, "MeteringMode"},
723
{ 0x9208, "LightSource"},
725
{ 0x920A, "FocalLength"},
726
{ 0x920B, "FlashEnergy"}, /* 0xA20B in JPEG */
727
{ 0x920C, "SpatialFrequencyResponse"}, /* 0xA20C - - */
729
{ 0x920E, "FocalPlaneXResolution"}, /* 0xA20E - - */
730
{ 0x920F, "FocalPlaneYResolution"}, /* 0xA20F - - */
731
{ 0x9210, "FocalPlaneResolutionUnit"}, /* 0xA210 - - */
732
{ 0x9211, "ImageNumber"},
733
{ 0x9212, "SecurityClassification"},
734
{ 0x9213, "ImageHistory"},
735
{ 0x9214, "SubjectLocation"}, /* 0xA214 - - */
736
{ 0x9215, "ExposureIndex"}, /* 0xA215 - - */
737
{ 0x9216, "TIFF/EPStandardID"},
738
{ 0x9217, "SensingMethod"}, /* 0xA217 - - */
739
{ 0x923F, "StoNits"},
740
{ 0x927C, "MakerNote"},
741
{ 0x9286, "UserComment"},
742
{ 0x9290, "SubSecTime"},
743
{ 0x9291, "SubSecTimeOriginal"},
744
{ 0x9292, "SubSecTimeDigitized"},
745
{ 0x935C, "ImageSourceData"}, /* "Adobe Photoshop Document Data Block": 8BIM... */
746
{ 0x9c9b, "Title" }, /* Win XP specific, Unicode */
747
{ 0x9c9c, "Comments" }, /* Win XP specific, Unicode */
748
{ 0x9c9d, "Author" }, /* Win XP specific, Unicode */
749
{ 0x9c9e, "Keywords" }, /* Win XP specific, Unicode */
750
{ 0x9c9f, "Subject" }, /* Win XP specific, Unicode, not to be confused with SubjectDistance and SubjectLocation */
751
{ 0xA000, "FlashPixVersion"},
752
{ 0xA001, "ColorSpace"},
753
{ 0xA002, "ExifImageWidth"},
754
{ 0xA003, "ExifImageLength"},
755
{ 0xA004, "RelatedSoundFile"},
756
{ 0xA005, "InteroperabilityOffset"},
757
{ 0xA20B, "FlashEnergy"}, /* 0x920B in TIFF/EP */
758
{ 0xA20C, "SpatialFrequencyResponse"}, /* 0x920C - - */
760
{ 0xA20E, "FocalPlaneXResolution"}, /* 0x920E - - */
761
{ 0xA20F, "FocalPlaneYResolution"}, /* 0x920F - - */
762
{ 0xA210, "FocalPlaneResolutionUnit"}, /* 0x9210 - - */
763
{ 0xA211, "ImageNumber"},
764
{ 0xA212, "SecurityClassification"},
765
{ 0xA213, "ImageHistory"},
766
{ 0xA214, "SubjectLocation"}, /* 0x9214 - - */
767
{ 0xA215, "ExposureIndex"}, /* 0x9215 - - */
768
{ 0xA216, "TIFF/EPStandardID"},
769
{ 0xA217, "SensingMethod"}, /* 0x9217 - - */
770
{ 0xA300, "FileSource"},
771
{ 0xA301, "SceneType"},
772
{ 0xA302, "CFAPattern"},
773
{ 0xA401, "CustomRendered"},
774
{ 0xA402, "ExposureMode"},
775
{ 0xA403, "WhiteBalance"},
776
{ 0xA404, "DigitalZoomRatio"},
777
{ 0xA405, "FocalLengthIn35mmFilm"},
778
{ 0xA406, "SceneCaptureType"},
779
{ 0xA407, "GainControl"},
780
{ 0xA408, "Contrast"},
781
{ 0xA409, "Saturation"},
782
{ 0xA40A, "Sharpness"},
783
{ 0xA40B, "DeviceSettingDescription"},
784
{ 0xA40C, "SubjectDistanceRange"},
785
{ 0xA420, "ImageUniqueID"},
789
static tag_info_array tag_table_GPS = {
790
{ 0x0000, "GPSVersion"},
791
{ 0x0001, "GPSLatitudeRef"},
792
{ 0x0002, "GPSLatitude"},
793
{ 0x0003, "GPSLongitudeRef"},
794
{ 0x0004, "GPSLongitude"},
795
{ 0x0005, "GPSAltitudeRef"},
796
{ 0x0006, "GPSAltitude"},
797
{ 0x0007, "GPSTimeStamp"},
798
{ 0x0008, "GPSSatellites"},
799
{ 0x0009, "GPSStatus"},
800
{ 0x000A, "GPSMeasureMode"},
802
{ 0x000C, "GPSSpeedRef"},
803
{ 0x000D, "GPSSpeed"},
804
{ 0x000E, "GPSTrackRef"},
805
{ 0x000F, "GPSTrack"},
806
{ 0x0010, "GPSImgDirectionRef"},
807
{ 0x0011, "GPSImgDirection"},
808
{ 0x0012, "GPSMapDatum"},
809
{ 0x0013, "GPSDestLatitudeRef"},
810
{ 0x0014, "GPSDestLatitude"},
811
{ 0x0015, "GPSDestLongitudeRef"},
812
{ 0x0016, "GPSDestLongitude"},
813
{ 0x0017, "GPSDestBearingRef"},
814
{ 0x0018, "GPSDestBearing"},
815
{ 0x0019, "GPSDestDistanceRef"},
816
{ 0x001A, "GPSDestDistance"},
817
{ 0x001B, "GPSProcessingMode"},
818
{ 0x001C, "GPSAreaInformation"},
819
{ 0x001D, "GPSDateStamp"},
820
{ 0x001E, "GPSDifferential"},
824
static tag_info_array tag_table_IOP = {
825
{ 0x0001, "InterOperabilityIndex"}, /* should be 'R98' or 'THM' */
826
{ 0x0002, "InterOperabilityVersion"},
827
{ 0x1000, "RelatedFileFormat"},
828
{ 0x1001, "RelatedImageWidth"},
829
{ 0x1002, "RelatedImageHeight"},
833
static tag_info_array tag_table_VND_CANON = {
834
{ 0x0001, "ModeArray"}, /* guess */
835
{ 0x0004, "ImageInfo"}, /* guess */
836
{ 0x0006, "ImageType"},
837
{ 0x0007, "FirmwareVersion"},
838
{ 0x0008, "ImageNumber"},
839
{ 0x0009, "OwnerName"},
841
{ 0x000F, "CustomFunctions"},
845
static tag_info_array tag_table_VND_CASIO = {
846
{ 0x0001, "RecordingMode"},
847
{ 0x0002, "Quality"},
848
{ 0x0003, "FocusingMode"},
849
{ 0x0004, "FlashMode"},
850
{ 0x0005, "FlashIntensity"},
851
{ 0x0006, "ObjectDistance"},
852
{ 0x0007, "WhiteBalance"},
853
{ 0x000A, "DigitalZoom"},
854
{ 0x000B, "Sharpness"},
855
{ 0x000C, "Contrast"},
856
{ 0x000D, "Saturation"},
857
{ 0x0014, "CCDSensitivity"},
861
static tag_info_array tag_table_VND_FUJI = {
862
{ 0x0000, "Version"},
863
{ 0x1000, "Quality"},
864
{ 0x1001, "Sharpness"},
865
{ 0x1002, "WhiteBalance"},
868
{ 0x1010, "FlashMode"},
869
{ 0x1011, "FlashStrength"},
871
{ 0x1021, "FocusMode"},
872
{ 0x1030, "SlowSync"},
873
{ 0x1031, "PictureMode"},
874
{ 0x1100, "ContTake"},
875
{ 0x1300, "BlurWarning"},
876
{ 0x1301, "FocusWarning"},
877
{ 0x1302, "AEWarning "},
881
static tag_info_array tag_table_VND_NIKON = {
882
{ 0x0003, "Quality"},
883
{ 0x0004, "ColorMode"},
884
{ 0x0005, "ImageAdjustment"},
885
{ 0x0006, "CCDSensitivity"},
886
{ 0x0007, "WhiteBalance"},
888
{ 0x000a, "DigitalZoom"},
889
{ 0x000b, "Converter"},
893
static tag_info_array tag_table_VND_NIKON_990 = {
894
{ 0x0001, "Version"},
895
{ 0x0002, "ISOSetting"},
896
{ 0x0003, "ColorMode"},
897
{ 0x0004, "Quality"},
898
{ 0x0005, "WhiteBalance"},
899
{ 0x0006, "ImageSharpening"},
900
{ 0x0007, "FocusMode"},
901
{ 0x0008, "FlashSetting"},
902
{ 0x000F, "ISOSelection"},
903
{ 0x0080, "ImageAdjustment"},
904
{ 0x0082, "AuxiliaryLens"},
905
{ 0x0085, "ManualFocusDistance"},
906
{ 0x0086, "DigitalZoom"},
907
{ 0x0088, "AFFocusPosition"},
908
{ 0x0010, "DataDump"},
912
static tag_info_array tag_table_VND_OLYMPUS = {
913
{ 0x0200, "SpecialMode"},
914
{ 0x0201, "JPEGQuality"},
916
{ 0x0204, "DigitalZoom"},
917
{ 0x0207, "SoftwareRelease"},
918
{ 0x0208, "PictureInfo"},
919
{ 0x0209, "CameraId"},
920
{ 0x0F00, "DataDump"},
924
typedef enum mn_byte_order_t {
926
MN_ORDER_MOTOROLA = 1,
930
typedef enum mn_offset_mode_t {
937
tag_table_type tag_table;
943
mn_byte_order_t byte_order;
944
mn_offset_mode_t offset_mode;
947
static const maker_note_type maker_note_array[] = {
948
{ tag_table_VND_CANON, "Canon", NULL, NULL, 0, 0, MN_ORDER_INTEL, MN_OFFSET_GUESS},
949
/* { tag_table_VND_CANON, "Canon", NULL, NULL, 0, 0, MN_ORDER_NORMAL, MN_OFFSET_NORMAL},*/
950
{ tag_table_VND_CASIO, "CASIO", NULL, NULL, 0, 0, MN_ORDER_MOTOROLA, MN_OFFSET_NORMAL},
951
{ tag_table_VND_FUJI, "FUJIFILM", NULL, "FUJIFILM\x0C\x00\x00\x00", 12, 12, MN_ORDER_INTEL, MN_OFFSET_MAKER},
952
{ tag_table_VND_NIKON, "NIKON", NULL, "Nikon\x00\x01\x00", 8, 8, MN_ORDER_NORMAL, MN_OFFSET_NORMAL},
953
{ tag_table_VND_NIKON_990, "NIKON", NULL, NULL, 0, 0, MN_ORDER_NORMAL, MN_OFFSET_NORMAL},
954
{ tag_table_VND_OLYMPUS, "OLYMPUS OPTICAL CO.,LTD", NULL, "OLYMP\x00\x01\x00", 8, 8, MN_ORDER_NORMAL, MN_OFFSET_NORMAL},
958
/* {{{ exif_get_tagname
959
Get headername for tag_num or NULL if not defined */
960
static char * exif_get_tagname(int tag_num, char *ret, int len, tag_table_type tag_table TSRMLS_DC)
966
if ((t=tag_table[i].Tag) == tag_num || t==TAG_END_OF_LIST) {
967
if (t==TAG_END_OF_LIST) {
971
strncpy(ret, tag_table[i].Desc, abs(len));
975
for(i=strlen(ret);i<len;i++)ret[i]=' ';
980
return tag_table[i].Desc;
984
snprintf(tmp, sizeof(tmp), "UndefinedTag:0x%04X", tag_num);
985
strncpy(ret, tmp, abs(len));
989
for(i=strlen(ret);i<len;i++)ret[i]=' ';
998
/* {{{ exif_char_dump
999
* Do not use! This is a debug function... */
1001
static unsigned char* exif_char_dump(unsigned char * addr, int len, int offset)
1003
static unsigned char buf[4096+1];
1004
static unsigned char tmp[20];
1005
int c, i, p=0, n = 5+31;
1007
p += sprintf(buf+p, "\nDump Len: %08X (%d)", len, len);
1009
for(i=0; i<len+15 && p+n<=sizeof(buf); i++) {
1011
p += sprintf(buf+p, "\n%08X: ", i+offset);
1015
p += sprintf(buf+p, "%02X ", c);
1016
tmp[i%16] = c>=32 ? c : '.';
1017
tmp[(i%16)+1] = '\0';
1019
p += sprintf(buf+p, " ");
1022
p += sprintf(buf+p, " %s", tmp);
1029
buf[sizeof(buf)-1] = '\0';
1035
/* {{{ php_jpg_get16
1036
Get 16 bits motorola order (always) for jpeg header stuff.
1038
static int php_jpg_get16(void *value)
1040
return (((uchar *)value)[0] << 8) | ((uchar *)value)[1];
1044
/* {{{ php_ifd_get16u
1045
* Convert a 16 bit unsigned value from file's native byte order */
1046
static int php_ifd_get16u(void *value, int motorola_intel)
1048
if (motorola_intel) {
1049
return (((uchar *)value)[0] << 8) | ((uchar *)value)[1];
1051
return (((uchar *)value)[1] << 8) | ((uchar *)value)[0];
1056
/* {{{ php_ifd_get16s
1057
* Convert a 16 bit signed value from file's native byte order */
1058
static signed short php_ifd_get16s(void *value, int motorola_intel)
1060
return (signed short)php_ifd_get16u(value, motorola_intel);
1064
/* {{{ php_ifd_get32s
1065
* Convert a 32 bit signed value from file's native byte order */
1066
static int php_ifd_get32s(void *value, int motorola_intel)
1068
if (motorola_intel) {
1069
return (((char *)value)[0] << 24)
1070
| (((uchar *)value)[1] << 16)
1071
| (((uchar *)value)[2] << 8 )
1072
| (((uchar *)value)[3] );
1074
return (((char *)value)[3] << 24)
1075
| (((uchar *)value)[2] << 16)
1076
| (((uchar *)value)[1] << 8 )
1077
| (((uchar *)value)[0] );
1082
/* {{{ php_ifd_get32u
1083
* Write 32 bit unsigned value to data */
1084
static unsigned php_ifd_get32u(void *value, int motorola_intel)
1086
return (unsigned)php_ifd_get32s(value, motorola_intel) & 0xffffffff;
1090
/* {{{ php_ifd_set16u
1091
* Write 16 bit unsigned value to data */
1092
static void php_ifd_set16u(char *data, unsigned int value, int motorola_intel)
1094
if (motorola_intel) {
1095
data[0] = (value & 0xFF00) >> 8;
1096
data[1] = (value & 0x00FF);
1098
data[1] = (value & 0xFF00) >> 8;
1099
data[0] = (value & 0x00FF);
1104
/* {{{ php_ifd_set32u
1105
* Convert a 32 bit unsigned value from file's native byte order */
1106
static void php_ifd_set32u(char *data, size_t value, int motorola_intel)
1108
if (motorola_intel) {
1109
data[0] = (value & 0xFF000000) >> 24;
1110
data[1] = (value & 0x00FF0000) >> 16;
1111
data[2] = (value & 0x0000FF00) >> 8;
1112
data[3] = (value & 0x000000FF);
1114
data[3] = (value & 0xFF000000) >> 24;
1115
data[2] = (value & 0x00FF0000) >> 16;
1116
data[1] = (value & 0x0000FF00) >> 8;
1117
data[0] = (value & 0x000000FF);
1122
/* {{{ exif_convert_any_format
1123
* Evaluate number, be it int, rational, or float from directory. */
1124
static double exif_convert_any_format(void *value, int format, int motorola_intel TSRMLS_DC)
1130
case TAG_FMT_SBYTE: return *(signed char *)value;
1131
case TAG_FMT_BYTE: return *(uchar *)value;
1133
case TAG_FMT_USHORT: return php_ifd_get16u(value, motorola_intel);
1134
case TAG_FMT_ULONG: return php_ifd_get32u(value, motorola_intel);
1136
case TAG_FMT_URATIONAL:
1137
u_den = php_ifd_get32u(4+(char *)value, motorola_intel);
1141
return (double)php_ifd_get32u(value, motorola_intel) / u_den;
1144
case TAG_FMT_SRATIONAL:
1145
s_den = php_ifd_get32s(4+(char *)value, motorola_intel);
1149
return (double)php_ifd_get32s(value, motorola_intel) / s_den;
1152
case TAG_FMT_SSHORT: return (signed short)php_ifd_get16u(value, motorola_intel);
1153
case TAG_FMT_SLONG: return php_ifd_get32s(value, motorola_intel);
1155
/* Not sure if this is correct (never seen float used in Exif format) */
1156
case TAG_FMT_SINGLE:
1158
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type single");
1160
return (double)*(float *)value;
1161
case TAG_FMT_DOUBLE:
1163
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type double");
1165
return *(double *)value;
1171
/* {{{ exif_convert_any_to_int
1172
* Evaluate number, be it int, rational, or float from directory. */
1173
static size_t exif_convert_any_to_int(void *value, int format, int motorola_intel TSRMLS_DC)
1179
case TAG_FMT_SBYTE: return *(signed char *)value;
1180
case TAG_FMT_BYTE: return *(uchar *)value;
1182
case TAG_FMT_USHORT: return php_ifd_get16u(value, motorola_intel);
1183
case TAG_FMT_ULONG: return php_ifd_get32u(value, motorola_intel);
1185
case TAG_FMT_URATIONAL:
1186
u_den = php_ifd_get32u(4+(char *)value, motorola_intel);
1190
return php_ifd_get32u(value, motorola_intel) / u_den;
1193
case TAG_FMT_SRATIONAL:
1194
s_den = php_ifd_get32s(4+(char *)value, motorola_intel);
1198
return php_ifd_get32s(value, motorola_intel) / s_den;
1201
case TAG_FMT_SSHORT: return php_ifd_get16u(value, motorola_intel);
1202
case TAG_FMT_SLONG: return php_ifd_get32s(value, motorola_intel);
1204
/* Not sure if this is correct (never seen float used in Exif format) */
1205
case TAG_FMT_SINGLE:
1207
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type single");
1209
return (size_t)*(float *)value;
1210
case TAG_FMT_DOUBLE:
1212
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found value of type double");
1214
return (size_t)*(double *)value;
1220
/* {{{ struct image_info_value, image_info_list
1223
#define WORD unsigned short
1226
#define DWORD unsigned int
1237
} unsigned_rational;
1239
typedef union _image_info_value {
1246
unsigned_rational ur;
1247
union _image_info_value *list;
1254
DWORD dummy; /* value ptr of tiff directory entry */
1256
image_info_value value;
1261
image_info_data *list;
1265
/* {{{ exif_get_sectionname
1266
Returns the name of a section
1268
#define SECTION_FILE 0
1269
#define SECTION_COMPUTED 1
1270
#define SECTION_ANY_TAG 2
1271
#define SECTION_IFD0 3
1272
#define SECTION_THUMBNAIL 4
1273
#define SECTION_COMMENT 5
1274
#define SECTION_APP0 6
1275
#define SECTION_EXIF 7
1276
#define SECTION_FPIX 8
1277
#define SECTION_GPS 9
1278
#define SECTION_INTEROP 10
1279
#define SECTION_APP12 11
1280
#define SECTION_WINXP 12
1281
#define SECTION_MAKERNOTE 13
1282
#define SECTION_COUNT 14
1284
#define FOUND_FILE (1<<SECTION_FILE)
1285
#define FOUND_COMPUTED (1<<SECTION_COMPUTED)
1286
#define FOUND_ANY_TAG (1<<SECTION_ANY_TAG)
1287
#define FOUND_IFD0 (1<<SECTION_IFD0)
1288
#define FOUND_THUMBNAIL (1<<SECTION_THUMBNAIL)
1289
#define FOUND_COMMENT (1<<SECTION_COMMENT)
1290
#define FOUND_APP0 (1<<SECTION_APP0)
1291
#define FOUND_EXIF (1<<SECTION_EXIF)
1292
#define FOUND_FPIX (1<<SECTION_FPIX)
1293
#define FOUND_GPS (1<<SECTION_GPS)
1294
#define FOUND_INTEROP (1<<SECTION_INTEROP)
1295
#define FOUND_APP12 (1<<SECTION_APP12)
1296
#define FOUND_WINXP (1<<SECTION_WINXP)
1297
#define FOUND_MAKERNOTE (1<<SECTION_MAKERNOTE)
1299
static char *exif_get_sectionname(int section)
1302
case SECTION_FILE: return "FILE";
1303
case SECTION_COMPUTED: return "COMPUTED";
1304
case SECTION_ANY_TAG: return "ANY_TAG";
1305
case SECTION_IFD0: return "IFD0";
1306
case SECTION_THUMBNAIL: return "THUMBNAIL";
1307
case SECTION_COMMENT: return "COMMENT";
1308
case SECTION_APP0: return "APP0";
1309
case SECTION_EXIF: return "EXIF";
1310
case SECTION_FPIX: return "FPIX";
1311
case SECTION_GPS: return "GPS";
1312
case SECTION_INTEROP: return "INTEROP";
1313
case SECTION_APP12: return "APP12";
1314
case SECTION_WINXP: return "WINXP";
1315
case SECTION_MAKERNOTE: return "MAKERNOTE";
1320
static tag_table_type exif_get_tag_table(int section)
1323
case SECTION_FILE: return &tag_table_IFD[0];
1324
case SECTION_COMPUTED: return &tag_table_IFD[0];
1325
case SECTION_ANY_TAG: return &tag_table_IFD[0];
1326
case SECTION_IFD0: return &tag_table_IFD[0];
1327
case SECTION_THUMBNAIL: return &tag_table_IFD[0];
1328
case SECTION_COMMENT: return &tag_table_IFD[0];
1329
case SECTION_APP0: return &tag_table_IFD[0];
1330
case SECTION_EXIF: return &tag_table_IFD[0];
1331
case SECTION_FPIX: return &tag_table_IFD[0];
1332
case SECTION_GPS: return &tag_table_GPS[0];
1333
case SECTION_INTEROP: return &tag_table_IOP[0];
1334
case SECTION_APP12: return &tag_table_IFD[0];
1335
case SECTION_WINXP: return &tag_table_IFD[0];
1337
return &tag_table_IFD[0];
1341
/* {{{ exif_get_sectionlist
1342
Return list of sectionnames specified by sectionlist. Return value must be freed
1344
static char *exif_get_sectionlist(int sectionlist TSRMLS_DC)
1349
for(i=0; i<SECTION_COUNT; i++) {
1350
len += strlen(exif_get_sectionname(i))+2;
1352
sections = safe_emalloc(len, 1, 1);
1355
for(i=0; i<SECTION_COUNT; i++) {
1356
if (sectionlist&(1<<i)) {
1357
sprintf(sections+len, "%s, ", exif_get_sectionname(i));
1358
len = strlen(sections);
1362
sections[len-2] = '\0';
1367
/* {{{ struct image_info_type
1368
This structure stores Exif header image elements in a simple manner
1369
Used to store camera data as extracted from the various ways that it can be
1370
stored in a nexif header
1382
} file_section_list;
1385
image_filetype filetype;
1386
size_t width, height;
1400
xp_field_type *list;
1403
/* This structure is used to store a section of a Jpeg file. */
1407
time_t FileDateTime;
1409
image_filetype FileType;
1416
float ApertureFNumber;
1418
double FocalplaneUnits;
1420
double FocalplaneXRes;
1421
size_t ExifImageWidth;
1425
int motorola_intel; /* 1 Motorola; 0 Intel */
1428
int UserCommentLength;
1429
char *UserCommentEncoding;
1430
char *encode_unicode;
1431
char *decode_unicode_be;
1432
char *decode_unicode_le;
1434
char *decode_jis_be;
1435
char *decode_jis_le;
1436
char *Copyright;/* EXIF standard defines Copyright as "<Photographer> [ '\0' <Editor> ] ['\0']" */
1437
char *CopyrightPhotographer;
1438
char *CopyrightEditor;
1440
xp_field_list xp_fields;
1442
thumbnail_data Thumbnail;
1444
int sections_found; /* FOUND_<marker> */
1445
image_info_list info_list[SECTION_COUNT];
1449
int ifd_nesting_level;
1451
file_section_list file;
1455
/* {{{ exif_error_docref */
1456
static void exif_error_docref(const char *docref EXIFERR_DC, const image_info_type *ImageInfo, int type, const char *format, ...)
1460
va_start(args, format);
1465
spprintf(&buf, 0, "%s(%d): %s", _file, _line, format);
1466
php_verror(docref, ImageInfo->FileName?ImageInfo->FileName:"", type, buf, args TSRMLS_CC);
1470
php_verror(docref, ImageInfo->FileName?ImageInfo->FileName:"", type, format, args TSRMLS_CC);
1476
/* {{{ jpeg_sof_info
1479
int bits_per_sample;
1486
/* {{{ exif_file_sections_add
1487
Add a file_section to image_info
1488
returns the used block or -1. if size>0 and data == NULL buffer of size is allocated
1490
static int exif_file_sections_add(image_info_type *ImageInfo, int type, size_t size, uchar *data)
1493
int count = ImageInfo->file.count;
1495
tmp = erealloc(ImageInfo->file.list, (count+1)*sizeof(file_section));
1496
ImageInfo->file.list = tmp;
1497
ImageInfo->file.list[count].type = 0xFFFF;
1498
ImageInfo->file.list[count].data = NULL;
1499
ImageInfo->file.list[count].size = 0;
1500
ImageInfo->file.count = count+1;
1503
} else if (data == NULL) {
1504
data = emalloc(size);
1506
ImageInfo->file.list[count].type = type;
1507
ImageInfo->file.list[count].data = data;
1508
ImageInfo->file.list[count].size = size;
1513
/* {{{ exif_file_sections_realloc
1514
Reallocate a file section returns 0 on success and -1 on failure
1516
static int exif_file_sections_realloc(image_info_type *ImageInfo, int section_index, size_t size TSRMLS_DC)
1520
/* This is not a malloc/realloc check. It is a plausibility check for the
1521
* function parameters (requirements engineering).
1523
if (section_index >= ImageInfo->file.count) {
1524
EXIF_ERRLOG_FSREALLOC(ImageInfo)
1527
tmp = erealloc(ImageInfo->file.list[section_index].data, size);
1528
ImageInfo->file.list[section_index].data = tmp;
1529
ImageInfo->file.list[section_index].size = size;
1534
/* {{{ exif_file_section_free
1535
Discard all file_sections in ImageInfo
1537
static int exif_file_sections_free(image_info_type *ImageInfo)
1541
if (ImageInfo->file.count) {
1542
for (i=0; i<ImageInfo->file.count; i++) {
1543
EFREE_IF(ImageInfo->file.list[i].data);
1546
EFREE_IF(ImageInfo->file.list);
1547
ImageInfo->file.count = 0;
1552
/* {{{ exif_iif_add_value
1553
Add a value to image_info
1555
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)
1559
image_info_value *info_value;
1560
image_info_data *info_data;
1561
image_info_data *list;
1563
if (length >= LONG_MAX) {
1567
list = erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1)*sizeof(image_info_data));
1568
image_info->info_list[section_index].list = list;
1570
info_data = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
1571
info_data->tag = tag;
1572
info_data->format = format;
1573
info_data->length = length;
1574
info_data->name = estrdup(name);
1575
info_value = &info_data->value;
1578
case TAG_FMT_STRING:
1580
length = php_strnlen(value, length);
1581
if (PG(magic_quotes_runtime)) {
1582
info_value->s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
1584
info_value->s = estrndup(value, length);
1586
info_data->length = length;
1588
info_data->length = 0;
1589
info_value->s = estrdup("");
1594
/* Standard says more types possible but skip them...
1595
* but allow users to handle data if they know how to
1596
* So not return but use type UNDEFINED
1599
info_data->tag = TAG_FMT_UNDEFINED;/* otherwise not freed from memory */
1602
/* in contrast to strings bytes do not need to allocate buffer for NULL if length==0 */
1605
case TAG_FMT_UNDEFINED:
1607
/* do not recompute length here */
1608
if (PG(magic_quotes_runtime)) {
1609
info_value->s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
1611
info_value->s = estrndup(value, length);
1613
info_data->length = length;
1615
info_data->length = 0;
1616
info_value->s = estrdup("");
1620
case TAG_FMT_USHORT:
1622
case TAG_FMT_URATIONAL:
1623
case TAG_FMT_SSHORT:
1625
case TAG_FMT_SRATIONAL:
1626
case TAG_FMT_SINGLE:
1627
case TAG_FMT_DOUBLE:
1632
info_value->list = safe_emalloc(length, sizeof(image_info_value), 0);
1634
info_value = &info_data->value;
1636
for (idex=0,vptr=value; idex<(size_t)length; idex++,vptr=(char *) vptr + php_tiff_bytes_per_format[format]) {
1638
info_value = &info_data->value.list[idex];
1641
case TAG_FMT_USHORT:
1642
info_value->u = php_ifd_get16u(vptr, motorola_intel);
1646
info_value->u = php_ifd_get32u(vptr, motorola_intel);
1649
case TAG_FMT_URATIONAL:
1650
info_value->ur.num = php_ifd_get32u(vptr, motorola_intel);
1651
info_value->ur.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
1654
case TAG_FMT_SSHORT:
1655
info_value->i = php_ifd_get16s(vptr, motorola_intel);
1659
info_value->i = php_ifd_get32s(vptr, motorola_intel);
1662
case TAG_FMT_SRATIONAL:
1663
info_value->sr.num = php_ifd_get32u(vptr, motorola_intel);
1664
info_value->sr.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
1667
case TAG_FMT_SINGLE:
1669
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Found value of type single");
1671
info_value->f = *(float *)value;
1673
case TAG_FMT_DOUBLE:
1675
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Found value of type double");
1677
info_value->d = *(double *)value;
1682
image_info->sections_found |= 1<<section_index;
1683
image_info->info_list[section_index].count++;
1687
/* {{{ exif_iif_add_tag
1688
Add a tag from IFD to image_info
1690
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)
1692
exif_iif_add_value(image_info, section_index, name, tag, format, (int)length, value, image_info->motorola_intel TSRMLS_CC);
1696
/* {{{ exif_iif_add_int
1697
Add an int value to image_info
1699
static void exif_iif_add_int(image_info_type *image_info, int section_index, char *name, int value TSRMLS_DC)
1701
image_info_data *info_data;
1702
image_info_data *list;
1704
list = erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1)*sizeof(image_info_data));
1705
image_info->info_list[section_index].list = list;
1707
info_data = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
1708
info_data->tag = TAG_NONE;
1709
info_data->format = TAG_FMT_SLONG;
1710
info_data->length = 1;
1711
info_data->name = estrdup(name);
1712
info_data->value.i = value;
1713
image_info->sections_found |= 1<<section_index;
1714
image_info->info_list[section_index].count++;
1718
/* {{{ exif_iif_add_str
1719
Add a string value to image_info MUST BE NUL TERMINATED
1721
static void exif_iif_add_str(image_info_type *image_info, int section_index, char *name, char *value TSRMLS_DC)
1723
image_info_data *info_data;
1724
image_info_data *list;
1727
list = erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1)*sizeof(image_info_data));
1728
image_info->info_list[section_index].list = list;
1729
info_data = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
1730
info_data->tag = TAG_NONE;
1731
info_data->format = TAG_FMT_STRING;
1732
info_data->length = 1;
1733
info_data->name = estrdup(name);
1734
if (PG(magic_quotes_runtime)) {
1735
info_data->value.s = php_addslashes(value, strlen(value), NULL, 0 TSRMLS_CC);
1737
info_data->value.s = estrdup(value);
1739
image_info->sections_found |= 1<<section_index;
1740
image_info->info_list[section_index].count++;
1745
/* {{{ exif_iif_add_fmt
1746
Add a format string value to image_info MUST BE NUL TERMINATED
1748
static void exif_iif_add_fmt(image_info_type *image_info, int section_index, char *name TSRMLS_DC, char *value, ...)
1753
va_start(arglist, value);
1755
vspprintf(&tmp, 0, value, arglist);
1756
exif_iif_add_str(image_info, section_index, name, tmp TSRMLS_CC);
1763
/* {{{ exif_iif_add_str
1764
Add a string value to image_info MUST BE NUL TERMINATED
1766
static void exif_iif_add_buffer(image_info_type *image_info, int section_index, char *name, int length, char *value TSRMLS_DC)
1768
image_info_data *info_data;
1769
image_info_data *list;
1772
list = erealloc(image_info->info_list[section_index].list, (image_info->info_list[section_index].count+1)*sizeof(image_info_data));
1773
image_info->info_list[section_index].list = list;
1774
info_data = &image_info->info_list[section_index].list[image_info->info_list[section_index].count];
1775
info_data->tag = TAG_NONE;
1776
info_data->format = TAG_FMT_UNDEFINED;
1777
info_data->length = length;
1778
info_data->name = estrdup(name);
1779
if (PG(magic_quotes_runtime)) {
1781
exif_error_docref(NULL EXIFERR_CC, image_info, E_NOTICE, "Adding %s as buffer%s", name, exif_char_dump(value, length, 0));
1783
info_data->value.s = php_addslashes(value, length, &length, 0 TSRMLS_CC);
1784
info_data->length = length;
1786
info_data->value.s = safe_emalloc(length, 1, 1);
1787
memcpy(info_data->value.s, value, length);
1788
info_data->value.s[length] = 0;
1790
image_info->sections_found |= 1<<section_index;
1791
image_info->info_list[section_index].count++;
1796
/* {{{ exif_iif_free
1797
Free memory allocated for image_info
1799
static void exif_iif_free(image_info_type *image_info, int section_index) {
1801
void *f; /* faster */
1803
if (image_info->info_list[section_index].count) {
1804
for (i=0; i < image_info->info_list[section_index].count; i++) {
1805
if ((f=image_info->info_list[section_index].list[i].name) != NULL) {
1808
switch(image_info->info_list[section_index].list[i].format) {
1811
/* in contrast to strings bytes do not need to allocate buffer for NULL if length==0 */
1812
if (image_info->info_list[section_index].list[i].length<1)
1815
case TAG_FMT_UNDEFINED:
1816
case TAG_FMT_STRING:
1817
if ((f=image_info->info_list[section_index].list[i].value.s) != NULL) {
1822
case TAG_FMT_USHORT:
1824
case TAG_FMT_URATIONAL:
1825
case TAG_FMT_SSHORT:
1827
case TAG_FMT_SRATIONAL:
1828
case TAG_FMT_SINGLE:
1829
case TAG_FMT_DOUBLE:
1830
/* nothing to do here */
1831
if (image_info->info_list[section_index].list[i].length > 1) {
1832
if ((f=image_info->info_list[section_index].list[i].value.list) != NULL) {
1840
EFREE_IF(image_info->info_list[section_index].list);
1844
/* {{{ add_assoc_image_info
1845
* Add image_info to associative array value. */
1846
static void add_assoc_image_info(pval *value, int sub_array, image_info_type *image_info, int section_index TSRMLS_DC)
1848
char buffer[64], *val, *name, uname[64];
1849
int i, ap, l, b, idx=0, unknown=0;
1853
image_info_value *info_value;
1854
image_info_data *info_data;
1855
pval *tmpi, *array = NULL;
1858
/* 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));*/
1860
if (image_info->info_list[section_index].count) {
1862
MAKE_STD_ZVAL(tmpi);
1868
for(i=0; i<image_info->info_list[section_index].count; i++) {
1869
info_data = &image_info->info_list[section_index].list[i];
1871
info_tag = info_data->tag; /* conversion */
1873
info_value = &info_data->value;
1874
if (!(name = info_data->name)) {
1875
snprintf(uname, sizeof(uname), "%d", unknown++);
1879
/* 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));*/
1881
if (info_data->length==0) {
1882
add_assoc_null(tmpi, name);
1884
switch (info_data->format) {
1886
/* Standard says more types possible but skip them...
1887
* but allow users to handle data if they know how to
1888
* So not return but use type UNDEFINED
1893
case TAG_FMT_UNDEFINED:
1894
if (!info_value->s) {
1895
add_assoc_stringl(tmpi, name, "", 0, 1);
1897
add_assoc_stringl(tmpi, name, info_value->s, info_data->length, 1);
1901
case TAG_FMT_STRING:
1902
if (!(val = info_value->s)) {
1905
if (section_index==SECTION_COMMENT) {
1906
add_index_string(tmpi, idx++, val, 1);
1908
add_assoc_string(tmpi, name, val, 1);
1912
case TAG_FMT_URATIONAL:
1913
case TAG_FMT_SRATIONAL:
1914
/*case TAG_FMT_BYTE:
1915
case TAG_FMT_SBYTE:*/
1916
case TAG_FMT_USHORT:
1917
case TAG_FMT_SSHORT:
1918
case TAG_FMT_SINGLE:
1919
case TAG_FMT_DOUBLE:
1922
/* now the rest, first see if it becomes an array */
1923
if ((l = info_data->length) > 1) {
1925
MAKE_STD_ZVAL(array);
1928
for(ap=0; ap<l; ap++) {
1930
info_value = &info_data->value.list[ap];
1932
switch (info_data->format) {
1935
info_value = &info_data->value;
1937
add_index_long(array, b, (int)(info_value->s[b]));
1941
case TAG_FMT_USHORT:
1944
add_assoc_long(tmpi, name, (int)info_value->u);
1946
add_index_long(array, ap, (int)info_value->u);
1950
case TAG_FMT_URATIONAL:
1951
snprintf(buffer, sizeof(buffer), "%i/%i", info_value->ur.num, info_value->ur.den);
1953
add_assoc_string(tmpi, name, buffer, 1);
1955
add_index_string(array, ap, buffer, 1);
1961
info_value = &info_data->value;
1963
add_index_long(array, ap, (int)info_value->s[b]);
1967
case TAG_FMT_SSHORT:
1970
add_assoc_long(tmpi, name, info_value->i);
1972
add_index_long(array, ap, info_value->i);
1976
case TAG_FMT_SRATIONAL:
1977
snprintf(buffer, sizeof(buffer), "%i/%i", info_value->sr.num, info_value->sr.den);
1979
add_assoc_string(tmpi, name, buffer, 1);
1981
add_index_string(array, ap, buffer, 1);
1985
case TAG_FMT_SINGLE:
1987
add_assoc_double(tmpi, name, info_value->f);
1989
add_index_double(array, ap, info_value->f);
1993
case TAG_FMT_DOUBLE:
1995
add_assoc_double(tmpi, name, info_value->d);
1997
add_index_double(array, ap, info_value->d);
2001
info_value = &info_data->value.list[ap];
2004
add_assoc_zval(tmpi, name, array);
2011
add_assoc_zval(value, exif_get_sectionname(section_index), tmpi);
2018
JPEG markers consist of one or more 0xFF bytes, followed by a marker
2019
code byte (which is not an FF). Here are the marker codes of interest
2020
in this program. (See jdmarker.c for a more complete list.)
2023
#define M_TEM 0x01 /* temp for arithmetic coding */
2024
#define M_RES 0x02 /* reserved */
2025
#define M_SOF0 0xC0 /* Start Of Frame N */
2026
#define M_SOF1 0xC1 /* N indicates which compression process */
2027
#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
2030
#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
2033
#define M_JPEG 0x08 /* reserved for extensions */
2035
#define M_SOF10 0xCA
2036
#define M_SOF11 0xCB
2037
#define M_DAC 0xCC /* arithmetic table */
2038
#define M_SOF13 0xCD
2039
#define M_SOF14 0xCE
2040
#define M_SOF15 0xCF
2041
#define M_RST0 0xD0 /* restart segment */
2049
#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */
2050
#define M_EOI 0xD9 /* End Of Image (end of datastream) */
2051
#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
2057
#define M_APP0 0xE0 /* JPEG: 'JFIFF' AND (additional 'JFXX') */
2058
#define M_EXIF 0xE1 /* Exif Attribute Information */
2059
#define M_APP2 0xE2 /* Flash Pix Extension Data? */
2067
#define M_APP10 0xEA
2068
#define M_APP11 0xEB
2069
#define M_APP12 0xEC
2070
#define M_APP13 0xED /* IPTC International Press Telecommunications Council */
2071
#define M_APP14 0xEE /* Software, Copyright? */
2072
#define M_APP15 0xEF
2083
#define M_JPG10 0xFA
2084
#define M_JPG11 0xFB
2085
#define M_JPG12 0xFC
2086
#define M_JPG13 0xFD
2087
#define M_COM 0xFE /* COMment */
2089
#define M_PSEUDO 0x123 /* Extra value. */
2093
/* {{{ jpeg2000 markers
2095
/* Markers x30 - x3F do not have a segment */
2096
/* Markers x00, x01, xFE, xC0 - xDF ISO/IEC 10918-1 -> M_<xx> */
2097
/* Markers xF0 - xF7 ISO/IEC 10918-3 */
2098
/* Markers xF7 - xF8 ISO/IEC 14495-1 */
2099
/* XY=Main/Tile-header:(R:required, N:not_allowed, O:optional, L:last_marker) */
2100
#define JC_SOC 0x4F /* NN, Start of codestream */
2101
#define JC_SIZ 0x51 /* RN, Image and tile size */
2102
#define JC_COD 0x52 /* RO, Codeing style defaulte */
2103
#define JC_COC 0x53 /* OO, Coding style component */
2104
#define JC_TLM 0x55 /* ON, Tile part length main header */
2105
#define JC_PLM 0x57 /* ON, Packet length main header */
2106
#define JC_PLT 0x58 /* NO, Packet length tile part header */
2107
#define JC_QCD 0x5C /* RO, Quantization default */
2108
#define JC_QCC 0x5D /* OO, Quantization component */
2109
#define JC_RGN 0x5E /* OO, Region of interest */
2110
#define JC_POD 0x5F /* OO, Progression order default */
2111
#define JC_PPM 0x60 /* ON, Packed packet headers main header */
2112
#define JC_PPT 0x61 /* NO, Packet packet headers tile part header */
2113
#define JC_CME 0x64 /* OO, Comment: "LL E <text>" E=0:binary, E=1:ascii */
2114
#define JC_SOT 0x90 /* NR, Start of tile */
2115
#define JC_SOP 0x91 /* NO, Start of packeter default */
2116
#define JC_EPH 0x92 /* NO, End of packet header */
2117
#define JC_SOD 0x93 /* NL, Start of data */
2118
#define JC_EOC 0xD9 /* NN, End of codestream */
2121
/* {{{ exif_process_COM
2122
Process a COM marker.
2123
We want to print out the marker contents as legible text;
2124
we must guard against random junk and varying newline representations.
2126
static void exif_process_COM (image_info_type *image_info, char *value, size_t length TSRMLS_DC)
2128
exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_STRING, length-2, value+2 TSRMLS_CC);
2132
/* {{{ exif_process_CME
2133
Process a CME marker.
2134
We want to print out the marker contents as legible text;
2135
we must guard against random junk and varying newline representations.
2137
#ifdef EXIF_JPEG2000
2138
static void exif_process_CME (image_info_type *image_info, char *value, size_t length TSRMLS_DC)
2143
exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_UNDEFINED, length, value TSRMLS_CC);
2146
exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_STRING, length, value);
2149
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined JPEG2000 comment encoding");
2153
exif_iif_add_tag(image_info, SECTION_COMMENT, "Comment", TAG_COMPUTED_VALUE, TAG_FMT_UNDEFINED, 0, NULL);
2154
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "JPEG2000 comment section to small");
2160
/* {{{ exif_process_SOFn
2161
* Process a SOFn marker. This is useful for the image dimensions */
2162
static void exif_process_SOFn (uchar *Data, int marker, jpeg_sof_info *result)
2164
/* 0xFF SOSn SectLen(2) Bits(1) Height(2) Width(2) Channels(1) 3*Channels (1) */
2165
result->bits_per_sample = Data[2];
2166
result->height = php_jpg_get16(Data+3);
2167
result->width = php_jpg_get16(Data+5);
2168
result->num_components = Data[7];
2170
/* switch (marker) {
2171
case M_SOF0: process = "Baseline"; break;
2172
case M_SOF1: process = "Extended sequential"; break;
2173
case M_SOF2: process = "Progressive"; break;
2174
case M_SOF3: process = "Lossless"; break;
2175
case M_SOF5: process = "Differential sequential"; break;
2176
case M_SOF6: process = "Differential progressive"; break;
2177
case M_SOF7: process = "Differential lossless"; break;
2178
case M_SOF9: process = "Extended sequential, arithmetic coding"; break;
2179
case M_SOF10: process = "Progressive, arithmetic coding"; break;
2180
case M_SOF11: process = "Lossless, arithmetic coding"; break;
2181
case M_SOF13: process = "Differential sequential, arithmetic coding"; break;
2182
case M_SOF14: process = "Differential progressive, arithmetic coding"; break;
2183
case M_SOF15: process = "Differential lossless, arithmetic coding"; break;
2184
default: process = "Unknown"; break;
2189
/* forward declarations */
2190
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);
2191
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);
2193
/* {{{ exif_get_markername
2194
Get name of marker */
2196
static char * exif_get_markername(int marker)
2199
case 0xC0: return "SOF0";
2200
case 0xC1: return "SOF1";
2201
case 0xC2: return "SOF2";
2202
case 0xC3: return "SOF3";
2203
case 0xC4: return "DHT";
2204
case 0xC5: return "SOF5";
2205
case 0xC6: return "SOF6";
2206
case 0xC7: return "SOF7";
2207
case 0xC9: return "SOF9";
2208
case 0xCA: return "SOF10";
2209
case 0xCB: return "SOF11";
2210
case 0xCD: return "SOF13";
2211
case 0xCE: return "SOF14";
2212
case 0xCF: return "SOF15";
2213
case 0xD8: return "SOI";
2214
case 0xD9: return "EOI";
2215
case 0xDA: return "SOS";
2216
case 0xDB: return "DQT";
2217
case 0xDC: return "DNL";
2218
case 0xDD: return "DRI";
2219
case 0xDE: return "DHP";
2220
case 0xDF: return "EXP";
2221
case 0xE0: return "APP0";
2222
case 0xE1: return "EXIF";
2223
case 0xE2: return "FPIX";
2224
case 0xE3: return "APP3";
2225
case 0xE4: return "APP4";
2226
case 0xE5: return "APP5";
2227
case 0xE6: return "APP6";
2228
case 0xE7: return "APP7";
2229
case 0xE8: return "APP8";
2230
case 0xE9: return "APP9";
2231
case 0xEA: return "APP10";
2232
case 0xEB: return "APP11";
2233
case 0xEC: return "APP12";
2234
case 0xED: return "APP13";
2235
case 0xEE: return "APP14";
2236
case 0xEF: return "APP15";
2237
case 0xF0: return "JPG0";
2238
case 0xFD: return "JPG13";
2239
case 0xFE: return "COM";
2240
case 0x01: return "TEM";
2247
/* {{{ proto string exif_tagname(index)
2248
Get headername for index or false if not defined */
2249
PHP_FUNCTION(exif_tagname)
2252
int tag, ac = ZEND_NUM_ARGS();
2255
if ((ac < 1 || ac > 1) || zend_get_parameters_ex(ac, &p_num) == FAILURE) {
2259
convert_to_long_ex(p_num);
2260
tag = Z_LVAL_PP(p_num);
2261
szTemp = exif_get_tagname(tag, NULL, 0, tag_table_IFD TSRMLS_CC);
2262
if (tag<0 || !szTemp || !szTemp[0]) {
2265
RETURN_STRING(szTemp, 1)
2270
/* {{{ exif_ifd_make_value
2271
* Create a value for an ifd from an info_data pointer */
2272
static void* exif_ifd_make_value(image_info_data *info_data, int motorola_intel TSRMLS_DC) {
2274
char *value_ptr, *data_ptr;
2277
image_info_value *info_value;
2279
byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
2280
value_ptr = emalloc(max(byte_count, 4));
2281
memset(value_ptr, 0, 4);
2282
if (!info_data->length) {
2285
if (info_data->format == TAG_FMT_UNDEFINED || info_data->format == TAG_FMT_STRING
2286
|| (byte_count>1 && (info_data->format == TAG_FMT_BYTE || info_data->format == TAG_FMT_SBYTE))
2288
memmove(value_ptr, info_data->value.s, byte_count);
2290
} else if (info_data->format == TAG_FMT_BYTE) {
2291
*value_ptr = info_data->value.u;
2293
} else if (info_data->format == TAG_FMT_SBYTE) {
2294
*value_ptr = info_data->value.i;
2297
data_ptr = value_ptr;
2298
for(i=0; i<info_data->length; i++) {
2299
if (info_data->length==1) {
2300
info_value = &info_data->value;
2302
info_value = &info_data->value.list[i];
2304
switch(info_data->format) {
2305
case TAG_FMT_USHORT:
2306
php_ifd_set16u(data_ptr, info_value->u, motorola_intel);
2310
php_ifd_set32u(data_ptr, info_value->u, motorola_intel);
2313
case TAG_FMT_SSHORT:
2314
php_ifd_set16u(data_ptr, info_value->i, motorola_intel);
2318
php_ifd_set32u(data_ptr, info_value->i, motorola_intel);
2321
case TAG_FMT_URATIONAL:
2322
php_ifd_set32u(data_ptr, info_value->sr.num, motorola_intel);
2323
php_ifd_set32u(data_ptr+4, info_value->sr.den, motorola_intel);
2326
case TAG_FMT_SRATIONAL:
2327
php_ifd_set32u(data_ptr, info_value->ur.num, motorola_intel);
2328
php_ifd_set32u(data_ptr+4, info_value->ur.den, motorola_intel);
2331
case TAG_FMT_SINGLE:
2332
memmove(data_ptr, &info_data->value.f, byte_count);
2335
case TAG_FMT_DOUBLE:
2336
memmove(data_ptr, &info_data->value.d, byte_count);
2346
/* {{{ exif_thumbnail_build
2347
* Check and build thumbnail */
2348
static void exif_thumbnail_build(image_info_type *ImageInfo TSRMLS_DC) {
2349
size_t new_size, new_move, new_value;
2353
image_info_list *info_list;
2354
image_info_data *info_data;
2359
if (!ImageInfo->read_thumbnail || !ImageInfo->Thumbnail.offset || !ImageInfo->Thumbnail.size) {
2360
return; /* ignore this call */
2363
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: filetype = %d", ImageInfo->Thumbnail.filetype);
2365
switch(ImageInfo->Thumbnail.filetype) {
2367
case IMAGE_FILETYPE_JPEG:
2370
case IMAGE_FILETYPE_TIFF_II:
2371
case IMAGE_FILETYPE_TIFF_MM:
2372
info_list = &ImageInfo->info_list[SECTION_THUMBNAIL];
2373
new_size = 8 + 2 + info_list->count * 12 + 4;
2375
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: size of signature + directory(%d): 0x%02X", info_list->count, new_size);
2377
new_value= new_size; /* offset for ifd values outside ifd directory */
2378
for (i=0; i<info_list->count; i++) {
2379
info_data = &info_list->list[i];
2380
byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
2381
if (byte_count > 4) {
2382
new_size += byte_count;
2385
new_move = new_size;
2386
new_data = erealloc(ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size+new_size);
2387
ImageInfo->Thumbnail.data = new_data;
2388
memmove(ImageInfo->Thumbnail.data + new_move, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
2389
ImageInfo->Thumbnail.size += new_size;
2391
if (ImageInfo->motorola_intel) {
2392
memmove(new_data, "MM\x00\x2a\x00\x00\x00\x08", 8);
2394
memmove(new_data, "II\x2a\x00\x08\x00\x00\x00", 8);
2397
php_ifd_set16u(new_data, info_list->count, ImageInfo->motorola_intel);
2399
for (i=0; i<info_list->count; i++) {
2400
info_data = &info_list->list[i];
2401
byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
2403
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);
2405
if (info_data->tag==TAG_STRIP_OFFSETS || info_data->tag==TAG_JPEG_INTERCHANGE_FORMAT) {
2406
php_ifd_set16u(new_data + 0, info_data->tag, ImageInfo->motorola_intel);
2407
php_ifd_set16u(new_data + 2, TAG_FMT_ULONG, ImageInfo->motorola_intel);
2408
php_ifd_set32u(new_data + 4, 1, ImageInfo->motorola_intel);
2409
php_ifd_set32u(new_data + 8, new_move, ImageInfo->motorola_intel);
2411
php_ifd_set16u(new_data + 0, info_data->tag, ImageInfo->motorola_intel);
2412
php_ifd_set16u(new_data + 2, info_data->format, ImageInfo->motorola_intel);
2413
php_ifd_set32u(new_data + 4, info_data->length, ImageInfo->motorola_intel);
2414
value_ptr = exif_ifd_make_value(info_data, ImageInfo->motorola_intel TSRMLS_CC);
2415
if (byte_count <= 4) {
2416
memmove(new_data+8, value_ptr, 4);
2418
php_ifd_set32u(new_data+8, new_value, ImageInfo->motorola_intel);
2420
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: writing with value offset: 0x%04X + 0x%02X", new_value, byte_count);
2422
memmove(ImageInfo->Thumbnail.data+new_value, value_ptr, byte_count);
2423
new_value += byte_count;
2429
memset(new_data, 0, 4); /* next ifd pointer */
2431
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: created");
2438
/* {{{ exif_thumbnail_extract
2439
* Grab the thumbnail, corrected */
2440
static void exif_thumbnail_extract(image_info_type *ImageInfo, char *offset, size_t length TSRMLS_DC) {
2441
if (ImageInfo->Thumbnail.data) {
2442
exif_error_docref("exif_read_data#error_mult_thumb" EXIFERR_CC, ImageInfo, E_WARNING, "Multiple possible thumbnails");
2443
return; /* Should not happen */
2445
if (!ImageInfo->read_thumbnail) {
2446
return; /* ignore this call */
2448
/* according to exif2.1, the thumbnail is not supposed to be greater than 64K */
2449
if (ImageInfo->Thumbnail.size >= 65536
2450
|| ImageInfo->Thumbnail.size <= 0
2451
|| ImageInfo->Thumbnail.offset <= 0
2453
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Illegal thumbnail size/offset");
2456
/* Check to make sure we are not going to go past the ExifLength */
2457
if ((ImageInfo->Thumbnail.offset + ImageInfo->Thumbnail.size) > length) {
2458
EXIF_ERRLOG_THUMBEOF(ImageInfo)
2461
ImageInfo->Thumbnail.data = estrndup(offset + ImageInfo->Thumbnail.offset, ImageInfo->Thumbnail.size);
2462
exif_thumbnail_build(ImageInfo TSRMLS_CC);
2466
/* {{{ exif_process_undefined
2467
* Copy a string/buffer in Exif header to a character string and return length of allocated buffer if any. */
2468
static int exif_process_undefined(char **result, char *value, size_t byte_count TSRMLS_DC) {
2469
/* we cannot use strlcpy - here the problem is that we have to copy NUL
2470
* chars up to byte_count, we also have to add a single NUL character to
2471
* force end of string.
2472
* estrndup does not return length
2475
(*result) = estrndup(value, byte_count); /* NULL @ byte_count!!! */
2476
return byte_count+1;
2482
/* {{{ exif_process_string_raw
2483
* Copy a string in Exif header to a character string returns length of allocated buffer if any. */
2484
#if !EXIF_USE_MBSTRING
2485
static int exif_process_string_raw(char **result, char *value, size_t byte_count) {
2486
/* we cannot use strlcpy - here the problem is that we have to copy NUL
2487
* chars up to byte_count, we also have to add a single NUL character to
2488
* force end of string.
2491
(*result) = safe_emalloc(byte_count, 1, 1);
2492
memcpy(*result, value, byte_count);
2493
(*result)[byte_count] = '\0';
2494
return byte_count+1;
2501
/* {{{ exif_process_string
2502
* Copy a string in Exif header to a character string and return length of allocated buffer if any.
2503
* In contrast to exif_process_string this function does allways return a string buffer */
2504
static int exif_process_string(char **result, char *value, size_t byte_count TSRMLS_DC) {
2505
/* we cannot use strlcpy - here the problem is that we cannot use strlen to
2506
* determin length of string and we cannot use strlcpy with len=byte_count+1
2507
* because then we might get into an EXCEPTION if we exceed an allocated
2508
* memory page...so we use php_strnlen in conjunction with memcpy and add the NUL
2510
* estrdup would sometimes allocate more memory and does not return length
2512
if ((byte_count=php_strnlen(value, byte_count)) > 0) {
2513
return exif_process_undefined(result, value, byte_count TSRMLS_CC);
2515
(*result) = estrndup("", 1); /* force empty string */
2516
return byte_count+1;
2520
/* {{{ exif_process_user_comment
2521
* Process UserComment in IFD. */
2522
static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoPtr, char **pszEncoding, char *szValuePtr, int ByteCount TSRMLS_DC)
2526
#if EXIF_USE_MBSTRING
2531
*pszEncoding = NULL;
2532
/* Copy the comment */
2534
if (!memcmp(szValuePtr, "UNICODE\0", 8)) {
2535
*pszEncoding = estrdup((const char*)szValuePtr);
2536
szValuePtr = szValuePtr+8;
2538
#if EXIF_USE_MBSTRING
2539
/* First try to detect BOM: ZERO WIDTH NOBREAK SPACE (FEFF 16)
2540
* since we have no encoding support for the BOM yet we skip that.
2542
if (!memcmp(szValuePtr, "\xFE\xFF", 2)) {
2544
szValuePtr = szValuePtr+2;
2546
} else if (!memcmp(szValuePtr, "\xFF\xFE", 2)) {
2548
szValuePtr = szValuePtr+2;
2550
} else if (ImageInfo->motorola_intel) {
2551
decode = ImageInfo->decode_unicode_be;
2553
decode = ImageInfo->decode_unicode_le;
2555
*pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, decode, &len TSRMLS_CC);
2558
return exif_process_string_raw(pszInfoPtr, szValuePtr, ByteCount);
2561
if (!memcmp(szValuePtr, "ASCII\0\0\0", 8)) {
2562
*pszEncoding = estrdup((const char*)szValuePtr);
2563
szValuePtr = szValuePtr+8;
2566
if (!memcmp(szValuePtr, "JIS\0\0\0\0\0", 8)) {
2567
/* JIS should be tanslated to MB or we leave it to the user - leave it to the user */
2568
*pszEncoding = estrdup((const char*)szValuePtr);
2569
szValuePtr = szValuePtr+8;
2571
#if EXIF_USE_MBSTRING
2572
if (ImageInfo->motorola_intel) {
2573
*pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_jis, ImageInfo->decode_jis_be, &len TSRMLS_CC);
2575
*pszInfoPtr = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_jis, ImageInfo->decode_jis_le, &len TSRMLS_CC);
2579
return exif_process_string_raw(pszInfoPtr, szValuePtr, ByteCount);
2582
if (!memcmp(szValuePtr, "\0\0\0\0\0\0\0\0", 8)) {
2583
/* 8 NULL means undefined and should be ASCII... */
2584
*pszEncoding = estrdup("UNDEFINED");
2585
szValuePtr = szValuePtr+8;
2590
/* Olympus has this padded with trailing spaces. Remove these first. */
2592
for (a=ByteCount-1;a && szValuePtr[a]==' ';a--) {
2593
(szValuePtr)[a] = '\0';
2597
/* normal text without encoding */
2598
exif_process_string(pszInfoPtr, szValuePtr, ByteCount TSRMLS_CC);
2599
return strlen(*pszInfoPtr);
2603
/* {{{ exif_process_unicode
2604
* Process unicode field in IFD. */
2605
static int exif_process_unicode(image_info_type *ImageInfo, xp_field_type *xp_field, int tag, char *szValuePtr, int ByteCount TSRMLS_DC)
2607
xp_field->tag = tag;
2609
/* Copy the comment */
2610
#if EXIF_USE_MBSTRING
2611
/* What if MS supports big-endian with XP? */
2612
/* if (ImageInfo->motorola_intel) {
2613
xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_be, &xp_field->size TSRMLS_CC);
2615
xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_le, &xp_field->size TSRMLS_CC);
2617
xp_field->value = php_mb_convert_encoding(szValuePtr, ByteCount, ImageInfo->encode_unicode, ImageInfo->decode_unicode_le, &xp_field->size TSRMLS_CC);
2618
return xp_field->size;
2620
xp_field->size = exif_process_string_raw(&xp_field->value, szValuePtr, ByteCount);
2621
return xp_field->size;
2626
/* {{{ exif_process_IFD_in_MAKERNOTE
2627
* Process nested IFDs directories in Maker Note. */
2628
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)
2630
int de, i=0, section_index = SECTION_MAKERNOTE;
2631
int NumDirEntries, old_motorola_intel, offset_diff;
2632
const maker_note_type *maker_note;
2635
for (i=0; i<=sizeof(maker_note_array)/sizeof(maker_note_type); i++) {
2636
if (i==sizeof(maker_note_array)/sizeof(maker_note_type))
2638
maker_note = maker_note_array+i;
2640
/*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "check (%s,%s)", maker_note->make?maker_note->make:"", maker_note->model?maker_note->model:"");*/
2641
if (maker_note->make && (!ImageInfo->make || strcmp(maker_note->make, ImageInfo->make)))
2643
if (maker_note->model && (!ImageInfo->model || strcmp(maker_note->model, ImageInfo->model)))
2645
if (maker_note->id_string && strncmp(maker_note->id_string, value_ptr, maker_note->id_string_len))
2650
dir_start = value_ptr + maker_note->offset;
2653
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));
2656
ImageInfo->sections_found |= FOUND_MAKERNOTE;
2658
old_motorola_intel = ImageInfo->motorola_intel;
2659
switch (maker_note->byte_order) {
2660
case MN_ORDER_INTEL:
2661
ImageInfo->motorola_intel = 0;
2663
case MN_ORDER_MOTOROLA:
2664
ImageInfo->motorola_intel = 1;
2667
case MN_ORDER_NORMAL:
2671
NumDirEntries = php_ifd_get16u(dir_start, ImageInfo->motorola_intel);
2673
switch (maker_note->offset_mode) {
2674
case MN_OFFSET_MAKER:
2675
offset_base = value_ptr;
2677
case MN_OFFSET_GUESS:
2678
offset_diff = 2 + NumDirEntries*12 + 4 - php_ifd_get32u(dir_start+10, ImageInfo->motorola_intel);
2680
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);
2682
offset_base = value_ptr + offset_diff;
2685
case MN_OFFSET_NORMAL:
2689
if ((2+NumDirEntries*12) > value_len) {
2690
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);
2694
for (de=0;de<NumDirEntries;de++) {
2695
if (!exif_process_IFD_TAG(ImageInfo, dir_start + 2 + 12 * de,
2696
offset_base, IFDlength, displacement, section_index, 0, maker_note->tag_table TSRMLS_CC)) {
2700
ImageInfo->motorola_intel = old_motorola_intel;
2701
/* NextDirOffset (must be NULL) = php_ifd_get32u(dir_start+2+12*de, ImageInfo->motorola_intel);*/
2703
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Subsection %s done", exif_get_sectionname(SECTION_MAKERNOTE));
2709
/* {{{ exif_process_IFD_TAG
2710
* Process one of the nested IFDs directories. */
2711
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)
2714
int tag, format, components;
2715
char *value_ptr, tagname[64], cbuf[32], *outside=NULL;
2716
size_t byte_count, offset_val, fpos, fgot;
2717
xp_field_type *tmp_xp;
2719
/* Protect against corrupt headers */
2720
if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
2721
exif_error_docref("exif_read_data#error_ifd" TSRMLS_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached");
2724
ImageInfo->ifd_nesting_level++;
2726
tag = php_ifd_get16u(dir_entry, ImageInfo->motorola_intel);
2727
format = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
2728
components = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel);
2730
if (!format || format > NUM_FORMATS) {
2731
/* (-1) catches illegal zero case as unsigned underflows to positive large. */
2732
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);
2733
format = TAG_FMT_BYTE;
2737
byte_count = components * php_tiff_bytes_per_format[format];
2739
if ((ssize_t)byte_count < 0) {
2740
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal byte_count(%ld)", tag, exif_get_tagname(tag, tagname, -12, tag_table TSRMLS_CC), byte_count);
2744
if (byte_count > 4) {
2745
offset_val = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
2746
/* If its bigger than 4 bytes, the dir entry contains an offset. */
2747
value_ptr = offset_base+offset_val;
2748
if (offset_val+byte_count > IFDlength || value_ptr < dir_entry) {
2750
// It is important to check for IMAGE_FILETYPE_TIFF
2751
// JPEG does not use absolute pointers instead its pointers are relative to the start
2752
// of the TIFF header in APP1 section.
2754
if (offset_val+byte_count>ImageInfo->FileSize || (ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_II && ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_MM && ImageInfo->FileType!=IMAGE_FILETYPE_JPEG)) {
2755
if (value_ptr < dir_entry) {
2756
/* we can read this if offset_val > 0 */
2757
/* some files have their values in other parts of the file */
2758
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);
2760
/* this is for sure not allowed */
2761
/* exception are IFD pointers */
2762
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);
2766
if (byte_count>sizeof(cbuf)) {
2767
/* mark as outside range and get buffer */
2768
value_ptr = emalloc(byte_count);
2769
outside = value_ptr;
2772
// in most cases we only access a small range so
2773
// it is faster to use a static buffer there
2774
// BUT it offers also the possibility to have
2775
// pointers read without the need to free them
2776
// explicitley before returning.
2781
fpos = php_stream_tell(ImageInfo->infile);
2782
php_stream_seek(ImageInfo->infile, offset_val, SEEK_SET);
2783
fgot = php_stream_tell(ImageInfo->infile);
2784
if (fgot!=offset_val) {
2786
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x08X", fgot, offset_val);
2789
fgot = php_stream_read(ImageInfo->infile, value_ptr, byte_count);
2790
php_stream_seek(ImageInfo->infile, fpos, SEEK_SET);
2791
if (fgot<byte_count) {
2793
EXIF_ERRLOG_FILEEOF(ImageInfo)
2798
/* 4 bytes or less and value is in the dir entry itself */
2799
value_ptr = dir_entry+8;
2800
offset_val= value_ptr-offset_base;
2803
ImageInfo->sections_found |= FOUND_ANY_TAG;
2805
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process tag(x%04X=%s,@x%04X + x%04X(=%d)): %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 ":"", format==TAG_FMT_STRING?(value_ptr?value_ptr:"<no data>"):exif_get_tagformat(format));
2807
if (section_index==SECTION_THUMBNAIL) {
2808
if (!ImageInfo->Thumbnail.data) {
2810
case TAG_IMAGEWIDTH:
2811
case TAG_COMP_IMAGE_WIDTH:
2812
ImageInfo->Thumbnail.width = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2815
case TAG_IMAGEHEIGHT:
2816
case TAG_COMP_IMAGE_HEIGHT:
2817
ImageInfo->Thumbnail.height = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2820
case TAG_STRIP_OFFSETS:
2821
case TAG_JPEG_INTERCHANGE_FORMAT:
2822
/* accept both formats */
2823
ImageInfo->Thumbnail.offset = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2826
case TAG_STRIP_BYTE_COUNTS:
2827
if (ImageInfo->FileType == IMAGE_FILETYPE_TIFF_II || ImageInfo->FileType == IMAGE_FILETYPE_TIFF_MM) {
2828
ImageInfo->Thumbnail.filetype = ImageInfo->FileType;
2830
/* motorola is easier to read */
2831
ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_TIFF_MM;
2833
ImageInfo->Thumbnail.size = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2836
case TAG_JPEG_INTERCHANGE_FORMAT_LEN:
2837
if (ImageInfo->Thumbnail.filetype == IMAGE_FILETYPE_UNKNOWN) {
2838
ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_JPEG;
2839
ImageInfo->Thumbnail.size = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2845
if (section_index==SECTION_IFD0 || section_index==SECTION_EXIF)
2848
/* check for "<photographer> NUL <editor> NUL" */
2849
if (byte_count>1 && (length=php_strnlen(value_ptr, byte_count)) > 0) {
2850
if (length<byte_count-1) {
2851
/* When there are any characters after the first NUL */
2852
ImageInfo->CopyrightPhotographer = estrdup(value_ptr);
2853
ImageInfo->CopyrightEditor = estrdup(value_ptr+length+1);
2854
spprintf(&ImageInfo->Copyright, 0, "%s, %s", value_ptr, value_ptr+length+1);
2855
/* format = TAG_FMT_UNDEFINED; this musn't be ASCII */
2856
/* but we are not supposed to change this */
2857
/* keep in mind that image_info does not store editor value */
2859
ImageInfo->Copyright = estrdup(value_ptr);
2864
case TAG_USERCOMMENT:
2865
ImageInfo->UserCommentLength = exif_process_user_comment(ImageInfo, &(ImageInfo->UserComment), &(ImageInfo->UserCommentEncoding), value_ptr, byte_count TSRMLS_CC);
2869
case TAG_XP_COMMENTS:
2871
case TAG_XP_KEYWORDS:
2872
case TAG_XP_SUBJECT:
2873
tmp_xp = (xp_field_type*)erealloc(ImageInfo->xp_fields.list, sizeof(xp_field_type)*(ImageInfo->xp_fields.count+1));
2874
ImageInfo->sections_found |= FOUND_WINXP;
2875
ImageInfo->xp_fields.list = tmp_xp;
2876
ImageInfo->xp_fields.count++;
2877
exif_process_unicode(ImageInfo, &(ImageInfo->xp_fields.list[ImageInfo->xp_fields.count-1]), tag, value_ptr, byte_count TSRMLS_CC);
2881
/* Simplest way of expressing aperture, so I trust it the most.
2882
(overwrite previously computed value if there is one) */
2883
ImageInfo->ApertureFNumber = (float)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2887
case TAG_MAX_APERTURE:
2888
/* More relevant info always comes earlier, so only use this field if we don't
2889
have appropriate aperture information yet. */
2890
if (ImageInfo->ApertureFNumber == 0) {
2891
ImageInfo->ApertureFNumber
2892
= (float)exp(exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)*log(2)*0.5);
2896
case TAG_SHUTTERSPEED:
2897
/* More complicated way of expressing exposure time, so only use
2898
this value if we don't already have it from somewhere else.
2899
SHUTTERSPEED comes after EXPOSURE TIME
2901
if (ImageInfo->ExposureTime == 0) {
2902
ImageInfo->ExposureTime
2903
= (float)(1/exp(exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)*log(2)));
2906
case TAG_EXPOSURETIME:
2907
ImageInfo->ExposureTime = -1;
2910
case TAG_COMP_IMAGE_WIDTH:
2911
ImageInfo->ExifImageWidth = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2914
case TAG_FOCALPLANE_X_RES:
2915
ImageInfo->FocalplaneXRes = exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2918
case TAG_SUBJECT_DISTANCE:
2919
/* Inidcates the distacne the autofocus camera is focused to.
2920
Tends to be less accurate as distance increases. */
2921
ImageInfo->Distance = (float)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC);
2924
case TAG_FOCALPLANE_RESOLUTION_UNIT:
2925
switch((int)exif_convert_any_format(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC)) {
2926
case 1: ImageInfo->FocalplaneUnits = 25.4; break; /* inch */
2928
/* According to the information I was using, 2 measn meters.
2929
But looking at the Cannon powershot's files, inches is the only
2931
ImageInfo->FocalplaneUnits = 25.4;
2934
case 3: ImageInfo->FocalplaneUnits = 10; break; /* centimeter */
2935
case 4: ImageInfo->FocalplaneUnits = 1; break; /* milimeter */
2936
case 5: ImageInfo->FocalplaneUnits = .001; break; /* micrometer */
2941
if (format==TAG_FMT_IFD) {
2942
/* If this is called we are either in a TIFFs thumbnail or a JPEG where we cannot handle it */
2943
/* TIFF thumbnail: our data structure cannot store a thumbnail of a thumbnail */
2944
/* JPEG do we have the data area and what to do with it */
2945
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Skip SUB IFD");
2950
ImageInfo->make = estrdup(value_ptr);
2953
ImageInfo->model = estrdup(value_ptr);
2956
case TAG_MAKER_NOTE:
2957
exif_process_IFD_in_MAKERNOTE(ImageInfo, value_ptr, byte_count, offset_base, IFDlength, displacement TSRMLS_CC);
2960
case TAG_EXIF_IFD_POINTER:
2961
case TAG_GPS_IFD_POINTER:
2962
case TAG_INTEROP_IFD_POINTER:
2965
int sub_section_index = 0;
2967
case TAG_EXIF_IFD_POINTER:
2969
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found EXIF");
2971
ImageInfo->sections_found |= FOUND_EXIF;
2972
sub_section_index = SECTION_EXIF;
2974
case TAG_GPS_IFD_POINTER:
2976
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found GPS");
2978
ImageInfo->sections_found |= FOUND_GPS;
2979
sub_section_index = SECTION_GPS;
2981
case TAG_INTEROP_IFD_POINTER:
2983
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Found INTEROPERABILITY");
2985
ImageInfo->sections_found |= FOUND_INTEROP;
2986
sub_section_index = SECTION_INTEROP;
2989
Subdir_start = offset_base + php_ifd_get32u(value_ptr, ImageInfo->motorola_intel);
2990
if (Subdir_start < offset_base || Subdir_start > offset_base+IFDlength) {
2991
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD Pointer");
2994
exif_process_IFD_in_JPEG(ImageInfo, Subdir_start, offset_base, IFDlength, displacement, sub_section_index TSRMLS_CC);
2996
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Subsection %s done", exif_get_sectionname(sub_section_index));
3001
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);
3007
/* {{{ exif_process_IFD_in_JPEG
3008
* Process one of the nested IFDs directories. */
3009
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)
3016
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process %s (x%04X(=%d))", exif_get_sectionname(section_index), IFDlength, IFDlength);
3019
ImageInfo->sections_found |= FOUND_IFD0;
3021
NumDirEntries = php_ifd_get16u(dir_start, ImageInfo->motorola_intel);
3023
if ((dir_start+2+NumDirEntries*12) > (offset_base+IFDlength)) {
3024
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);
3028
for (de=0;de<NumDirEntries;de++) {
3029
if (!exif_process_IFD_TAG(ImageInfo, dir_start + 2 + 12 * de,
3030
offset_base, IFDlength, displacement, section_index, 1, exif_get_tag_table(section_index) TSRMLS_CC)) {
3035
* Hack to make it process IDF1 I hope
3036
* There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail
3038
NextDirOffset = php_ifd_get32u(dir_start+2+12*de, ImageInfo->motorola_intel);
3039
if (NextDirOffset) {
3040
/* the next line seems false but here IFDlength means length of all IFDs */
3041
if (offset_base + NextDirOffset < offset_base || offset_base + NextDirOffset > offset_base+IFDlength) {
3042
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD offset");
3045
/* That is the IFD for the first thumbnail */
3047
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Expect next IFD to be thumbnail");
3049
if (exif_process_IFD_in_JPEG(ImageInfo, offset_base + NextDirOffset, offset_base, IFDlength, displacement, SECTION_THUMBNAIL TSRMLS_CC)) {
3051
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail size: 0x%04X", ImageInfo->Thumbnail.size);
3053
if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN
3054
&& ImageInfo->Thumbnail.size
3055
&& ImageInfo->Thumbnail.offset
3056
&& ImageInfo->read_thumbnail
3058
exif_thumbnail_extract(ImageInfo, offset_base, IFDlength TSRMLS_CC);
3069
/* {{{ exif_process_TIFF_in_JPEG
3070
Process a TIFF header in a JPEG file
3072
static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo, char *CharBuf, size_t length, size_t displacement TSRMLS_DC)
3074
unsigned exif_value_2a, offset_of_ifd;
3076
/* set the thumbnail stuff to nothing so we can test to see if they get set up */
3077
if (memcmp(CharBuf, "II", 2) == 0) {
3078
ImageInfo->motorola_intel = 0;
3079
} else if (memcmp(CharBuf, "MM", 2) == 0) {
3080
ImageInfo->motorola_intel = 1;
3082
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF alignment marker");
3086
/* Check the next two values for correctness. */
3087
exif_value_2a = php_ifd_get16u(CharBuf+2, ImageInfo->motorola_intel);
3088
offset_of_ifd = php_ifd_get32u(CharBuf+4, ImageInfo->motorola_intel);
3089
if ( exif_value_2a != 0x2a || offset_of_ifd < 0x08) {
3090
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF start (1)");
3094
ImageInfo->sections_found |= FOUND_IFD0;
3095
/* First directory starts at offset 8. Offsets starts at 0. */
3096
exif_process_IFD_in_JPEG(ImageInfo, CharBuf+offset_of_ifd, CharBuf, length/*-14*/, displacement, SECTION_IFD0 TSRMLS_CC);
3099
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process TIFF in JPEG done");
3102
/* Compute the CCD width, in milimeters. */
3103
if (ImageInfo->FocalplaneXRes != 0) {
3104
ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth * ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes);
3109
/* {{{ exif_process_APP1
3110
Process an JPEG APP1 block marker
3111
Describes all the drivel that most digital cameras include...
3113
static void exif_process_APP1(image_info_type *ImageInfo, char *CharBuf, size_t length, size_t displacement TSRMLS_DC)
3115
/* Check the APP1 for Exif Identifier Code */
3116
static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
3117
if (memcmp(CharBuf+2, ExifHeader, 6)) {
3118
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Incorrect APP1 Exif Identifier Code");
3121
exif_process_TIFF_in_JPEG(ImageInfo, CharBuf + 8, length - 8, displacement+8 TSRMLS_CC);
3123
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process APP1/EXIF done");
3128
/* {{{ exif_process_APP12
3129
Process an JPEG APP12 block marker used by OLYMPUS
3131
static void exif_process_APP12(image_info_type *ImageInfo, char *buffer, size_t length TSRMLS_DC)
3135
if ((l1 = php_strnlen(buffer+2, length-2)) > 0) {
3136
exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company", TAG_NONE, TAG_FMT_STRING, l1, buffer+2 TSRMLS_CC);
3137
if (length > 2+l1+1) {
3138
l2 = php_strnlen(buffer+2+l1+1, length-2-l1+1);
3139
exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info", TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1 TSRMLS_CC);
3143
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process section APP12 with l1=%d, l2=%d done", l1, l2);
3148
/* {{{ exif_scan_JPEG_header
3149
* Parse the marker stream until SOS or EOI is seen; */
3150
static int exif_scan_JPEG_header(image_info_type *ImageInfo TSRMLS_DC)
3153
int marker = 0, last_marker = M_PSEUDO, comment_correction=1;
3154
unsigned int ll, lh;
3156
size_t fpos, size, got, itemlen;
3157
jpeg_sof_info sof_info;
3159
for(section=0;;section++) {
3161
fpos = php_stream_tell(ImageInfo->infile);
3162
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Needing section %d @ 0x%08X", ImageInfo->file.count, fpos);
3165
/* get marker byte, swallowing possible padding */
3166
/* some software does not count the length bytes of COM section */
3167
/* one company doing so is very much envolved in JPEG... so we accept too */
3168
if (last_marker==M_COM && comment_correction) {
3169
comment_correction = 2;
3172
if ((marker = php_stream_getc(ImageInfo->infile)) == EOF) {
3173
EXIF_ERRLOG_CORRUPT(ImageInfo)
3176
if (last_marker==M_COM && comment_correction>0) {
3179
comment_correction--;
3181
last_marker = M_PSEUDO; /* stop skipping 0 for M_COM */
3184
} while (marker == 0xff);
3185
if (last_marker==M_COM && !comment_correction) {
3186
exif_error_docref("exif_read_data#error_mcom" EXIFERR_CC, ImageInfo, E_NOTICE, "Image has corrupt COM section: some software set wrong length information");
3188
if (last_marker==M_COM && comment_correction)
3189
return M_EOI; /* ah illegal: char after COM section not 0xFF */
3191
fpos = php_stream_tell(ImageInfo->infile);
3193
if (marker == 0xff) {
3194
/* 0xff is legal padding, but if we get that many, something's wrong. */
3195
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "To many padding bytes");
3199
/* Read the length of the section. */
3200
lh = php_stream_getc(ImageInfo->infile);
3201
ll = php_stream_getc(ImageInfo->infile);
3203
itemlen = (lh << 8) | ll;
3207
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "%s, Section length: 0x%02X%02X", EXIF_ERROR_CORRUPT, lh, ll);
3209
EXIF_ERRLOG_CORRUPT(ImageInfo)
3214
sn = exif_file_sections_add(ImageInfo, marker, itemlen+1, NULL);
3215
Data = ImageInfo->file.list[sn].data;
3217
/* Store first two pre-read bytes. */
3218
Data[0] = (uchar)lh;
3219
Data[1] = (uchar)ll;
3221
got = php_stream_read(ImageInfo->infile, (char*)(Data+2), itemlen-2); /* Read the whole section. */
3222
if (got != itemlen-2) {
3223
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);
3228
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);
3231
case M_SOS: /* stop before hitting compressed data */
3232
/* If reading entire image is requested, read the rest of the data. */
3233
if (ImageInfo->read_all) {
3234
/* Determine how much file is left. */
3235
fpos = php_stream_tell(ImageInfo->infile);
3236
size = ImageInfo->FileSize - fpos;
3237
sn = exif_file_sections_add(ImageInfo, M_PSEUDO, size, NULL);
3238
Data = ImageInfo->file.list[sn].data;
3239
got = php_stream_read(ImageInfo->infile, (char*)Data, size);
3241
EXIF_ERRLOG_FILEEOF(ImageInfo)
3247
case M_EOI: /* in case it's a tables-only JPEG stream */
3248
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "No image in jpeg!");
3249
return (ImageInfo->sections_found&(~FOUND_COMPUTED)) ? TRUE : FALSE;
3251
case M_COM: /* Comment section */
3252
exif_process_COM(ImageInfo, (char *)Data, itemlen TSRMLS_CC);
3256
if (!(ImageInfo->sections_found&FOUND_IFD0)) {
3257
/*ImageInfo->sections_found |= FOUND_EXIF;*/
3258
/* Seen files from some 'U-lead' software with Vivitar scanner
3259
that uses marker 31 later in the file (no clue what for!) */
3260
exif_process_APP1(ImageInfo, (char *)Data, itemlen, fpos TSRMLS_CC);
3265
exif_process_APP12(ImageInfo, (char *)Data, itemlen TSRMLS_CC);
3282
exif_process_SOFn(Data, marker, &sof_info);
3283
ImageInfo->Width = sof_info.width;
3284
ImageInfo->Height = sof_info.height;
3285
if (sof_info.num_components == 3) {
3286
ImageInfo->IsColor = 1;
3288
ImageInfo->IsColor = 0;
3292
/* skip any other marker silently. */
3296
/* keep track of last marker */
3297
last_marker = marker;
3300
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Done");
3306
/* {{{ exif_scan_thumbnail
3307
* scan JPEG in thumbnail (memory) */
3308
static int exif_scan_thumbnail(image_info_type *ImageInfo TSRMLS_DC)
3310
uchar c, *data = (uchar*)ImageInfo->Thumbnail.data;
3312
size_t length=2, pos=0;
3313
jpeg_sof_info sof_info;
3316
return FALSE; /* nothing to do here */
3318
if (memcmp(data, "\xFF\xD8\xFF", 3)) {
3319
if (!ImageInfo->Thumbnail.width && !ImageInfo->Thumbnail.height) {
3320
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Thumbnail is not a JPEG image");
3326
if (pos>=ImageInfo->Thumbnail.size)
3329
if (pos>=ImageInfo->Thumbnail.size)
3335
while ((c = data[pos++]) == 0xFF && n--) {
3336
if (pos+3>=ImageInfo->Thumbnail.size)
3338
/* +3 = pos++ of next check when reaching marker + 2 bytes for length */
3343
length = php_jpg_get16(data+pos);
3344
if (pos+length>=ImageInfo->Thumbnail.size) {
3348
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);
3364
/* handle SOFn block */
3365
exif_process_SOFn(data+pos, marker, &sof_info);
3366
ImageInfo->Thumbnail.height = sof_info.height;
3367
ImageInfo->Thumbnail.width = sof_info.width;
3369
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Thumbnail: size: %d * %d", sof_info.width, sof_info.height);
3375
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Could not compute size of thumbnail");
3385
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Could not compute size of thumbnail");
3390
/* {{{ exif_process_IFD_in_TIFF
3391
* Parse the TIFF header; */
3392
static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index TSRMLS_DC)
3394
int i, sn, num_entries, sub_section_index = 0;
3395
unsigned char *dir_entry;
3397
size_t ifd_size, dir_size, entry_offset, next_offset, entry_length, entry_value=0, fgot;
3398
int entry_tag , entry_type;
3399
tag_table_type tag_table = exif_get_tag_table(section_index);
3401
if (ImageInfo->FileSize >= dir_offset+2) {
3402
sn = exif_file_sections_add(ImageInfo, M_PSEUDO, 2, NULL);
3404
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);
3406
php_stream_seek(ImageInfo->infile, dir_offset, SEEK_SET); /* we do not know the order of sections */
3407
php_stream_read(ImageInfo->infile, (char*)ImageInfo->file.list[sn].data, 2);
3408
num_entries = php_ifd_get16u(ImageInfo->file.list[sn].data, ImageInfo->motorola_intel);
3409
dir_size = 2/*num dir entries*/ +12/*length of entry*/*num_entries +4/* offset to next ifd (points to thumbnail or NULL)*/;
3410
if (ImageInfo->FileSize >= dir_offset+dir_size) {
3412
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);
3414
if (exif_file_sections_realloc(ImageInfo, sn, dir_size TSRMLS_CC)) {
3417
php_stream_read(ImageInfo->infile, (char*)(ImageInfo->file.list[sn].data+2), dir_size-2);
3418
/*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Dump: %s", exif_char_dump(ImageInfo->file.list[sn].data, dir_size, 0));*/
3419
next_offset = php_ifd_get32u(ImageInfo->file.list[sn].data + dir_size - 4, ImageInfo->motorola_intel);
3421
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF done, next offset x%04X", next_offset);
3423
/* now we have the directory we can look how long it should be */
3424
ifd_size = dir_size;
3425
for(i=0;i<num_entries;i++) {
3426
dir_entry = ImageInfo->file.list[sn].data+2+i*12;
3427
entry_tag = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
3428
entry_type = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
3429
if (entry_type > NUM_FORMATS) {
3430
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);
3431
/* Since this is repeated in exif_process_IFD_TAG make it a notice here */
3432
/* and make it a warning in the exif_process_IFD_TAG which is called */
3434
entry_type = TAG_FMT_BYTE;
3435
/*The next line would break the image on writeback: */
3436
/* php_ifd_set16u(dir_entry+2, entry_type, ImageInfo->motorola_intel);*/
3438
entry_length = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel) * php_tiff_bytes_per_format[entry_type];
3439
if (entry_length <= 4) {
3440
switch(entry_type) {
3441
case TAG_FMT_USHORT:
3442
entry_value = php_ifd_get16u(dir_entry+8, ImageInfo->motorola_intel);
3444
case TAG_FMT_SSHORT:
3445
entry_value = php_ifd_get16s(dir_entry+8, ImageInfo->motorola_intel);
3448
entry_value = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
3451
entry_value = php_ifd_get32s(dir_entry+8, ImageInfo->motorola_intel);
3455
case TAG_IMAGEWIDTH:
3456
case TAG_COMP_IMAGE_WIDTH:
3457
ImageInfo->Width = entry_value;
3459
case TAG_IMAGEHEIGHT:
3460
case TAG_COMP_IMAGE_HEIGHT:
3461
ImageInfo->Height = entry_value;
3463
case TAG_PHOTOMETRIC_INTERPRETATION:
3464
switch (entry_value) {
3465
case PMI_BLACK_IS_ZERO:
3466
case PMI_WHITE_IS_ZERO:
3467
case PMI_TRANSPARENCY_MASK:
3468
ImageInfo->IsColor = 0;
3471
case PMI_PALETTE_COLOR:
3475
ImageInfo->IsColor = 1;
3481
entry_offset = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
3482
/* if entry needs expading ifd cache and entry is at end of current ifd cache. */
3483
/* otherwise there may be huge holes between two entries */
3484
if (entry_offset + entry_length > dir_offset + ifd_size
3485
&& entry_offset == dir_offset + ifd_size) {
3486
ifd_size = entry_offset + entry_length - dir_offset;
3488
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);
3493
if (ImageInfo->FileSize >= dir_offset + ImageInfo->file.list[sn].size) {
3494
if (ifd_size > dir_size) {
3495
if (dir_offset + ifd_size > ImageInfo->FileSize) {
3496
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);
3499
if (exif_file_sections_realloc(ImageInfo, sn, ifd_size TSRMLS_CC)) {
3502
/* read values not stored in directory itself */
3504
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);
3506
php_stream_read(ImageInfo->infile, (char*)(ImageInfo->file.list[sn].data+dir_size), ifd_size-dir_size);
3508
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read from TIFF, done");
3511
/* now process the tags */
3512
for(i=0;i<num_entries;i++) {
3513
dir_entry = ImageInfo->file.list[sn].data+2+i*12;
3514
entry_tag = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
3515
entry_type = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
3516
/*entry_length = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel);*/
3517
if (entry_tag == TAG_EXIF_IFD_POINTER ||
3518
entry_tag == TAG_INTEROP_IFD_POINTER ||
3519
entry_tag == TAG_GPS_IFD_POINTER ||
3520
entry_tag == TAG_SUB_IFD
3523
case TAG_EXIF_IFD_POINTER:
3524
ImageInfo->sections_found |= FOUND_EXIF;
3525
sub_section_index = SECTION_EXIF;
3527
case TAG_GPS_IFD_POINTER:
3528
ImageInfo->sections_found |= FOUND_GPS;
3529
sub_section_index = SECTION_GPS;
3531
case TAG_INTEROP_IFD_POINTER:
3532
ImageInfo->sections_found |= FOUND_INTEROP;
3533
sub_section_index = SECTION_INTEROP;
3536
ImageInfo->sections_found |= FOUND_THUMBNAIL;
3537
sub_section_index = SECTION_THUMBNAIL;
3540
entry_offset = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
3542
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @x%04X", exif_get_sectionname(sub_section_index), entry_offset);
3544
exif_process_IFD_in_TIFF(ImageInfo, entry_offset, sub_section_index TSRMLS_CC);
3545
if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) {
3546
if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN
3547
&& ImageInfo->Thumbnail.size
3548
&& ImageInfo->Thumbnail.offset
3549
&& ImageInfo->read_thumbnail
3552
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);
3554
if (!ImageInfo->Thumbnail.data) {
3555
ImageInfo->Thumbnail.data = emalloc(ImageInfo->Thumbnail.size);
3556
php_stream_seek(ImageInfo->infile, ImageInfo->Thumbnail.offset, SEEK_SET);
3557
fgot = php_stream_read(ImageInfo->infile, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
3558
if (fgot < ImageInfo->Thumbnail.size) {
3559
EXIF_ERRLOG_THUMBEOF(ImageInfo)
3561
exif_thumbnail_build(ImageInfo TSRMLS_CC);
3566
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s done", exif_get_sectionname(sub_section_index));
3569
if (!exif_process_IFD_TAG(ImageInfo, (char*)dir_entry,
3570
(char*)(ImageInfo->file.list[sn].data-dir_offset),
3571
ifd_size, 0, section_index, 0, tag_table TSRMLS_CC)) {
3576
/* If we had a thumbnail in a SUB_IFD we have ANOTHER image in NEXT IFD */
3577
if (next_offset && section_index != SECTION_THUMBNAIL) {
3578
/* this should be a thumbnail IFD */
3579
/* the thumbnail itself is stored at Tag=StripOffsets */
3581
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) at x%04X", next_offset);
3583
exif_process_IFD_in_TIFF(ImageInfo, next_offset, SECTION_THUMBNAIL TSRMLS_CC);
3585
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);
3587
if (!ImageInfo->Thumbnail.data && ImageInfo->Thumbnail.offset && ImageInfo->Thumbnail.size && ImageInfo->read_thumbnail) {
3588
ImageInfo->Thumbnail.data = emalloc(ImageInfo->Thumbnail.size);
3589
php_stream_seek(ImageInfo->infile, ImageInfo->Thumbnail.offset, SEEK_SET);
3590
fgot = php_stream_read(ImageInfo->infile, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
3591
if (fgot < ImageInfo->Thumbnail.size) {
3592
EXIF_ERRLOG_THUMBEOF(ImageInfo)
3594
exif_thumbnail_build(ImageInfo TSRMLS_CC);
3597
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) done");
3602
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);
3606
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);
3610
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);
3616
/* {{{ exif_scan_FILE_header
3617
* Parse the marker stream until SOS or EOI is seen; */
3618
static int exif_scan_FILE_header(image_info_type *ImageInfo TSRMLS_DC)
3620
unsigned char file_header[8];
3623
ImageInfo->FileType = IMAGE_FILETYPE_UNKNOWN;
3625
if (ImageInfo->FileSize >= 2) {
3626
php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
3627
php_stream_read(ImageInfo->infile, (char*)file_header, 2);
3628
if ((file_header[0]==0xff) && (file_header[1]==M_SOI)) {
3629
ImageInfo->FileType = IMAGE_FILETYPE_JPEG;
3630
if (exif_scan_JPEG_header(ImageInfo TSRMLS_CC)) {
3633
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid JPEG file");
3635
} else if (ImageInfo->FileSize >= 8) {
3636
php_stream_read(ImageInfo->infile, (char*)(file_header+2), 6);
3637
if (!memcmp(file_header, "II\x2A\x00", 4)) {
3638
ImageInfo->FileType = IMAGE_FILETYPE_TIFF_II;
3639
ImageInfo->motorola_intel = 0;
3641
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/II format");
3643
ImageInfo->sections_found |= FOUND_IFD0;
3644
if (exif_process_IFD_in_TIFF(ImageInfo,
3645
php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
3646
SECTION_IFD0 TSRMLS_CC)) {
3649
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
3653
if (!memcmp(file_header, "MM\x00\x2a", 4)) {
3654
ImageInfo->FileType = IMAGE_FILETYPE_TIFF_MM;
3655
ImageInfo->motorola_intel = 1;
3657
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/MM format");
3659
ImageInfo->sections_found |= FOUND_IFD0;
3660
if (exif_process_IFD_in_TIFF(ImageInfo,
3661
php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
3662
SECTION_IFD0 TSRMLS_CC)) {
3665
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
3668
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File not supported");
3673
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File to small (%d)", ImageInfo->FileSize);
3679
/* {{{ exif_discard_imageinfo
3680
Discard data scanned by exif_read_file.
3682
static int exif_discard_imageinfo(image_info_type *ImageInfo)
3686
EFREE_IF(ImageInfo->FileName);
3687
EFREE_IF(ImageInfo->UserComment);
3688
EFREE_IF(ImageInfo->UserCommentEncoding);
3689
EFREE_IF(ImageInfo->Copyright);
3690
EFREE_IF(ImageInfo->CopyrightPhotographer);
3691
EFREE_IF(ImageInfo->CopyrightEditor);
3692
EFREE_IF(ImageInfo->Thumbnail.data);
3693
EFREE_IF(ImageInfo->encode_unicode);
3694
EFREE_IF(ImageInfo->decode_unicode_be);
3695
EFREE_IF(ImageInfo->decode_unicode_le);
3696
EFREE_IF(ImageInfo->encode_jis);
3697
EFREE_IF(ImageInfo->decode_jis_be);
3698
EFREE_IF(ImageInfo->decode_jis_le);
3699
EFREE_IF(ImageInfo->make);
3700
EFREE_IF(ImageInfo->model);
3701
for (i=0; i<ImageInfo->xp_fields.count; i++) {
3702
EFREE_IF(ImageInfo->xp_fields.list[i].value);
3704
EFREE_IF(ImageInfo->xp_fields.list);
3705
for (i=0; i<SECTION_COUNT; i++) {
3706
exif_iif_free(ImageInfo, i);
3708
exif_file_sections_free(ImageInfo);
3709
memset(ImageInfo, 0, sizeof(*ImageInfo));
3714
/* {{{ exif_read_file
3716
static int exif_read_file(image_info_type *ImageInfo, char *FileName, int read_thumbnail, int read_all TSRMLS_DC)
3721
/* Start with an empty image information structure. */
3722
memset(ImageInfo, 0, sizeof(*ImageInfo));
3724
ImageInfo->motorola_intel = -1; /* flag as unknown */
3726
ImageInfo->infile = php_stream_open_wrapper(FileName, "rb", STREAM_MUST_SEEK|IGNORE_PATH|ENFORCE_SAFE_MODE, NULL);
3727
if (!ImageInfo->infile) {
3728
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Unable to open file");
3732
php_basename(FileName, strlen(FileName), NULL, 0, &(ImageInfo->FileName), NULL TSRMLS_CC);
3733
ImageInfo->read_thumbnail = read_thumbnail;
3734
ImageInfo->read_all = read_all;
3735
ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_UNKNOWN;
3737
ImageInfo->encode_unicode = safe_estrdup(EXIF_G(encode_unicode));
3738
ImageInfo->decode_unicode_be = safe_estrdup(EXIF_G(decode_unicode_be));
3739
ImageInfo->decode_unicode_le = safe_estrdup(EXIF_G(decode_unicode_le));
3740
ImageInfo->encode_jis = safe_estrdup(EXIF_G(encode_jis));
3741
ImageInfo->decode_jis_be = safe_estrdup(EXIF_G(decode_jis_be));
3742
ImageInfo->decode_jis_le = safe_estrdup(EXIF_G(decode_jis_le));
3744
if (php_stream_is(ImageInfo->infile, PHP_STREAM_IS_STDIO)) {
3745
if (VCWD_STAT(FileName, &st) >= 0) {
3746
/* Store file date/time. */
3748
ImageInfo->FileDateTime = st.st_mtime.tv_sec;
3750
ImageInfo->FileDateTime = st.st_mtime;
3752
ImageInfo->FileSize = st.st_size;
3753
/*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Opened stream is file: %d", ImageInfo->FileSize);*/
3756
if (!ImageInfo->FileSize) {
3757
php_stream_seek(ImageInfo->infile, 0, SEEK_END);
3758
ImageInfo->FileSize = php_stream_tell(ImageInfo->infile);
3759
php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
3763
ImageInfo->ifd_nesting_level = 0;
3765
/* Scan the JPEG headers. */
3766
ret = exif_scan_FILE_header(ImageInfo TSRMLS_CC);
3768
php_stream_close(ImageInfo->infile);
3773
/* {{{ proto array exif_read_data(string filename [, sections_needed [, sub_arrays[, read_thumbnail]]])
3774
Reads header data from the JPEG/TIFF image filename and optionally reads the internal thumbnails */
3775
PHP_FUNCTION(exif_read_data)
3777
pval **p_name, **p_sections_needed, **p_sub_arrays, **p_read_thumbnail, **p_read_all;
3778
int i, ac = ZEND_NUM_ARGS(), ret, sections_needed=0, sub_arrays=0, read_thumbnail=0, read_all=0;
3779
image_info_type ImageInfo;
3780
char tmp[64], *sections_str, *s;
3782
memset(&ImageInfo, 0, sizeof(ImageInfo));
3784
if ((ac < 1 || ac > 4) || zend_get_parameters_ex(ac, &p_name, &p_sections_needed, &p_sub_arrays, &p_read_thumbnail, &p_read_all) == FAILURE) {
3788
convert_to_string_ex(p_name);
3791
convert_to_string_ex(p_sections_needed);
3792
sections_str = safe_emalloc(strlen(Z_STRVAL_PP(p_sections_needed)), 1, 3);
3793
sprintf(sections_str, ",%s,", Z_STRVAL_PP(p_sections_needed));
3794
/* sections_str DOES start with , and SPACES are NOT allowed in names */
3801
for (i=0; i<SECTION_COUNT; i++) {
3802
snprintf(tmp, sizeof(tmp), ",%s,", exif_get_sectionname(i));
3803
if (strstr(sections_str, tmp)) {
3804
sections_needed |= 1<<i;
3807
EFREE_IF(sections_str);
3808
/* now see what we need */
3810
sections_str = exif_get_sectionlist(sections_needed TSRMLS_CC);
3811
if (!sections_str) {
3814
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Sections needed: %s", sections_str[0] ? sections_str : "None");
3815
EFREE_IF(sections_str);
3819
convert_to_long_ex(p_sub_arrays);
3820
sub_arrays = Z_LVAL_PP(p_sub_arrays);
3823
convert_to_long_ex(p_read_thumbnail);
3824
read_thumbnail = Z_LVAL_PP(p_read_thumbnail);
3827
convert_to_long_ex(p_read_all);
3828
read_all = Z_LVAL_PP(p_read_all);
3830
/* parameters 3,4 will be working in later versions.... */
3831
read_all = 0; /* just to make function work for 4.2 tree */
3833
ret = exif_read_file(&ImageInfo, Z_STRVAL_PP(p_name), read_thumbnail, read_all TSRMLS_CC);
3835
sections_str = exif_get_sectionlist(ImageInfo.sections_found TSRMLS_CC);
3839
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Sections found: %s", sections_str[0] ? sections_str : "None");
3842
ImageInfo.sections_found |= FOUND_COMPUTED|FOUND_FILE;/* do not inform about in debug*/
3844
if (ret==FALSE || (sections_needed && !(sections_needed&ImageInfo.sections_found))) {
3845
/* array_init must be checked at last! otherwise the array must be freed if a later test fails. */
3846
exif_discard_imageinfo(&ImageInfo);
3847
EFREE_IF(sections_str);
3851
array_init(return_value);
3854
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Generate section FILE");
3857
/* now we can add our information */
3858
exif_iif_add_str(&ImageInfo, SECTION_FILE, "FileName", ImageInfo.FileName TSRMLS_CC);
3859
exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileDateTime", ImageInfo.FileDateTime TSRMLS_CC);
3860
exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileSize", ImageInfo.FileSize TSRMLS_CC);
3861
exif_iif_add_int(&ImageInfo, SECTION_FILE, "FileType", ImageInfo.FileType TSRMLS_CC);
3862
exif_iif_add_str(&ImageInfo, SECTION_FILE, "MimeType", (char*)php_image_type_to_mime_type(ImageInfo.FileType) TSRMLS_CC);
3863
exif_iif_add_str(&ImageInfo, SECTION_FILE, "SectionsFound", sections_str ? sections_str : "NONE" TSRMLS_CC);
3866
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Generate section COMPUTED");
3869
if (ImageInfo.Width>0 && ImageInfo.Height>0) {
3870
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "html" TSRMLS_CC, "width=\"%d\" height=\"%d\"", ImageInfo.Width, ImageInfo.Height);
3871
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Height", ImageInfo.Height TSRMLS_CC);
3872
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Width", ImageInfo.Width TSRMLS_CC);
3874
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "IsColor", ImageInfo.IsColor TSRMLS_CC);
3875
if (ImageInfo.motorola_intel != -1) {
3876
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "ByteOrderMotorola", ImageInfo.motorola_intel TSRMLS_CC);
3878
if (ImageInfo.FocalLength) {
3879
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "FocalLength" TSRMLS_CC, "%4.1fmm", ImageInfo.FocalLength);
3880
if(ImageInfo.CCDWidth) {
3881
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "35mmFocalLength" TSRMLS_CC, "%dmm", (int)(ImageInfo.FocalLength/ImageInfo.CCDWidth*35+0.5));
3884
if(ImageInfo.CCDWidth) {
3885
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "CCDWidth" TSRMLS_CC, "%dmm", (int)ImageInfo.CCDWidth);
3887
if(ImageInfo.ExposureTime>0) {
3888
if(ImageInfo.ExposureTime <= 0.5) {
3889
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ExposureTime" TSRMLS_CC, "%0.3f s (1/%d)", ImageInfo.ExposureTime, (int)(0.5 + 1/ImageInfo.ExposureTime));
3891
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ExposureTime" TSRMLS_CC, "%0.3f s", ImageInfo.ExposureTime);
3894
if(ImageInfo.ApertureFNumber) {
3895
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "ApertureFNumber" TSRMLS_CC, "f/%.1f", ImageInfo.ApertureFNumber);
3897
if(ImageInfo.Distance) {
3898
if(ImageInfo.Distance<0) {
3899
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "FocusDistance", "Infinite" TSRMLS_CC);
3901
exif_iif_add_fmt(&ImageInfo, SECTION_COMPUTED, "FocusDistance" TSRMLS_CC, "%0.2fm", ImageInfo.Distance);
3904
if (ImageInfo.UserComment) {
3905
exif_iif_add_buffer(&ImageInfo, SECTION_COMPUTED, "UserComment", ImageInfo.UserCommentLength, ImageInfo.UserComment TSRMLS_CC);
3906
if (ImageInfo.UserCommentEncoding && strlen(ImageInfo.UserCommentEncoding)) {
3907
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "UserCommentEncoding", ImageInfo.UserCommentEncoding TSRMLS_CC);
3911
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright", ImageInfo.Copyright TSRMLS_CC);
3912
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright.Photographer", ImageInfo.CopyrightPhotographer TSRMLS_CC);
3913
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Copyright.Editor", ImageInfo.CopyrightEditor TSRMLS_CC);
3915
for (i=0; i<ImageInfo.xp_fields.count; i++) {
3916
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);
3918
if (ImageInfo.Thumbnail.size) {
3919
if (read_thumbnail) {
3920
/* not exif_iif_add_str : this is a buffer */
3921
exif_iif_add_tag(&ImageInfo, SECTION_THUMBNAIL, "THUMBNAIL", TAG_NONE, TAG_FMT_UNDEFINED, ImageInfo.Thumbnail.size, ImageInfo.Thumbnail.data TSRMLS_CC);
3923
if (!ImageInfo.Thumbnail.width || !ImageInfo.Thumbnail.height) {
3924
/* try to evaluate if thumbnail data is present */
3925
exif_scan_thumbnail(&ImageInfo TSRMLS_CC);
3927
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.FileType", ImageInfo.Thumbnail.filetype TSRMLS_CC);
3928
exif_iif_add_str(&ImageInfo, SECTION_COMPUTED, "Thumbnail.MimeType", (char*)php_image_type_to_mime_type(ImageInfo.Thumbnail.filetype) TSRMLS_CC);
3930
if (ImageInfo.Thumbnail.width && ImageInfo.Thumbnail.height) {
3931
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.Height", ImageInfo.Thumbnail.height TSRMLS_CC);
3932
exif_iif_add_int(&ImageInfo, SECTION_COMPUTED, "Thumbnail.Width", ImageInfo.Thumbnail.width TSRMLS_CC);
3934
EFREE_IF(sections_str);
3937
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Adding image infos");
3940
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_FILE TSRMLS_CC);
3941
add_assoc_image_info(return_value, 1, &ImageInfo, SECTION_COMPUTED TSRMLS_CC);
3942
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_ANY_TAG TSRMLS_CC);
3943
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_IFD0 TSRMLS_CC);
3944
add_assoc_image_info(return_value, 1, &ImageInfo, SECTION_THUMBNAIL TSRMLS_CC);
3945
add_assoc_image_info(return_value, 1, &ImageInfo, SECTION_COMMENT TSRMLS_CC);
3946
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_EXIF TSRMLS_CC);
3947
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_GPS TSRMLS_CC);
3948
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_INTEROP TSRMLS_CC);
3949
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_FPIX TSRMLS_CC);
3950
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_APP12 TSRMLS_CC);
3951
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_WINXP TSRMLS_CC);
3952
add_assoc_image_info(return_value, sub_arrays, &ImageInfo, SECTION_MAKERNOTE TSRMLS_CC);
3955
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Discarding info");
3958
exif_discard_imageinfo(&ImageInfo);
3961
php_error_docref1(NULL TSRMLS_CC, Z_STRVAL_PP(p_name), E_NOTICE, "done");
3966
/* {{{ proto string exif_thumbnail(string filename [, &width, &height [, &imagetype]])
3967
Reads the embedded thumbnail */
3968
PHP_FUNCTION(exif_thumbnail)
3970
zval *p_width, *p_height, *p_imagetype;
3972
int p_name_len, ret, arg_c = ZEND_NUM_ARGS();
3973
image_info_type ImageInfo;
3975
memset(&ImageInfo, 0, sizeof(ImageInfo));
3977
if (arg_c!=1 && arg_c!=3 && arg_c!=4) {
3981
if (zend_parse_parameters(arg_c TSRMLS_CC, "s|z/z/z/", &p_name, &p_name_len, &p_width, &p_height, &p_imagetype) == FAILURE) {
3985
ret = exif_read_file(&ImageInfo, p_name, 1, 0 TSRMLS_CC);
3991
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);
3993
if (!ImageInfo.Thumbnail.data || !ImageInfo.Thumbnail.size) {
3994
exif_discard_imageinfo(&ImageInfo);
3999
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Returning thumbnail(%d)", ImageInfo.Thumbnail.size);
4002
ZVAL_STRINGL(return_value, ImageInfo.Thumbnail.data, ImageInfo.Thumbnail.size, 1);
4004
if (!ImageInfo.Thumbnail.width || !ImageInfo.Thumbnail.height) {
4005
exif_scan_thumbnail(&ImageInfo TSRMLS_CC);
4008
zval_dtor(p_height);
4009
ZVAL_LONG(p_width, ImageInfo.Thumbnail.width);
4010
ZVAL_LONG(p_height, ImageInfo.Thumbnail.height);
4013
zval_dtor(p_imagetype);
4014
ZVAL_LONG(p_imagetype, ImageInfo.Thumbnail.filetype);
4018
exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Discarding info");
4021
exif_discard_imageinfo(&ImageInfo);
4024
php_error_docref1(NULL TSRMLS_CC, p_name, E_NOTICE, "Done");
4029
/* {{{ proto int exif_imagetype(string imagefile)
4030
Get the type of an image */
4031
PHP_FUNCTION(exif_imagetype)
4034
php_stream * stream;
4037
if (ZEND_NUM_ARGS() != 1)
4040
if (zend_get_parameters_ex(1, &arg1) == FAILURE)
4043
convert_to_string_ex(arg1);
4044
stream = php_stream_open_wrapper(Z_STRVAL_PP(arg1), "rb", IGNORE_PATH|ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL);
4046
if (stream == NULL) {
4050
itype = php_getimagetype(stream, NULL TSRMLS_CC);
4052
php_stream_close(stream);
4054
if (itype == IMAGE_FILETYPE_UNKNOWN) {
4057
ZVAL_LONG(return_value, itype);
4069
* vim600: sw=4 ts=4 tw=78 fdm=marker
4070
* vim<600: sw=4 ts=4 tw=78