Coverage Report

Created: 2025-06-24 07:01

/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, "&lt;"); l--; p++; break;
80
4
            case '>' : stream_puts(s, "&gt;"); l--; p++; break;
81
6
            case '&' : stream_puts(s, "&amp;"); l--; p++; break;
82
11
            case '\'': stream_puts(s, "&apos;"); l--; p++; break;
83
9
            case '"' : stream_puts(s, "&quot;"); 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
/* --------------------------------------------  */