From: Marcus Boerger Date: Fri, 1 Mar 2002 04:01:26 +0000 (+0000) Subject: +Support for Photographer/Editor Copyright as associative array as this is a new... X-Git-Tag: php-4.2.0RC1~232 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0857bdbcde229efa1044caa07dcdba8100256a0d;p=php +Support for Photographer/Editor Copyright as associative array as this is a new feature the change (optionally being an array) has to be mentioned in documentation. +New function exif_headername can be used to read the internal Tag namelist (was mainly created for debugging purpose but maybe somone writes code to create/update exif headers here). +An internal version number is present. +A testpage is supplied test.txt describes how the test works. +The oldfunction read_exif_data has got an alias exif_read_data As the old version of this module is very buggy i decided to implement the testpage (test.txt) and to create the alias. The test script only works with the alias as the old version does not pass tests. By the way it seems a good way to prepend 'exif_' to all functions in the module. --- diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 5db3728add..e453b9f328 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -20,41 +20,51 @@ /* $Id$ */ /* Much of the code in this module was borrowed from the public domain - jhead.c package with the author's consent. The main changes have been - to eliminate all the global variables to make it thread safe and to wrap - it in the PHP 4 API. - - Aug.3 2001 - Added support for multiple M_COM entries - Rasmus - - Additional changes and fixes mainly to expand functionality for image - galleries. - - Feb.26 2002 - Marcus - - Added UNICODE support for Comments - Added Description,Artist - Added missing memory deallocation - Corrected error with multiple comments - Corrected handling of ExifVersion, Tag has 4 ASCII - characters *WITHOUT* NUL - Corrected handling of Thumbnailsize if current source - detects size < 0 - Changed all fields to char* that do not have a maximum - length in EXIF standard - Undocumented second Parameter ReadAll frees memory to - early -> moved to third position default changed to - false -> faster - New second Parameter [true|false] to specify whether or - not to to read thumbnails -> reading is timeconsumpting - suppose default should be false -> done so - - ToDos - Copyright tag stores " '\0' '\0'" but we - stop at first '\0' - JIS encoding for comments - See if example images from http://www.exif.org have illegal - thumbnail sizes or if code is corrupt. - - The original header from the jhead.c file was: + jhead.c package with the author's consent. The main changes have been + to eliminate all the global variables to make it thread safe and to wrap + it in the PHP 4 API. + + Aug.3 2001 - Added support for multiple M_COM entries - Rasmus + + Feb.26 2002 - Marcus + + Additional changes and fixes mainly to expand functionality for image + galleries. + + Added UNICODE support for Comments + Added Description,Artist + Added missing memory deallocation + Corrected error with multiple comments + Corrected handling of ExifVersion, Tag has 4 ASCII + characters *WITHOUT* NUL + Corrected handling of Thumbnailsize if current source + detects size < 0 + Changed all fields to char* that do not have a maximum + length in EXIF standard + Undocumented second Parameter ReadAll frees memory to + early -> moved to third position default changed to + false -> faster + New second Parameter [true|false] to specify whether or + not to to read thumbnails -> reading is timeconsumpting + suppose default should be false -> done so + + Feb.28 2002 - Marcus + + Support for Photographer/Editor Copyright as associative + array as this is a new feature the change (optionally + being an array) has to be mentioned in documentation. + New function exif_headername can be used to read the internal Tag + namelist (was mainly created for debugging purpose but maybe + somone writes code to create/update exif headers here). + A testpage is supplied test.txt describes how the test works. + + ToDos - JIS encoding for comments + See if example images from http://www.exif.org have illegal + thumbnail sizes or if code is corrupt. + Create/Update exif headers. + Create/Remove/Update image thumbnails. + + The original header from the jhead.c file was: -------------------------------------------------------------------------- Program to pull the information out of various types of EFIF digital @@ -69,7 +79,7 @@ Matthias Wandel, Dec 1999 - April 2000 -------------------------------------------------------------------------- - */ +*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -93,18 +103,21 @@ typedef unsigned char uchar; #define EXIF_MAX_COMMENTS 12 +/* EXIF standard defines Copyright as " [ '\0' ] ['\0']" */ +#define EXIF_MAX_COPYRIGHT 2 + /* {{{ structs This structure stores Exif header image elements in a simple manner Used to store camera data as extracted from the various ways that it can be stored in a nexif header */ typedef struct { - char FileName [120]; + char FileName[120]; time_t FileDateTime; unsigned FileSize; char *CameraMake; char *CameraModel; - char DateTime [20]; + char DateTime[20]; int Height, Width; int IsColor; int FlashUsed; @@ -124,7 +137,10 @@ typedef struct { char GPSinfo[48]; int ISOspeed; char ExifVersion[8]; - char *Copyright; /* maybe two strings see ToDos */ + char *RawCopyright; + int lenRawCopyright; + char *Copyright[EXIF_MAX_COPYRIGHT]; + int numCopyrights; char *Artist; char *Software; char *Thumbnail; @@ -156,10 +172,14 @@ typedef struct { */ function_entry exif_functions[] = { PHP_FE(read_exif_data, NULL) + PHP_FALIAS(exif_read_data,read_exif_data, NULL) + PHP_FE(exif_headername, NULL) {NULL, NULL, NULL} }; /* }}} */ +#define EXIF_VERSION "2.0a $Revision$" + PHP_MINFO_FUNCTION(exif); /* {{{ exif_module_entry @@ -171,7 +191,7 @@ zend_module_entry exif_module_entry = { NULL, NULL, NULL, NULL, PHP_MINFO(exif), - NO_VERSION_YET, + EXIF_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -186,6 +206,8 @@ PHP_MINFO_FUNCTION(exif) { php_info_print_table_start(); php_info_print_table_row(2, "EXIF Support", "enabled" ); + php_info_print_table_row(2, "EXIF Version", EXIF_VERSION ); + php_info_print_table_row(2, "Supported EXIF Version", "02100"); php_info_print_table_end(); } /* }}} */ @@ -264,7 +286,7 @@ static void process_COM (ImageInfoType *ImageInfo, uchar *Data, int length) a = ImageInfo->numComments; (ImageInfo->Comments)[a] = emalloc(nch+1); - strcpy(ImageInfo->Comments[a], Comment); + strcpy(ImageInfo->Comments[a], Comment); (ImageInfo->numComments)++; } /* }}} */ @@ -468,10 +490,44 @@ static const struct { { 0xA217, "SensingMethod"}, /* 0x9217 - - */ { 0xA300, "FileSource"}, { 0xA301, "SceneType"}, - { 0, NULL} + { 0, NULL} /* Important for get_exif_headername() */ } ; /* }}} */ +/* {{{ get_exif_headername + Get headername for tag_num or NULL if not defined */ +char * get_exif_headername(int tag_num) +{ + int i,t; + + for (i=0;;i++) { + if ( (t=TagTable[i].Tag) == tag_num || !t) return TagTable[i].Desc; + } + return NULL; +} +/* }}} */ + +/* {{{ proto string|false exif_headername(index) + Get headername for index or false if not defined */ +PHP_FUNCTION(exif_headername) +{ + pval **p_num; + int ac = ZEND_NUM_ARGS(); + char *szTemp; + + if ((ac < 1 || ac > 1) || zend_get_parameters_ex(ac, &p_num) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_long_ex(p_num); + if ( (szTemp = get_exif_headername(Z_LVAL_PP(p_num))) == NULL) { + RETURN_BOOL(FALSE); + } else { + RETURN_STRING(szTemp, 1) + } +} +/* }}} */ + /* {{{ Get16u * Convert a 16 bit unsigned value from file's native byte order */ static int Get16u(void *Short, int MotorolaOrder) @@ -638,17 +694,38 @@ static int ProcessExifComment(char **pszInfoPtr,char *szValuePtr,int ByteCount) } /* }}} */ +/* {{{ CopyExifStringRaw + * Copy a string in Exif header to a character string returns length of allocated buffer if any. */ +static int CopyExifStringRaw(char **pszInfoPtr,char *szValuePtr,int ByteCount) { + /* we cannot use strlcpy - here the problem is that we have to copy NUL + * chars up to ByteCount, we also have to add a single NUL character to + * force end of string. + */ + if (ByteCount) { + (*pszInfoPtr) = emalloc(ByteCount+1); + memcpy(*pszInfoPtr, szValuePtr, ByteCount); + (*pszInfoPtr)[ByteCount] = '\0'; + return ByteCount+1; + } + return 0; +} +/* }}} */ + /* {{{ CopyExifString - * Copy a string in Exif header to a character string. */ + * Copy a string in Exif header to a character string returns length of allocated buffer if any. */ static int CopyExifString(char **pszInfoPtr,char *szValuePtr,int ByteCount) { int l; - l = strlen(szValuePtr)+1; - if (ByteCount1) { - (*pszInfoPtr) = emalloc(l); - strlcpy(*pszInfoPtr, szValuePtr, l); - return 1; + /* we cannot use strlcpy - here the problem is that we cannot use strlen to + * determin length of string and we cannot use strlcpy with len=ByteCount+1 + * because then we might get into an EXCEPTION if we exceed an aallocate + * memory page... + */ + if (ByteCount && CopyExifStringRaw(pszInfoPtr,szValuePtr,ByteCount)) { + if ( (l = strlen(*pszInfoPtr)) < ByteCount) { + (*pszInfoPtr)[l] = '\0'; + } + return l+1; } return 0; } @@ -659,7 +736,7 @@ static int CopyExifString(char **pszInfoPtr,char *szValuePtr,int ByteCount) { static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *OffsetBase, unsigned ExifLength, char *LastExifRefd, int ReadThumbnail) { int de; - int a; + int a,l; int NumDirEntries; int NextDirOffset; @@ -723,6 +800,7 @@ static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *Offse } /* Extract useful components of tag */ + /* php_error(E_NOTICE,"Scan tag: %s", get_exif_headername(Tag)); */ switch(Tag) { case TAG_MAKE: @@ -743,7 +821,16 @@ static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *Offse break; case TAG_COPYRIGHT: - CopyExifString(&ImageInfo->Copyright,ValuePtr,ByteCount); + if (CopyExifStringRaw(&ImageInfo->RawCopyright,ValuePtr,ByteCount)) { + l = strlen(ImageInfo->RawCopyright); + } else { + l = 0; + } + ImageInfo->lenRawCopyright = ByteCount; + if ( ImageInfo->numCopyrights==0 && ByteCount>1 && lCopyright)[ImageInfo->numCopyrights++]), ValuePtr, l); + CopyExifString(&((ImageInfo->Copyright)[ImageInfo->numCopyrights++]), ValuePtr+l+1, ByteCount-l-1); + } break; case TAG_ARTIST: @@ -1155,7 +1242,7 @@ int ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections, } /* CurrentFile = FileName; */ -/* php_error(E_NOTICE,"EXIF: Process %s%s: %s", ReadThumbnail?"thumbs ":"", ReadAll?"All ":"", FileName); */ +/* php_error(E_WARNING,"EXIF: Process %s%s: %s", ReadThumbnail?"thumbs ":"", ReadAll?"All ":"", FileName); */ /* Start with an empty image information structure. */ memset(ImageInfo, 0, sizeof(*ImageInfo)); memset(Sections, 0, sizeof(*Sections)); @@ -1239,7 +1326,7 @@ int php_read_jpeg_exif(ImageInfoType *ImageInfo, char *FileName, int ReadThumbna Reads the EXIF header data from the JPEG identified by filename and optionally reads the internal thumbnails */ PHP_FUNCTION(read_exif_data) { - pval **p_name, **p_readthumbnail, **p_readall, *tmpi; + pval **p_name, **p_readthumbnail, **p_readall; int i, ac = ZEND_NUM_ARGS(), ret, readthumbnail=0, readall=0; ImageInfoType ImageInfo; char tmp[64]; @@ -1335,11 +1422,28 @@ PHP_FUNCTION(read_exif_data) if (ImageInfo.ExifVersion[0]) { add_assoc_string(return_value, "ExifVersion", ImageInfo.ExifVersion, 1); } - if (ImageInfo.Copyright) { - if (ImageInfo.Copyright[0]) { - add_assoc_string(return_value, "Copyright", ImageInfo.Copyright, 1); + if (ImageInfo.RawCopyright || ImageInfo.numCopyrights) { + if (ImageInfo.RawCopyright && !ImageInfo.numCopyrights && ImageInfo.lenRawCopyright) { + add_assoc_stringl(return_value, "Copyright", ImageInfo.RawCopyright, ImageInfo.lenRawCopyright, 1); + }else{ + pval *tmpi; + + MAKE_STD_ZVAL(tmpi); + array_init(tmpi); + /*for(i=0; ivalue.ht, "Comments", 9, &tmpi, sizeof(zval *), NULL); + add_assoc_zval(return_value, "Comments", tmpi); + /* zend_hash_add(return_value->value.ht, "Comments", 9, &tmpi, sizeof(zval *), NULL); */ } for(i=0; i $found) { + _search_file($root,$possible,$path.'/'.$found); + } + } +} + +/****************************************************************************/ +// function: search_file($file,$ext) +// +// Searches for $file in document tree. The path is ignored. +// +function search_file() { + global $argv; + $possible = array(); + + if ( array_key_exists('SCRIPT_FILENAME',$_SERVER)) { + $path = $_SERVER['SCRIPT_FILENAME']; + } else { + $path = $argv[0]; + } + if ( ($p=strpos($path,'?')) !== false) $path = substr($path,0,$p); + if ( ($p=strrpos($path,'/')) < strlen($path)-1) $path = substr($path,0,$p); + _search_file($path,$possible); + return $possible; +} + +/****************************************************************************/ +// function: search_file($file,$ext) +// +// Searches for $file in document tree. The path is ignored. +// +function AddInfo($Name,$Value) { + $Value = nl2br($Value); + return "$Name$Value \n"; +} + +$possible = search_file(); + +?> + + +PHP moduls exif test page + + + +

