cvs: php4 /ext/exif .cvsignore exif.c php_exif.h test.txt

From: Date: Fri, 01 Mar 2002 04:01:26 +0000
Subject: cvs: php4 /ext/exif .cvsignore exif.c php_exif.h test.txt
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-9523@lists.php.net to get a copy of this message
helly Thu Feb 28 23:01:26 2002 EDT Added files: /php4/ext/exif test.txt Modified files: /php4/ext/exif .cvsignore exif.c php_exif.h Log: +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.

Index: php4/ext/exif/.cvsignore diff -u php4/ext/exif/.cvsignore:1.3 php4/ext/exif/.cvsignore:1.4 --- php4/ext/exif/.cvsignore:1.3 Wed Jun 6 08:43:24 2001 +++ php4/ext/exif/.cvsignore Thu Feb 28 23:01:25 2002 @@ -1,5 +1,6 @@ .deps Makefile +*.o *.lo *.la .libs Index: php4/ext/exif/exif.c diff -u php4/ext/exif/exif.c:1.39 php4/ext/exif/exif.c:1.40 --- php4/ext/exif/exif.c:1.39 Thu Feb 28 07:20:17 2002 +++ php4/ext/exif/exif.c Thu Feb 28 23:01:25 2002 @@ -17,44 +17,54 @@ +----------------------------------------------------------------------+ */ -/* $Id: exif.c,v 1.39 2002/02/28 12:20:17 helly Exp $ */ +/* $Id: exif.c,v 1.40 2002/03/01 04:01:25 helly Exp $ */ /* 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 "<Photographer> '\0' <Editor> '\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. + 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: + 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 @@ #define EXIF_MAX_COMMENTS 12 +/* EXIF standard defines Copyright as "<Photographer> [ '\0' <Editor> ] ['\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 @@ 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 @@ */ 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: 1.40 $" + PHP_MINFO_FUNCTION(exif); /* {{{ exif_module_entry @@ -171,7 +191,7 @@ NULL, NULL, NULL, NULL, PHP_MINFO(exif), - NO_VERSION_YET, + EXIF_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -186,6 +206,8 @@ { 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 @@ 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 @@ { 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 @@ } /* }}} */ +/* {{{ 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 (ByteCount<l) l = ByteCount; - if (l>1) { - (*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 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 @@ } /* 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 @@ 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 && l<ByteCount-1) { + CopyExifString(&((ImageInfo->Copyright)[ImageInfo->numCopyrights++]), ValuePtr, l); + CopyExifString(&((ImageInfo->Copyright)[ImageInfo->numCopyrights++]), ValuePtr+l+1, ByteCount-l-1); + } break; case TAG_ARTIST: @@ -1155,7 +1242,7 @@ } /* 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 @@ 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 @@ 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; i<ImageInfo.numCopyrights; i++) { + * add_index_string(tmpi, i, (ImageInfo.Copyright)[i], 0); + *} + */ + add_assoc_string(tmpi, "Photographer", ImageInfo.Copyright[0], 1); + add_assoc_string(tmpi, "Editor", ImageInfo.Copyright[1], 1); + add_assoc_zval(return_value, "Copyright", tmpi); + } + if (ImageInfo.RawCopyright) { + efree(ImageInfo.RawCopyright); + } + for(i=0; i<ImageInfo.numCopyrights; i++) { + efree((ImageInfo.Copyright)[i]); } - efree(ImageInfo.Copyright); } if (ImageInfo.Software) { if (ImageInfo.Software[0]) { @@ -1351,16 +1455,18 @@ if(ImageInfo.numComments==1) { add_assoc_string(return_value, "Comments", (ImageInfo.Comments)[0], 1); } else { + pval *tmpi; + MAKE_STD_ZVAL(tmpi); array_init(tmpi); for(i=0; i<ImageInfo.numComments; i++) { add_index_string(tmpi, i, (ImageInfo.Comments)[i], 0); } - zend_hash_update(return_value->value.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<ImageInfo.numComments; i++) { efree((ImageInfo.Comments)[i]); - (ImageInfo.Comments)[i] = NULL; } } if (ImageInfo.Description) { Index: php4/ext/exif/php_exif.h diff -u php4/ext/exif/php_exif.h:1.7 php4/ext/exif/php_exif.h:1.8 --- php4/ext/exif/php_exif.h:1.7 Thu Feb 28 01:26:50 2002 +++ php4/ext/exif/php_exif.h Thu Feb 28 23:01:26 2002 @@ -17,11 +17,12 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_exif.h,v 1.7 2002/02/28 06:26:50 sebastian Exp $ */ +/* $Id: php_exif.h,v 1.8 2002/03/01 04:01:26 helly Exp $ */ #if HAVE_EXIF extern zend_module_entry exif_module_entry; #define phpext_exif_ptr &exif_module_entry PHP_FUNCTION(read_exif_data); +PHP_FUNCTION(exif_headername); #endif Index: php4/ext/exif/test.txt +++ php4/ext/exif/test.txt <?php /* Test script for PHP module ext/exif * * (c) Marcus Boerger, 2002 * * Rename the file to test.php and read the instructions. If the * script cannot be executed or does not generate any output check * you error log. In most cases this would mean you found an error * if the rest of your php environment works fine. * * The original version of module exif has many errors and mostly * fails on executing this script. */ $possible = array(); /****************************************************************************/ // message function is used for debugging purpose: just to se what happens function message($msg) { error_log($msg,0); echo "$msg\n"; } function error_msg() { if (array_key_exists('php_errormsg',$GLOBALS)) return $GLOBALS['php_errormsg']; return 'php.ini: track_errors is off'; } /****************************************************************************/ // private to function search_file() function _search_file($root,&$possible,$path='') { $sub = array(); //error_log("search_file($root,$path)",0); if ($dir = @opendir($root.$path.'/')) { while (($found = @readdir($dir)) !== false) { $type = @filetype($root.$path.'/'.$found); //error_log("search_file($root$path):$type=$found",0); switch( $type) { case 'file': $pos = strpos($found,'.'); if ( $pos !== false && strtolower(substr($found,$pos+1))=='jpg') { $possible[] = $root.$path.'/'.$found; //error_log("search_file($root$path) add:$path/$found",0); } break; case 'dir': if ( $found!='.' && $found!='..') { $sub[count($sub)] = $found; } break; } } @closedir($dir); foreach( $sub as $idx => $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 "<tr><td>$Name</td><td>$Value&nbsp;</td></tr>\n"; } $possible = search_file(); ?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional"> <html> <head> <title>PHP moduls exif test page</title> <style type="text/css"> body { font-size: 12pt; } h1 { font-size: 20pt; font-weight:bold; } h2 { font-size: 16pt; font-weight:bold; } th { text-align: left; } </style> </head> <body> <h1>Test script for PHP module ext/exif</h1> <p> (c) Marcus Boerger, 2002 </p> <p> Images taken from <a href="http://www.exif.org">www.exif.org</a>, <a href="http://marcus-boerger.de">marcus-boerger.de</a> all rights reserved by their authors and artists, see exif headers. The files can be downloaded <a href="http://marcus-boerger.de/php/ext/exif/test/">here</a>. 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. </p> <p> Youmay take a look at the test <a href="http://marcus-boerger.de/php/ext/exif/test.txt">source here</a>. </p> <p> 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. </p> <p> What to look for in detail: </p> <ul> <li>kodak-dc4800-plus-acdsee.jpg <ul> <li>should provide a long comment 'by marcus b&ouml;rger&lt;%04i&gt;'*n</li> </ul> </li> <li>hp-photosmart <ul> <li>should provide a ***two line*** copyright notice</li> </ul> </li> </ul> <h2>function exif_headername</h2> <table border='1' cellspacing='0' cellpadding='3' summary="EXIF headernames"> <?php if (function_exists('exif_headername')) { ?> <tr><td>ImageWidth</td><td><?=@exif_headername(0x0100)?></td><td><?=error_msg()?></td></tr> <tr><td>JPEGProc</td><td><?=@exif_headername(0x0200)?></td><td><?=error_msg()?></td></tr> <tr><td>SceneType</td><td><?=@exif_headername(0xA301)?></td><td><?=error_msg()?></td></tr> <tr><td>false</td><td><?=@exif_headername(0x0000)===false?'false':'value'?></td><td><?=error_msg()?></td></tr> <?php } else { echo "<tr><td>function exif_headername is not supported</td></tr>\n"; } ?> </table> <br clear="all"> <h2>function read_exif_data for <?=count($possible)?> images</h2> <table border='1' cellspacing='0' cellpadding='3' summary="EXIF information"> <?php if (function_exists('read_exif_data')) { $num = 0; foreach($possible as $idx => $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 "<tr><td rowspan='$len' valign='top'>$num</td></tr><tr><th>$file</th><td>$error</td></tr>\n$res"; } } else { echo "<tr><td>function read_exif_data is not supported</td></tr>\n"; } ?> </table> </body> </html>
« previous php.cvs (#9523) next »