php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
iptc.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Thies C. Arntzen <thies@thieso.net> |
14 +----------------------------------------------------------------------+
15 */
16
17/*
18 * Functions to parse & compse IPTC data.
19 * PhotoShop >= 3.0 can read and write textual data to JPEG files.
20 * ... more to come .....
21 *
22 * i know, parts of this is now duplicated in image.c
23 * but in this case i think it's okay!
24 */
25
26/*
27 * TODO:
28 * - add IPTC translation table
29 */
30
31#include "php.h"
32#include "ext/standard/head.h"
33
34#include <sys/stat.h>
35
36#include <stdint.h>
37#ifndef PHP_WIN32
38# include <inttypes.h>
39#endif
40
41/* some defines for the different JPEG block types */
42#define M_SOF0 0xC0 /* Start Of Frame N */
43#define M_SOF1 0xC1 /* N indicates which compression process */
44#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
45#define M_SOF3 0xC3
46#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
47#define M_SOF6 0xC6
48#define M_SOF7 0xC7
49#define M_SOF9 0xC9
50#define M_SOF10 0xCA
51#define M_SOF11 0xCB
52#define M_SOF13 0xCD
53#define M_SOF14 0xCE
54#define M_SOF15 0xCF
55#define M_SOI 0xD8
56#define M_EOI 0xD9 /* End Of Image (end of datastream) */
57#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
58#define M_APP0 0xe0
59#define M_APP1 0xe1
60#define M_APP2 0xe2
61#define M_APP3 0xe3
62#define M_APP4 0xe4
63#define M_APP5 0xe5
64#define M_APP6 0xe6
65#define M_APP7 0xe7
66#define M_APP8 0xe8
67#define M_APP9 0xe9
68#define M_APP10 0xea
69#define M_APP11 0xeb
70#define M_APP12 0xec
71#define M_APP13 0xed
72#define M_APP14 0xee
73#define M_APP15 0xef
74
75/* {{{ php_iptc_put1 */
76static int php_iptc_put1(FILE *fp, int spool, unsigned char c, unsigned char **spoolbuf)
77{
78 if (spool > 0)
79 PUTC(c);
80
81 if (spoolbuf) *(*spoolbuf)++ = c;
82
83 return c;
84}
85/* }}} */
86
87/* {{{ php_iptc_get1 */
88static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf)
89{
90 int c;
91 char cc;
92
93 c = getc(fp);
94
95 if (c == EOF) return EOF;
96
97 if (spool > 0) {
98 cc = c;
99 PUTC(cc);
100 }
101
102 if (spoolbuf) *(*spoolbuf)++ = c;
103
104 return c;
105}
106/* }}} */
107
108/* {{{ php_iptc_read_remaining */
109static int php_iptc_read_remaining(FILE *fp, int spool, unsigned char **spoolbuf)
110{
111 while (php_iptc_get1(fp, spool, spoolbuf) != EOF) continue;
112
113 return M_EOI;
114}
115/* }}} */
116
117/* {{{ php_iptc_skip_variable */
118static int php_iptc_skip_variable(FILE *fp, int spool, unsigned char **spoolbuf)
119{
120 unsigned int length;
121 int c1, c2;
122
123 if ((c1 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI;
124
125 if ((c2 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI;
126
127 length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
128
129 length -= 2;
130
131 while (length--)
132 if (php_iptc_get1(fp, spool, spoolbuf) == EOF) return M_EOI;
133
134 return 0;
135}
136/* }}} */
137
138/* {{{ php_iptc_next_marker */
139static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf)
140{
141 int c;
142
143 /* skip unimportant stuff */
144
145 c = php_iptc_get1(fp, spool, spoolbuf);
146
147 if (c == EOF) return M_EOI;
148
149 while (c != 0xff) {
150 if ((c = php_iptc_get1(fp, spool, spoolbuf)) == EOF)
151 return M_EOI; /* we hit EOF */
152 }
153
154 /* get marker byte, swallowing possible padding */
155 do {
156 c = php_iptc_get1(fp, 0, 0);
157 if (c == EOF)
158 return M_EOI; /* we hit EOF */
159 else
160 if (c == 0xff)
161 php_iptc_put1(fp, spool, (unsigned char)c, spoolbuf);
162 } while (c == 0xff);
163
164 return (unsigned int) c;
165}
166/* }}} */
167
168static char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
169
170/* {{{ Embed binary IPTC data into a JPEG image. */
172{
173 char *iptcdata, *jpeg_file;
174 size_t iptcdata_len, jpeg_file_len;
175 zend_long spool = 0;
176 FILE *fp;
177 unsigned int marker, done = 0;
178 size_t inx;
179 zend_string *spoolbuf = NULL;
180 unsigned char *poi = NULL;
181 zend_stat_t sb = {0};
182 bool written = 0;
183
185 Z_PARAM_STRING(iptcdata, iptcdata_len)
186 Z_PARAM_PATH(jpeg_file, jpeg_file_len)
188 Z_PARAM_LONG(spool)
190
191 if (php_check_open_basedir(jpeg_file)) {
193 }
194
195 if (iptcdata_len >= SIZE_MAX - sizeof(psheader) - 1025) {
196 zend_argument_value_error(1, "is too large");
198 }
199
200 if ((fp = VCWD_FOPEN(jpeg_file, "rb")) == 0) {
201 php_error_docref(NULL, E_WARNING, "Unable to open %s", jpeg_file);
203 }
204
205 if (spool < 2) {
206 if (zend_fstat(fileno(fp), &sb) != 0) {
207 fclose(fp);
209 }
210
211 spoolbuf = zend_string_safe_alloc(1, iptcdata_len + sizeof(psheader) + 1024 + 1, sb.st_size, 0);
212 poi = (unsigned char*)ZSTR_VAL(spoolbuf);
213 memset(poi, 0, iptcdata_len + sizeof(psheader) + sb.st_size + 1024 + 1);
214 }
215
216 if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xFF) {
217 fclose(fp);
218 if (spoolbuf) {
219 zend_string_efree(spoolbuf);
220 }
222 }
223
224 if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xD8) {
225 fclose(fp);
226 if (spoolbuf) {
227 zend_string_efree(spoolbuf);
228 }
230 }
231
232 while (!done) {
233 marker = php_iptc_next_marker(fp, spool, poi?&poi:0);
234
235 if (marker == M_EOI) { /* EOF */
236 break;
237 } else if (marker != M_APP13) {
238 php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0);
239 }
240
241 switch (marker) {
242 case M_APP13:
243 /* we are going to write a new APP13 marker, so don't output the old one */
244 php_iptc_skip_variable(fp, 0, 0);
245 fgetc(fp); /* skip already copied 0xFF byte */
246 php_iptc_read_remaining(fp, spool, poi?&poi:0);
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);
260
261 if (iptcdata_len & 1) {
262 iptcdata_len++; /* make the length even */
263 }
264
265 psheader[ 2 ] = (char) ((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);
270 }
271
272 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0);
273 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0);
274
275 for (inx = 0; inx < iptcdata_len; inx++) {
276 php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0);
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);
283 done = 1;
284 break;
285
286 default:
287 php_iptc_skip_variable(fp, spool, poi?&poi:0);
288 break;
289 }
290 }
291
292 fclose(fp);
293
294 if (spool < 2) {
295 spoolbuf = zend_string_truncate(spoolbuf, poi - (unsigned char*)ZSTR_VAL(spoolbuf), 0);
296 RETURN_NEW_STR(spoolbuf);
297 } else {
299 }
300}
301/* }}} */
302
303/* {{{ Parse binary IPTC-data into associative array */
305{
306 size_t inx = 0, len;
307 unsigned int tagsfound = 0;
308 unsigned char *buffer, recnum, dataset;
309 char *str, key[16];
310 size_t str_len;
311 zval values, *element;
312
314 Z_PARAM_STRING(str, str_len)
316
317 buffer = (unsigned char *)str;
318
319 while (inx < str_len) { /* find 1st tag */
320 if ((buffer[inx] == 0x1c) && ((buffer[inx+1] == 0x01) || (buffer[inx+1] == 0x02))){
321 break;
322 } else {
323 inx++;
324 }
325 }
326
327 while (inx < str_len) {
328 if (buffer[ inx++ ] != 0x1c) {
329 break; /* we ran against some data which does not conform to IPTC - stop parsing! */
330 }
331
332 if ((inx + 4) >= str_len)
333 break;
334
335 dataset = buffer[ inx++ ];
336 recnum = buffer[ inx++ ];
337
338 if (buffer[ inx ] & (unsigned char) 0x80) { /* long tag */
339 if((inx+6) >= str_len) {
340 break;
341 }
342 len = (((zend_long) buffer[ inx + 2 ]) << 24) + (((zend_long) buffer[ inx + 3 ]) << 16) +
343 (((zend_long) buffer[ inx + 4 ]) << 8) + (((zend_long) buffer[ inx + 5 ]));
344 inx += 6;
345 } else { /* short tag */
346 len = (((unsigned short) buffer[ inx ])<<8) | (unsigned short)buffer[ inx+1 ];
347 inx += 2;
348 }
349
350 if ((len > str_len) || (inx + len) > str_len) {
351 break;
352 }
353
354 snprintf(key, sizeof(key), "%d#%03d", (unsigned int) dataset, (unsigned int) recnum);
355
356 if (tagsfound == 0) { /* found the 1st tag - initialize the return array */
358 }
359
360 if ((element = zend_hash_str_find(Z_ARRVAL_P(return_value), key, strlen(key))) == NULL) {
361 array_init(&values);
362
364 }
365
366 add_next_index_stringl(element, (char *) buffer+inx, len);
367 inx += len;
368 tagsfound++;
369 }
370
371 if (! tagsfound) {
373 }
374}
375/* }}} */
size_t len
Definition apprentice.c:174
iptcparse(string $iptc_block)
fgetc($stream)
iptcembed(string $iptc_data, string $filename, int $spool=0)
fclose($stream)
#define M_EOI
Definition exif.c:2643
#define M_APP13
Definition exif.c:2663
#define M_APP0
Definition exif.c:2650
#define M_SOS
Definition exif.c:2644
memset(ptr, 0, type->size)
PHPAPI int php_check_open_basedir(const char *path)
#define SIZE_MAX
Definition funcs.c:51
#define NULL
Definition gdcache.h:45
#define M_APP1
Definition image.c:315
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define PHP_FUNCTION
Definition php.h:364
#define PUTC(c)
unsigned char key[REFLECTION_KEY_LEN]
Definition file.h:177
ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t length)
Definition zend_API.c:2195
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define Z_PARAM_STRING(dest, dest_len)
Definition zend_API.h:2071
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
#define RETURN_THROWS()
Definition zend_API.h:1060
#define Z_PARAM_PATH(dest, dest_len)
Definition zend_API.h:2026
#define RETURN_TRUE
Definition zend_API.h:1059
#define array_init(arg)
Definition zend_API.h:537
struct _zval_struct zval
strlen(string $string)
#define snprintf
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:2689
ZEND_API zval *ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
Definition zend_hash.c:1031
int32_t zend_long
Definition zend_long.h:42
struct _zend_string zend_string
#define zend_fstat
Definition zend_stream.h:98
struct stat zend_stat_t
Definition zend_stream.h:94
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define VCWD_FOPEN(path, mode)
zval * return_value