Test script for PHP module ext/exif

+

+(c) Marcus Boerger, 2002 +

+

+Images taken from www.exif.org, +marcus-boerger.de +all rights reserved by their authors and artists, see exif headers. +The files can be downloaded here. +To start the test you simple have to put all images into the same directory as this script. +The test will work with all files in that directory and all subdirectories. To test private +images just put them into that directory. +

+

+Youmay take a look at the test source here. +

+

+This test just prooves that some exif headers can be scanned. +If all files produce a header in output the module might be o.k. +

+

+What to look for in detail: +

+
    +
  • kodak-dc4800-plus-acdsee.jpg +
      +
    • should provide a long comment 'by marcus börger<%04i>'*n
    • +
    +
  • +
  • hp-photosmart +
      +
    • should provide a ***two line*** copyright notice
    • +
    +
  • +
+

function exif_headername

+ + + + + + +\n"; +} +?> +
ImageWidth
JPEGProc
SceneType
false
function exif_headername is not supported
+
+

function read_exif_data for images

+ + $file) { + $num++; + $res = ''; + $len = 2; + $image = @read_exif_data($file,false); + $error = error_msg(); + //error_log("read_exif_data($file)",0); + foreach($image as $Name => $Value) { + if ( $Name!='Thumbnail') { + if ( is_array($Value)) { + $len++; + $res .= AddInfo($Name,'Array('.count($Value).')'); + foreach( $Value as $idx => $Entry) { + $len++; + $res .= AddInfo($Name.':'.$idx,$Entry); + } + } else { + $len++; + $res .= AddInfo($Name,$Value); + } + } + } + echo "\n$res"; + } +} else { + echo "\n"; +} +?> +
$num
$file$error
function read_exif_data is not supported
+ + \ No newline at end of file