root/ext/standard/iptc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_iptc_put1
  2. php_iptc_get1
  3. php_iptc_read_remaining
  4. php_iptc_skip_variable
  5. php_iptc_next_marker
  6. PHP_FUNCTION
  7. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 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_01.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    | Author: Thies C. Arntzen <thies@thieso.net>                          |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 /*
  22  * Functions to parse & compse IPTC data.
  23  * PhotoShop >= 3.0 can read and write textual data to JPEG files.
  24  * ... more to come .....
  25  * 
  26  * i know, parts of this is now duplicated in image.c 
  27  * but in this case i think it's okay!
  28  */
  29 
  30 /*
  31  * TODO:
  32  *  - add IPTC translation table
  33  */
  34  
  35 #include "php.h"
  36 #include "php_iptc.h"
  37 #include "ext/standard/head.h"
  38 
  39 #include <sys/stat.h>
  40 
  41 #ifdef PHP_WIN32
  42 # include "win32/php_stdint.h"
  43 #else
  44 # if HAVE_INTTYPES_H
  45 #  include <inttypes.h>
  46 # elif HAVE_STDINT_H
  47 #  include <stdint.h>
  48 # endif
  49 #endif
  50 
  51 /* some defines for the different JPEG block types */
  52 #define M_SOF0  0xC0            /* Start Of Frame N */
  53 #define M_SOF1  0xC1            /* N indicates which compression process */
  54 #define M_SOF2  0xC2            /* Only SOF0-SOF2 are now in common use */
  55 #define M_SOF3  0xC3
  56 #define M_SOF5  0xC5            /* NB: codes C4 and CC are NOT SOF markers */
  57 #define M_SOF6  0xC6
  58 #define M_SOF7  0xC7
  59 #define M_SOF9  0xC9
  60 #define M_SOF10 0xCA
  61 #define M_SOF11 0xCB
  62 #define M_SOF13 0xCD
  63 #define M_SOF14 0xCE
  64 #define M_SOF15 0xCF
  65 #define M_SOI   0xD8
  66 #define M_EOI   0xD9            /* End Of Image (end of datastream) */
  67 #define M_SOS   0xDA            /* Start Of Scan (begins compressed data) */
  68 #define M_APP0  0xe0
  69 #define M_APP1  0xe1
  70 #define M_APP2  0xe2
  71 #define M_APP3  0xe3
  72 #define M_APP4  0xe4
  73 #define M_APP5  0xe5
  74 #define M_APP6  0xe6
  75 #define M_APP7  0xe7
  76 #define M_APP8  0xe8
  77 #define M_APP9  0xe9
  78 #define M_APP10 0xea
  79 #define M_APP11 0xeb
  80 #define M_APP12 0xec
  81 #define M_APP13 0xed
  82 #define M_APP14 0xee
  83 #define M_APP15 0xef
  84 
  85 /* {{{ php_iptc_put1
  86  */
  87 static int php_iptc_put1(FILE *fp, int spool, unsigned char c, unsigned char **spoolbuf TSRMLS_DC)
  88 { 
  89         if (spool > 0)
  90                 PUTC(c);
  91 
  92         if (spoolbuf) *(*spoolbuf)++ = c;
  93 
  94         return c;
  95 }
  96 /* }}} */
  97 
  98 /* {{{ php_iptc_get1
  99  */
 100 static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf TSRMLS_DC)
 101 {       
 102         int c;
 103         char cc;
 104 
 105         c = getc(fp);
 106 
 107         if (c == EOF) return EOF;
 108 
 109         if (spool > 0) {
 110                 cc = c;
 111                 PUTC(cc);
 112         }
 113 
 114         if (spoolbuf) *(*spoolbuf)++ = c;
 115 
 116         return c;
 117 }
 118 /* }}} */
 119 
 120 /* {{{ php_iptc_read_remaining
 121  */
 122 static int php_iptc_read_remaining(FILE *fp, int spool, unsigned char **spoolbuf TSRMLS_DC)
 123 {
 124         while (php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC) != EOF) continue;
 125 
 126         return M_EOI;
 127 }
 128 /* }}} */
 129 
 130 /* {{{ php_iptc_skip_variable
 131  */
 132 static int php_iptc_skip_variable(FILE *fp, int spool, unsigned char **spoolbuf TSRMLS_DC)
 133 { 
 134         unsigned int  length;
 135         int c1, c2;
 136 
 137     if ((c1 = php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC)) == EOF) return M_EOI;
 138 
 139     if ((c2 = php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC)) == EOF) return M_EOI;
 140 
 141         length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
 142 
 143         length -= 2;
 144 
 145         while (length--)
 146                 if (php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC) == EOF) return M_EOI;
 147 
 148         return 0;
 149 }
 150 /* }}} */
 151 
 152 /* {{{ php_iptc_next_marker
 153  */
 154 static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf TSRMLS_DC)
 155 {
 156     int c;
 157 
 158     /* skip unimportant stuff */
 159 
 160     c = php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC);
 161 
 162         if (c == EOF) return M_EOI;
 163 
 164     while (c != 0xff) {
 165         if ((c = php_iptc_get1(fp, spool, spoolbuf TSRMLS_CC)) == EOF)
 166             return M_EOI; /* we hit EOF */
 167     }
 168 
 169     /* get marker byte, swallowing possible padding */
 170     do {
 171         c = php_iptc_get1(fp, 0, 0 TSRMLS_CC);
 172                 if (c == EOF)
 173             return M_EOI;       /* we hit EOF */
 174                 else
 175                 if (c == 0xff)
 176                         php_iptc_put1(fp, spool, (unsigned char)c, spoolbuf TSRMLS_CC);
 177     } while (c == 0xff);
 178 
 179     return (unsigned int) c;
 180 }
 181 /* }}} */
 182 
 183 static char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
 184 
 185 /* {{{ proto array iptcembed(string iptcdata, string jpeg_file_name [, int spool])
 186    Embed binary IPTC data into a JPEG image. */
 187 PHP_FUNCTION(iptcembed)
 188 {
 189         char *iptcdata, *jpeg_file;
 190         int iptcdata_len, jpeg_file_len;
 191         long spool = 0;
 192         FILE *fp;
 193         unsigned int marker, done = 0;
 194         int inx;
 195         unsigned char *spoolbuf = NULL, *poi = NULL;
 196         struct stat sb;
 197         zend_bool written = 0;
 198 
 199         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|l", &iptcdata, &iptcdata_len, &jpeg_file, &jpeg_file_len, &spool) != SUCCESS) {
 200                 return;
 201         }
 202 
 203         if (php_check_open_basedir(jpeg_file TSRMLS_CC)) {
 204                 RETURN_FALSE;
 205         }
 206 
 207         if ((size_t)iptcdata_len >= SIZE_MAX - sizeof(psheader) - 1025) {
 208                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "IPTC data too large");
 209                 RETURN_FALSE;
 210         }
 211 
 212         if ((fp = VCWD_FOPEN(jpeg_file, "rb")) == 0) {
 213                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s", jpeg_file);
 214                 RETURN_FALSE;
 215         }
 216 
 217         if (spool < 2) {
 218                 fstat(fileno(fp), &sb);
 219 
 220                 poi = spoolbuf = safe_emalloc(1, (size_t)iptcdata_len + sizeof(psheader) + 1024 + 1, sb.st_size);
 221                 memset(poi, 0, iptcdata_len + sizeof(psheader) + sb.st_size + 1024 + 1);
 222         } 
 223 
 224         if (php_iptc_get1(fp, spool, poi?&poi:0 TSRMLS_CC) != 0xFF) {
 225                 fclose(fp);
 226                 if (spoolbuf) {
 227                         efree(spoolbuf);
 228                 }
 229                 RETURN_FALSE;
 230         }
 231 
 232         if (php_iptc_get1(fp, spool, poi?&poi:0 TSRMLS_CC) != 0xD8) {
 233                 fclose(fp);
 234                 if (spoolbuf) {
 235                         efree(spoolbuf);
 236                 }
 237                 RETURN_FALSE;
 238         }
 239 
 240         while (!done) {
 241                 marker = php_iptc_next_marker(fp, spool, poi?&poi:0 TSRMLS_CC);
 242 
 243                 if (marker == M_EOI) { /* EOF */
 244                         break;
 245                 } else if (marker != M_APP13) { 
 246                         php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0 TSRMLS_CC);
 247                 }
 248 
 249                 switch (marker) {
 250                         case M_APP13:
 251                                 /* we are going to write a new APP13 marker, so don't output the old one */
 252                                 php_iptc_skip_variable(fp, 0, 0 TSRMLS_CC);    
 253                                 fgetc(fp); /* skip already copied 0xFF byte */
 254                                 php_iptc_read_remaining(fp, spool, poi?&poi:0 TSRMLS_CC);
 255                                 done = 1;
 256                                 break;
 257 
 258                         case M_APP0:
 259                                 /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
 260                         case M_APP1:
 261                                 if (written) {
 262                                         /* don't try to write the data twice */
 263                                         break;
 264                                 }
 265                                 written = 1;
 266 
 267                                 php_iptc_skip_variable(fp, spool, poi?&poi:0 TSRMLS_CC);
 268 
 269                                 if (iptcdata_len & 1) {
 270                                         iptcdata_len++; /* make the length even */
 271                                 }
 272 
 273                                 psheader[ 2 ] = (iptcdata_len+28)>>8;
 274                                 psheader[ 3 ] = (iptcdata_len+28)&0xff;
 275 
 276                                 for (inx = 0; inx < 28; inx++) {
 277                                         php_iptc_put1(fp, spool, psheader[inx], poi?&poi:0 TSRMLS_CC);
 278                                 }
 279 
 280                                 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0 TSRMLS_CC);
 281                                 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0 TSRMLS_CC);
 282 
 283                                 for (inx = 0; inx < iptcdata_len; inx++) {
 284                                         php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0 TSRMLS_CC);
 285                                 }
 286                                 break;
 287 
 288                         case M_SOS:                                                             
 289                                 /* we hit data, no more marker-inserting can be done! */
 290                                 php_iptc_read_remaining(fp, spool, poi?&poi:0 TSRMLS_CC);
 291                                 done = 1;
 292                                 break;
 293 
 294                         default:
 295                                 php_iptc_skip_variable(fp, spool, poi?&poi:0 TSRMLS_CC);
 296                                 break;
 297                 }
 298         }
 299 
 300         fclose(fp);
 301 
 302         if (spool < 2) {
 303                 RETVAL_STRINGL(spoolbuf, poi - spoolbuf, 0);
 304         } else {
 305                 RETURN_TRUE;
 306         }
 307 }
 308 /* }}} */
 309 
 310 /* {{{ proto array iptcparse(string iptcdata)
 311    Parse binary IPTC-data into associative array */
 312 PHP_FUNCTION(iptcparse)
 313 {
 314         int inx = 0, len;
 315         unsigned int tagsfound = 0;
 316         unsigned char *buffer, recnum, dataset, key[ 16 ];
 317         char *str;
 318         int str_len;
 319         zval *values, **element;
 320 
 321         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) != SUCCESS) {
 322                 return;
 323         }
 324 
 325         buffer = (unsigned char *)str;
 326 
 327         while (inx < str_len) { /* find 1st tag */
 328                 if ((buffer[inx] == 0x1c) && ((buffer[inx+1] == 0x01) || (buffer[inx+1] == 0x02))){ 
 329                         break;
 330                 } else {
 331                         inx++;
 332                 }
 333         }
 334 
 335         while (inx < str_len) {
 336                 if (buffer[ inx++ ] != 0x1c) {
 337                         break;   /* we ran against some data which does not conform to IPTC - stop parsing! */
 338                 } 
 339                 
 340                 if ((inx + 4) >= str_len)
 341                         break;
 342 
 343                 dataset = buffer[ inx++ ];
 344                 recnum = buffer[ inx++ ];
 345 
 346                 if (buffer[ inx ] & (unsigned char) 0x80) { /* long tag */
 347                         if((inx+6) >= str_len) {
 348                                 break;
 349                         }
 350                         len = (((long) buffer[ inx + 2 ]) << 24) + (((long) buffer[ inx + 3 ]) << 16) + 
 351                                   (((long) buffer[ inx + 4 ]) <<  8) + (((long) buffer[ inx + 5 ]));
 352                         inx += 6;
 353                 } else { /* short tag */
 354                         len = (((unsigned short) buffer[ inx ])<<8) | (unsigned short)buffer[ inx+1 ];
 355                         inx += 2;
 356                 }
 357                 
 358                 if ((len < 0) || (len > str_len) || (inx + len) > str_len) {
 359                         break;
 360                 }
 361 
 362                 snprintf(key, sizeof(key), "%d#%03d", (unsigned int) dataset, (unsigned int) recnum);
 363 
 364                 if (tagsfound == 0) { /* found the 1st tag - initialize the return array */
 365                         array_init(return_value);
 366                 }
 367 
 368                 if (zend_hash_find(Z_ARRVAL_P(return_value), key, strlen(key) + 1, (void **) &element) == FAILURE) {
 369                         MAKE_STD_ZVAL(values);
 370                         array_init(values);
 371                         
 372                         zend_hash_update(Z_ARRVAL_P(return_value), key, strlen(key) + 1, (void *) &values, sizeof(zval*), (void **) &element);
 373                 } 
 374                         
 375                 add_next_index_stringl(*element, buffer+inx, len, 1);
 376                 inx += len;
 377                 tagsfound++;
 378         }
 379 
 380         if (! tagsfound) {
 381                 RETURN_FALSE;
 382         }
 383 }
 384 /* }}} */
 385 
 386 /*
 387  * Local variables:
 388  * tab-width: 4
 389  * c-basic-offset: 4
 390  * End:
 391  * vim600: sw=4 ts=4 fdm=marker
 392  * vim<600: sw=4 ts=4
 393  */

/* [<][>][^][v][top][bottom][index][help] */