PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/standard/iptc.c

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