/src/ghostpdl/devices/vector/gdevpdfe.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Metadata writer. */ |
18 | | #include "gx.h" |
19 | | #include "gserrors.h" |
20 | | #include "string_.h" |
21 | | #include "time_.h" |
22 | | #include "stream.h" |
23 | | #include "gp.h" |
24 | | #include "smd5.h" |
25 | | #include "gscdefs.h" |
26 | | #include "gdevpdfx.h" |
27 | | #include "gdevpdfg.h" |
28 | | #include "gdevpdfo.h" |
29 | | |
30 | | /* These two tables map PDFDocEncoding character codes (0x00->0x20 and 0x80->0xAD) |
31 | | * to their equivalent UTF-16BE value. That allows us to convert a PDFDocEncoding |
32 | | * string to UTF-16BE, and then further translate that into UTF-8. |
33 | | * Note 0x7F is individually treated. |
34 | | * See pdf_xmp_write_translated(). |
35 | | */ |
36 | | static char PDFDocEncodingLookupLo [64] = { |
37 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
38 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
39 | | 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, |
40 | | 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, |
41 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
42 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
43 | | 0x02, 0xD8, 0x02, 0xC7, 0x02, 0xC6, 0x02, 0xD9, |
44 | | 0x02, 0xDD, 0x02, 0xDB, 0x02, 0xDA, 0x02, 0xDC |
45 | | }; |
46 | | |
47 | | static char PDFDocEncodingLookupHi [92] = { |
48 | | 0x20, 0x22, 0x20, 0x20, 0x20, 0x21, 0x20, 0x26, |
49 | | 0x20, 0x14, 0x20, 0x13, 0x01, 0x92, 0x20, 0x44, |
50 | | 0x20, 0x39, 0x20, 0x3A, 0x22, 0x12, 0x20, 0x30, |
51 | | 0x20, 0x1E, 0x20, 0x1C, 0x20, 0x1D, 0x20, 0x18, |
52 | | 0x20, 0x19, 0x20, 0x1A, 0x21, 0x22, 0xFB, 0x01, |
53 | | 0xFB, 0x02, 0x01, 0x41, 0x01, 0x52, 0x01, 0x60, |
54 | | 0x01, 0x78, 0x01, 0x7D, 0x01, 0x31, 0x01, 0x42, |
55 | | 0x01, 0x53, 0x01, 0x61, 0x01, 0x7E, 0x00, 0x00, |
56 | | 0x20, 0xAC, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, |
57 | | 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, |
58 | | 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, |
59 | | 0x00, 0xAC, 0x00, 0x00 |
60 | | }; |
61 | | |
62 | | static void |
63 | | copy_bytes(stream *s, const byte **data, int *data_length, int n) |
64 | 2.38k | { |
65 | 7.34k | while (n-- && (*data_length)--) { |
66 | 4.96k | stream_putc(s, *((*data)++)); |
67 | 4.96k | } |
68 | 2.38k | } |
69 | | |
70 | | /* Write XML data */ |
71 | | static void |
72 | | pdf_xml_data_write(stream *s, const byte *data, int data_length) |
73 | 120k | { |
74 | 120k | int l = data_length; |
75 | 120k | const byte *p = data; |
76 | | |
77 | 3.04M | while (l > 0) { |
78 | 2.92M | switch (*p) { |
79 | 1 | case '<' : stream_puts(s, "<"); l--; p++; break; |
80 | 4 | case '>' : stream_puts(s, ">"); l--; p++; break; |
81 | 6 | case '&' : stream_puts(s, "&"); l--; p++; break; |
82 | 11 | case '\'': stream_puts(s, "'"); l--; p++; break; |
83 | 9 | case '"' : stream_puts(s, """); l--; p++; break; |
84 | 2.92M | default: |
85 | 2.92M | if (*p < 32) { |
86 | | /* Not allowed in XML. */ |
87 | 301 | pprintd1(s, "&#%d;", *p); |
88 | 301 | l--; p++; |
89 | 2.92M | } else if (*p >= 0x7F && *p <= 0x9f) { |
90 | | /* Control characters are discouraged in XML. */ |
91 | 0 | pprintd1(s, "&#%d;", *p); |
92 | 0 | l--; p++; |
93 | 2.92M | } else if ((*p & 0xE0) == 0xC0) { |
94 | | /* A 2-byte UTF-8 sequence */ |
95 | 2.19k | copy_bytes(s, &p, &l, 2); |
96 | 2.91M | } else if ((*p & 0xF0) == 0xE0) { |
97 | | /* A 3-byte UTF-8 sequence */ |
98 | 193 | copy_bytes(s, &p, &l, 3); |
99 | 2.91M | } else if ((*p & 0xF0) == 0xF0) { |
100 | | /* A 4-byte UTF-8 sequence */ |
101 | 0 | copy_bytes(s, &p, &l, 4); |
102 | 2.91M | } else { |
103 | 2.91M | stream_putc(s, *p); |
104 | 2.91M | l--; p++; |
105 | 2.91M | } |
106 | 2.92M | } |
107 | 2.92M | } |
108 | 120k | } |
109 | | |
110 | | /* Write XML string */ |
111 | | static inline void |
112 | | pdf_xml_string_write(stream *s, const char *data) |
113 | 109k | { |
114 | 109k | pdf_xml_data_write(s, (const byte *)data, strlen(data)); |
115 | 109k | } |
116 | | |
117 | | /* Begin an opening XML tag */ |
118 | | static inline void |
119 | | pdf_xml_tag_open_beg(stream *s, const char *data) |
120 | 100k | { |
121 | 100k | stream_putc(s, '<'); |
122 | 100k | stream_puts(s, data); |
123 | 100k | } |
124 | | |
125 | | /* End an XML tag */ |
126 | | static inline void |
127 | | pdf_xml_tag_end(stream *s) |
128 | 64.5k | { |
129 | 64.5k | stream_putc(s, '>'); |
130 | 64.5k | } |
131 | | |
132 | | /* End an empty XML tag */ |
133 | | static inline void |
134 | | pdf_xml_tag_end_empty(stream *s) |
135 | 36.4k | { |
136 | 36.4k | stream_puts(s, "/>"); |
137 | 36.4k | } |
138 | | |
139 | | /* Write an opening XML tag */ |
140 | | static inline void |
141 | | pdf_xml_tag_open(stream *s, const char *data) |
142 | 18.7k | { |
143 | 18.7k | stream_putc(s, '<'); |
144 | 18.7k | stream_puts(s, data); |
145 | 18.7k | stream_putc(s, '>'); |
146 | 18.7k | } |
147 | | |
148 | | /* Write a closing XML tag */ |
149 | | static inline void |
150 | | pdf_xml_tag_close(stream *s, const char *data) |
151 | 82.9k | { |
152 | 82.9k | stream_puts(s, "</"); |
153 | 82.9k | stream_puts(s, data); |
154 | 82.9k | stream_putc(s, '>'); |
155 | 82.9k | } |
156 | | |
157 | | /* Write an attribute name */ |
158 | | static inline void |
159 | | pdf_xml_attribute_name(stream *s, const char *data) |
160 | 127k | { |
161 | 127k | stream_putc(s, ' '); |
162 | 127k | stream_puts(s, data); |
163 | 127k | stream_putc(s, '='); |
164 | 127k | } |
165 | | |
166 | | /* Write a attribute value */ |
167 | | static inline void |
168 | | pdf_xml_attribute_value(stream *s, const char *data) |
169 | 109k | { |
170 | 109k | stream_putc(s, '\''); |
171 | 109k | pdf_xml_string_write(s, data); |
172 | 109k | stream_putc(s, '\''); |
173 | 109k | } |
174 | | /* Write a attribute value */ |
175 | | static inline void |
176 | | pdf_xml_attribute_value_data(stream *s, const byte *data, int data_length) |
177 | 9.06k | { |
178 | 9.06k | stream_putc(s, '\''); |
179 | 9.06k | pdf_xml_data_write(s, data, data_length); |
180 | 9.06k | stream_putc(s, '\''); |
181 | 9.06k | } |
182 | | |
183 | | /* Begin an XML instruction */ |
184 | | static inline void |
185 | | pdf_xml_ins_beg(stream *s, const char *data) |
186 | 9.18k | { |
187 | 9.18k | stream_puts(s, "<?"); |
188 | 9.18k | stream_puts(s, data); |
189 | 9.18k | } |
190 | | |
191 | | /* End an XML instruction */ |
192 | | static inline void |
193 | | pdf_xml_ins_end(stream *s) |
194 | 9.18k | { |
195 | 9.18k | stream_puts(s, "?>"); |
196 | 9.18k | } |
197 | | |
198 | | /* Write a newline character */ |
199 | | static inline void |
200 | | pdf_xml_newline(stream *s) |
201 | 91.7k | { |
202 | 91.7k | stream_puts(s, "\n"); |
203 | 91.7k | } |
204 | | |
205 | | /* Copy to XML output */ |
206 | | static inline void |
207 | | pdf_xml_copy(stream *s, const char *data) |
208 | 164k | { |
209 | 164k | stream_puts(s, data); |
210 | 164k | } |
211 | | |
212 | | /* -------------------------------------------- */ |
213 | | |
214 | | static int |
215 | | pdf_xmp_time(char *buf, int buf_length) |
216 | 0 | { |
217 | | /* We don't write a day time because we don't have a time zone. */ |
218 | 0 | struct tm tms; |
219 | 0 | time_t t; |
220 | 0 | char buf1[4+1+2+1+2+1]; /* yyyy-mm-dd\0 */ |
221 | |
|
222 | | #ifdef CLUSTER |
223 | | memset(&t, 0, sizeof(t)); |
224 | | memset(&tms, 0, sizeof(tms)); |
225 | | #else |
226 | 0 | time(&t); |
227 | 0 | tms = *localtime(&t); |
228 | 0 | #endif |
229 | 0 | gs_snprintf(buf1, sizeof(buf1), |
230 | 0 | "%04d-%02d-%02d", |
231 | 0 | tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday); |
232 | 0 | strncpy(buf, buf1, buf_length); |
233 | 0 | return strlen(buf); |
234 | 0 | } |
235 | | |
236 | | static int |
237 | | pdf_xmp_convert_time(char *dt, int dtl, char *buf, int bufl) |
238 | 18.3k | { /* The 'dt' buffer is of same size as 'buf'. */ |
239 | | /* Input sample : D:199812231952?08'00' */ |
240 | | /* Output sample : 1997-07-16T19:20:30+01:00 */ |
241 | 18.3k | int l = dtl; |
242 | | |
243 | 18.3k | if (l > bufl) |
244 | 0 | l = bufl; |
245 | 18.3k | if (dt[0] == 'D' && dt[1] == ':') { |
246 | 18.3k | l -= 2; |
247 | 18.3k | memcpy(buf, dt + 2, l); |
248 | 18.3k | } else |
249 | 0 | memcpy(buf, dt, l); |
250 | 18.3k | memcpy(dt, buf, 4); /* year */ |
251 | 18.3k | if (l <= 4) |
252 | 0 | return 4; |
253 | | |
254 | 18.3k | dt[4] = '-'; |
255 | 18.3k | memcpy(dt + 5, buf + 4, 2); /* month */ |
256 | 18.3k | if (l <= 6) |
257 | 0 | return 7; |
258 | | |
259 | 18.3k | dt[7] = '-'; |
260 | 18.3k | memcpy(dt + 8, buf + 6, 2); /* day */ |
261 | 18.3k | if (l <= 8) |
262 | 0 | return 10; |
263 | | |
264 | 18.3k | dt[10] = 'T'; |
265 | 18.3k | memcpy(dt + 11, buf + 8, 2); /* hour */ |
266 | 18.3k | dt[13] = ':'; |
267 | 18.3k | memcpy(dt + 14, buf + 10, 2); /* minute */ |
268 | 18.3k | if (l <= 12) { |
269 | 0 | dt[16] = 'Z'; /* Default time zone 0. */ |
270 | 0 | return 17; |
271 | 0 | } |
272 | | |
273 | 18.3k | dt[16] = ':'; |
274 | 18.3k | memcpy(dt + 17, buf + 12, 2); /* second */ |
275 | 18.3k | if (l <= 14) { |
276 | 0 | dt[19] = 'Z'; /* Default time zone 0. */ |
277 | 0 | return 20; |
278 | 0 | } |
279 | | |
280 | 18.3k | dt[19] = buf[14]; /* designator */ |
281 | 18.3k | if (dt[19] == 'Z') |
282 | 18.3k | return 20; |
283 | 0 | if (l <= 15) |
284 | 0 | return 20; |
285 | 0 | memcpy(dt + 20, buf + 15, 2); /* Time zone hour difference. */ |
286 | 0 | if (l <= 17) |
287 | 0 | return 22; |
288 | | |
289 | 0 | dt[22] = ':'; |
290 | | /* Skipping '\'' in 'buf'. */ |
291 | 0 | memcpy(dt + 23, buf + 18, 2); /* Time zone minutes difference. */ |
292 | 0 | return 25; |
293 | 0 | } |
294 | | |
295 | | int |
296 | | pdf_get_docinfo_item(gx_device_pdf *pdev, const char *key, char *buf, int buf_length) |
297 | 43.2k | { |
298 | 43.2k | const cos_value_t *v = cos_dict_find(pdev->Info, (const byte *)key, strlen(key)); |
299 | 43.2k | int l; |
300 | 43.2k | const byte *s; |
301 | | |
302 | 43.2k | if (v != NULL && (v->value_type == COS_VALUE_SCALAR || |
303 | 43.2k | v->value_type == COS_VALUE_CONST)) { |
304 | 43.2k | if (v->contents.chars.size >= 2 && v->contents.chars.data[0] == '(') { |
305 | 43.2k | s = v->contents.chars.data + 1; |
306 | 43.2k | l = v->contents.chars.size - 2; |
307 | 43.2k | } else { |
308 | 0 | s = v->contents.chars.data; |
309 | 0 | l = v->contents.chars.size; |
310 | 0 | } |
311 | 43.2k | } else |
312 | 0 | return 0; |
313 | 43.2k | if (l < 0) |
314 | 0 | l = 0; |
315 | 43.2k | if (l > buf_length) |
316 | 0 | l = buf_length; |
317 | 43.2k | memcpy(buf, s, l); |
318 | 43.2k | return l; |
319 | 43.2k | } |
320 | | |
321 | | static inline byte |
322 | | decode_escape(const byte *data, int data_length, size_t *index) |
323 | 9.52k | { |
324 | 9.52k | byte c; |
325 | | |
326 | 9.52k | (*index)++; /* skip '\' */ |
327 | 9.52k | if (*index >= data_length) |
328 | 0 | return 0; /* Must_not_happen, because the string is PS encoded. */ |
329 | 9.52k | c = data[*index]; |
330 | 9.52k | switch (c) { |
331 | 236 | case '(': return '('; |
332 | 94 | case ')': return ')'; |
333 | 1.23k | case '\\': return '\\'; |
334 | 2 | case 'n': return '\n'; |
335 | 2 | case 'r': return '\r'; |
336 | 497 | case 't': return '\t'; |
337 | 9 | case 'b': return '\b'; |
338 | 0 | case 'f': return '\f'; |
339 | 7.45k | default: |
340 | 7.45k | break; |
341 | 9.52k | } |
342 | 7.45k | if (c >= '0' && c <= '7') { |
343 | 7.45k | int oct_loop; |
344 | | /* octal */ |
345 | 7.45k | byte v = c - '0'; |
346 | | |
347 | | /* Octal values should always be three digits, one is consumed above! */ |
348 | 22.3k | for (oct_loop = 0;oct_loop < 2; oct_loop++) { |
349 | 14.9k | (*index)++; |
350 | 14.9k | if (*index >= data_length) |
351 | | /* Ran out of data, return what we found */ |
352 | 0 | return v; |
353 | 14.9k | c = data[*index]; |
354 | 14.9k | if (c < '0' || c > '7') { |
355 | | /* Ran out of numeric data, return what we found */ |
356 | | /* Need to 'unget' the non-numeric character */ |
357 | 0 | (*index)--; |
358 | 0 | break; |
359 | 0 | } |
360 | 14.9k | v = v * 8 + (c - '0'); |
361 | 14.9k | } |
362 | 7.45k | return v; |
363 | 7.45k | } |
364 | 0 | return c; /* A wrong escapement sequence. */ |
365 | 7.45k | } |
366 | | |
367 | | /* |
368 | | * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed |
369 | | * into the first byte, depending on how many bytes follow. There are |
370 | | * as many entries in this table as there are UTF-8 sequence types. |
371 | | * (I.e., one byte sequence, two byte... etc.). Remember that sequencs |
372 | | * for *legal* UTF-8 will be 4 or fewer bytes total. |
373 | | */ |
374 | | static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; |
375 | | |
376 | | static int gs_ConvertUTF16(unsigned char *UTF16, size_t UTF16Len, unsigned char **UTF8Start, int UTF8Len) |
377 | 10.5k | { |
378 | 10.5k | size_t i, bytes = 0; |
379 | 10.5k | uint32_t U32 = 0; |
380 | 10.5k | unsigned short U16; |
381 | 10.5k | unsigned char *UTF8 = *UTF8Start; |
382 | 10.5k | unsigned char *UTF8End = UTF8 + UTF8Len; |
383 | | |
384 | 10.5k | if (UTF16Len % sizeof(short) != 0) |
385 | 0 | return gs_note_error(gs_error_rangecheck); |
386 | | |
387 | 390k | for (i=0;i<UTF16Len / sizeof(short);i++) |
388 | 380k | { |
389 | 380k | U16 = (*UTF16++) << 8; |
390 | 380k | U16 += *UTF16++; |
391 | | |
392 | 380k | if (U16 >= 0xD800 && U16 <= 0xDBFF) { |
393 | | /* Ensure at least two bytes of input left */ |
394 | 0 | if (i == (UTF16Len / sizeof(short)) - 1) |
395 | 0 | return gs_note_error(gs_error_rangecheck); |
396 | | |
397 | 0 | U32 += (U16 & 0x3FF) << 10; |
398 | 0 | U16 = (*(UTF16++) << 8); |
399 | 0 | U16 += *(UTF16++); |
400 | 0 | i++; |
401 | | |
402 | | /* Ensure a high order surrogate is followed by a low order surrogate */ |
403 | 0 | if (U16 < 0xDC00 || U16 > 0xDFFF) |
404 | 0 | return gs_note_error(gs_error_rangecheck); |
405 | | |
406 | 0 | U32 += (U16 & 0x3FF) | 0x10000; |
407 | 0 | bytes = 4; |
408 | 380k | } else { |
409 | 380k | if (U16 >= 0xDC00 && U16 <= 0xDFFF) { |
410 | | /* We got a low order surrogate without a preceding high-order */ |
411 | 0 | return gs_note_error(gs_error_rangecheck); |
412 | 0 | } |
413 | | |
414 | 380k | if(U16 < 0x80) { |
415 | 377k | bytes = 1; |
416 | 377k | } else { |
417 | 2.38k | if (U16 < 0x800) { |
418 | 2.19k | bytes = 2; |
419 | 2.19k | } else { |
420 | 193 | bytes = 3; |
421 | 193 | } |
422 | 2.38k | } |
423 | 380k | } |
424 | | |
425 | 380k | if (UTF8 + bytes > UTF8End) |
426 | 0 | return gs_note_error(gs_error_VMerror); |
427 | | |
428 | | /* Write from end to beginning, low bytes first */ |
429 | 380k | UTF8 += bytes; |
430 | | |
431 | 380k | switch(bytes) { |
432 | 0 | case 4: |
433 | 0 | *--UTF8 = (unsigned char)((U32 | 0x80) & 0xBF); |
434 | 0 | U16 = U32 >> 6; |
435 | 193 | case 3: |
436 | 193 | *--UTF8 = (unsigned char)((U16 | 0x80) & 0xBF); |
437 | 193 | U16 >>= 6; |
438 | 2.38k | case 2: |
439 | 2.38k | *--UTF8 = (unsigned char)((U16 | 0x80) & 0xBF); |
440 | 2.38k | U16 >>= 6; |
441 | 380k | case 1: |
442 | 380k | *--UTF8 = (unsigned char)(U16 | firstByteMark[bytes]); |
443 | 380k | break; |
444 | 0 | default: |
445 | 0 | return gs_note_error(gs_error_rangecheck); |
446 | 380k | } |
447 | | |
448 | | /* Move to start of next set */ |
449 | 380k | UTF8 += bytes; |
450 | 380k | } |
451 | 10.5k | *UTF8Start = UTF8; |
452 | 10.5k | return 0; |
453 | 10.5k | } |
454 | | |
455 | | int |
456 | | pdf_xmp_write_translated(gx_device_pdf *pdev, stream *s, const byte *data, int data_length, |
457 | | void(*write)(stream *s, const byte *data, int data_length)) |
458 | 10.7k | { |
459 | 10.7k | int code = 0; |
460 | 10.7k | size_t i, j=0; |
461 | 10.7k | unsigned char *buf0 = NULL, *buf1 = NULL; |
462 | | |
463 | 10.7k | if (data_length == 0) |
464 | 133 | return 0; |
465 | | |
466 | 10.6k | buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, data_length * sizeof(unsigned char), |
467 | 10.6k | "pdf_xmp_write_translated"); |
468 | 10.6k | if (buf0 == NULL) |
469 | 0 | return_error(gs_error_VMerror); |
470 | 401k | for (i = 0; i < (size_t)data_length; i++) { |
471 | 390k | byte c = data[i]; |
472 | | |
473 | 390k | if (c == '\\') |
474 | 9.52k | c = decode_escape(data, data_length, &i); |
475 | 390k | buf0[j] = c; |
476 | 390k | j++; |
477 | 390k | } |
478 | 10.6k | if (buf0[0] != 0xfe || buf0[1] != 0xff) { |
479 | | /* We must assume that the information is PDFDocEncoding. In this case |
480 | | * we need to convert it into UTF-8. If we just convert it to UTF-16 |
481 | | * then we can safely fall through to the code below. |
482 | | */ |
483 | | /* NB the code below skips the BOM in positions 0 and 1, so we need |
484 | | * two extra bytes, to be ignored. |
485 | | */ |
486 | 10.6k | buf1 = (unsigned char *)gs_alloc_bytes(pdev->memory, (j * sizeof(short)) + 2, |
487 | 10.6k | "pdf_xmp_write_translated"); |
488 | 10.6k | if (buf1 == NULL) { |
489 | 0 | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
490 | 0 | return_error(gs_error_VMerror); |
491 | 0 | } |
492 | 10.6k | memset(buf1, 0x00, (j * sizeof(short)) + 2); |
493 | 393k | for (i = 0; i < j; i++) { |
494 | 382k | if ((buf0[i] >= 0x20 && buf0[i] < 0x7F) || buf0[i] >= 0xAE) |
495 | 381k | buf1[(i * 2) + 3] = buf0[i]; |
496 | 1.24k | else { |
497 | 1.24k | if (buf0[i] == 0x7F) { |
498 | 3 | emprintf1(pdev->memory, "PDFDocEncoding %x is undefined\n", buf0[i]); |
499 | 3 | code = gs_note_error(gs_error_rangecheck); |
500 | 3 | goto error; |
501 | 3 | } |
502 | 1.23k | else { |
503 | 1.23k | if (buf0[i] < 0x20) { |
504 | 553 | buf1[(i * 2) + 2] = PDFDocEncodingLookupLo[(buf0[i]) * 2]; |
505 | 553 | buf1[(i * 2) + 3] = PDFDocEncodingLookupLo[((buf0[i]) * 2) + 1]; |
506 | 553 | if (PDFDocEncodingLookupLo[((buf0[i]) * 2) + 1] == 0x00) { |
507 | 118 | emprintf1(pdev->memory, "PDFDocEncoding %x is undefined\n", buf0[i]); |
508 | 118 | code = gs_note_error(gs_error_rangecheck); |
509 | 118 | goto error; |
510 | 118 | } |
511 | 685 | } else { |
512 | 685 | buf1[(i * 2) + 2] = PDFDocEncodingLookupHi[(buf0[i] - 0x80) * 2]; |
513 | 685 | buf1[(i * 2) + 3] = PDFDocEncodingLookupHi[((buf0[i] - 0x80) * 2) + 1]; |
514 | 685 | if (PDFDocEncodingLookupHi[((buf0[i] - 0x80) * 2) + 1] == 0x00) { |
515 | 1 | emprintf1(pdev->memory, "PDFDocEncoding %x is undefined\n", buf0[i]); |
516 | 1 | code = gs_note_error(gs_error_rangecheck); |
517 | 1 | goto error; |
518 | 1 | } |
519 | 685 | } |
520 | 1.23k | } |
521 | 1.24k | } |
522 | 382k | } |
523 | 10.4k | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
524 | 10.4k | buf0 = buf1; |
525 | 10.4k | data_length = j = (j * 2) + 2; |
526 | 10.4k | } |
527 | 10.5k | { |
528 | | /* Its a Unicode (UTF-16BE) string, convert to UTF-8 */ |
529 | 10.5k | short *buf0b; |
530 | 10.5k | char *buf1, *buf1b; |
531 | 10.5k | int code; |
532 | | |
533 | | /* A single UTF-16 (2 bytes) can end up as 4 bytes in UTF-8 */ |
534 | 10.5k | buf1 = (char *)gs_alloc_bytes(pdev->memory, data_length * 2 * sizeof(unsigned char), |
535 | 10.5k | "pdf_xmp_write_translated"); |
536 | 10.5k | if (buf1 == NULL) { |
537 | 0 | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
538 | 0 | return_error(gs_error_VMerror); |
539 | 0 | } |
540 | 10.5k | buf1b = buf1; |
541 | | /* Skip the Byte Order Mark (0xfe 0xff) */ |
542 | 10.5k | buf0b = (short *)(buf0 + 2); |
543 | 10.5k | code = gs_ConvertUTF16((unsigned char *)buf0b, j - 2, (unsigned char **)&buf1b, data_length * 2 * sizeof(unsigned char)); |
544 | 10.5k | if (code < 0) { |
545 | 0 | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
546 | 0 | gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated"); |
547 | 0 | return code; |
548 | 0 | } |
549 | | |
550 | | /* s and write can be NULL in order to use this function to test whether the specified data can be converted to UTF8 */ |
551 | 10.5k | if (s && write) |
552 | 10.5k | write(s, (const byte*)buf1, buf1b - buf1); |
553 | | |
554 | 10.5k | gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated"); |
555 | 10.5k | } |
556 | 10.5k | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
557 | 10.5k | return 0; |
558 | | |
559 | 122 | error: |
560 | 122 | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
561 | 122 | gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated"); |
562 | 122 | return code; |
563 | 10.5k | } |
564 | | |
565 | | static int |
566 | | pdf_xmp_write_docinfo_item(gx_device_pdf *pdev, stream *s, const char *key, const char *default_value, |
567 | | void(*write)(stream *s, const byte *data, int data_length)) |
568 | 27.7k | { |
569 | 27.7k | const cos_value_t *v = cos_dict_find(pdev->Info, (const byte *)key, strlen(key)); |
570 | | |
571 | 27.7k | if (v != NULL && (v->value_type == COS_VALUE_SCALAR || |
572 | 10.7k | v->value_type == COS_VALUE_CONST)) { |
573 | 10.7k | if (v->contents.chars.size >= 2 && v->contents.chars.data[0] == '/') |
574 | 0 | return pdf_xmp_write_translated(pdev, s, v->contents.chars.data + 1, |
575 | 0 | v->contents.chars.size - 1, write); |
576 | 10.7k | else |
577 | 10.7k | if (v->contents.chars.size >= 2 && v->contents.chars.data[0] == '(') |
578 | 10.7k | return pdf_xmp_write_translated(pdev, s, v->contents.chars.data + 1, |
579 | 10.7k | v->contents.chars.size - 2, write); |
580 | 8 | else |
581 | 8 | return pdf_xmp_write_translated(pdev, s, v->contents.chars.data, |
582 | 8 | v->contents.chars.size, write); |
583 | 17.0k | } else { |
584 | 17.0k | stream_puts(s, default_value); |
585 | 17.0k | return 0; |
586 | 17.0k | } |
587 | 27.7k | } |
588 | | |
589 | | static uint64_t |
590 | | pdf_uuid_time(gx_device_pdf *pdev) |
591 | 18.3k | { |
592 | 18.3k | long *dt = pdev->uuid_time; /* In seconds since Jan. 1, 1980 and fraction in nanoseconds. */ |
593 | 18.3k | uint64_t t; |
594 | | |
595 | | /* UUIDs use time in 100ns ticks since Oct 15, 1582. */ |
596 | 18.3k | t = (uint64_t)10000000 * dt[0] + dt[0] / 100; /* since Jan. 1, 1980 */ |
597 | 18.3k | t += (uint64_t) (1000*1000*10) /* seconds */ |
598 | 18.3k | * (uint64_t) (60 * 60 * 24) /* days */ |
599 | 18.3k | * (uint64_t) (17+30+31+365*397+99); /* # of days */ |
600 | 18.3k | return t; |
601 | 18.3k | } |
602 | | |
603 | | static void writehex(char **p, ulong v, int l) |
604 | 201k | { |
605 | 201k | int i = l * 2; |
606 | 201k | static const char digit[] = "0123456789abcdef"; |
607 | | |
608 | 789k | for (; i--;) |
609 | 587k | *((*p)++) = digit[v >> (i * 4) & 15]; |
610 | 201k | } |
611 | | |
612 | | static void |
613 | | pdf_make_uuid(const byte node[6], uint64_t uuid_time, ulong time_seq, char *buf, int buf_length) |
614 | 18.3k | { |
615 | 18.3k | char b[45], *p = b; |
616 | 18.3k | ulong uuid_time_lo = (ulong)(uuid_time & 0xFFFFFFFF); /* MSVC 7.1.3088 */ |
617 | 18.3k | ushort uuid_time_md = (ushort)((uuid_time >> 32) & 0xFFFF); /* cannot compile this */ |
618 | 18.3k | ushort uuid_time_hi = (ushort)((uuid_time >> 48) & 0x0FFF); /* as function arguments. */ |
619 | | |
620 | 18.3k | writehex(&p, uuid_time_lo, 4); /* time_low */ |
621 | 18.3k | *(p++) = '-'; |
622 | 18.3k | writehex(&p, uuid_time_md, 2); /* time_mid */ |
623 | 18.3k | *(p++) = '-'; |
624 | 18.3k | writehex(&p, uuid_time_hi | (ushort)(1 << 12), 2); /* time_hi_and_version */ |
625 | 18.3k | *(p++) = '-'; |
626 | 18.3k | writehex(&p, (time_seq & 0x3F00) >> 8, 1); /* clock_seq_hi_and_reserved */ |
627 | 18.3k | writehex(&p, time_seq & 0xFF, 1); /* clock_seq & 0xFF */ |
628 | 18.3k | *(p++) = '-'; |
629 | 18.3k | writehex(&p, node[0], 1); |
630 | 18.3k | writehex(&p, node[1], 1); |
631 | 18.3k | writehex(&p, node[2], 1); |
632 | 18.3k | writehex(&p, node[3], 1); |
633 | 18.3k | writehex(&p, node[4], 1); |
634 | 18.3k | writehex(&p, node[5], 1); |
635 | 18.3k | *p = 0; |
636 | 18.3k | strncpy(buf, b, strlen(b) + 1); |
637 | 18.3k | } |
638 | | |
639 | | static int |
640 | | pdf_make_instance_uuid(gx_device_pdf *pdev, const byte digest[6], char *buf, int buf_length) |
641 | 9.18k | { |
642 | 9.18k | char URI_prefix[5] = "uuid:"; |
643 | | |
644 | 9.18k | memcpy(buf, URI_prefix, 5); |
645 | 9.18k | if (pdev->InstanceUUID.size) { |
646 | 0 | int l = min(buf_length - 6, pdev->InstanceUUID.size); |
647 | |
|
648 | 0 | memcpy(buf+5, pdev->InstanceUUID.data, l); |
649 | 0 | buf[l+5] = 0; |
650 | 0 | } else |
651 | 9.18k | pdf_make_uuid(digest, pdf_uuid_time(pdev), pdev->DocumentTimeSeq, buf + 5, buf_length - 5); |
652 | 9.18k | return 0; |
653 | 9.18k | } |
654 | | |
655 | | static int |
656 | | pdf_make_document_uuid(gx_device_pdf *pdev, const byte digest[6], char *buf, int buf_length) |
657 | 9.18k | { |
658 | 9.18k | char URI_prefix[5] = "uuid:"; |
659 | | |
660 | 9.18k | memcpy(buf, URI_prefix, 5); |
661 | 9.18k | if (pdev->DocumentUUID.size) { |
662 | 0 | int l = min(buf_length - 6, pdev->DocumentUUID.size); |
663 | |
|
664 | 0 | memcpy(buf+5, pdev->DocumentUUID.data, l); |
665 | 0 | buf[l+5] = 0; |
666 | 0 | } else |
667 | 9.18k | pdf_make_uuid(digest, pdf_uuid_time(pdev), pdev->DocumentTimeSeq, buf+5, buf_length - 5); |
668 | 9.18k | return 0; |
669 | 9.18k | } |
670 | | |
671 | | static const char dd[]={'\'', '\357', '\273', '\277', '\'', 0}; |
672 | | |
673 | | /* -------------------------------------------- */ |
674 | | |
675 | | /* Write Document metadata */ |
676 | | static int |
677 | | pdf_write_document_metadata(gx_device_pdf *pdev, const byte digest[6]) |
678 | 9.18k | { |
679 | 9.18k | char instance_uuid[45], document_uuid[45], cre_date_time[40], mod_date_time[40], date_time_buf[40]; |
680 | 9.18k | int cre_date_time_len, mod_date_time_len; |
681 | 9.18k | int code; |
682 | 9.18k | stream *s = pdev->strm; |
683 | | |
684 | 9.18k | code = pdf_make_instance_uuid(pdev, digest, instance_uuid, sizeof(instance_uuid)); |
685 | 9.18k | if (code < 0) |
686 | 0 | return code; |
687 | 9.18k | code = pdf_make_document_uuid(pdev, digest, document_uuid, sizeof(document_uuid)); |
688 | 9.18k | if (code < 0) |
689 | 0 | return code; |
690 | | |
691 | | /* PDF/A XMP reference recommends setting UUID to empty. If not empty must be a URI */ |
692 | 9.18k | if (pdev->PDFA != 0) |
693 | 0 | instance_uuid[0] = 0x00; |
694 | | |
695 | 9.18k | cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time)); |
696 | 9.18k | if (!cre_date_time_len) |
697 | 0 | cre_date_time_len = pdf_xmp_time(cre_date_time, sizeof(cre_date_time)); |
698 | 9.18k | else |
699 | 9.18k | cre_date_time_len = pdf_xmp_convert_time(cre_date_time, cre_date_time_len, date_time_buf, sizeof(date_time_buf)); |
700 | 9.18k | mod_date_time_len = pdf_get_docinfo_item(pdev, "/ModDate", mod_date_time, sizeof(mod_date_time)); |
701 | 9.18k | if (!mod_date_time_len) |
702 | 0 | mod_date_time_len = pdf_xmp_time(mod_date_time, sizeof(mod_date_time)); |
703 | 9.18k | else |
704 | 9.18k | mod_date_time_len = pdf_xmp_convert_time(mod_date_time, mod_date_time_len, date_time_buf, sizeof(date_time_buf)); |
705 | | |
706 | 9.18k | pdf_xml_ins_beg(s, "xpacket"); |
707 | 9.18k | pdf_xml_attribute_name(s, "begin"); |
708 | 9.18k | pdf_xml_copy(s, dd); |
709 | 9.18k | pdf_xml_attribute_name(s, "id"); |
710 | 9.18k | pdf_xml_attribute_value(s, "W5M0MpCehiHzreSzNTczkc9d"); |
711 | 9.18k | pdf_xml_ins_end(s); |
712 | 9.18k | pdf_xml_newline(s); |
713 | | |
714 | 9.18k | pdf_xml_copy(s, "<?adobe-xap-filters esc=\"CRLF\"?>\n"); |
715 | 9.18k | pdf_xml_copy(s, "<x:xmpmeta xmlns:x='adobe:ns:meta/'" |
716 | 9.18k | " x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'>\n"); |
717 | 9.18k | { |
718 | 9.18k | pdf_xml_copy(s, "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' " |
719 | 9.18k | "xmlns:iX='http://ns.adobe.com/iX/1.0/'>\n"); |
720 | 9.18k | { |
721 | | |
722 | 9.18k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
723 | 9.18k | pdf_xml_copy(s, " rdf:about=\"\""); |
724 | 9.18k | pdf_xml_attribute_name(s, "xmlns:pdf"); |
725 | 9.18k | pdf_xml_attribute_value(s, "http://ns.adobe.com/pdf/1.3/"); |
726 | | |
727 | 9.18k | if (cos_dict_find(pdev->Info, (const byte *)"/Keywords", 9)) { |
728 | 118 | pdf_xml_tag_end(s); |
729 | 118 | pdf_xml_tag_open_beg(s, "pdf:Producer"); |
730 | 118 | pdf_xml_tag_end(s); |
731 | 118 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Producer", "UnknownProducer", |
732 | 118 | pdf_xml_data_write); |
733 | 118 | if (code < 0) |
734 | 0 | return code; |
735 | 118 | pdf_xml_tag_close(s, "pdf:Producer"); |
736 | 118 | pdf_xml_newline(s); |
737 | | |
738 | 118 | pdf_xml_tag_open_beg(s, "pdf:Keywords"); |
739 | 118 | pdf_xml_tag_end(s); |
740 | 118 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Keywords", "Unknown", |
741 | 118 | pdf_xml_data_write); |
742 | 118 | if (code < 0) |
743 | 0 | return code; |
744 | 118 | pdf_xml_tag_close(s, "pdf:Keywords"); |
745 | 118 | pdf_xml_newline(s); |
746 | | |
747 | 118 | pdf_xml_tag_close(s, "rdf:Description"); |
748 | 118 | pdf_xml_newline(s); |
749 | 9.06k | } else { |
750 | 9.06k | if (cos_dict_find(pdev->Info, (const byte *)"/Trapped", 8)) { |
751 | 0 | pdf_xml_tag_end(s); |
752 | 0 | pdf_xml_tag_open_beg(s, "pdf:Producer"); |
753 | 0 | pdf_xml_tag_end(s); |
754 | 0 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Producer", "UnknownProducer", |
755 | 0 | pdf_xml_data_write); |
756 | 0 | if (code < 0) |
757 | 0 | return code; |
758 | 0 | pdf_xml_tag_close(s, "pdf:Producer"); |
759 | 0 | pdf_xml_newline(s); |
760 | |
|
761 | 0 | pdf_xml_tag_open_beg(s, "pdf:Trapped"); |
762 | 0 | pdf_xml_tag_end(s); |
763 | 0 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Trapped", "False", |
764 | 0 | pdf_xml_data_write); |
765 | 0 | if (code < 0) |
766 | 0 | return code; |
767 | 0 | pdf_xml_tag_close(s, "pdf:Trapped"); |
768 | 0 | pdf_xml_newline(s); |
769 | |
|
770 | 0 | pdf_xml_tag_close(s, "rdf:Description"); |
771 | 0 | pdf_xml_newline(s); |
772 | 9.06k | } else { |
773 | 9.06k | pdf_xml_attribute_name(s, "pdf:Producer"); |
774 | 9.06k | code = pdf_xmp_write_docinfo_item(pdev, s, "/Producer", "UnknownProducer", |
775 | 9.06k | pdf_xml_attribute_value_data); |
776 | 9.06k | if (code < 0) |
777 | 0 | return code; |
778 | 9.06k | pdf_xml_tag_end_empty(s); |
779 | 9.06k | pdf_xml_newline(s); |
780 | 9.06k | } |
781 | 9.06k | } |
782 | | |
783 | 9.18k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
784 | 9.18k | pdf_xml_copy(s, " rdf:about=\"\""); |
785 | 9.18k | pdf_xml_attribute_name(s, "xmlns:xmp"); |
786 | 9.18k | pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/"); |
787 | 9.18k | pdf_xml_tag_end(s); |
788 | 9.18k | if (!pdev->OmitInfoDate) { |
789 | 9.18k | { |
790 | 9.18k | pdf_xml_tag_open_beg(s, "xmp:ModifyDate"); |
791 | 9.18k | pdf_xml_tag_end(s); |
792 | 9.18k | mod_date_time[mod_date_time_len] = 0x00; |
793 | 9.18k | pdf_xml_copy(s, mod_date_time); |
794 | 9.18k | pdf_xml_tag_close(s, "xmp:ModifyDate"); |
795 | 9.18k | pdf_xml_newline(s); |
796 | 9.18k | } |
797 | 9.18k | { |
798 | 9.18k | pdf_xml_tag_open_beg(s, "xmp:CreateDate"); |
799 | 9.18k | pdf_xml_tag_end(s); |
800 | 9.18k | cre_date_time[cre_date_time_len] = 0x00; |
801 | 9.18k | pdf_xml_copy(s, cre_date_time); |
802 | 9.18k | pdf_xml_tag_close(s, "xmp:CreateDate"); |
803 | 9.18k | pdf_xml_newline(s); |
804 | 9.18k | } |
805 | 9.18k | { |
806 | 9.18k | pdf_xml_tag_open_beg(s, "xmp:MetadataDate"); |
807 | 9.18k | pdf_xml_tag_end(s); |
808 | 9.18k | cre_date_time[cre_date_time_len] = 0x00; |
809 | 9.18k | pdf_xml_copy(s, cre_date_time); |
810 | 9.18k | pdf_xml_tag_close(s, "xmp:MetadataDate"); |
811 | 9.18k | pdf_xml_newline(s); |
812 | 9.18k | } |
813 | 9.18k | } |
814 | 9.18k | { |
815 | 9.18k | pdf_xml_tag_open_beg(s, "xmp:CreatorTool"); |
816 | 9.18k | pdf_xml_tag_end(s); |
817 | 9.18k | code = pdf_xmp_write_docinfo_item(pdev, s, "/Creator", "UnknownApplication", |
818 | 9.18k | pdf_xml_data_write); |
819 | 9.18k | if (code < 0) |
820 | 50 | return code; |
821 | 9.13k | pdf_xml_tag_close(s, "xmp:CreatorTool"); |
822 | 9.13k | } |
823 | 0 | pdf_xml_tag_close(s, "rdf:Description"); |
824 | 9.13k | pdf_xml_newline(s); |
825 | | |
826 | 9.13k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
827 | 9.13k | pdf_xml_copy(s, " rdf:about=\"\""); |
828 | 9.13k | pdf_xml_attribute_name(s, "xmlns:xmpMM"); |
829 | 9.13k | pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/mm/"); |
830 | 9.13k | pdf_xml_attribute_name(s, "xmpMM:DocumentID"); |
831 | 9.13k | pdf_xml_attribute_value(s, document_uuid); |
832 | 9.13k | pdf_xml_tag_end_empty(s); |
833 | 9.13k | pdf_xml_newline(s); |
834 | | |
835 | 9.13k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
836 | 9.13k | pdf_xml_copy(s, " rdf:about=\"\""); |
837 | 9.13k | pdf_xml_attribute_name(s, "xmlns:xmpMM"); |
838 | 9.13k | pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/mm/"); |
839 | 9.13k | pdf_xml_attribute_name(s, "xmpMM:RenditionClass"); |
840 | 9.13k | pdf_xml_attribute_value(s, "default"); |
841 | 9.13k | pdf_xml_tag_end_empty(s); |
842 | 9.13k | pdf_xml_newline(s); |
843 | | |
844 | 9.13k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
845 | 9.13k | pdf_xml_copy(s, " rdf:about=\"\""); |
846 | 9.13k | pdf_xml_attribute_name(s, "xmlns:xmpMM"); |
847 | 9.13k | pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/mm/"); |
848 | 9.13k | pdf_xml_attribute_name(s, "xmpMM:VersionID"); |
849 | 9.13k | pdf_xml_attribute_value(s, "1"); |
850 | 9.13k | pdf_xml_tag_end_empty(s); |
851 | 9.13k | pdf_xml_newline(s); |
852 | | |
853 | 9.13k | pdf_xml_tag_open_beg(s, "rdf:Description"); |
854 | 9.13k | pdf_xml_copy(s, " rdf:about=\"\""); |
855 | 9.13k | pdf_xml_attribute_name(s, "xmlns:dc"); |
856 | 9.13k | pdf_xml_attribute_value(s, "http://purl.org/dc/elements/1.1/"); |
857 | 9.13k | pdf_xml_attribute_name(s, "dc:format"); |
858 | 9.13k | pdf_xml_attribute_value(s,"application/pdf"); |
859 | 9.13k | pdf_xml_tag_end(s); |
860 | 9.13k | { |
861 | 9.13k | pdf_xml_tag_open(s, "dc:title"); |
862 | 9.13k | { |
863 | 9.13k | pdf_xml_tag_open(s, "rdf:Alt"); |
864 | 9.13k | { |
865 | 9.13k | pdf_xml_tag_open_beg(s, "rdf:li"); |
866 | 9.13k | pdf_xml_attribute_name(s, "xml:lang"); |
867 | 9.13k | pdf_xml_attribute_value(s, "x-default"); |
868 | 9.13k | pdf_xml_tag_end(s); |
869 | 9.13k | { |
870 | 9.13k | code = pdf_xmp_write_docinfo_item(pdev, s, "/Title", "Untitled", |
871 | 9.13k | pdf_xml_data_write); |
872 | 9.13k | if (code < 0) |
873 | 72 | return code; |
874 | 9.13k | } |
875 | 9.05k | pdf_xml_tag_close(s, "rdf:li"); |
876 | 9.05k | } |
877 | 0 | pdf_xml_tag_close(s, "rdf:Alt"); |
878 | 9.05k | } |
879 | 0 | pdf_xml_tag_close(s, "dc:title"); |
880 | | |
881 | 9.05k | if (cos_dict_find(pdev->Info, (const byte *)"/Author", 7)) { |
882 | 168 | pdf_xml_tag_open(s, "dc:creator"); |
883 | 168 | { /* According to the PDF/A specification |
884 | | "it shall be represented by an ordered Text array of |
885 | | length one whose single entry shall consist |
886 | | of one or more names". */ |
887 | 168 | pdf_xml_tag_open(s, "rdf:Seq"); |
888 | 168 | { |
889 | 168 | pdf_xml_tag_open(s, "rdf:li"); |
890 | 168 | { |
891 | 168 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Author", "Unknown", |
892 | 168 | pdf_xml_data_write); |
893 | 168 | if (code < 0) |
894 | 0 | return code; |
895 | 168 | } |
896 | 168 | pdf_xml_tag_close(s, "rdf:li"); |
897 | 168 | } |
898 | 0 | pdf_xml_tag_close(s, "rdf:Seq"); |
899 | 168 | } |
900 | 0 | pdf_xml_tag_close(s, "dc:creator"); |
901 | 168 | } |
902 | 9.05k | if (cos_dict_find(pdev->Info, (const byte *)"/Subject", 8)) { |
903 | 14 | pdf_xml_tag_open(s, "dc:description"); |
904 | 14 | { |
905 | 14 | pdf_xml_tag_open(s, "rdf:Alt"); |
906 | 14 | { |
907 | 14 | pdf_xml_tag_open_beg(s, "rdf:li"); |
908 | 14 | pdf_xml_attribute_name(s, "xml:lang"); |
909 | 14 | pdf_xml_attribute_value(s, "x-default"); |
910 | 14 | pdf_xml_tag_end(s); |
911 | 14 | { |
912 | 14 | code = pdf_xmp_write_docinfo_item(pdev, s, "/Subject", "No Subject", |
913 | 14 | pdf_xml_data_write); |
914 | 14 | if (code < 0) |
915 | 0 | return code; |
916 | 14 | } |
917 | 14 | pdf_xml_tag_close(s, "rdf:li"); |
918 | 14 | } |
919 | 0 | pdf_xml_tag_close(s, "rdf:Alt"); |
920 | 14 | } |
921 | 0 | pdf_xml_tag_close(s, "dc:description"); |
922 | 14 | } |
923 | 9.05k | } |
924 | 9.05k | pdf_xml_tag_close(s, "rdf:Description"); |
925 | 9.05k | pdf_xml_newline(s); |
926 | 9.05k | if (pdev->PDFA != 0) { |
927 | 0 | pdf_xml_tag_open_beg(s, "rdf:Description"); |
928 | 0 | pdf_xml_copy(s, " rdf:about=\"\""); |
929 | 0 | pdf_xml_attribute_name(s, "xmlns:pdfaid"); |
930 | 0 | pdf_xml_attribute_value(s, "http://www.aiim.org/pdfa/ns/id/"); |
931 | 0 | pdf_xml_attribute_name(s, "pdfaid:part"); |
932 | 0 | switch(pdev->PDFA) { |
933 | 0 | case 1: |
934 | 0 | pdf_xml_attribute_value(s,"1"); |
935 | 0 | break; |
936 | 0 | case 2: |
937 | 0 | pdf_xml_attribute_value(s,"2"); |
938 | 0 | break; |
939 | 0 | case 3: |
940 | 0 | pdf_xml_attribute_value(s,"3"); |
941 | 0 | break; |
942 | 0 | } |
943 | 0 | pdf_xml_attribute_name(s, "pdfaid:conformance"); |
944 | 0 | pdf_xml_attribute_value(s,"B"); |
945 | 0 | pdf_xml_tag_end_empty(s); |
946 | 0 | } |
947 | 9.05k | if (pdev->PDFX > 3) { |
948 | 0 | pdf_xml_tag_open_beg(s, "rdf:Description"); |
949 | 0 | pdf_xml_copy(s, " rdf:about=\"\""); |
950 | 0 | pdf_xml_attribute_name(s, "xmlns:pdfxid"); |
951 | 0 | pdf_xml_attribute_value(s, "http://www.npes.org/pdfx/ns/id/"); |
952 | 0 | pdf_xml_attribute_name(s, "pdfxid:GTS_PDFXVersion"); |
953 | 0 | switch(pdev->PDFX) { |
954 | 0 | case 4: |
955 | 0 | pdf_xml_attribute_value(s,"PDF/X-4"); |
956 | 0 | break; |
957 | 0 | default: |
958 | 0 | break; |
959 | 0 | } |
960 | 0 | pdf_xml_tag_end_empty(s); |
961 | 0 | } |
962 | 9.05k | } |
963 | 9.05k | if (pdev->ExtensionMetadata) { |
964 | 0 | pdf_xml_copy(s, pdev->ExtensionMetadata); |
965 | 0 | } |
966 | 9.05k | pdf_xml_copy(s, "</rdf:RDF>\n"); |
967 | 9.05k | } |
968 | 0 | pdf_xml_copy(s, "</x:xmpmeta>\n"); |
969 | | |
970 | 9.05k | pdf_xml_copy(s, " \n"); |
971 | 9.05k | pdf_xml_copy(s, " \n"); |
972 | 9.05k | pdf_xml_copy(s, "<?xpacket end='w'?>"); |
973 | 9.05k | return 0; |
974 | 9.05k | } |
975 | | |
976 | | int |
977 | | pdf_document_metadata(gx_device_pdf *pdev) |
978 | 9.18k | { |
979 | 9.18k | if (pdev->CompatibilityLevel < 1.4) |
980 | 0 | return 0; |
981 | 9.18k | if (cos_dict_find_c_key(pdev->Catalog, "/Metadata")) |
982 | 0 | return 0; |
983 | | |
984 | 9.18k | if (pdev->ParseDSCCommentsForDocInfo || pdev->PreserveEPSInfo || pdev->PDFA) { |
985 | 9.18k | pdf_resource_t *pres; |
986 | 9.18k | char buf[20]; |
987 | 9.18k | byte digest[6] = {0,0,0,0,0,0}; |
988 | 9.18k | int code; |
989 | 9.18k | int options = DATA_STREAM_NOT_BINARY; |
990 | | |
991 | 9.18k | sflush(pdev->strm); |
992 | 9.18k | s_MD5C_get_digest(pdev->strm, digest, sizeof(digest)); |
993 | 9.18k | if (pdev->EncryptMetadata) |
994 | 9.18k | options |= DATA_STREAM_ENCRYPT; |
995 | 9.18k | code = pdf_open_aside(pdev, resourceMetadata, gs_no_id, &pres, true, options); |
996 | 9.18k | if (code < 0) |
997 | 0 | return code; |
998 | 9.18k | code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/Metadata", 9); |
999 | 9.18k | if (code < 0) { |
1000 | 0 | pdf_close_aside(pdev); |
1001 | 0 | return code; |
1002 | 0 | } |
1003 | 9.18k | code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Subtype", (const byte *)"/XML", 4); |
1004 | 9.18k | if (code < 0) { |
1005 | 0 | pdf_close_aside(pdev); |
1006 | 0 | return code; |
1007 | 0 | } |
1008 | 9.18k | code = pdf_write_document_metadata(pdev, digest); |
1009 | 9.18k | if (code < 0) { |
1010 | 122 | pdf_close_aside(pdev); |
1011 | 122 | return code; |
1012 | 122 | } |
1013 | 9.05k | code = pdf_close_aside(pdev); |
1014 | 9.05k | if (code < 0) |
1015 | 0 | return code; |
1016 | 9.05k | code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone); |
1017 | 9.05k | if (code < 0) |
1018 | 0 | return code; |
1019 | 9.05k | gs_snprintf(buf, sizeof(buf), "%"PRId64" 0 R", pres->object->id); |
1020 | 9.05k | pdf_record_usage(pdev, pres->object->id, resource_usage_part9_structure); |
1021 | | |
1022 | 9.05k | code = cos_dict_put_c_key_object(pdev->Catalog, "/Metadata", pres->object); |
1023 | 9.05k | if (code < 0) |
1024 | 0 | return code; |
1025 | | |
1026 | 9.05k | } |
1027 | 9.05k | return 0; |
1028 | 9.18k | } |
1029 | | |
1030 | | /* -------------------------------------------- */ |