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