Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/devices/vector/gdevpdfm.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
/* pdfmark processing for PDF-writing driver */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gsutil.h"   /* for bytes_compare */
23
#include "gdevpdfx.h"
24
#include "gdevpdfo.h"
25
#include "szlibx.h"
26
#include "slzwx.h"
27
#include "sbrotlix.h"
28
29
/* GC descriptors */
30
private_st_pdf_article();
31
32
/*
33
 * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark
34
 * operator in the input file.  Its "value" is the arguments of the operator,
35
 * passed through essentially unchanged:
36
 *      (key, value)*, CTM, type
37
 */
38
39
/*
40
 * Define an entry in a table of pdfmark-processing procedures.
41
 * (The actual table is at the end of this file, to avoid the need for
42
 * forward declarations for the procedures.)
43
 */
44
29.0k
#define PDFMARK_NAMEABLE 1  /* allows _objdef */
45
29.0k
#define PDFMARK_ODD_OK 2  /* OK if odd # of parameters */
46
21.5k
#define PDFMARK_KEEP_NAME 4  /* don't substitute reference for name */
47
                                /* in 1st argument */
48
29.0k
#define PDFMARK_NO_REFS 8  /* don't substitute references for names */
49
                                /* anywhere */
50
29.0k
#define PDFMARK_TRUECTM 16  /* pass the true CTM to the procedure, */
51
                                /* not the one transformed to reflect the default user space */
52
typedef struct pdfmark_name_s {
53
    const char *mname;
54
    pdfmark_proc((*proc));
55
    byte options;
56
} pdfmark_name;
57
58
/* ---------------- Public utilities ---------------- */
59
60
/* Compare a C string and a gs_param_string. */
61
bool
62
pdf_key_eq(const gs_param_string * pcs, const char *str)
63
893k
{
64
893k
    return (strlen(str) == pcs->size && pcs->data &&
65
893k
            !strncmp(str, (const char *)pcs->data, pcs->size));
66
893k
}
67
68
/* Scan an integer out of a parameter string. */
69
int
70
pdfmark_scan_int(const gs_param_string * pstr, int *pvalue)
71
563
{
72
563
#define MAX_INT_STR 20
73
563
    uint size = pstr->size;
74
563
    char str[MAX_INT_STR + 1];
75
76
563
    if (size > MAX_INT_STR)
77
0
        return_error(gs_error_limitcheck);
78
563
    memcpy(str, pstr->data, size);
79
563
    str[size] = 0;
80
563
    return (sscanf(str, "%d", pvalue) == 1 ? 0 :
81
563
            gs_note_error(gs_error_rangecheck));
82
563
#undef MAX_INT_STR
83
563
}
84
85
/* ---------------- Private utilities ---------------- */
86
87
/* Find a key in a dictionary. */
88
static bool
89
pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count,
90
                 gs_param_string * pstr)
91
2.04k
{
92
2.04k
    uint i;
93
94
5.61k
    for (i = 0; i < count; i += 2)
95
4.29k
        if (pdf_key_eq(&pairs[i], key)) {
96
726
            *pstr = pairs[i + 1];
97
726
            return true;
98
726
        }
99
1.32k
    pstr->data = 0;
100
1.32k
    pstr->size = 0;
101
1.32k
    return false;
102
2.04k
}
103
104
/*
105
 * Get the page number for a page referenced by number or as /Next or /Prev.
106
 * The result may be 0 if the page number is 0 or invalid.
107
 */
108
static int
109
pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr)
110
346
{
111
346
    int page = pdev->next_page + 1;
112
113
346
    if (pnstr->data == 0);
114
346
    else if (pdf_key_eq(pnstr, "/Next"))
115
0
        ++page;
116
346
    else if (pdf_key_eq(pnstr, "/Prev"))
117
0
        --page;
118
346
    else if (pdfmark_scan_int(pnstr, &page) < 0)
119
0
        page = 0;
120
346
    return page;
121
346
}
122
123
/*
124
 * This routine checks the page number is inside the valid range FirstPage->LastPage
125
 * and if it is, and FirstPage is not 0, it updates the page number by rebasing it to
126
 * the new FirstPage. If all conditions are met we also update the 'max_referred_page'
127
 * which we check during closedown, and emit a warning if a reference should somehow
128
 * point outside the valid range of pages in the document. This can happen if we reference
129
 * a destination page that we haven't created yet, and when we get to the end of the
130
 * job we discover we never did create it.
131
 */
132
static int
133
update_max_page_reference(gx_device_pdf * pdev, int *page)
134
346
{
135
346
    if (*page < pdev->FirstPage || (pdev->LastPage != 0 && *page > pdev->LastPage)) {
136
0
        emprintf1(pdev->memory, "Destination page %d lies outside the valid page range.\n", *page);
137
0
        return -1;
138
0
    }
139
346
    else {
140
346
        if (pdev->FirstPage != 0)
141
0
            *page = (*page - pdev->FirstPage) + 1;
142
143
346
        if (pdev->max_referred_page < *page)
144
285
            pdev->max_referred_page = *page;
145
346
    }
146
346
    return 0;
147
346
}
148
149
/* Construct a destination string specified by /Page and/or /View. */
150
/* Return 0 if none (but still fill in a default), 1 or 2 if present */
151
/* (1 if only one of /Page or /View, 2 if both), <0 if error. */
152
static int
153
pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev,
154
                  const char *Page_key, const char *View_key,
155
                  const gs_param_string * pairs, uint count, uint RequirePage)
156
833
{
157
833
    gs_param_string page_string, view_string;
158
833
    int present =
159
833
        pdfmark_find_key(Page_key, pairs, count, &page_string) +
160
833
        pdfmark_find_key(View_key, pairs, count, &view_string);
161
833
    int page=0;
162
833
    gs_param_string action;
163
833
    int len, code = 0;
164
165
833
    if (present || RequirePage)
166
346
        page = pdfmark_page_number(pdev, &page_string);
167
168
833
    if (view_string.size == 0)
169
487
        param_string_from_string(view_string, "[/XYZ null null null]");
170
833
    if (page == 0)
171
487
        strcpy(dstr, "[null ");
172
346
    else if (pdfmark_find_key("/Action", pairs, count, &action) &&
173
346
             pdf_key_eq(&action, "/GoToR")
174
346
        )
175
0
        gs_snprintf(dstr, MAX_DEST_STRING, "[%d ", page - 1);
176
346
    else {
177
346
        code = update_max_page_reference(pdev, &page);
178
346
        if (code < 0)
179
0
            return code;
180
346
        gs_snprintf(dstr, MAX_DEST_STRING, "[%"PRId64" 0 R ", pdf_page_id(pdev, page));
181
346
    }
182
833
    len = strlen(dstr);
183
833
    if (len + view_string.size > MAX_DEST_STRING)
184
0
        return_error(gs_error_limitcheck);
185
833
    if (view_string.data[0] != '[' ||
186
833
        view_string.data[view_string.size - 1] != ']'
187
833
        )
188
0
        return_error(gs_error_rangecheck);
189
833
    memcpy(dstr + len, view_string.data + 1, view_string.size - 1);
190
833
    dstr[len + view_string.size - 1] = 0;
191
833
    return present;
192
833
}
193
194
/*
195
 * If a named destination is specified by a string, convert it to a name,
196
 * update *dstr, and return 1; otherwise return 0.
197
 */
198
static int
199
pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
200
0
{
201
0
    const byte *data = dstr->data;
202
0
    uint size = dstr->size;
203
0
    if (size > MAX_DEST_STRING)
204
0
        return_error(gs_error_limitcheck);
205
0
    if (size == 0 || data[0] != '(')
206
0
        return 0;
207
    /****** HANDLE ESCAPES ******/
208
0
    memcpy(dest, data, size - 1);
209
0
    dest[0] = '/';
210
0
    dest[size - 1] = 0;
211
0
    dstr->data = (byte *)dest;
212
0
    dstr->size = size - 1;
213
0
    return 1;
214
0
}
215
216
/* Put pairs in a dictionary. */
217
static int
218
pdfmark_put_c_pair(cos_dict_t *pcd, const char *key,
219
                   const gs_param_string * pvalue)
220
756
{
221
756
    return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size);
222
756
}
223
static int
224
pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair)
225
10.6k
{
226
10.6k
    return cos_dict_put_string(pcd, pair->data, pair->size,
227
10.6k
                               pair[1].data, pair[1].size);
228
10.6k
}
229
230
/* Scan a Rect value. */
231
static int
232
pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str,
233
                  const gs_matrix * pctm)
234
567
{
235
567
    uint size = str->size;
236
567
    double v[4];
237
1.13k
#define MAX_RECT_STRING 100
238
567
    char chars[MAX_RECT_STRING + 3];
239
567
    int end_check;
240
241
567
    if (str->size > MAX_RECT_STRING)
242
0
        return_error(gs_error_limitcheck);
243
567
    memcpy(chars, str->data, size);
244
567
    strcpy(chars + size, " 0");
245
567
    if (sscanf(chars, "[%lg %lg %lg %lg]%d",
246
567
               &v[0], &v[1], &v[2], &v[3], &end_check) != 5
247
567
        )
248
0
        return_error(gs_error_rangecheck);
249
567
    gs_point_transform(v[0], v[1], pctm, &prect->p);
250
567
    gs_point_transform(v[2], v[3], pctm, &prect->q);
251
567
    return 0;
252
567
}
253
254
/* Make a Rect value. */
255
static void
256
pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect)
257
567
{
258
    /*
259
     * We have to use a stream and pprintf, rather than sprintf,
260
     * because printf formats can't express the PDF restrictions on
261
     * the form of the output.
262
     */
263
567
    stream s;
264
265
567
    s_init(&s, NULL);
266
567
    swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1);
267
567
    pprintg4(&s, "[%g %g %g %g]",
268
567
             prect->p.x, prect->p.y, prect->q.x, prect->q.y);
269
567
    str[stell(&s)] = 0;
270
567
}
271
272
670
#define MAX_BORDER_STRING 100
273
/* Write a transformed Border value on a stream. */
274
static int
275
pdfmark_write_border(stream *s, const gs_param_string *str,
276
                     const gs_matrix *pctm)
277
335
{
278
335
    int i;
279
280
2.73k
    for (i = 0; i < str->size; i++)
281
2.39k
        stream_putc(s, str->data[i]);
282
335
    return 0;
283
335
}
284
285
/* Put an element in a stream's dictionary. */
286
static int
287
cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value)
288
0
{
289
0
    return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value);
290
0
}
291
292
static int
293
setup_pdfmark_stream_no_compression(gx_device_psdf *pdev0,
294
                        cos_stream_t *pco)
295
0
{
296
    /* This function is for pdfwrite only. */
297
0
    gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
298
0
    gs_memory_t *mem = pdev->pdf_memory;
299
300
0
    pco->input_strm = cos_write_stream_alloc(pco, pdev,
301
0
                                  "setup_pdfmark_stream_compression");
302
0
    if (pco->input_strm == 0)
303
0
        return_error(gs_error_VMerror);
304
0
    if (!pdev->binary_ok) {
305
0
        stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
306
0
                          "setup_pdfmark_stream_compression");
307
0
        if (ss == 0)
308
0
            return_error(gs_error_VMerror);
309
0
        if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
310
0
            gs_free_object(mem, ss, "setup_image_compression");
311
0
            return_error(gs_error_VMerror);
312
0
        }
313
0
    }
314
0
    return 0;
315
0
}
316
317
/* Setup pdfmak stream compression. */
318
static int
319
setup_pdfmark_stream_compression(gx_device_psdf *pdev0,
320
                        cos_stream_t *pco)
321
204
{
322
    /* This function is for pdfwrite only. */
323
204
    gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
324
204
    gs_memory_t *mem = pdev->pdf_memory;
325
204
    static const pdf_filter_names_t fnames = {
326
204
        PDF_FILTER_NAMES
327
204
    };
328
204
    stream_state *st;
329
204
    const stream_template *templat;
330
331
204
    if (pdev->CompressStreams) {
332
204
        if(pdev->version >= psdf_version_ll3) {
333
196
            if (pdev->UseBrotli)
334
0
                templat = &s_brotliE_template;
335
196
            else
336
196
                templat = &s_zlibE_template;
337
196
        } else
338
8
            templat = &s_LZWE_template;
339
204
    } else
340
0
        return 0;
341
342
204
    pco->input_strm = cos_write_stream_alloc(pco, pdev,
343
204
                                  "setup_pdfmark_stream_compression");
344
204
    if (pco->input_strm == 0)
345
0
        return_error(gs_error_VMerror);
346
204
    if (!pdev->binary_ok) {
347
8
        stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
348
8
                          "setup_pdfmark_stream_compression");
349
8
        if (ss == 0)
350
0
            return_error(gs_error_VMerror);
351
8
        if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
352
0
            gs_free_object(mem, ss, "setup_image_compression");
353
0
            return_error(gs_error_VMerror);
354
0
        }
355
8
    }
356
204
    st = s_alloc_state(mem, templat->stype,
357
204
                            "setup_pdfmark_stream_compression");
358
204
    if (st == 0)
359
0
        return_error(gs_error_VMerror);
360
204
    if (templat->set_defaults)
361
204
        (*templat->set_defaults) (st);
362
204
    if (s_add_filter(&pco->input_strm, templat, st, mem) == 0) {
363
0
        gs_free_object(mem, st, "setup_image_compression");
364
0
        return_error(gs_error_VMerror);
365
0
    }
366
204
    return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames);
367
204
}
368
369
static int
370
pdfmark_bind_named_object(gx_device_pdf *pdev, const gs_const_string *objname,
371
                          pdf_resource_t **pres)
372
0
{
373
0
    int code;
374
375
0
    if (objname != NULL && objname->size) {
376
0
        const cos_value_t *v = cos_dict_find(pdev->local_named_objects, objname->data, objname->size);
377
378
0
        if (v != NULL) {
379
0
            if (v->value_type == COS_VALUE_OBJECT) {
380
0
                if (cos_type(v->contents.object) == &cos_generic_procs) {
381
                    /* The object was referred but not defined.
382
                       Use the old object id.
383
                       The old object stub to be dropped. */
384
0
                    pdf_reserve_object_id(pdev, *pres, v->contents.object->id);
385
0
                } else if (!v->contents.object->written) {
386
                    /* We can't know whether the old object was referred or not.
387
                       Write it out for a consistent result in any case. */
388
0
                    code = cos_write_object(v->contents.object, pdev, resourceOther);
389
390
0
                    if (code < 0)
391
0
                        return code;
392
0
                    v->contents.object->written = true;
393
0
                }
394
0
            } else
395
0
                return_error(gs_error_rangecheck); /* Must not happen. */
396
0
        }
397
0
    }
398
0
    if ((*pres)->object->id == -1) {
399
0
        if(objname != NULL && objname->size)
400
0
            code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, false);
401
0
        else
402
0
            code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, true);
403
0
        (*pres)->where_used |= pdev->used_mask;
404
0
        if (code < 0)
405
0
            return code;
406
0
    } else {
407
        /* Unfortunately we can't apply pdf_substitute_resource,
408
           because the object may already be referred by its id.
409
           Redundant objects may happen in this case.
410
           For better results users should define objects before usage.
411
         */
412
0
    }
413
0
    if (objname != NULL && objname->size) {
414
0
        cos_value_t value;
415
416
0
        code = cos_dict_put(pdev->local_named_objects, objname->data,
417
0
                            objname->size, cos_object_value(&value, (cos_object_t *)(*pres)->object));
418
0
        if (code < 0)
419
0
            return code;
420
0
    }
421
0
    return 0;
422
0
}
423
424
/* ---------------- Miscellaneous pdfmarks ---------------- */
425
426
/*
427
 * Create the dictionary for an annotation or outline.  For some
428
 * unfathomable reason, PDF requires the following key substitutions
429
 * relative to pdfmarks:
430
 *   In annotation and link dictionaries:
431
 *     /Action => /A, /Color => /C, /Title => /T
432
 *   In outline directionaries:
433
 *     /Action => /A, but *not* /Color or /Title
434
 *   In Action subdictionaries:
435
 *     /Dest => /D, /File => /F, /Subtype => /S
436
 * and also the following substitutions:
437
 *     /Action /Launch /File xxx =>
438
 *       /A << /S /Launch /F xxx >>
439
 *     /Action /GoToR /File xxx /Dest yyy =>
440
 *       /A << /S /GoToR /F xxx /D yyy' >>
441
 *     /Action /Article /Dest yyy =>
442
 *       /A << /S /Thread /D yyy' >>
443
 *     /Action /GoTo => drop the Action key
444
 * Also, \n in Contents strings must be replaced with \r.
445
 * Also, an outline dictionary with no action, Dest, Page, or View has an
446
 * implied GoTo action with Dest = [{ThisPage} /XYZ null null null].
447
 * Note that for Thread actions, the Dest is not a real destination,
448
 * and must not be processed as one.
449
 *
450
 * We always treat /A and /F as equivalent to /Action and /File
451
 * respectively.  The pdfmark and PDF documentation is so confused on the
452
 * issue of when the long and short names should be used that we only give
453
 * this a 50-50 chance of being right.
454
 *
455
 * Note that we must transform Rect and Border coordinates.
456
 */
457
458
typedef struct ao_params_s {
459
    gx_device_pdf *pdev;  /* for pdfmark_make_dest */
460
    const char *subtype;  /* default Subtype in top-level dictionary */
461
    int src_pg;   /* set to SrcPg - 1 if any */
462
} ao_params_t;
463
static int
464
pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd,
465
                     const gs_param_string * pairs, uint count,
466
                     const gs_matrix * pctm, ao_params_t * params,
467
                     bool for_outline)
468
765
{
469
765
    const gs_param_string *Action = 0;
470
765
    const gs_param_string *File = 0;
471
765
    const gs_param_string *URI = 0;
472
765
    gs_param_string Dest;
473
765
    gs_param_string Subtype;
474
765
    uint i;
475
765
    int code;
476
765
    char dest[MAX_DEST_STRING];
477
765
    bool coerce_dest = false;
478
479
765
    Subtype.data = 0;
480
765
    Subtype.size = 0;
481
765
    Dest.data = 0;
482
765
    Dest.size = 0;
483
765
    if (params->subtype)
484
548
        param_string_from_string(Subtype, params->subtype);
485
217
    else
486
217
        Subtype.data = 0;
487
6.76k
    for (i = 0; i < count; i += 2) {
488
6.00k
        const gs_param_string *pair = &pairs[i];
489
6.00k
        unsigned int src_pg;
490
491
6.00k
        if (pdf_key_eq(pair, "/SrcPg")){
492
0
            unsigned char *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, (pair[1].size + 1) * sizeof(unsigned char),
493
0
                        "pdf_xmp_write_translated");
494
495
0
            if (buf0 == NULL)
496
0
                return_error(gs_error_VMerror);
497
498
0
            memcpy(buf0, pair[1].data, pair[1].size);
499
0
            buf0[pair[1].size] = 0x00;
500
0
            if (sscanf((char *)buf0, "%ud", &src_pg) == 1)
501
0
                params->src_pg = src_pg - 1;
502
0
            gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
503
0
        }
504
6.00k
        else if (!for_outline && pdf_key_eq(pair, "/Color"))
505
0
            pdfmark_put_c_pair(pcd, "/C", pair + 1);
506
6.00k
        else if (!for_outline && pdf_key_eq(pair, "/Title"))
507
0
            pdfmark_put_c_pair(pcd, "/T", pair + 1);
508
6.00k
        else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A"))
509
218
            Action = pair;
510
        /* Previously also catered for '/F', but at the top level (outside an
511
         * Action dict which is handled below), a /F can only be the Flags for
512
         * the annotation, not a File or JavaScript action.
513
         */
514
5.78k
        else if (pdf_key_eq(pair, "/File") /* || pdf_key_eq(pair, "/F")*/)
515
0
            File = pair;
516
5.78k
        else if (pdf_key_eq(pair, "/Dest")) {
517
0
            Dest = pair[1];
518
0
            coerce_dest = true;
519
0
        }
520
5.78k
        else if (pdf_key_eq(pair, "/URI")) {
521
0
            URI = pair;   /* save it for placing into the Action dict */
522
0
        }
523
5.78k
        else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) {
524
            /* Make a destination even if this is for an outline. */
525
416
            if (Dest.data == 0) {
526
208
                code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
527
208
                                         pairs, count, 0);
528
208
                if (code >= 0) {
529
208
                    param_string_from_string(Dest, dest);
530
208
                    if (for_outline)
531
192
                        coerce_dest = false;
532
208
                } else {
533
0
                    emprintf(pdev->memory, "   **** Warning: Outline has invalid link that was discarded.\n");
534
0
                    return gs_note_error(gs_error_rangecheck);
535
0
                }
536
208
            }
537
5.36k
        } else if (pdf_key_eq(pair, "/Subtype"))
538
548
            Subtype = pair[1];
539
        /*
540
         * We also have to replace all occurrences of \n in Contents
541
         * strings with \r.  Unfortunately, they probably have already
542
         * been converted to \012....
543
         */
544
4.82k
        else if (pdf_key_eq(pair, "/Contents")) {
545
182
            byte *cstr;
546
182
            uint csize = pair[1].size;
547
182
            cos_value_t *pcv;
548
182
            uint i, j;
549
550
            /*
551
             * Copy the string into value storage, then update it in place.
552
             */
553
182
            pdfmark_put_pair(pcd, pair);
554
            /* Break const so we can update the (copied) string. */
555
182
            pcv = (cos_value_t *)cos_dict_find_c_key(pcd, "/Contents");
556
182
            if (pcv == NULL)
557
0
                return_error(gs_error_ioerror); /* shouldn't be possible */
558
182
            cstr = pcv->contents.chars.data;
559
            /* Loop invariant: j <= i < csize. */
560
80.2k
            for (i = j = 0; i < csize;)
561
80.0k
                if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) &&
562
80.0k
                    (i == 0 || cstr[i - 1] != '\\')
563
80.0k
                    ) {
564
2
                    cstr[j] = '\\', cstr[j + 1] = 'r';
565
2
                    i += 2, j += 2;
566
80.0k
                } else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) &&
567
80.0k
                           (i == 0 || cstr[i - 1] != '\\')
568
80.0k
                    ) {
569
0
                    cstr[j] = '\\', cstr[j + 1] = 'r';
570
0
                    i += 4, j += 2;
571
0
                } else
572
80.0k
                    cstr[j++] = cstr[i++];
573
182
            if (j != i) {
574
0
                pcv->contents.chars.data =
575
0
                gs_resize_string(pdev->pdf_memory, cstr, csize, j,
576
0
                                 "pdfmark_put_ao_pairs");
577
0
                pcv->contents.chars.size = j;
578
0
            }
579
4.63k
        } else if (pdf_key_eq(pair, "/L")) {
580
21
            gs_rect rect;
581
21
            char rstr[MAX_RECT_STRING];
582
21
            int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
583
21
            if (code < 0)
584
0
                return code;
585
21
            pdfmark_make_rect(rstr, &rect);
586
21
            cos_dict_put_c_key_string(pcd, "/L", (byte *)rstr,
587
21
                                      strlen(rstr));
588
4.61k
        } else if (pdf_key_eq(pair, "/Rect")) {
589
546
            gs_rect rect;
590
546
            char rstr[MAX_RECT_STRING];
591
546
            int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
592
593
546
            if (code < 0)
594
0
                return code;
595
546
            pdfmark_make_rect(rstr, &rect);
596
546
            cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr,
597
546
                                      strlen(rstr));
598
4.07k
        } else if (pdf_key_eq(pair, "/Border")) {
599
335
            stream s;
600
335
            char bstr[MAX_BORDER_STRING + 1];
601
335
            int code;
602
603
335
            s_init(&s, NULL);
604
335
            swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1);
605
335
            code = pdfmark_write_border(&s, pair + 1, pctm);
606
335
            if (code < 0)
607
0
                return code;
608
335
            if (stell(&s) > MAX_BORDER_STRING)
609
0
                return_error(gs_error_limitcheck);
610
335
            bstr[stell(&s)] = 0;
611
335
            cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr,
612
335
                                      strlen(bstr));
613
3.73k
        } else if (for_outline && pdf_key_eq(pair, "/Count"))
614
3.73k
            DO_NOTHING;
615
3.51k
        else {
616
3.51k
            int i, j=0;
617
3.51k
            unsigned char *temp, *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, pair[1].size * 2 * sizeof(unsigned char),
618
3.51k
                        "pdf_xmp_write_translated");
619
3.51k
            if (buf0 == NULL)
620
0
                return_error(gs_error_VMerror);
621
129k
            for (i = 0; i < pair[1].size; i++) {
622
126k
                byte c = pair[1].data[i];
623
624
126k
                buf0[j++] = c;
625
                /* Acrobat doesn't like the 'short' escapes. and wants them as octal....
626
                 * I beliwvw this should be considered an Acrtobat bug, either escapes
627
                 * can be used, or not, we shouldn't have to force them to octal.
628
                 */
629
126k
                if (c == '\\') {
630
3.30k
                    switch(pair[1].data[i + 1]) {
631
0
                        case 'b':
632
0
                            buf0[j++] = '0';
633
0
                            buf0[j++] = '1';
634
0
                            buf0[j++] = '0';
635
0
                            i++;
636
0
                            break;
637
0
                        case 'f':
638
0
                            buf0[j++] = '0';
639
0
                            buf0[j++] = '1';
640
0
                            buf0[j++] = '4';
641
0
                            i++;
642
0
                            break;
643
109
                        case 'n':
644
109
                            buf0[j++] = '0';
645
109
                            buf0[j++] = '1';
646
109
                            buf0[j++] = '2';
647
109
                            i++;
648
109
                            break;
649
4
                        case 'r':
650
4
                            buf0[j++] = '0';
651
4
                            buf0[j++] = '1';
652
4
                            buf0[j++] = '5';
653
4
                            i++;
654
4
                            break;
655
0
                        case 't':
656
0
                            buf0[j++] = '0';
657
0
                            buf0[j++] = '1';
658
0
                            buf0[j++] = '1';
659
0
                            i++;
660
0
                            break;
661
2
                        case '\\':
662
2
                            buf0[j++] = '1';
663
2
                            buf0[j++] = '3';
664
2
                            buf0[j++] = '4';
665
2
                            i++;
666
2
                            break;
667
3.18k
                        default:
668
3.18k
                            break;
669
3.30k
                    }
670
3.30k
                }
671
126k
            }
672
3.51k
            i = pair[1].size;
673
3.51k
            temp = (unsigned char *)pair[1].data;
674
3.51k
            ((gs_param_string *)pair)[1].data = buf0;
675
3.51k
            ((gs_param_string *)pair)[1].size = j;
676
677
3.51k
            pdfmark_put_pair(pcd, pair);
678
679
3.51k
            ((gs_param_string *)pair)[1].data = temp;
680
3.51k
            ((gs_param_string *)pair)[1].size = i;
681
3.51k
            gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
682
3.51k
        }
683
6.00k
    }
684
765
    if (!for_outline && pdf_key_eq(&Subtype, "/Link")) {
685
213
        if (Action) {
686
            /* Don't delete the Dest for GoTo or file-GoToR. */
687
197
            if (pdf_key_eq(Action + 1, "/GoTo") ||
688
197
                (File && pdf_key_eq(Action + 1, "/GoToR"))
689
197
                )
690
197
                DO_NOTHING;
691
197
            else
692
197
                Dest.data = 0;
693
197
        }
694
213
    }
695
696
    /* Now handle the deferred keys. */
697
765
    if (Action) {
698
218
        const byte *astr = Action[1].data;
699
218
        const uint asize = Action[1].size;
700
701
218
        if ((File != 0 || Dest.data != 0 || URI != 0) &&
702
218
            (pdf_key_eq(Action + 1, "/Launch") ||
703
0
             (pdf_key_eq(Action + 1, "/GoToR") && File) ||
704
0
             pdf_key_eq(Action + 1, "/Article"))
705
218
            ) {
706
0
            cos_dict_t *adict;
707
0
            cos_value_t avalue;
708
709
0
            if (pdf_key_eq(Action + 1, "/Launch") && pdev->PDFA != 0) {
710
0
                switch (pdev->PDFACompatibilityPolicy) {
711
                    /* Default behaviour matches Adobe Acrobat, warn and continue,
712
                     * output file will not be PDF/A compliant
713
                     */
714
0
                    case 0:
715
0
                        emprintf(pdev->memory,
716
0
                                 "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n");
717
0
                        pdev->AbortPDFAX = true;
718
0
                        pdev->PDFA = 0;
719
0
                        break;
720
                        /* Since the annotation would break PDF/A compatibility, do not
721
                         * include it, but warn the user that it has been dropped.
722
                         */
723
0
                    case 1:
724
0
                        emprintf(pdev->memory,
725
0
                                 "Launch annotations not permitted in PDF/A,  cannot drop annotation, aborting conversion\n");
726
0
                        return_error (gs_error_typecheck);
727
0
                        break;
728
0
                    case 2:
729
0
                        emprintf(pdev->memory,
730
0
                                 "Launch annotations not permitted in PDF/A,  aborting conversion\n");
731
0
                        return_error (gs_error_typecheck);
732
0
                        break;
733
0
                    default:
734
0
                        emprintf(pdev->memory,
735
0
                                 "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
736
0
                        pdev->AbortPDFAX = true;
737
0
                        pdev->PDFA = 0;
738
0
                        break;
739
0
                }
740
0
            }
741
742
0
            adict = cos_dict_alloc(pdev, "action dict");
743
0
            if (adict == 0)
744
0
                return_error(gs_error_VMerror);
745
0
            if (!for_outline) {
746
                /* We aren't sure whether this is really needed.... */
747
0
                code = cos_dict_put_c_strings(adict, "/Type", "/Action");
748
0
                if (code < 0)
749
0
                    return code;
750
0
            }
751
0
            if (pdf_key_eq(Action + 1, "/Article")) {
752
0
                code = cos_dict_put_c_strings(adict, "/S", "/Thread");
753
0
                if (code < 0)
754
0
                    return code;
755
0
                coerce_dest = false; /* Dest is not a real destination */
756
0
            }
757
0
            else
758
0
                pdfmark_put_c_pair(adict, "/S", Action + 1);
759
0
            if (Dest.data) {
760
0
                if (coerce_dest)
761
0
                    pdfmark_coerce_dest(&Dest, dest);
762
0
                pdfmark_put_c_pair(adict, "/D", &Dest);
763
0
                Dest.data = 0;  /* so we don't write it again */
764
0
            }
765
0
            if (File) {
766
0
                pdfmark_put_c_pair(adict, "/F", File + 1);
767
0
                File = 0; /* so we don't write it again */
768
0
            }
769
0
            if (URI) {
770
                /* Adobe Distiller puts a /URI key from pdfmark into the */
771
                /* Action dict with /S /URI as Subtype */
772
0
                pdfmark_put_pair(adict, URI);
773
0
                code = cos_dict_put_c_strings(adict, "/S", "/URI");
774
0
                if (code < 0)
775
0
                    return code;
776
0
            }
777
0
            cos_dict_put(pcd, (const byte *)"/A", 2,
778
0
                         COS_OBJECT_VALUE(&avalue, adict));
779
218
        } else if (asize >= 4 && !memcmp(astr, "<<", 2)) {
780
            /* Replace occurrences of /Dest, /File, and /Subtype. */
781
106
            const byte *scan = astr + 2;
782
106
            const byte *end = astr + asize;
783
106
            gs_param_string key, value;
784
106
            cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
785
106
            cos_value_t avalue;
786
106
            int code = 0;
787
788
106
            if (adict == 0)
789
0
                return_error(gs_error_VMerror);
790
106
            if (URI) {
791
                /* Adobe Distiller puts a /URI key from pdfmark into the */
792
                /* Action dict with /S /URI as Subtype */
793
0
                pdfmark_put_pair(adict, URI);
794
0
                if(cos_dict_put_c_strings(adict, "/S", "/URI") < 0)
795
0
                    return code;
796
0
            }
797
384
            while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) {
798
384
                key.size = scan - key.data;
799
384
                if (key.data[0] != '/' ||
800
384
                    (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1)
801
106
                    break;
802
278
                value.size = scan - value.data;
803
278
                if (pdev->PDFA != 0 && (pdf_key_eq(&key, "/Subtype") || pdf_key_eq(&key, "/S"))) {
804
0
                    if (pdf_key_eq(&value, "/Launch")) {
805
0
                        switch (pdev->PDFACompatibilityPolicy) {
806
                            /* Default behaviour matches Adobe Acrobat, warn and continue,
807
                             * output file will not be PDF/A compliant
808
                             */
809
0
                            case 0:
810
0
                                emprintf(pdev->memory,
811
0
                                         "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n");
812
0
                                pdev->AbortPDFAX = true;
813
0
                                pdev->PDFA = 0;
814
0
                                break;
815
                                /* Since the annotation would break PDF/A compatibility, do not
816
                                 * include it, but warn the user that it has been dropped.
817
                                 */
818
0
                            case 1:
819
0
                                emprintf(pdev->memory,
820
0
                                         "Launch annotations not permitted in PDF/A,  cannot drop annotation, aborting conversion\n");
821
0
                                gs_free_object(pdev->memory->stable_memory, adict,  "action dict");
822
0
                                return_error (gs_error_typecheck);
823
0
                                break;
824
0
                            case 2:
825
0
                                emprintf(pdev->memory,
826
0
                                         "Launch annotations not permitted in PDF/A,  aborting conversion\n");
827
0
                                gs_free_object(pdev->memory->stable_memory, adict,  "action dict");
828
0
                                return_error (gs_error_typecheck);
829
0
                                break;
830
0
                            default:
831
0
                                emprintf(pdev->memory,
832
0
                                         "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
833
0
                                pdev->AbortPDFAX = true;
834
0
                                pdev->PDFA = 0;
835
0
                                break;
836
0
                        }
837
0
                    }
838
0
                }
839
278
                if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) {
840
20
                    param_string_from_string(key, "/D");
841
20
                    if (value.data[0] == '(' && pdev->CompatibilityLevel < 1.2) {
842
0
                        int i;
843
844
0
                        for (i = 0;i < value.size; i++) {
845
0
                            if (value.data[i] == '\\') {
846
0
                                emprintf(pdev->memory,
847
0
                                         "Link Destination contains characters which cannot be represented in a name.\nDestinations cannot be strings in versions prior to PDF 1.2. Annotation removed in the output.\n");
848
0
                                return gs_error_typecheck;
849
0
                            }
850
0
                        }
851
                        /****** FIXME: DETECT ESCAPES ******/
852
0
                        pdfmark_coerce_dest(&value, dest);
853
0
                    }
854
258
                } else if (pdf_key_eq(&key, "/File"))
855
0
                    param_string_from_string(key, "/F");
856
258
                else if (pdf_key_eq(&key, "/Subtype"))
857
0
                    param_string_from_string(key, "/S");
858
278
                cos_dict_put_string(adict, key.data, key.size,
859
278
                                    value.data, value.size);
860
278
            }
861
106
            if (code <= 0 || !pdf_key_eq(&key, ">>")) {
862
0
                cos_free((cos_object_t *)adict, "action dict");
863
0
                return_error(gs_error_rangecheck);
864
0
            }
865
106
            cos_dict_put(pcd, (const byte *)"/A", 2,
866
106
                         COS_OBJECT_VALUE(&avalue, adict));
867
112
        } else if (pdf_key_eq(Action + 1, "/GoTo"))
868
0
            pdfmark_put_pair(pcd, Action);
869
112
        else if (Action[1].size < 30) {
870
            /* Hack: we could substitute names in pdfmark_process,
871
               now should recognize whether it was done.
872
               Not a perfect method though.
873
               Go with it for a while. */
874
112
            char buf[30];
875
112
            int d0, d1;
876
877
112
            if (Action[1].size > 29)
878
0
                return_error(gs_error_rangecheck);
879
112
            memcpy(buf, Action[1].data, Action[1].size);
880
112
            buf[Action[1].size] = 0;
881
112
            if (sscanf(buf, "%d %d R", &d0, &d1) == 2)
882
112
                pdfmark_put_pair(pcd, Action);
883
112
        }
884
218
    }
885
    /*
886
     * If we have /Dest or /File without the right kind of action,
887
     * simply write it at the top level.  This doesn't seem right,
888
     * but I'm not sure what else to do.
889
     */
890
765
    if (Dest.data) {
891
208
        if (coerce_dest)
892
0
            pdfmark_coerce_dest(&Dest, dest);
893
        /*
894
         * For PDF 1.2 or better  we write a Names tree, but in this case the names
895
         * are required to be (counter-intuitively) strings, NOT name objects....
896
         */
897
208
        if (pdev->CompatibilityLevel > 1.1) {
898
208
            gs_param_string DestString;
899
208
            int i = 0, j;
900
208
            char *D;
901
902
            /*
903
             * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
904
             * I suspect we no longer need that, but here we remove the escaping NULLs
905
             */
906
208
            if (Dest.size > 3 && Dest.data[0] == 0x00 && Dest.data[1] == 0x00 && Dest.data[Dest.size - 1] == 0x00) {
907
0
                i = 2;
908
0
                if (Dest.size > 5 && Dest.data[2] == 0x00 && Dest.data[3] == 0x00)
909
0
                    i += 2;
910
                /* If it has preceeding NULLs, then it has a terminating NULL as well, get rid of that too */
911
0
                Dest.size--;
912
0
            }
913
914
208
            if (Dest.data[i] == '/') {
915
0
                i++;
916
917
0
                DestString.data = gs_alloc_bytes(pdev->memory->stable_memory, (Dest.size * 2) + 1, "DEST pdfmark temp string");
918
0
                if (DestString.data == 0)
919
0
                    return_error(gs_error_VMerror);
920
0
                DestString.size = (Dest.size * 2) + 1;
921
0
                DestString.persistent = 0;
922
0
                D = (char *)DestString.data;
923
0
                D[0] = '(';
924
0
                for (j=1;i<Dest.size;i++, j++) {
925
0
                    if (Dest.data[i] == '(' || Dest.data[i] == ')') {
926
0
                        D[j++] = '\\';
927
0
                    }
928
0
                    D[j] = Dest.data[i];
929
0
                }
930
0
                D[j++] = ')';
931
0
                DestString.size = j;
932
0
                pdfmark_put_c_pair(pcd, "/Dest", &DestString);
933
0
                gs_free_object(pdev->memory->stable_memory, D, "DEST pdfmark temp string");
934
0
            } else
935
208
                pdfmark_put_c_pair(pcd, "/Dest", &Dest);
936
208
        } else
937
0
            pdfmark_put_c_pair(pcd, "/Dest", &Dest);
938
557
    } else if (for_outline && !Action) {
939
        /* Make an implicit destination. */
940
4
        char dstr[1 + (sizeof(int64_t) * 8 / 3 + 1) + 25 + 1];
941
4
        int64_t page_id = pdf_page_id(pdev, pdev->next_page + 1);
942
943
4
        gs_snprintf(dstr, MAX_DEST_STRING, "[%"PRId64" 0 R /XYZ null null null]", page_id);
944
4
        cos_dict_put_c_key_string(pcd, "/Dest", (const unsigned char*) dstr,
945
4
                                  strlen(dstr));
946
4
    }
947
765
    if (File)
948
0
        pdfmark_put_pair(pcd, File);
949
765
    if (Subtype.data)
950
548
        pdfmark_put_c_pair(pcd, "/Subtype", &Subtype);
951
765
    return 0;
952
765
}
953
954
/* Copy an annotation dictionary. */
955
static int
956
pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
957
              const gs_matrix * pctm, const gs_param_string *objname,
958
              const char *subtype)
959
548
{
960
548
    ao_params_t params;
961
548
    cos_dict_t *pcd;
962
548
    int page_index = pdev->next_page;
963
548
    cos_array_t *annots;
964
548
    cos_value_t value;
965
548
    int code;
966
967
    /* Annotations are only permitted in PDF/A if they have the
968
     * Print flag enabled, so we need to prescan for that here.
969
     */
970
548
    if(pdev->PDFA != 0) {
971
0
        int i;
972
0
        unsigned int Flags = 0;
973
        /* Check all the keys to see if we have a /F (Flags) key/value pair defined */
974
0
        for (i = 0; i < count; i += 2) {
975
0
            const gs_param_string *pair = &pairs[i];
976
977
0
            if (pdf_key_eq(pair, "/F")) {
978
0
                char Buffer[32];
979
980
0
                pair = &pairs[i+1];
981
0
                if (pair->size >= sizeof(Buffer))
982
0
                    code = 0;
983
0
                else {
984
0
                    memcpy(Buffer, pair->data, pair->size);
985
0
                    Buffer[pair->size] = 0x00;
986
0
                    code = sscanf(Buffer, "%u", &Flags);
987
0
                }
988
0
                if (code != 1)
989
0
                    emprintf(pdev->memory,
990
0
                             "Annotation has an invalid /Flags attribute\n");
991
0
                break;
992
0
            }
993
0
        }
994
        /* Check the Print flag, PDF/A annotations *must* be set to print */
995
0
        if ((Flags & 4) == 0){
996
0
            switch (pdev->PDFACompatibilityPolicy) {
997
                /* Default behaviour matches Adobe Acrobat, warn and continue,
998
                 * output file will not be PDF/A compliant
999
                 */
1000
0
                case 0:
1001
0
                    emprintf(pdev->memory,
1002
0
                             "Annotation set to non-printing,\n not permitted in PDF/A, reverting to normal PDF output\n");
1003
0
                    pdev->AbortPDFAX = true;
1004
0
                    pdev->PDFA = 0;
1005
0
                    break;
1006
                    /* Since the annotation would break PDF/A compatibility, do not
1007
                     * include it, but warn the user that it has been dropped.
1008
                     */
1009
0
                case 1:
1010
0
                    emprintf(pdev->memory,
1011
0
                             "Annotation set to non-printing,\n not permitted in PDF/A, annotation will not be present in output file\n");
1012
0
                    return 0;
1013
0
                    break;
1014
0
                case 2:
1015
0
                    emprintf(pdev->memory,
1016
0
                             "Annotation set to non-printing,\n not permitted in PDF/A, aborting conversion\n");
1017
0
                    return_error(gs_error_invalidfont);
1018
0
                    break;
1019
0
                default:
1020
0
                    emprintf(pdev->memory,
1021
0
                             "Annotation set to non-printing,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
1022
0
                    pdev->AbortPDFAX = true;
1023
0
                    pdev->PDFA = 0;
1024
0
                    break;
1025
0
            }
1026
0
        }
1027
0
    }
1028
548
    if (pdev->PDFX != 0) {
1029
0
        gs_param_string Subtype;
1030
0
        bool discard = true;
1031
0
        pdf_page_t *page = 0L;
1032
1033
0
        page = &pdev->pages[pdev->next_page];
1034
1035
0
        if (subtype) {
1036
0
            param_string_from_string(Subtype, subtype);
1037
0
            if (pdf_key_eq(&Subtype, "/TrapNet") ||
1038
0
                pdf_key_eq(&Subtype, "/PrinterMark"))
1039
0
                discard = false;
1040
0
        }
1041
0
        if (discard) {
1042
0
            int i;
1043
0
            for (i = 0; i < count; i += 2) {
1044
0
                const gs_param_string *pair = &pairs[i];
1045
0
                if (pdf_key_eq(pair, "/Rect")) {
1046
0
                    gs_rect rect;
1047
0
                    const cos_value_t *v_trimbox, *v_bleedbox, *v_artbox, *v_cropbox;
1048
0
                    const byte *p;
1049
0
                    char buf[100];
1050
0
                    int size, code;
1051
0
                    float temp[4]; /* the type is float for sscanf. */
1052
0
                    double pagebox[4] = {0, 0};
1053
1054
0
                    pagebox[2] = pdev->MediaSize[0];
1055
0
                    pagebox[3] = pdev->MediaSize[1];
1056
1057
0
                    v_cropbox = v_bleedbox = v_trimbox = v_artbox = 0x0L;
1058
1059
0
                    code = pdfmark_scan_rect(&rect, pair + 1, pctm);
1060
1061
0
                    if (code < 0)
1062
0
                        return code;
1063
1064
0
                    if (page && page->Page) {
1065
0
                        v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox");
1066
0
                        v_bleedbox = cos_dict_find_c_key(page->Page, "/BleedBox");
1067
0
                        v_artbox = cos_dict_find_c_key(page->Page, "/ArtBox");
1068
0
                        v_cropbox = cos_dict_find_c_key(page->Page, "/CropBox");
1069
0
                    }
1070
1071
0
                    if (v_cropbox != NULL && v_cropbox->value_type == COS_VALUE_SCALAR) {
1072
0
                        p = v_cropbox->contents.chars.data;
1073
0
                        size = min (v_cropbox->contents.chars.size, sizeof(buf) - 1);
1074
0
                        memcpy(buf, p, size);
1075
0
                        buf[size] = 0;
1076
0
                        if (sscanf(buf, "[ %g %g %g %g ]",
1077
0
                                &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
1078
0
                            if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
1079
0
                            if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
1080
0
                        }
1081
0
                    }
1082
0
                    if (v_bleedbox != NULL && v_bleedbox->value_type == COS_VALUE_SCALAR) {
1083
0
                        p = v_bleedbox->contents.chars.data;
1084
0
                        size = min (v_bleedbox->contents.chars.size, sizeof(buf) - 1);
1085
0
                        memcpy(buf, p, size);
1086
0
                        buf[size] = 0;
1087
0
                        if (sscanf(buf, "[ %g %g %g %g ]",
1088
0
                                &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
1089
0
                            if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
1090
0
                            if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
1091
0
                        }
1092
0
                    }
1093
0
                    if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) {
1094
0
                        p = v_trimbox->contents.chars.data;
1095
0
                        size = min (v_trimbox->contents.chars.size, sizeof(buf) - 1);
1096
0
                        memcpy(buf, p, size);
1097
0
                        buf[size] = 0;
1098
0
                        if (sscanf(buf, "[ %g %g %g %g ]",
1099
0
                                &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
1100
0
                            if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
1101
0
                            if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
1102
0
                        }
1103
0
                    }
1104
0
                    if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR) {
1105
0
                        p = v_artbox->contents.chars.data;
1106
0
                        size = min (v_artbox->contents.chars.size, sizeof(buf) - 1);
1107
0
                        memcpy(buf, p, size);
1108
0
                        buf[size] = 0;
1109
0
                        if (sscanf(buf, "[ %g %g %g %g ]",
1110
0
                                &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
1111
0
                            if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
1112
0
                            if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
1113
0
                        }
1114
0
                    }
1115
0
                    if (v_cropbox == 0 && v_trimbox == 0 && v_artbox == 0 && v_bleedbox == 0) {
1116
0
                        if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 &&
1117
0
                                    pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 &&
1118
0
                                    pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 &&
1119
0
                                    pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 &&
1120
0
                                    pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) {
1121
0
                            pagebox[0] += pdev->PDFXTrimBoxToMediaBoxOffset.data[0];
1122
0
                            pagebox[1] += pdev->PDFXTrimBoxToMediaBoxOffset.data[3];
1123
0
                            pagebox[2] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[1];
1124
0
                            pagebox[3] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[2];
1125
0
                        } else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 &&
1126
0
                                pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 &&
1127
0
                                pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 &&
1128
0
                                pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 &&
1129
0
                                pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) {
1130
0
                            pagebox[0] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[0];
1131
0
                            pagebox[1] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[3];
1132
0
                            pagebox[2] += pdev->PDFXBleedBoxToTrimBoxOffset.data[1];
1133
0
                            pagebox[3] += pdev->PDFXBleedBoxToTrimBoxOffset.data[2];
1134
0
                        }
1135
0
                    }
1136
1137
0
                    if (rect.p.x > pagebox[2] || rect.q.x < pagebox[0] ||
1138
0
                        rect.p.y > pagebox[3] || rect.q.y < pagebox[1])
1139
0
                        break;
1140
0
                    switch (pdev->PDFACompatibilityPolicy) {
1141
                        /* Default behaviour matches Adobe Acrobat, warn and continue,
1142
                         * output file will not be PDF/A compliant
1143
                         */
1144
0
                        case 0:
1145
0
                            emprintf(pdev->memory,
1146
0
                                     "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, reverting to normal PDF output\n");
1147
0
                            pdev->AbortPDFAX = true;
1148
0
                            pdev->PDFX = 0;
1149
0
                            break;
1150
                            /* Since the annotation would break PDF/A compatibility, do not
1151
                             * include it, but warn the user that it has been dropped.
1152
                             */
1153
0
                        case 1:
1154
0
                            emprintf(pdev->memory,
1155
0
                                     "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, annotation will not be present in output file\n");
1156
0
                            return 0;
1157
0
                            break;
1158
0
                        case 2:
1159
0
                            emprintf(pdev->memory,
1160
0
                                     "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, aborting conversion\n");
1161
0
                            return_error(gs_error_invalidfont);
1162
0
                            break;
1163
0
                        default:
1164
0
                            emprintf(pdev->memory,
1165
0
                                     "Annotation s(not TrapNet or PrinterMark) on page,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
1166
0
                            pdev->AbortPDFAX = true;
1167
0
                            pdev->PDFX = 0;
1168
0
                            break;
1169
0
                    }
1170
0
                    break;
1171
0
                }
1172
0
            }
1173
0
            if (i > count) {
1174
0
                switch (pdev->PDFACompatibilityPolicy) {
1175
                    /* Default behaviour matches Adobe Acrobat, warn and continue,
1176
                     * output file will not be PDF/X compliant
1177
                     */
1178
0
                    case 0:
1179
0
                        emprintf(pdev->memory,
1180
0
                                 "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, reverting to normal PDF output\n");
1181
0
                        pdev->AbortPDFAX = true;
1182
0
                        pdev->PDFX = 0;
1183
0
                        break;
1184
                        /* Since the annotation would break PDF/X compatibility, do not
1185
                         * include it, but warn the user that it has been dropped.
1186
                         */
1187
0
                    case 1:
1188
0
                        emprintf(pdev->memory,
1189
0
                                 "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, annotation will not be present in output file\n");
1190
0
                        return 0;
1191
0
                        break;
1192
0
                    case 2:
1193
0
                        emprintf(pdev->memory,
1194
0
                                 "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, aborting conversion\n");
1195
0
                        return_error(gs_error_invalidfont);
1196
0
                        break;
1197
0
                    default:
1198
0
                        emprintf(pdev->memory,
1199
0
                                 "Annotation s(not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
1200
0
                        pdev->AbortPDFAX = true;
1201
0
                        pdev->PDFX = 0;
1202
0
                        break;
1203
0
                }
1204
0
            }
1205
0
        }
1206
0
    }
1207
548
    params.pdev = pdev;
1208
548
    params.subtype = subtype;
1209
548
    params.src_pg = -1;
1210
548
    code = pdf_make_named_dict(pdev, objname, &pcd, true);
1211
548
    if (code < 0)
1212
0
        return code;
1213
548
    code = cos_dict_put_c_strings(pcd, "/Type", "/Annot");
1214
548
    if (code < 0) {
1215
0
        (void)pdf_obj_mark_unused(pdev, pcd->id);
1216
0
        cos_free((cos_object_t *)pcd, "pdfmark_annot");
1217
0
        return code;
1218
0
    }
1219
548
    code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, &params, false);
1220
548
    if (code < 0) {
1221
0
        (void)pdf_obj_mark_unused(pdev, pcd->id);
1222
0
        cos_free((cos_object_t *)pcd, "pdfmark_annot");
1223
0
        return code;
1224
0
    }
1225
548
    if (params.src_pg >= 0)
1226
0
        page_index = params.src_pg;
1227
548
    if (pdf_page_id(pdev, page_index + 1) <= 0) {
1228
0
        (void)pdf_obj_mark_unused(pdev, pcd->id);
1229
0
        cos_free((cos_object_t *)pcd, "pdfmark_annot");
1230
0
        return_error(gs_error_rangecheck);
1231
0
    }
1232
548
    annots = pdev->pages[page_index].Annots;
1233
548
    if (annots == 0) {
1234
274
        annots = cos_array_alloc(pdev, "pdfmark_annot");
1235
274
        if (annots == 0) {
1236
0
            (void)pdf_obj_mark_unused(pdev, pcd->id);
1237
0
            cos_free((cos_object_t *)pcd, "pdfmark_annot");
1238
0
            return_error(gs_error_VMerror);
1239
0
        }
1240
274
        pdev->pages[page_index].Annots = annots;
1241
274
    }
1242
548
    if (!objname) {
1243
        /* Write the annotation now. */
1244
548
        COS_WRITE_OBJECT(pcd, pdev, resourceAnnotation);
1245
548
        COS_RELEASE(pcd, "pdfmark_annot");
1246
548
    }
1247
548
    return cos_array_add(annots,
1248
548
                         cos_object_value(&value, COS_OBJECT(pcd)));
1249
548
}
1250
1251
/* ANN pdfmark */
1252
static int
1253
pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1254
            const gs_matrix * pctm, const gs_param_string * objname)
1255
335
{
1256
335
    return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text");
1257
335
}
1258
1259
/* LNK pdfmark (obsolescent) */
1260
static int
1261
pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1262
            const gs_matrix * pctm, const gs_param_string * objname)
1263
213
{
1264
213
    return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link");
1265
213
}
1266
1267
/* Write and release one node of the outline tree. */
1268
static int
1269
pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode,
1270
                      int64_t next_id)
1271
217
{
1272
217
    stream *s;
1273
217
    int code = 0;
1274
1275
217
    pdf_open_separate(pdev, pnode->id, resourceOutline);
1276
217
    if (pnode->action != NULL)
1277
217
        pnode->action->id = pnode->id;
1278
0
    else {
1279
0
        emprintf1(pdev->memory,
1280
0
                  "pdfmark error: Outline node %ld has no action or destination.\n",
1281
0
                  (unsigned long)pnode->id);
1282
0
        code = gs_note_error(gs_error_undefined);
1283
0
    }
1284
217
    s = pdev->strm;
1285
217
    stream_puts(s, "<< ");
1286
217
    if (pnode->action != NULL)
1287
217
        cos_dict_elements_write(pnode->action, pdev);
1288
217
    if (pnode->count)
1289
35
        pprintd1(s, "/Count %d ", pnode->count);
1290
217
    pprinti64d1(s, "/Parent %"PRId64" 0 R\n", pnode->parent_id);
1291
217
    if (pnode->prev_id)
1292
44
        pprinti64d1(s, "/Prev %"PRId64" 0 R\n", pnode->prev_id);
1293
217
    if (next_id)
1294
44
        pprinti64d1(s, "/Next %"PRId64" 0 R\n", next_id);
1295
217
    if (pnode->first_id)
1296
33
        pprinti64d2(s, "/First %"PRId64" 0 R /Last %"PRId64" 0 R\n",
1297
33
                  pnode->first_id, pnode->last_id);
1298
217
    stream_puts(s, ">>\n");
1299
217
    pdf_end_separate(pdev, resourceOutline);
1300
217
    if (pnode->action != NULL)
1301
217
        COS_FREE(pnode->action, "pdfmark_write_outline");
1302
217
    pnode->action = 0;
1303
217
    return code;
1304
217
}
1305
1306
/* Adjust the parent's count when writing an outline node. */
1307
static void
1308
pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)
1309
59
{
1310
59
    pdf_outline_level_t *parent = plevel - 1;
1311
59
    int count = plevel->last.count;
1312
1313
59
    if (count > 0) {
1314
0
        if (parent->last.count < 0)
1315
0
            parent->last.count -= count;
1316
0
        else
1317
0
            parent->last.count += count;
1318
0
    }
1319
59
}
1320
1321
/*
1322
 * Close the current level of the outline tree.  Note that if we are at
1323
 * the end of the document, some of the levels may be incomplete if the
1324
 * Count values were incorrect.
1325
 */
1326
int
1327
pdfmark_close_outline(gx_device_pdf * pdev)
1328
175
{
1329
175
    int depth = pdev->outline_depth;
1330
175
    pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
1331
175
    int code = 0;
1332
1333
175
    if (plevel->last.id) { /* check for incomplete tree */
1334
173
        code = pdfmark_write_outline(pdev, &plevel->last, 0);
1335
173
    }
1336
175
    if (depth > 0) {
1337
35
        plevel[-1].last.last_id = plevel->last.id;
1338
35
        pdfmark_adjust_parent_count(plevel);
1339
35
        --plevel;
1340
35
        if (plevel->last.count < 0)
1341
3
            pdev->closed_outline_depth--;
1342
35
        pdev->outline_depth--;
1343
35
    }
1344
175
    return code;
1345
175
}
1346
1347
/* OUT pdfmark */
1348
static int
1349
pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1350
            const gs_matrix * pctm, const gs_param_string * no_objname)
1351
217
{
1352
217
    int depth = pdev->outline_depth;
1353
217
    pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
1354
217
    int sub_count = 0;
1355
217
    uint i;
1356
217
    pdf_outline_node_t node;
1357
217
    ao_params_t ao;
1358
217
    int code;
1359
1360
1.10k
    for (i = 0; i < count; i += 2) {
1361
883
        const gs_param_string *pair = &pairs[i];
1362
1363
883
        if (pdf_key_eq(pair, "/Count"))
1364
217
            pdfmark_scan_int(pair + 1, &sub_count);
1365
883
    }
1366
217
    if (sub_count != 0 && depth == pdev->max_outline_depth - 1) {
1367
0
        pdf_outline_level_t *new_ptr;
1368
1369
0
        new_ptr = (pdf_outline_level_t *)gs_alloc_bytes(pdev->pdf_memory, (pdev->max_outline_depth + INITIAL_MAX_OUTLINE_DEPTH) * sizeof(pdf_outline_level_t) * sizeof(pdf_outline_level_t), "outline_levels array");
1370
0
        if (!new_ptr)
1371
0
            return_error(gs_error_VMerror);
1372
0
        memcpy (new_ptr, pdev->outline_levels, pdev->max_outline_depth * sizeof(pdf_outline_level_t));
1373
0
        gs_free_object(pdev->pdf_memory, pdev->outline_levels, "outline_levels array");
1374
0
        pdev->outline_levels = new_ptr;
1375
0
        pdev->max_outline_depth += INITIAL_MAX_OUTLINE_DEPTH;
1376
0
        plevel = &pdev->outline_levels[depth];
1377
0
    }
1378
217
    node.action = cos_dict_alloc(pdev, "pdfmark_OUT");
1379
217
    if (node.action == 0)
1380
0
        return_error(gs_error_VMerror);
1381
217
    ao.pdev = pdev;
1382
217
    ao.subtype = 0;
1383
217
    ao.src_pg = -1;
1384
217
    code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao,
1385
217
                                true);
1386
217
    if (code < 0) {
1387
0
        cos_free((cos_object_t *)node.action, "pdfmark_OUT");
1388
0
        return code;
1389
0
    }
1390
217
    if (pdev->outlines_id == 0)
1391
140
        pdev->outlines_id = pdf_obj_ref(pdev);
1392
217
    node.id = pdf_obj_ref(pdev);
1393
217
    node.parent_id =
1394
217
        (depth == 0 ? pdev->outlines_id : plevel[-1].last.id);
1395
217
    node.prev_id = plevel->last.id;
1396
217
    node.first_id = node.last_id = 0;
1397
217
    node.count = sub_count;
1398
    /* Add this node to the outline at the current level. */
1399
217
    if (plevel->first.id == 0) { /* First node at this level. */
1400
173
        if (depth > 0)
1401
33
            plevel[-1].last.first_id = node.id;
1402
173
        node.prev_id = 0;
1403
173
        plevel->first = node;
1404
173
        plevel->first.action = 0; /* never used */
1405
173
    } else {     /* Write the previous node. */
1406
44
        if (depth > 0)
1407
24
            pdfmark_adjust_parent_count(plevel);
1408
44
        pdfmark_write_outline(pdev, &plevel->last, node.id);
1409
44
    }
1410
217
    plevel->last = node;
1411
217
    plevel->left--;
1412
217
    if (!pdev->closed_outline_depth)
1413
214
        pdev->outlines_open++;
1414
    /* If this node has sub-nodes, descend one level. */
1415
217
    if (sub_count != 0) {
1416
35
        pdev->outline_depth++;
1417
35
        ++plevel;
1418
35
        plevel->left = (sub_count > 0 ? sub_count : -sub_count);
1419
35
        plevel->first.id = 0;
1420
35
        plevel->last.count = plevel->last.id = 0;
1421
35
        plevel->first.action = plevel->last.action = 0; /* for GC */
1422
35
        if (sub_count < 0)
1423
3
            pdev->closed_outline_depth++;
1424
182
    } else {
1425
212
        while ((depth = pdev->outline_depth) > 0 &&
1426
212
               pdev->outline_levels[depth].left == 0
1427
182
            )
1428
30
            pdfmark_close_outline(pdev);
1429
182
    }
1430
217
    return 0;
1431
217
}
1432
1433
/* Write an article bead. */
1434
static int
1435
pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead)
1436
0
{
1437
0
    stream *s;
1438
0
    char rstr[MAX_RECT_STRING];
1439
1440
0
    pdf_open_separate(pdev, pbead->id, resourceArticle);
1441
0
    s = pdev->strm;
1442
0
    pprinti64d3(s, "<</T %"PRId64" 0 R/V %"PRId64" 0 R/N %"PRId64" 0 R",
1443
0
              pbead->article_id, pbead->prev_id, pbead->next_id);
1444
0
    if (pbead->page_id != 0)
1445
0
        pprinti64d1(s, "/P %"PRId64" 0 R", pbead->page_id);
1446
0
    pdfmark_make_rect(rstr, &pbead->rect);
1447
0
    pprints1(s, "/R%s>>\n", rstr);
1448
0
    return pdf_end_separate(pdev, resourceArticle);
1449
0
}
1450
1451
/* Finish writing an article, and release its data. */
1452
int
1453
pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part)
1454
0
{
1455
0
    pdf_article_t art;
1456
0
    stream *s;
1457
1458
0
    art = *part;
1459
0
    if (art.last.id == 0) {
1460
        /* Only one bead in the article. */
1461
0
        art.first.prev_id = art.first.next_id = art.first.id;
1462
0
    } else {
1463
        /* More than one bead in the article. */
1464
0
        art.first.prev_id = art.last.id;
1465
0
        art.last.next_id = art.first.id;
1466
0
        pdfmark_write_bead(pdev, &art.last);
1467
0
    }
1468
0
    pdfmark_write_bead(pdev, &art.first);
1469
0
    pdf_open_separate(pdev, art.contents->id, resourceArticle);
1470
0
    s = pdev->strm;
1471
0
    pprinti64d1(s, "<</F %"PRId64" 0 R/I<<", art.first.id);
1472
0
    cos_dict_elements_write(art.contents, pdev);
1473
0
    stream_puts(s, ">> >>\n");
1474
0
    return pdf_end_separate(pdev, resourceArticle);
1475
0
}
1476
1477
/* ARTICLE pdfmark */
1478
static int
1479
pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1480
                const gs_matrix * pctm, const gs_param_string * no_objname)
1481
0
{
1482
0
    gs_memory_t *mem = pdev->pdf_memory;
1483
0
    gs_param_string title;
1484
0
    gs_param_string rectstr;
1485
0
    gs_rect rect;
1486
0
    int64_t bead_id;
1487
0
    pdf_article_t *part;
1488
0
    int code;
1489
1490
0
    if (!pdfmark_find_key("/Title", pairs, count, &title) ||
1491
0
        !pdfmark_find_key("/Rect", pairs, count, &rectstr)
1492
0
        )
1493
0
        return_error(gs_error_rangecheck);
1494
0
    if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0)
1495
0
        return code;
1496
0
    bead_id = pdf_obj_ref(pdev);
1497
1498
    /* Find the article with this title, or create one. */
1499
0
    for (part = pdev->articles; part != 0; part = part->next) {
1500
0
        const cos_value_t *a_title =
1501
0
            cos_dict_find_c_key(part->contents, "/Title");
1502
1503
0
        if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) &&
1504
0
            !bytes_compare(a_title->contents.chars.data,
1505
0
                           a_title->contents.chars.size,
1506
0
                           title.data, title.size))
1507
0
            break;
1508
0
    }
1509
0
    if (part == 0) {   /* Create the article. */
1510
0
        cos_dict_t *contents =
1511
0
            cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)");
1512
1513
0
        if (contents == 0)
1514
0
            return_error(gs_error_VMerror);
1515
0
        part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article,
1516
0
                               "pdfmark_ARTICLE(article)");
1517
0
        if (part == 0 || contents == 0) {
1518
0
            gs_free_object(mem, part, "pdfmark_ARTICLE(article)");
1519
0
            if (contents)
1520
0
                COS_FREE(contents, "pdfmark_ARTICLE(contents)");
1521
0
            return_error(gs_error_VMerror);
1522
0
        }
1523
0
        contents->id = pdf_obj_ref(pdev);
1524
0
        part->next = pdev->articles;
1525
0
        pdev->articles = part;
1526
0
        cos_dict_put_string(contents, (const byte *)"/Title", 6,
1527
0
                            title.data, title.size);
1528
0
        part->first.id = part->last.id = 0;
1529
0
        part->contents = contents;
1530
0
    }
1531
    /*
1532
     * Add the bead to the article.  This is similar to what we do for
1533
     * outline nodes, except that articles have only a page number and
1534
     * not View/Dest.
1535
     */
1536
0
    if (part->last.id == 0) {
1537
0
        part->first.next_id = bead_id;
1538
0
        part->last.id = part->first.id;
1539
0
    } else {
1540
0
        part->last.next_id = bead_id;
1541
0
        pdfmark_write_bead(pdev, &part->last);
1542
0
    }
1543
0
    part->last.prev_id = part->last.id;
1544
0
    part->last.id = bead_id;
1545
0
    part->last.article_id = part->contents->id;
1546
0
    part->last.next_id = 0;
1547
0
    part->last.rect = rect;
1548
0
    {
1549
0
        gs_param_string page_string;
1550
0
        int page = 0;
1551
0
        uint i;
1552
1553
0
        pdfmark_find_key("/Page", pairs, count, &page_string);
1554
0
        page = pdfmark_page_number(pdev, &page_string);
1555
0
        code = update_max_page_reference(pdev, &page);
1556
0
        if (code < 0)
1557
0
            return code;
1558
0
        part->last.page_id = pdf_page_id(pdev, page);
1559
0
        for (i = 0; i < count; i += 2) {
1560
0
            if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page"))
1561
0
                continue;
1562
0
            pdfmark_put_pair(part->contents, &pairs[i]);
1563
0
        }
1564
0
    }
1565
0
    if (part->first.id == 0) { /* This is the first bead of the article. */
1566
0
        part->first = part->last;
1567
0
        part->last.id = 0;
1568
0
    }
1569
0
    return 0;
1570
0
}
1571
1572
/* EMBED pdfmark */
1573
static int
1574
pdfmark_EMBED(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1575
             const gs_matrix * pctm, const gs_param_string * objname)
1576
17
{
1577
17
    gs_param_string key;
1578
17
    int i;
1579
1580
17
    if (pdev->CompatibilityLevel < 1.4)
1581
0
        return_error(gs_error_undefined);
1582
17
    if (pdev->PDFA > 0 && pdev->PDFA < 2) {
1583
0
        switch(pdev->PDFACompatibilityPolicy) {
1584
0
            default:
1585
0
            case 0:
1586
0
                emprintf(pdev->memory,
1587
0
                "The PDF/A-1 specifcation prohibits the embedding of files, reverting to normal PDF output.\n");
1588
0
                pdev->AbortPDFAX = true;
1589
0
                pdev->PDFA = 0;
1590
0
                return 0;
1591
0
            case 1:
1592
0
                emprintf(pdev->memory,
1593
0
                "The PDF/A-1 specifcation prohibits the embedding of files, pdfamrk operatoin ignored.\n");
1594
0
                break;
1595
0
            case 2:
1596
0
                return_error(gs_error_undefined);
1597
0
        }
1598
0
    }
1599
17
    if (pdev->PDFA > 0 && pdev->PDFA < 3) {
1600
0
        emprintf(pdev->memory,
1601
0
        "The PDF/A-2 specifcation only permits the embedding of PDF/A-1 or PDF/A-2 files.\n");
1602
0
        emprintf(pdev->memory,
1603
0
        "The pdfwrite device has not validated this embedded file, output may not conform to PDF/A-2.\n");
1604
0
    }
1605
17
    if (!pdfmark_find_key("/FS", pairs, count, &key))
1606
0
        return_error(gs_error_rangecheck);
1607
17
    if (!pdfmark_find_key("/Name", pairs, count, &key))
1608
0
        return_error(gs_error_rangecheck);
1609
17
    if (!pdev->EmbeddedFiles) {
1610
17
        pdev->EmbeddedFiles = cos_dict_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)");
1611
17
        if (pdev->EmbeddedFiles == 0)
1612
0
            return_error(gs_error_VMerror);
1613
17
        pdev->EmbeddedFiles->id = pdf_obj_ref(pdev);
1614
17
    }
1615
17
    if (pdev->PDFA == 3 && !pdev->AF && !cos_dict_find_c_key(pdev->Catalog, "/AF")) {
1616
0
        pdev->AF = cos_array_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)");
1617
0
        if (pdev->AF == 0)
1618
0
            return_error(gs_error_VMerror);
1619
0
        pdev->AF->id = pdf_obj_ref(pdev);
1620
0
    }
1621
1622
34
    for (i = 0; i < count; i += 2) {
1623
34
        if (pdf_key_eq(&pairs[i], "/FS")) {
1624
17
            if (pdev->PDFA == 3 && !cos_dict_find_c_key(pdev->Catalog, "/AF")) {
1625
0
                uint written;
1626
0
                cos_value_t v;
1627
0
                cos_object_t *object;
1628
0
                int64_t id;
1629
0
                int code;
1630
0
                char *p = (char *)pairs[i+1].data;
1631
1632
                /* Skip past white space */
1633
0
                while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
1634
0
                    p++;
1635
1636
                /* If we have a dictionary, then we assume this is a 'normal' /EMBED pdfmark */
1637
0
                if (*p == '<') {
1638
                    /* Write the dictionary to an object in the PDF file, and note the id we are
1639
                     * using. We need that below to write references to the object from the /EmbeddedFiles
1640
                     * entry in the Catalog dictionary and the /AF array, also in the Catalog dictionary.
1641
                     */
1642
0
                    id = pdf_obj_ref(pdev);
1643
1644
0
                    pdf_open_separate(pdev, id, resourceNone);
1645
0
                    sputs(pdev->strm, pairs[i+1].data, pairs[i+1].size, &written);
1646
0
                    if (pairs[i+1].data[pairs[i+1].size] != 0x0d && pdev->PDFA != 0)
1647
0
                        sputc(pdev->strm, 0x0d);
1648
0
                    pdf_end_separate(pdev, resourceNone);
1649
0
                } else {
1650
                    /* Otherwise, try to determine if we have an indirect reference to an existing FileSpec dictionary
1651
                     * If we get what looks like an indirect reference, try using the id for the /EmbeddedFiles
1652
                     * and /AF entries
1653
                     */
1654
0
                    if (sscanf(p, "%"PRId64" 0 R", &id) != 0)
1655
0
                        return_error(gs_error_syntaxerror);
1656
0
                }
1657
1658
0
                object = cos_reference_alloc(pdev, "embedded file");
1659
0
                if (object == NULL)
1660
0
                    return_error(gs_error_VMerror);
1661
0
                object->id = id;
1662
0
                COS_OBJECT_VALUE(&v, object);
1663
0
                code = cos_dict_put(pdev->EmbeddedFiles, key.data, key.size, &v);
1664
0
                if (code < 0)
1665
0
                    return code;
1666
1667
0
                object = cos_reference_alloc(pdev, "embedded file");
1668
0
                if (object == NULL)
1669
0
                    return_error(gs_error_VMerror);
1670
0
                object->id = id;
1671
0
                COS_OBJECT_VALUE(&v, object);
1672
0
                code = cos_array_add(pdev->AF, &v);
1673
0
                if (code < 0)
1674
0
                    return code;
1675
0
            }
1676
17
            else
1677
17
                return cos_dict_put_string(pdev->EmbeddedFiles, key.data, key.size, pairs[i+1].data, pairs[i+1].size);
1678
17
        }
1679
34
    }
1680
0
    return 0;
1681
17
}
1682
1683
/* DEST pdfmark */
1684
static int
1685
pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1686
             const gs_matrix * pctm, const gs_param_string * objname)
1687
0
{
1688
0
    int present;
1689
0
    char dest[MAX_DEST_STRING];
1690
0
    gs_param_string key;
1691
0
    cos_value_t value;
1692
0
    cos_dict_t *ddict;
1693
0
    int i, code;
1694
1695
0
    if (!pdfmark_find_key("/Dest", pairs, count, &key) ||
1696
0
        (present =
1697
0
         pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 1)) < 0
1698
0
        )
1699
0
        return_error(gs_error_rangecheck);
1700
0
    cos_string_value(&value, (byte *)dest, strlen(dest));
1701
0
    if (!pdev->Dests) {
1702
0
        pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)");
1703
0
        if (pdev->Dests == 0)
1704
0
            return_error(gs_error_VMerror);
1705
0
        pdev->Dests->id = pdf_obj_ref(pdev);
1706
0
    }
1707
1708
    /*
1709
     * Create the destination as a dictionary with a D key.
1710
     */
1711
0
    code = pdf_make_named_dict(pdev, objname, &ddict, false);
1712
0
    ddict->id = pdf_obj_ref(pdev);
1713
1714
0
    if (code < 0)
1715
0
        return code;
1716
0
    code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest,
1717
0
                                     strlen(dest));
1718
0
    for (i = 0; code >= 0 && i < count; i += 2)
1719
0
        if (!pdf_key_eq(&pairs[i], "/Dest") &&
1720
0
            !pdf_key_eq(&pairs[i], "/Page") &&
1721
0
            !pdf_key_eq(&pairs[i], "/View")
1722
0
            )
1723
0
            code = pdfmark_put_pair(ddict, &pairs[i]);
1724
0
    if (code < 0)
1725
0
        return code;
1726
0
    COS_WRITE_OBJECT(ddict, pdev, resourceOther);
1727
0
    COS_OBJECT_VALUE(&value, ddict);
1728
0
    COS_RELEASE(ddict, "pdfmark_DEST(Dests dict)");
1729
1730
0
    return cos_dict_put(pdev->Dests, key.data, key.size, &value);
1731
0
}
1732
1733
/* Check that pass-through PostScript code is a string. */
1734
static bool
1735
ps_source_ok(const gs_memory_t *mem, const gs_param_string * psource)
1736
0
{
1737
0
    if (psource->size >= 2 && psource->data[0] == '(' &&
1738
0
        psource->data[psource->size - 1] == ')'
1739
0
        )
1740
0
        return true;
1741
0
    else {
1742
0
        int i;
1743
0
        lprintf("bad PS passthrough: ");
1744
0
        for (i=0; i<psource->size; i++)
1745
0
            errprintf(mem, "%c", psource->data[i]);
1746
0
        errprintf(mem, "\n");
1747
0
        return false;
1748
0
    }
1749
0
}
1750
1751
/* Write the contents of pass-through PostScript code. */
1752
/* Return the size written on the file. */
1753
static uint
1754
pdfmark_write_ps(stream *s, const gs_param_string * psource)
1755
0
{
1756
    /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/
1757
0
    uint size = psource->size - 2;
1758
1759
0
    stream_write(s, psource->data + 1, size);
1760
0
    stream_putc(s, '\n');
1761
0
    return size + 1;
1762
0
}
1763
1764
/* Start a XObject. */
1765
static int
1766
start_XObject(gx_device_pdf * pdev, bool compress, cos_stream_t **ppcs)
1767
0
{   pdf_resource_t *pres;
1768
0
    cos_stream_t *pcs;
1769
0
    int code;
1770
1771
0
    code = pdf_open_page(pdev, PDF_IN_STREAM);
1772
0
    if (code < 0)
1773
0
        return code;
1774
0
    code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
1775
0
                pdev->CompressStreams);
1776
0
    if (code < 0)
1777
0
        return code;
1778
0
    pdev->accumulating_a_global_object = true;
1779
0
    pcs = (cos_stream_t *)pres->object;
1780
0
    pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject");
1781
0
    if (!pdev->substream_Resources)
1782
0
        return_error(gs_error_VMerror);
1783
0
    if (pdev->ForOPDFRead) {
1784
0
        code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
1785
0
        if (code < 0)
1786
0
            return code;
1787
0
    }
1788
0
    pres->named = true;
1789
0
    pres->where_used = 0; /* initially not used */
1790
0
    pcs->pres = pres;
1791
0
    *ppcs = pcs;
1792
0
    return 0;
1793
0
}
1794
1795
/* PS pdfmark */
1796
0
#define MAX_PS_INLINE 100
1797
static int
1798
pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1799
           const gs_matrix * pctm, const gs_param_string * objname)
1800
0
{
1801
0
    gs_param_string source;
1802
0
    gs_param_string level1;
1803
1804
0
    if (!pdfmark_find_key("/DataSource", pairs, count, &source) ||
1805
0
        !ps_source_ok(pdev->memory, &source) ||
1806
0
        (pdfmark_find_key("/Level1", pairs, count, &level1) &&
1807
0
         !ps_source_ok(pdev->memory, &level1))
1808
0
        )
1809
0
        return_error(gs_error_rangecheck);
1810
0
    if (level1.data == 0 && source.size <= MAX_PS_INLINE && objname == 0) {
1811
        /* Insert the PostScript code in-line */
1812
0
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1813
0
        stream *s;
1814
1815
0
        if (code < 0)
1816
0
            return code;
1817
0
        s = pdev->strm;
1818
0
        stream_write(s, source.data, source.size);
1819
0
        stream_puts(s, " PS\n");
1820
0
    } else {
1821
        /* Put the PostScript code in a resource. */
1822
0
        cos_stream_t *pcs;
1823
0
        int code;
1824
0
        gs_id level1_id = gs_no_id;
1825
0
        pdf_resource_t *pres;
1826
1827
0
        if (level1.data != 0) {
1828
0
            pdf_resource_t *pres;
1829
1830
0
            code = pdf_enter_substream(pdev,
1831
0
                        resourceXObject,
1832
0
                        gs_no_id, &pres, true,
1833
0
                        pdev->CompressStreams);
1834
0
            if (code < 0)
1835
0
                return code;
1836
0
            pcs = (cos_stream_t *)pres->object;
1837
0
            if (pdev->ForOPDFRead && objname != 0) {
1838
0
                code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
1839
0
                if (code < 0)
1840
0
                    return code;
1841
0
            }
1842
0
            pres->named = (objname != 0);
1843
0
            pres->where_used = 0;
1844
0
            pcs->pres = pres;
1845
0
            DISCARD(pdfmark_write_ps(pdev->strm, &level1));
1846
0
            code = pdf_exit_substream(pdev);
1847
0
            if (code < 0)
1848
0
                return code;
1849
0
            code = cos_write_object(pres->object, pdev, resourceOther);
1850
0
            if (code < 0)
1851
0
                return code;
1852
0
            level1_id = pres->object->id;
1853
0
        }
1854
0
        code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
1855
0
        if (code < 0)
1856
0
            return code;
1857
0
        pres = pdev->accumulating_substream_resource;
1858
0
        if (pres == NULL)
1859
0
            return_error(gs_error_unregistered);
1860
0
        code = cos_stream_put_c_strings(pcs, "/Type", "/XObject");
1861
0
        if (code < 0)
1862
0
            return code;
1863
0
        code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS");
1864
0
        if (code < 0)
1865
0
            return code;
1866
0
        if (level1_id != gs_no_id) {
1867
0
            char r[MAX_DEST_STRING];
1868
1869
0
            gs_snprintf(r, sizeof(r), "%ld 0 R", level1_id);
1870
0
            code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1",
1871
0
                                             (byte *)r, strlen(r));
1872
0
            if (code < 0)
1873
0
                return code;
1874
0
        }
1875
0
        DISCARD(pdfmark_write_ps(pdev->strm, &source));
1876
0
        code = pdf_exit_substream(pdev);
1877
0
        if (code < 0)
1878
0
            return code;
1879
0
        {   gs_const_string objname1, *pon = NULL;
1880
1881
0
            if (objname != NULL) {
1882
0
                objname1.data = objname->data;
1883
0
                objname1.size = objname->size;
1884
0
                pon = &objname1;
1885
0
            }
1886
0
            code = pdfmark_bind_named_object(pdev, pon, &pres);
1887
0
            if (code < 0)
1888
0
                return code;
1889
0
        }
1890
0
        code = pdf_open_contents(pdev, PDF_IN_STREAM);
1891
0
        if (code < 0)
1892
0
            return code;
1893
0
        pcs->pres->where_used |= pdev->used_mask;
1894
0
        pprinti64d1(pdev->strm, "/R%"PRId64" Do\n", pcs->id);
1895
0
    }
1896
0
    return 0;
1897
0
}
1898
1899
/* Common code for pdfmarks that do PUT into a specific object. */
1900
static int
1901
pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count)
1902
5.87k
{
1903
5.87k
    int code = 0, i;
1904
1905
5.87k
    if (count & 1)
1906
0
        return_error(gs_error_rangecheck);
1907
11.0k
    for (i = 0; code >= 0 && i < count; i += 2)
1908
5.17k
        code = pdfmark_put_pair(pcd, pairs + i);
1909
5.87k
    return code;
1910
5.87k
}
1911
1912
/* PAGES pdfmark */
1913
static int
1914
pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1915
              const gs_matrix * pctm, const gs_param_string * no_objname)
1916
0
{
1917
0
    return pdfmark_put_pairs(pdev->Pages, pairs, count);
1918
0
}
1919
1920
/* PAGE pdfmark */
1921
static int
1922
pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1923
             const gs_matrix * pctm, const gs_param_string * no_objname)
1924
4.35k
{
1925
4.35k
    return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count);
1926
4.35k
}
1927
1928
/* Add a page label for the current page. The last label on a page
1929
 * overrides all previous labels for this page. Unlabeled pages will get
1930
 * empty page labels. label == NULL flushes the last label */
1931
static int
1932
pdfmark_add_pagelabel(gx_device_pdf * pdev, const gs_param_string *label)
1933
9.18k
{
1934
9.18k
    cos_value_t value;
1935
9.18k
    cos_dict_t *dict = 0;
1936
9.18k
    int code = 0;
1937
1938
    /* create label dict (and page label array if not present yet) */
1939
9.18k
    if (label != 0) {
1940
0
        if (!pdev->PageLabels) {
1941
0
            pdev->PageLabels = cos_array_alloc(pdev,
1942
0
                    "pdfmark_add_pagelabel(PageLabels)");
1943
0
            if (pdev->PageLabels == 0)
1944
0
                return_error(gs_error_VMerror);
1945
0
            pdev->PageLabels->id = pdf_obj_ref(pdev);
1946
1947
            /* empty label for unlabled pages before first labled page */
1948
0
            pdev->PageLabels_current_page = 0;
1949
0
            pdev->PageLabels_current_label = cos_dict_alloc(pdev,
1950
0
                                           "pdfmark_add_pagelabel(first)");
1951
0
            if (pdev->PageLabels_current_label == 0)
1952
0
                return_error(gs_error_VMerror);
1953
0
        }
1954
1955
0
        dict = cos_dict_alloc(pdev, "pdfmark_add_pagelabel(dict)");
1956
0
        if (dict == 0)
1957
0
            return_error(gs_error_VMerror);
1958
1959
0
        code = cos_dict_put_c_key(dict, "/P", cos_string_value(&value,
1960
0
            label->data, label->size));
1961
0
        if (code < 0) {
1962
0
            COS_FREE(dict, "pdfmark_add_pagelabel(dict)");
1963
0
            return code;
1964
0
        }
1965
0
    }
1966
1967
    /* flush current label */
1968
9.18k
    if (label == 0 || pdev->next_page != pdev->PageLabels_current_page) {
1969
        /* handle current label */
1970
9.18k
        if (pdev->PageLabels_current_label) {
1971
0
            if (code >= 0) {
1972
0
                code = cos_array_add_int(pdev->PageLabels,
1973
0
                        pdev->PageLabels_current_page);
1974
0
                if (code >= 0)
1975
0
                    code = cos_array_add(pdev->PageLabels,
1976
0
                            COS_OBJECT_VALUE(&value,
1977
0
                                pdev->PageLabels_current_label));
1978
0
            }
1979
0
            pdev->PageLabels_current_label = 0;
1980
0
        }
1981
1982
        /* handle unlabled pages between current labeled page and
1983
         * next labeled page */
1984
9.18k
        if (pdev->PageLabels) {
1985
0
            if (pdev->next_page - pdev->PageLabels_current_page > 1) {
1986
0
                cos_dict_t *tmp = cos_dict_alloc(pdev,
1987
0
                        "pdfmark_add_pagelabel(tmp)");
1988
0
                if (tmp == 0)
1989
0
                    return_error(gs_error_VMerror);
1990
1991
0
                code = cos_array_add_int(pdev->PageLabels,
1992
0
                        pdev->PageLabels_current_page + 1);
1993
0
                if (code >= 0)
1994
0
                    code = cos_array_add(pdev->PageLabels,
1995
0
                            COS_OBJECT_VALUE(&value, tmp));
1996
0
            }
1997
0
        }
1998
9.18k
    }
1999
2000
    /* new current label */
2001
9.18k
    if (pdev->PageLabels_current_label)
2002
0
        COS_FREE(pdev->PageLabels_current_label,
2003
9.18k
                "pdfmark_add_pagelabel(current_label)");
2004
9.18k
    pdev->PageLabels_current_label = dict;
2005
9.18k
    pdev->PageLabels_current_page = pdev->next_page;
2006
2007
9.18k
    return code;
2008
9.18k
}
2009
2010
/* Close the pagelabel numtree.*/
2011
int
2012
pdfmark_end_pagelabels(gx_device_pdf * pdev)
2013
9.18k
{
2014
9.18k
    return pdfmark_add_pagelabel(pdev, 0);
2015
9.18k
}
2016
2017
/* [ /Label string /PlateColor string pdfmark */
2018
/* FIXME: /PlateColor is ignored */
2019
static int
2020
pdfmark_PAGELABEL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2021
             const gs_matrix * pctm, const gs_param_string * no_objname)
2022
0
{
2023
0
    gs_param_string key;
2024
2025
0
    if (pdev->CompatibilityLevel >= 1.3) {
2026
0
        if (pdfmark_find_key("/Label", pairs, count, &key)) {
2027
0
            return pdfmark_add_pagelabel(pdev, &key);
2028
0
        }
2029
0
    }
2030
0
    return 0;
2031
0
}
2032
2033
static int is_XMP_Key(const gs_param_string *param)
2034
0
{
2035
0
    if (pdf_key_eq(param, "/Title"))
2036
0
        return 1;
2037
0
    if (pdf_key_eq(param, "/Author"))
2038
0
        return 1;
2039
0
    if (pdf_key_eq(param, "/Subject"))
2040
0
        return 1;
2041
0
    if (pdf_key_eq(param, "/Keywords"))
2042
0
        return 1;
2043
0
    if (pdf_key_eq(param, "/Creator"))
2044
0
        return 1;
2045
0
    if (pdf_key_eq(param, "/Producer"))
2046
0
        return 1;
2047
    /* These two aren't string data types and so won't affect anything
2048
     * in the DOCINFO pdfmark, which is the only client for this code currently
2049
     * but we may want to use this in future for other purposed.
2050
     */
2051
0
    if (pdf_key_eq(param, "/CreationDate"))
2052
0
        return 1;
2053
0
    if (pdf_key_eq(param, "/ModDate"))
2054
0
        return 1;
2055
0
    return 0;
2056
0
}
2057
2058
/* DOCINFO pdfmark */
2059
static int
2060
pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2061
                const gs_matrix * pctm, const gs_param_string * no_objname)
2062
940
{
2063
    /*
2064
     * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except
2065
     * that we want to replace "Distiller" with our own name as the
2066
     * Producer.
2067
     */
2068
940
    cos_dict_t *const pcd = pdev->Info;
2069
940
    int code = 0, i;
2070
2071
940
    if (count & 1)
2072
0
        return_error(gs_error_rangecheck);
2073
2.62k
    for (i = 0; code >= 0 && i < count; i += 2) {
2074
1.68k
        const gs_param_string *pair = pairs + i;
2075
2076
1.68k
        if (pdev->CompatibilityLevel >= 2.0) {
2077
0
            if (!pdf_key_eq(pairs + i, "/ModDate") && !pdf_key_eq(pairs + i, "/CreationDate"))
2078
0
                continue;
2079
0
        }
2080
2081
1.68k
        if (pdev->PDFA !=0 && is_XMP_Key(pair)) {
2082
0
            const gs_param_string *p = pairs + i + 1;
2083
0
            bool abort = false;
2084
2085
            /* Ensure that we can write the XMP string. If not, handle this according to PDFACompatibilityPolicy */
2086
0
            code = pdf_xmp_write_translated(pdev, NULL, p->data + 1, p->size - 2, NULL);
2087
0
            abort = code < 0;
2088
2089
0
            if (abort == true)
2090
0
            {
2091
                /* Can't handle UTF16BE in PDF/A1, so abort this pair or abort PDF/A or just abort,
2092
                 * depending on PDFACompatibilityPolicy
2093
                 */
2094
0
                switch (pdev->PDFACompatibilityPolicy) {
2095
0
                    case 0:
2096
0
                        emprintf(pdev->memory,
2097
0
                         "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, reverting to normal PDF output\n");
2098
0
                        pdev->AbortPDFAX = true;
2099
0
                        pdev->PDFA = 0;
2100
0
                        break;
2101
0
                    case 1:
2102
0
                        emprintf(pdev->memory,
2103
0
                         "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, discarding DOCINFO\n");
2104
0
                        continue;
2105
0
                        break;
2106
0
                    case 2:
2107
0
                        emprintf(pdev->memory,
2108
0
                         "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, aborting conversion.\n");
2109
                        /* If we don't return a fatal error then zputdeviceparams simply ignores it (!) */
2110
0
                        return_error(gs_error_Fatal);
2111
0
                        break;
2112
0
                    default:
2113
0
                        break;
2114
0
                }
2115
0
            }
2116
0
        }
2117
1.68k
        if (pdf_key_eq(pairs + i, "/Producer")) {
2118
            /* Slightly screwy - separating the G, P and L characters this way to avoid
2119
             * accidental replacement by the release script.
2120
             */
2121
0
            const byte gs_g_p_l_prod_fam[16] = {'G', 'P', 'L', ' ', 'G', 'h', 'o', 's', 't', 's', 'c', 'r', 'i', 'p', 't', '\0'};
2122
0
            string_match_params params;
2123
0
            params = string_match_params_default;
2124
0
            params.ignore_case = true;
2125
2126
0
            if (!string_match((const byte *)GS_PRODUCTFAMILY, strlen(GS_PRODUCTFAMILY), gs_g_p_l_prod_fam, 15, &params))
2127
0
                code = pdfmark_put_pair(pcd, pair);
2128
0
        } else
2129
1.68k
            code = pdfmark_put_pair(pcd, pair);
2130
1.68k
        if (code < 0)
2131
0
            break;
2132
1.68k
    }
2133
940
    return code;
2134
940
}
2135
2136
/* DOCVIEW pdfmark */
2137
static int
2138
pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2139
                const gs_matrix * pctm, const gs_param_string * no_objname)
2140
625
{
2141
625
    char dest[MAX_DEST_STRING];
2142
625
    int code = 0;
2143
2144
625
    if (count & 1)
2145
0
        return_error(gs_error_rangecheck);
2146
625
    code = pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 0);
2147
625
    if (code < 0)
2148
0
        return gs_note_error(gs_error_rangecheck);
2149
2150
625
    if (code > 0) {
2151
138
        int i;
2152
2153
138
        code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction",
2154
138
                                         (byte *)dest, strlen(dest));
2155
414
        for (i = 0; code >= 0 && i < count; i += 2)
2156
276
            if (!(pdf_key_eq(&pairs[i], "/Page") ||
2157
276
                  pdf_key_eq(&pairs[i], "/View"))
2158
276
                )
2159
0
                code = pdfmark_put_pair(pdev->Catalog, pairs + i);
2160
138
        return code;
2161
138
    } else
2162
487
        return pdfmark_put_pairs(pdev->Catalog, pairs, count);
2163
625
}
2164
2165
/* ---------------- Named object pdfmarks ---------------- */
2166
2167
/* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */
2168
static int
2169
pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2170
           const gs_matrix * pctm, const gs_param_string * objname)
2171
0
{
2172
0
    gs_rect bbox;
2173
0
    cos_stream_t *pcs;
2174
0
    int code;
2175
0
    gs_matrix ictm;
2176
0
    byte bbox_str[6 + 6 * 15], matrix_str[6 + 6 * 15];
2177
0
    char chars[MAX_RECT_STRING + 1];
2178
0
    int bbox_str_len, matrix_str_len;
2179
0
    stream s;
2180
2181
0
    if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox"))
2182
0
        return_error(gs_error_rangecheck);
2183
0
    code = gs_matrix_invert(pctm, &ictm);
2184
0
    if (code < 0)
2185
0
        return code;
2186
0
    if (pairs[1].size > MAX_RECT_STRING)
2187
0
        return_error(gs_error_limitcheck);
2188
0
    memcpy(chars, pairs[1].data, pairs[1].size);
2189
0
    chars[pairs[1].size] = 0;
2190
0
    if (sscanf(chars, "[%lg %lg %lg %lg]",
2191
0
               &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4)
2192
0
        return_error(gs_error_rangecheck);
2193
0
    if ((pdev->used_mask << 1) == 0)
2194
0
        return_error(gs_error_limitcheck);
2195
0
    code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
2196
0
    if (code < 0)
2197
0
        return code;
2198
0
    { byte *s = gs_alloc_string(pdev->memory, objname->size, "pdfmark_PS");
2199
2200
0
        if (s == NULL)
2201
0
            return_error(gs_error_VMerror);
2202
0
        memcpy(s, objname->data, objname->size);
2203
0
        pdev->objname.data = s;
2204
0
        pdev->objname.size = objname->size;
2205
0
    }
2206
0
    pcs->is_graphics = true;
2207
0
    gs_bbox_transform(&bbox, pctm, &bbox);
2208
0
    s_init(&s, NULL);
2209
0
    swrite_string(&s, bbox_str, sizeof(bbox_str));
2210
0
    pprintg4(&s, "[%g %g %g %g]",
2211
0
            bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
2212
0
    bbox_str_len = stell(&s);
2213
0
    swrite_string(&s, matrix_str, sizeof(bbox_str));
2214
0
    pprintg6(&s, "[%g %g %g %g %g %g]",
2215
0
            ictm.xx, ictm.xy, ictm.yx, ictm.yy, ictm.tx, ictm.ty);
2216
0
    matrix_str_len = stell(&s);
2217
0
    if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 ||
2218
0
        (code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 ||
2219
0
        (code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 ||
2220
0
        (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox",
2221
0
                                          bbox_str, bbox_str_len)) < 0 ||
2222
0
        (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Matrix",
2223
0
                                      matrix_str, matrix_str_len)) < 0 ||
2224
0
        (code = cos_dict_put_c_key_object(cos_stream_dict(pcs), "/Resources",
2225
0
                                          COS_OBJECT(pdev->substream_Resources))) < 0
2226
0
        )
2227
0
        return code;
2228
    /* Don't add to local_named_objects untill the object is created
2229
       to prevent pending references, which may appear
2230
       if /PUT pdfmark executes before pdf_substitute_resource in pdfmark_EP
2231
       drops this object.
2232
    */
2233
0
    pdev->FormDepth++;
2234
0
    return 0;
2235
0
}
2236
2237
/* [ /EP pdfmark */
2238
static int
2239
pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2240
           const gs_matrix * pctm, const gs_param_string * no_objname)
2241
0
{
2242
0
    int code;
2243
0
    pdf_resource_t *pres = pdev->accumulating_substream_resource;
2244
0
    gs_const_string objname = pdev->objname;
2245
2246
    /* We are not currently accumulating a resource, this suggests an /EP
2247
     * without an opening /BP. This is (obviously) an error, Distiller throws
2248
     * an 'undefined' error, so we will too.
2249
     */
2250
0
    if (pres == NULL)
2251
0
        return_error(gs_error_undefined);
2252
2253
0
    if (pdev->CompatibilityLevel <= 1.7) {
2254
0
        code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
2255
0
        if (code < 0)
2256
0
            return code;
2257
0
    }
2258
0
    code = pdf_exit_substream(pdev);
2259
0
    if (code < 0)
2260
0
        return code;
2261
0
    code = pdfmark_bind_named_object(pdev, &objname, &pres);
2262
0
    if (code < 0)
2263
0
        return 0;
2264
0
    gs_free_const_string(pdev->memory, objname.data, objname.size, "pdfmark_EP");
2265
0
    pdev->FormDepth--;
2266
0
    return 0;
2267
0
}
2268
2269
/* [ {obj} /SP pdfmark */
2270
static int
2271
pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2272
           const gs_matrix * pctm, const gs_param_string * no_objname)
2273
0
{
2274
0
    cos_object_t *pco;    /* stream */
2275
0
    int code;
2276
2277
0
    if (count != 1)
2278
0
        return_error(gs_error_rangecheck);
2279
0
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
2280
0
        return code;
2281
0
    if (pco->is_open || !pco->is_graphics)
2282
0
        return_error(gs_error_rangecheck);
2283
0
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2284
0
    if (code < 0)
2285
0
        return code;
2286
0
    pdf_put_matrix(pdev, "q ", pctm, "cm");
2287
0
    pprinti64d1(pdev->strm, "/R%"PRId64" Do Q\n", pco->id);
2288
0
    pco->pres->where_used |= pdev->used_mask;
2289
2290
0
    code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pco->pres);
2291
0
    if (code < 0)
2292
0
        return code;
2293
2294
0
    return 0;
2295
0
}
2296
2297
/* [ /_objdef {array} /type /array /OBJ pdfmark */
2298
/* [ /_objdef {dict} /type /dict /OBJ pdfmark */
2299
/* [ /_objdef {stream} /type /stream /OBJ pdfmark */
2300
static int
2301
pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2302
            const gs_matrix * pctm, const gs_param_string * objname)
2303
1.10k
{
2304
1.10k
    cos_type_t cotype;
2305
1.10k
    cos_object_t *pco;
2306
1.10k
    bool stream = false;
2307
1.10k
    int code;
2308
2309
1.10k
    if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type"))
2310
0
        return_error(gs_error_rangecheck);
2311
1.10k
    if (pdf_key_eq(&pairs[1], "/array"))
2312
0
        cotype = cos_type_array;
2313
1.10k
    else if (pdf_key_eq(&pairs[1], "/dict"))
2314
900
        cotype = cos_type_dict;
2315
204
    else if ((stream = pdf_key_eq(&pairs[1], "/stream")))
2316
204
        cotype = cos_type_stream;
2317
0
    else
2318
0
        return_error(gs_error_rangecheck);
2319
1.10k
    if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) {
2320
        /*
2321
         * For Distiller compatibility, allows multiple /OBJ pdfmarks with
2322
         * the same name and type, even though the pdfmark specification
2323
         * doesn't say anything about this being legal.
2324
         */
2325
0
        if (code == gs_error_rangecheck &&
2326
0
            pdf_refer_named(pdev, objname, &pco) >= 0 &&
2327
0
            cos_type(pco) == cotype
2328
0
            )
2329
0
            return 0;   /* already exists, but OK */
2330
0
        return code;
2331
0
    }
2332
1.10k
    if (stream) {
2333
204
        if (pdev->CompressStreams)
2334
204
            return setup_pdfmark_stream_compression((gx_device_psdf *)pdev,
2335
204
                                                     (cos_stream_t *)pco);
2336
0
        else
2337
0
            return setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev,
2338
0
                                                     (cos_stream_t *)pco);
2339
204
    }
2340
900
    return 0;
2341
1.10k
}
2342
2343
/* [ {array} index value /PUT pdfmark */
2344
/* Dictionaries are converted to .PUTDICT */
2345
/* Streams are converted to .PUTSTREAM */
2346
static int
2347
pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2348
            const gs_matrix * pctm, const gs_param_string * no_objname)
2349
0
{
2350
0
    cos_object_t *pco;
2351
0
    cos_value_t value;
2352
0
    int code, index;
2353
2354
0
    if (count != 3)
2355
0
        return_error(gs_error_rangecheck);
2356
0
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
2357
0
        return code;
2358
0
    if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
2359
0
        return code;
2360
0
    if (index < 0)
2361
0
        return_error(gs_error_rangecheck);
2362
0
    if (pco->written)
2363
0
        return_error(gs_error_rangecheck);
2364
0
    return cos_array_put((cos_array_t *)pco, index,
2365
0
                cos_string_value(&value, pairs[2].data, pairs[2].size));
2366
0
}
2367
2368
/* [ {dict} key value ... /.PUTDICT pdfmark */
2369
/* [ {stream} key value ... /.PUTDICT pdfmark */
2370
/*
2371
 * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream,
2372
 * but it's reasonable and unambiguous, and Acrobat Distiller accepts it,
2373
 * so we do too.
2374
 */
2375
static int
2376
pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2377
                const gs_matrix * pctm, const gs_param_string * no_objname)
2378
1.03k
{
2379
1.03k
    cos_object_t *pco;
2380
1.03k
    int code, i;
2381
2382
1.03k
    if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
2383
0
        return code;
2384
1.03k
    if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream)
2385
0
        return_error(gs_error_typecheck);
2386
1.03k
    if (pco->written)
2387
0
        return_error(gs_error_rangecheck);
2388
2389
    /* If this is a stream, and we are doing PDF/A output, and the stream
2390
     * is a Metadata stream, then we must not write it compressed. Bizarrely PDF/A
2391
     * excludes this.
2392
     * Actually, we cna't write Metadata streams at all. Because we don't know how to extend
2393
     * the XMP Metadata to include them.
2394
     */
2395
1.03k
    if (cos_type(pco) == cos_type_stream && pdev->PDFA) {
2396
0
        for (i=0;i<count;i++) {
2397
0
            if (pairs[i].size == 9 && strncmp((const char *)pairs[i].data, "/Metadata", 9) == 0) {
2398
0
                cos_dict_t *pcd = (cos_dict_t *)pco;
2399
2400
0
                if (pdev->PDFA) {
2401
0
                    switch (pdev->PDFACompatibilityPolicy) {
2402
0
                        case 0:
2403
0
                            emprintf(pdev->memory,
2404
0
                                     "Cannot preserve Marked Content in PDF/A, reverting to normal PDF output\n\n");
2405
0
                            pdev->AbortPDFAX = true;
2406
0
                            pdev->PDFA = 0;
2407
0
                            break;
2408
0
                        case 1:
2409
0
                            emprintf(pdev->memory,
2410
0
                                     "Cannot preserve Marked Content in PDF/A, dropping feature to preserve PDF/A compatibility\n");
2411
0
                            return 0;
2412
0
                            break;
2413
0
                        case 2:
2414
0
                            emprintf(pdev->memory,
2415
0
                                     "Cannot preserve Marked Content in PDF/A, aborting conversion\n");
2416
0
                            return_error (gs_error_typecheck);
2417
0
                            break;
2418
0
                        default:
2419
0
                            emprintf(pdev->memory,
2420
0
                                     "Cannot preserve Marked Content in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
2421
0
                            pdev->AbortPDFAX = true;
2422
0
                            pdev->PDFA = 0;
2423
0
                            break;
2424
0
                    }
2425
0
                }
2426
2427
                /* Close the compressed stream */
2428
0
                gs_free_object(pdev->pdf_memory, pco->input_strm, "free old stream, replacing with new stream");
2429
                /* And create a new uncompressed stream */
2430
0
                code = setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev,
2431
0
                                                     (cos_stream_t *)pco);
2432
0
                if (code < 0)
2433
0
                    return code;
2434
2435
                /* We also need to remove any compression filters from the stream dictionary
2436
                 * The only possible error here is that the key isn't found, which is possible
2437
                 * and we don't care, so ignore the return code.
2438
                 */
2439
0
                cos_dict_delete_c_key(pcd, "/Filter");
2440
0
                cos_dict_delete_c_key(pcd, "/DecodeParams");
2441
2442
0
            }
2443
0
        }
2444
0
    }
2445
1.03k
    return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1);
2446
1.03k
}
2447
2448
/* [ {stream} string ... /.PUTSTREAM pdfmark */
2449
static int
2450
pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2451
                  const gs_matrix * pctm, const gs_param_string * no_objname)
2452
201
{
2453
201
    cos_object_t *pco;
2454
201
    int code, i;
2455
201
    uint l;
2456
2457
201
    if (count < 2)
2458
0
        return_error(gs_error_rangecheck);
2459
201
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
2460
0
        return code;
2461
201
    if (!pco->is_open) {
2462
0
        pdev->pdfmark_dup_stream = true;
2463
0
        return 0;
2464
0
    }
2465
402
    for (i = 1; i < count; ++i)
2466
201
        if (sputs(pco->input_strm, pairs[i].data, pairs[i].size, &l) != 0)
2467
0
            return_error(gs_error_ioerror);
2468
201
    if (pco->written)
2469
0
        return_error(gs_error_rangecheck);
2470
201
    pdev->pdfmark_dup_stream = false;
2471
201
    return code;
2472
201
}
2473
2474
/* [ {array} value /APPEND pdfmark */
2475
static int
2476
pdfmark_APPEND(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2477
               const gs_matrix * pctm, const gs_param_string * objname)
2478
0
{
2479
0
    cos_object_t *pco;
2480
0
    cos_value_t value;
2481
0
    int code;
2482
2483
0
    if (count != 2)
2484
0
        return_error(gs_error_rangecheck);
2485
0
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
2486
0
        return code;
2487
0
    return cos_array_add((cos_array_t *)pco,
2488
0
                cos_string_value(&value, pairs[1].data, pairs[1].size));
2489
0
}
2490
2491
/* [ {array} index value ... /.PUTINTERVAL pdfmark */
2492
static int
2493
pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2494
                 const gs_matrix * pctm, const gs_param_string * no_objname)
2495
0
{
2496
0
    cos_object_t *pco;
2497
0
    cos_value_t value;
2498
0
    int code, index, i;
2499
2500
0
    if (count < 2)
2501
0
        return_error(gs_error_rangecheck);
2502
0
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
2503
0
        return code;
2504
0
    if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
2505
0
        return code;
2506
0
    if (index < 0)
2507
0
        return_error(gs_error_rangecheck);
2508
0
    for (i = 2; code >= 0 && i < count; ++i)
2509
0
        code = cos_array_put((cos_array_t *)pco, index + i - 2,
2510
0
                cos_string_value(&value, pairs[i].data, pairs[i].size));
2511
0
    return code;
2512
0
}
2513
2514
/* [ {stream} /CLOSE pdfmark */
2515
static int
2516
pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
2517
              const gs_matrix * pctm, const gs_param_string * no_objname)
2518
195
{
2519
195
    cos_object_t *pco;
2520
195
    int code;
2521
2522
195
    if (count != 1)
2523
0
        return_error(gs_error_rangecheck);
2524
195
    if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
2525
0
        return code;
2526
195
    if (!pco->is_open && !pdev->pdfmark_dup_stream)
2527
0
        return_error(gs_error_rangecheck);
2528
195
    pdev->pdfmark_dup_stream = false;
2529
    /* Currently we don't do anything special when closing a stream. */
2530
195
    pco->is_open = false;
2531
195
    return 0;
2532
195
}
2533
2534
/* [ /NamespacePush pdfmark */
2535
static int
2536
pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2537
                      const gs_matrix *pctm, const gs_param_string *objname)
2538
8
{
2539
8
    if (count != 0)
2540
0
        return_error(gs_error_rangecheck);
2541
8
    return pdf_push_namespace(pdev);
2542
8
}
2543
2544
/* [ /NamespacePop pdfmark */
2545
static int
2546
pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2547
                     const gs_matrix *pctm, const gs_param_string *objname)
2548
1
{
2549
1
    if (count != 0)
2550
0
        return_error(gs_error_rangecheck);
2551
1
    cos_dict_objects_write(pdev->local_named_objects, pdev);
2552
1
    return pdf_pop_namespace(pdev);
2553
1
}
2554
2555
/* [ /_objdef {image} /NI pdfmark */
2556
static int
2557
pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2558
           const gs_matrix *pctm, const gs_param_string *objname)
2559
6
{
2560
6
    cos_object_t *pco;
2561
6
    int code;
2562
2563
6
    if (objname == 0 || count != 0)
2564
0
        return_error(gs_error_rangecheck);
2565
6
    code = pdf_make_named(pdev, objname, cos_type_dict, &pco, true);
2566
6
    if (code < 0)
2567
0
        return code;
2568
6
    return cos_array_add_object(pdev->NI_stack, pco);
2569
6
}
2570
2571
/* ---------------- Named content pdfmarks ---------------- */
2572
2573
/* [ tag /MP pdfmark */
2574
static int
2575
pdfmark_MP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2576
           const gs_matrix *pctm, const gs_param_string *objname)
2577
39
{
2578
39
    int code;
2579
39
    char *tag;
2580
2581
39
    if (count != 1) return_error(gs_error_rangecheck);
2582
2583
39
    tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
2584
39
                "pdfmark_MP");
2585
39
    if (tag == NULL)
2586
0
        return_error(gs_error_VMerror);
2587
2588
39
    memcpy(tag, pairs[0].data, pairs[0].size);
2589
39
    tag[pairs[0].size] = 0x00;
2590
2591
39
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2592
39
    if (code < 0) return code;
2593
2594
39
    pprints1(pdev->strm, "%s MP\n", tag);
2595
2596
39
    gs_free_object(pdev->memory, tag, "pdfmark_MP");
2597
39
    return 0;
2598
39
}
2599
2600
/* [ tag propdict /DP pdfmark */
2601
static int
2602
pdfmark_DP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2603
           const gs_matrix *pctm, const gs_param_string *objname)
2604
1
{
2605
1
    int code;
2606
1
    cos_object_t *pco;
2607
1
    char *cstring;
2608
1
    pdf_resource_t *pres;
2609
2610
1
    if (count != 2) return_error(gs_error_rangecheck);
2611
2612
    /* check tag for /Name object syntax */
2613
1
    if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck);
2614
2615
    /* check propdict for {object name} syntax */
2616
1
    if (pdf_objname_is_valid(pairs[1].data, pairs[1].size))
2617
0
    {
2618
0
        code = pdf_refer_named(pdev, &pairs[1], &pco);
2619
0
        if(code < 0) return code;
2620
0
    }
2621
1
    else /* << inline prop dict >> */
2622
1
    {
2623
        /* strip << and >> */
2624
1
        if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<')
2625
1
        {
2626
1
            int ix = 0;
2627
1
            byte *p = (byte *)pairs[1].data;
2628
2629
            /* Fortunately we don't use the 'size' when freeing the memory
2630
             * so we can quickly copy the string content up two places and reduce
2631
             * the size by 2 to remove the '<<'. This saves an alloc and free of the
2632
             * string data.
2633
             */
2634
43
            for (ix = 0; ix < pairs[1].size - 2;ix++)
2635
42
                p[ix] = pairs[1].data[ix + 2];
2636
1
            pairs[1].size-=2;
2637
1
        }
2638
0
        else
2639
0
            return_error(gs_error_rangecheck);
2640
2641
1
        if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>')
2642
1
            pairs[1].size-=2;
2643
2644
        /* convert inline propdict to C string with object names replaced by refs */
2645
1
        code = pdf_replace_names(pdev, &pairs[1], &pairs[1]);
2646
1
        if (code<0) return code;
2647
1
        cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char),
2648
1
            "pdfmark_DP");
2649
1
        if (cstring == NULL)
2650
0
            return_error(gs_error_VMerror);
2651
2652
1
        memcpy(cstring, pairs[1].data, pairs[1].size);
2653
1
        cstring[pairs[1].size] = 0x00;
2654
2655
1
        code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true);
2656
1
        if (code<0) return code;
2657
2658
        /* copy inline propdict to new object */
2659
1
        code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, "");
2660
1
        if(code < 0) return code;
2661
1
        COS_WRITE_OBJECT(pco, pdev, resourceProperties);
2662
1
        COS_RELEASE(pco, "pdfmark_DP");
2663
1
        gs_free_object(pdev->memory, cstring, "pdfmark_DP");
2664
1
    }
2665
2666
1
    pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, pco->id);
2667
1
    if (pres==0){
2668
1
        if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0)
2669
0
            return code;
2670
1
    }
2671
2672
1
    cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
2673
1
                "pdfmark_DP");
2674
1
    if (cstring == NULL)
2675
0
        return_error(gs_error_VMerror);
2676
1
    memcpy(cstring, pairs[0].data, pairs[0].size);
2677
1
    cstring[pairs[0].size] = 0x00;
2678
2679
    /* make sure we write to the correct stream */
2680
1
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2681
1
    if (code < 0) return code;
2682
2683
1
    pprints1(pdev->strm, "%s", cstring); /* write tag */
2684
1
    pprinti64d1(pdev->strm, "/R%"PRId64" DP\n", pco->id);
2685
1
    pco->pres->where_used |= pdev->used_mask;
2686
1
    if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0)
2687
0
        return code;
2688
2689
1
    gs_free_object(pdev->memory, cstring, "pdfmark_DP");
2690
1
    return 0;
2691
1
}
2692
2693
/* [ tag /BMC pdfmark */
2694
static int
2695
pdfmark_BMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2696
            const gs_matrix *pctm, const gs_param_string *objname)
2697
370
{
2698
370
    int code;
2699
370
    char *tag;
2700
2701
370
    if (count != 1) return_error(gs_error_rangecheck);
2702
2703
370
    tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
2704
370
                "pdfmark_BMC");
2705
370
    if (tag == NULL)
2706
0
        return_error(gs_error_VMerror);
2707
2708
370
    memcpy(tag, pairs[0].data, pairs[0].size);
2709
370
    tag[pairs[0].size] = 0x00;
2710
2711
370
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2712
370
    if (code < 0) return code;
2713
2714
370
    pprints1(pdev->strm, "%s BMC\n", tag);
2715
2716
370
    gs_free_object(pdev->memory, tag, "pdfmark_BMC");
2717
370
    return 0;
2718
370
}
2719
2720
/* [ tag propdict /BDC pdfmark */
2721
static int
2722
pdfmark_BDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2723
            const gs_matrix *pctm, const gs_param_string *objname)
2724
7.29k
{
2725
7.29k
    int code, id = 0, i, esc_size = 0;
2726
7.29k
    cos_object_t *pco = NULL;
2727
7.29k
    char *cstring;
2728
7.29k
    pdf_resource_t *pres;
2729
2730
7.29k
    if (count != 2) return_error(gs_error_rangecheck);
2731
2732
    /* check tag for /Name object syntax */
2733
7.29k
    if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck);
2734
2735
    /* Check for /OC (Optional Content), if it is Optional Content, check the output PDF level is at least 1.5 */
2736
7.29k
    if (pairs[0].size == 3 && memcmp(pairs[0].data, "/OC", 3) == 0) {
2737
1.40k
        if (pdev->CompatibilityLevel < 1.4999) {
2738
0
            if (pdev->PDFA) {
2739
0
                switch (pdev->PDFACompatibilityPolicy) {
2740
0
                    case 0:
2741
0
                        emprintf(pdev->memory,
2742
0
                                 "Optional (Marked) Content not valid in this version of PDF, reverting to normal PDF output\n\n");
2743
0
                        pdev->AbortPDFAX = true;
2744
0
                        pdev->PDFA = 0;
2745
0
                        break;
2746
0
                    case 1:
2747
0
                        emprintf(pdev->memory,
2748
0
                                 "Optional (Marked) Content not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n");
2749
0
                        return 0;
2750
0
                        break;
2751
0
                    case 2:
2752
0
                        emprintf(pdev->memory,
2753
0
                                 "Optional (Marked) Content not valid in this version of PDF,  aborting conversion\n");
2754
0
                        return_error (gs_error_typecheck);
2755
0
                        break;
2756
0
                    default:
2757
0
                        emprintf(pdev->memory,
2758
0
                                 "Optional (Marked) Content not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
2759
0
                        pdev->AbortPDFAX = true;
2760
0
                        pdev->PDFA = 0;
2761
0
                        break;
2762
0
                }
2763
0
            } else {
2764
0
                emprintf(pdev->memory,
2765
0
                         "Optional (Marked) Content not valid in this version of PDF. Dropping feature to preserve compatibility\n");
2766
0
                return 0;
2767
0
            }
2768
0
        }
2769
1.40k
    }
2770
2771
    /* check propdict for {object name} syntax */
2772
7.29k
    if (pdf_objname_is_valid(pairs[1].data, pairs[1].size))
2773
1.64k
    {
2774
1.64k
        code = pdf_refer_named(pdev, &pairs[1], &pco);
2775
1.64k
        if(code < 0) return code;
2776
1.64k
        id = pco->id;
2777
1.64k
    }
2778
5.64k
    else /* << inline prop dict >> */
2779
5.64k
    {
2780
        /* strip << and >> */
2781
5.64k
        if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<')
2782
5.64k
        {
2783
5.64k
            int ix = 0;
2784
5.64k
            byte *p = (byte *)pairs[1].data;
2785
2786
            /* Fortunately we don't use the 'size' when freeing the memory
2787
             * so we can quickly copy the string content up two places and reduce
2788
             * the size by 2 to remove the '<<'. This saves an alloc and free of the
2789
             * string data.
2790
             */
2791
99.9k
            for (ix = 0; ix < pairs[1].size - 2;ix++)
2792
94.3k
                p[ix] = pairs[1].data[ix + 2];
2793
5.64k
            pairs[1].size-=2;
2794
2795
5.64k
            if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>')
2796
5.64k
            pairs[1].size-=2;
2797
2798
            /* convert inline propdict to C string with object names replaced by refs */
2799
5.64k
            code = pdf_replace_names(pdev, &pairs[1], &pairs[1]);
2800
5.64k
            if (code<0) return code;
2801
5.64k
            cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char),
2802
5.64k
                "pdfmark_BDC");
2803
5.64k
            if (cstring == NULL)
2804
0
                return_error(gs_error_VMerror);
2805
2806
5.64k
            memcpy(cstring, pairs[1].data, pairs[1].size);
2807
5.64k
            cstring[pairs[1].size] = 0x00;
2808
2809
5.64k
            code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true);
2810
5.64k
            if (code<0) return code;
2811
2812
            /* copy inline propdict to new object */
2813
5.64k
            code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, "");
2814
5.64k
            if(code < 0) return code;
2815
2816
5.64k
            COS_WRITE_OBJECT(pco, pdev, resourceProperties);
2817
2818
5.64k
            COS_RELEASE(pco, "pdfmark_BDC");
2819
5.64k
            gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
2820
5.64k
            id = pco->id;
2821
5.64k
        }
2822
0
        else {
2823
0
            if ((pairs[1].data)[pairs[1].size-1]!='R') {
2824
0
                if ((pairs[1].data)[pairs[1].size-2]==' ' && (pairs[1].data)[pairs[1].size-1]!='R')
2825
0
                    return_error(gs_error_rangecheck);
2826
0
                if (sscanf((const char *)pairs[1].data, "%d 0 R", &id) != 1)
2827
0
                    return_error(gs_error_unknownerror);
2828
0
            }
2829
2830
0
        }
2831
5.64k
    }
2832
2833
7.29k
    pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, id);
2834
7.29k
    if (pres==0){
2835
5.98k
        if (pco != NULL) {
2836
5.98k
            if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0) {
2837
0
                return code;
2838
0
            }
2839
5.98k
        }
2840
0
        else
2841
0
            if ((code = pdf_alloc_resource(pdev, resourceProperties, id, &pres, id))<0)
2842
0
                return code;
2843
5.98k
    }
2844
2845
    /* We need to make sure we escape any white space in the tag */
2846
53.6k
    for (i = 0;i < pairs[0].size;i++) {
2847
46.3k
        switch(pairs[0].data[i]) {
2848
0
            case 0x00:
2849
0
            case 0x09:
2850
0
            case 0x0a:
2851
0
            case 0x0c:
2852
0
            case 0x0d:
2853
0
            case 0x20:
2854
0
                esc_size += 3;
2855
0
                break;
2856
46.3k
            default:
2857
46.3k
                esc_size++;
2858
46.3k
                break;
2859
46.3k
        }
2860
46.3k
    }
2861
2862
7.29k
    cstring = (char *)gs_alloc_bytes(pdev->memory, (esc_size + 1) * sizeof(unsigned char),
2863
7.29k
                "pdfmark_BDC");
2864
7.29k
    if (cstring == NULL)
2865
0
        return_error(gs_error_VMerror);
2866
2867
7.29k
    esc_size = 0;
2868
53.6k
    for (i = 0;i < pairs[0].size;i++) {
2869
46.3k
        switch(pairs[0].data[i]) {
2870
0
            case 0x00:
2871
0
            case 0x09:
2872
0
            case 0x0a:
2873
0
            case 0x0c:
2874
0
            case 0x0d:
2875
0
            case 0x20:
2876
0
                cstring[esc_size++] = '#';
2877
0
                cstring[esc_size++] = (pairs[0].data[i] >> 4) + 0x30;
2878
0
                cstring[esc_size++] = (pairs[0].data[i] & 0x0f) + 0x30;
2879
0
                break;
2880
46.3k
            default:
2881
46.3k
                cstring[esc_size++] = pairs[0].data[i];
2882
46.3k
                break;
2883
46.3k
        }
2884
46.3k
    }
2885
7.29k
    cstring[esc_size] = 0x00;
2886
2887
    /* make sure we write to the correct stream */
2888
7.29k
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2889
7.29k
    if (code < 0) return code;
2890
2891
7.29k
    pprints1(pdev->strm, "%s", cstring); /* write tag */
2892
7.29k
    pprinti64d1(pdev->strm, "/R%"PRId64" BDC\n", id);
2893
7.29k
    if (pco != NULL) {
2894
7.29k
        pco->pres->where_used |= pdev->used_mask;
2895
7.29k
        if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0)
2896
0
            return code;
2897
7.29k
    }
2898
0
    else {
2899
0
        pres->where_used |= pdev->used_mask;
2900
0
        if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pres))<0)
2901
0
            return code;
2902
0
    }
2903
2904
2905
7.29k
    gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
2906
7.29k
    return 0;
2907
7.29k
}
2908
2909
/* [ /EMC pdfmark */
2910
static int
2911
pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2912
            const gs_matrix *pctm, const gs_param_string *objname)
2913
12.0k
{
2914
12.0k
    int code;
2915
2916
12.0k
    code = pdf_open_contents(pdev, PDF_IN_STREAM);
2917
12.0k
    if (code < 0) return code;
2918
12.0k
    stream_puts(pdev->strm, "EMC\n");
2919
2920
12.0k
    return 0;
2921
12.0k
}
2922
2923
/* ---------------- Document structure pdfmarks ---------------- */
2924
2925
/* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */
2926
static int
2927
pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2928
                  const gs_matrix *pctm, const gs_param_string *objname)
2929
0
{
2930
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2931
0
}
2932
2933
/* [ class1 {attrobj1} ... /StClassMap pdfmark */
2934
static int
2935
pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2936
                   const gs_matrix *pctm, const gs_param_string *objname)
2937
0
{
2938
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2939
0
}
2940
2941
/*
2942
 * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string]
2943
 *   [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...]
2944
 *   /StPNE pdfmark
2945
 */
2946
static int
2947
pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2948
              const gs_matrix *pctm, const gs_param_string *objname)
2949
0
{
2950
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2951
0
}
2952
2953
/* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */
2954
static int
2955
pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2956
                       const gs_matrix *pctm, const gs_param_string *objname)
2957
0
{
2958
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2959
0
}
2960
2961
/* [ [/E {elt}] /StPush pdfmark */
2962
static int
2963
pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2964
               const gs_matrix *pctm, const gs_param_string *objname)
2965
0
{
2966
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2967
0
}
2968
2969
/* [ /StPop pdfmark */
2970
static int
2971
pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2972
              const gs_matrix *pctm, const gs_param_string *objname)
2973
0
{
2974
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2975
0
}
2976
2977
/* [ /StPopAll pdfmark */
2978
static int
2979
pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2980
                 const gs_matrix *pctm, const gs_param_string *objname)
2981
0
{
2982
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2983
0
}
2984
2985
/* [ [/T tagname] [/At index] /StBMC pdfmark */
2986
static int
2987
pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2988
              const gs_matrix *pctm, const gs_param_string *objname)
2989
0
{
2990
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2991
0
}
2992
2993
/* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */
2994
static int
2995
pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
2996
              const gs_matrix *pctm, const gs_param_string *objname)
2997
0
{
2998
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
2999
0
}
3000
3001
/* [ /Obj {obj} [/At index] /StOBJ pdfmark */
3002
static int
3003
pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
3004
              const gs_matrix *pctm, const gs_param_string *objname)
3005
0
{
3006
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
3007
0
}
3008
3009
/* [ /Obj {obj} /StAttr pdfmark */
3010
static int
3011
pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
3012
               const gs_matrix *pctm, const gs_param_string *objname)
3013
0
{
3014
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
3015
0
}
3016
3017
/* [ /StoreName name /StStore pdfmark */
3018
static int
3019
pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
3020
                const gs_matrix *pctm, const gs_param_string *objname)
3021
0
{
3022
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
3023
0
}
3024
3025
/* [ /StoreName name /StRetrieve pdfmark */
3026
static int
3027
pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
3028
                   const gs_matrix *pctm, const gs_param_string *objname)
3029
0
{
3030
0
    return 0;     /****** NOT IMPLEMENTED YET ******/
3031
0
}
3032
3033
static int
3034
pdfmark_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
3035
             const gs_matrix * pctm, const gs_param_string * objname)
3036
0
{
3037
0
    int i;
3038
0
    gs_param_string key;
3039
0
    char data[10] = "/Metadata";
3040
3041
0
    if (pdev->CompatibilityLevel < 1.4) {
3042
0
        dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n");
3043
0
        return 0;
3044
0
    }
3045
0
    if (pdev->PDFA != 0)
3046
0
        dmprintf(pdev->pdf_memory, "Warning: PDF/A output requires specific metadata, this pdfmark has overridden that,\n         output conformance cannot be guaranteed\n");
3047
0
    if (pdev->PDFX != 0)
3048
0
        dmprintf(pdev->pdf_memory, "Warning: PDF/X output requires specific metadata, this pdfmark has overridden that,\n         output conformance cannot be guaranteed\n");
3049
3050
0
    if(pdev->ExtensionMetadata) {
3051
0
        dmprintf(pdev->pdf_memory, "Extension metadata exists when /Metadata pdfmark executed, discarding extension metadata.\n");
3052
0
        gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata discarded on /Metadata pdfmark");
3053
0
    }
3054
3055
0
    if (!pdev->Catalog) {
3056
0
        gs_param_string nstr;
3057
3058
0
        param_string_from_string(nstr, "{Catalog}");
3059
0
        pdf_create_named_dict(pdev, &nstr, &pdev->Catalog, 0L);
3060
0
    }
3061
3062
0
    key.data = (const byte *)&data;
3063
0
    key.size = 9;
3064
3065
0
    for (i = 0; i < count; i += 2) {
3066
0
        if (pdf_key_eq(&pairs[i], "{Catalog}")) {
3067
0
            return cos_dict_put_string(pdev->Catalog, key.data, key.size,
3068
0
                               pairs[i+1].data, pairs[i+1].size);
3069
0
        }
3070
0
    }
3071
0
    return 0;
3072
0
}
3073
3074
static int
3075
pdfmark_Ext_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
3076
             const gs_matrix * pctm, const gs_param_string * objname)
3077
0
{
3078
0
    int i, j=0;
3079
3080
0
    if (pdev->CompatibilityLevel < 1.4) {
3081
0
        dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n");
3082
0
        return 0;
3083
0
    }
3084
3085
0
    if (cos_dict_find_c_key(pdev->Catalog, "/Metadata")) {
3086
0
        dmprintf(pdev->pdf_memory, "Cannot add extension to Metadata specified with the /Metadata pdfmark\n");
3087
0
        return 0;
3088
0
    }
3089
3090
0
    if(pdev->ExtensionMetadata) {
3091
0
        dmprintf(pdev->pdf_memory, "Extension metadata already defined, discarding old data.\n");
3092
0
        gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata");
3093
0
    }
3094
0
    pdev->ExtensionMetadata = (char *)gs_alloc_bytes(pdev->pdf_memory->stable_memory, pairs[1].size - 1, "Extension metadata");
3095
0
    if (pdev->ExtensionMetadata == NULL)
3096
0
        return_error(gs_error_VMerror);
3097
3098
0
    memset(pdev->ExtensionMetadata, 0x00, pairs[1].size - 1);
3099
0
    for (i=1;i<pairs[1].size - 1;i++) {
3100
0
        if (pairs[1].data[i] == '\\') {
3101
0
            switch(pairs[1].data[i+1]) {
3102
0
                case '(':
3103
0
                case ')':
3104
0
                case '\\':
3105
0
                    pdev->ExtensionMetadata[j++] = pairs[1].data[i+1];
3106
0
                    i++;
3107
0
                    break;
3108
0
                case 'r':
3109
0
                    pdev->ExtensionMetadata[j++] = 0x0D;
3110
0
                    i++;
3111
0
                    break;
3112
0
                case 'n':
3113
0
                    pdev->ExtensionMetadata[j++] = 0x0A;
3114
0
                    i++;
3115
0
                    break;
3116
0
                case 't':
3117
0
                    pdev->ExtensionMetadata[j++] = 0x09;
3118
0
                    i++;
3119
0
                    break;
3120
0
                case 'b':
3121
0
                    pdev->ExtensionMetadata[j++] = 0x08;
3122
0
                    i++;
3123
0
                    break;
3124
0
                case 'f':
3125
0
                    pdev->ExtensionMetadata[j++] = 0x0C;
3126
0
                    i++;
3127
0
                    break;
3128
0
                default:
3129
0
                    if((pairs[1].data[i+1]) >= 0x30 && (pairs[1].data[i+1]) <= 0x39) {
3130
0
                        pdev->ExtensionMetadata[j++] = (pairs[1].data[i+1] - 0x30) * 64 + (pairs[1].data[i+2] - 0x30) * 8 + (pairs[1].data[i+3] - 0x30);
3131
0
                        i += 3;
3132
0
                    } else
3133
0
                        pdev->ExtensionMetadata[j++] = pairs[1].data[i];
3134
0
                    break;
3135
0
            }
3136
0
        } else
3137
0
            pdev->ExtensionMetadata[j++] = pairs[1].data[i];
3138
0
    }
3139
0
    return 0;
3140
0
}
3141
3142
static int
3143
pdfmark_OCProperties(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
3144
             const gs_matrix * pctm, const gs_param_string * objname)
3145
60
{
3146
60
    char *str;
3147
3148
60
    if (pdev->CompatibilityLevel < 1.4999) {
3149
0
        if (pdev->PDFA) {
3150
0
            switch (pdev->PDFACompatibilityPolicy) {
3151
0
                case 0:
3152
0
                    emprintf(pdev->memory,
3153
0
                             "Optional Content Properties not valid in this version of PDF, reverting to normal PDF output\n\n");
3154
0
                    pdev->AbortPDFAX = true;
3155
0
                    pdev->PDFA = 0;
3156
0
                    break;
3157
0
                case 1:
3158
0
                    emprintf(pdev->memory,
3159
0
                             "Optional Content Properties not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n");
3160
0
                    break;
3161
0
                case 2:
3162
0
                    emprintf(pdev->memory,
3163
0
                             "Optional Content Properties not valid in this version of PDF,  aborting conversion\n");
3164
0
                    return_error (gs_error_typecheck);
3165
0
                    break;
3166
0
                default:
3167
0
                    emprintf(pdev->memory,
3168
0
                             "Optional Content Properties not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
3169
0
                    pdev->AbortPDFAX = true;
3170
0
                    pdev->PDFA = 0;
3171
0
                    break;
3172
0
            }
3173
0
        } else {
3174
0
            emprintf(pdev->memory,
3175
0
                     "Optional Content Properties not valid in this version of PDF. Dropping feature to preserve compatibility\n");
3176
0
        }
3177
60
    } else {
3178
60
        str = (char *)gs_alloc_bytes(pdev->memory, pairs[0].size + 1, "pdfmark_OCProperties");
3179
60
        if (str == NULL)
3180
0
            return_error(gs_error_VMerror);
3181
3182
60
        memset(str, 0x00, pairs[0].size + 1);
3183
60
        memcpy(str, pairs[0].data, pairs[0].size);
3184
3185
60
        (void)cos_dict_put_c_key_string(pdev->Catalog, "/OCProperties",
3186
60
                                         (byte *)str, strlen(str));
3187
3188
60
        gs_free_object(pdev->memory, str, "pdfmark_OCProperties");
3189
60
    }
3190
60
    return 0;
3191
60
}
3192
3193
/* ---------------- Dispatch ---------------- */
3194
3195
/*
3196
 * Define the pdfmark types we know about.
3197
 */
3198
static const pdfmark_name mark_names[] =
3199
{
3200
        /* Miscellaneous. */
3201
    {"ANN",          pdfmark_ANN,         PDFMARK_NAMEABLE},
3202
    {"LNK",          pdfmark_LNK,         PDFMARK_NAMEABLE},
3203
    {"OUT",          pdfmark_OUT,         0},
3204
    {"ARTICLE",      pdfmark_ARTICLE,     0},
3205
    {"DEST",         pdfmark_DEST,        PDFMARK_NAMEABLE},
3206
    {"EMBED",        pdfmark_EMBED,       PDFMARK_NAMEABLE},
3207
    {"PS",           pdfmark_PS,          PDFMARK_NAMEABLE},
3208
    {"PAGES",        pdfmark_PAGES,       0},
3209
    {"PAGE",         pdfmark_PAGE,        0},
3210
    {"PAGELABEL",    pdfmark_PAGELABEL,   0},
3211
    {"DOCINFO",      pdfmark_DOCINFO,     0},
3212
    {"DOCVIEW",      pdfmark_DOCVIEW,     0},
3213
        /* Named objects. */
3214
    {"BP",           pdfmark_BP,          PDFMARK_NAMEABLE | PDFMARK_TRUECTM},
3215
    {"EP",           pdfmark_EP,          0},
3216
    {"SP",           pdfmark_SP,          PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | PDFMARK_TRUECTM},
3217
    {"OBJ",          pdfmark_OBJ,         PDFMARK_NAMEABLE},
3218
    {"PUT",          pdfmark_PUT,         PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
3219
    {".PUTDICT",     pdfmark_PUTDICT,     PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
3220
    {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
3221
    {".PUTSTREAM",   pdfmark_PUTSTREAM,   PDFMARK_ODD_OK | PDFMARK_KEEP_NAME |
3222
                                          PDFMARK_NO_REFS},
3223
    {"APPEND",       pdfmark_APPEND,      PDFMARK_KEEP_NAME},
3224
    {"CLOSE",        pdfmark_CLOSE,       PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
3225
    {"NamespacePush", pdfmark_NamespacePush, 0},
3226
    {"NamespacePop", pdfmark_NamespacePop, 0},
3227
    {"NI",           pdfmark_NI,          PDFMARK_NAMEABLE},
3228
        /* Marked content. */
3229
    {"MP",           pdfmark_MP,          PDFMARK_ODD_OK},
3230
    {"DP",           pdfmark_DP,          0},
3231
    {"BMC",          pdfmark_BMC,         PDFMARK_ODD_OK},
3232
    {"BDC",          pdfmark_BDC,         PDFMARK_NO_REFS},
3233
    {"EMC",          pdfmark_EMC,         0},
3234
        /* Document structure. */
3235
    {"StRoleMap",    pdfmark_StRoleMap,   0},
3236
    {"StClassMap",   pdfmark_StClassMap,  0},
3237
    {"StPNE",        pdfmark_StPNE,       PDFMARK_NAMEABLE},
3238
    {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0},
3239
    {"StPush",       pdfmark_StPush,       0},
3240
    {"StPop",        pdfmark_StPop,        0},
3241
    {"StPopAll",     pdfmark_StPopAll,     0},
3242
    {"StBMC",        pdfmark_StBMC,        0},
3243
    {"StBDC",        pdfmark_StBDC,        0},
3244
    /* EMC is listed under "Marked content" above. */
3245
    {"StOBJ",        pdfmark_StOBJ,        0},
3246
    {"StAttr",       pdfmark_StAttr,       0},
3247
    {"StStore",      pdfmark_StStore,      0},
3248
    {"StRetrieve",   pdfmark_StRetrieve,   0},
3249
    /* Metadata and extension */
3250
    {"Metadata",     pdfmark_Metadata,     0},
3251
    {"Ext_Metadata", pdfmark_Ext_Metadata, 0},
3252
    {"OCProperties", pdfmark_OCProperties, PDFMARK_ODD_OK},
3253
        /* End of list. */
3254
    {0, 0}
3255
};
3256
3257
/* Process a pdfmark. */
3258
int
3259
pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
3260
29.0k
{
3261
29.0k
    const gs_param_string *data = pma->data;
3262
29.0k
    uint size = pma->size;
3263
29.0k
    const gs_param_string *pts = &data[size - 1];
3264
29.0k
    const gs_param_string *objname = 0;
3265
29.0k
    gs_matrix ctm;
3266
29.0k
    const pdfmark_name *pmn;
3267
29.0k
    int code = 0;
3268
3269
29.0k
    if (size < 2)
3270
0
        return_error(gs_error_stackunderflow);
3271
3272
29.0k
    { int cnt, len = pts[-1].size;
3273
29.0k
        char buf[200]; /* 6 doubles should fit (%g == -0.14285714285714285e-101 = 25 chars) */
3274
3275
29.0k
        if (len > sizeof(buf) - 1)
3276
0
            return_error(gs_error_rangecheck);
3277
29.0k
        memcpy(buf, pts[-1].data, len);
3278
29.0k
        buf[len] = 0;
3279
29.0k
        cnt = sscanf(buf, "[%g %g %g %g %g %g]",
3280
29.0k
                   &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty);
3281
29.0k
        if (cnt != 6)
3282
5
            return_error(gs_error_rangecheck);
3283
29.0k
    }
3284
29.0k
    size -= 2;      /* remove CTM & pdfmark name */
3285
689k
    for (pmn = mark_names; pmn->mname != 0; ++pmn)
3286
689k
        if (pdf_key_eq(pts, pmn->mname)) {
3287
29.0k
            gs_memory_t *mem = pdev->pdf_memory;
3288
29.0k
            int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0;
3289
29.0k
            gs_param_string *pairs;
3290
29.0k
            int j, index;
3291
3292
            /*
3293
             * Our coordinate system is scaled so that user space is always
3294
             * default user space.  Adjust the CTM to match this, except if this
3295
             * particular pdfmark requires the "true" CTM.
3296
             */
3297
29.0k
            if (pmn->options & PDFMARK_TRUECTM)
3298
29.0k
                DO_NOTHING;
3299
29.0k
            else {
3300
29.0k
                double xscale = 72.0 / pdev->HWResolution[0],
3301
29.0k
                       yscale = 72.0 / pdev->HWResolution[1];
3302
29.0k
                ctm.xx *= xscale, ctm.xy *= yscale;
3303
29.0k
                ctm.yx *= xscale, ctm.yy *= yscale;
3304
29.0k
                ctm.tx *= xscale, ctm.ty *= yscale;
3305
29.0k
            }
3306
29.0k
            if (size & !odd_ok)
3307
0
                return_error(gs_error_rangecheck);
3308
29.0k
            if (pmn->options & PDFMARK_NAMEABLE) {
3309
                /* Look for an object name. */
3310
6.82k
                for (j = 0; j < size; j += 2) {
3311
6.26k
                    if (pdf_key_eq(&data[j], "/_objdef")) {
3312
1.11k
                        objname = &data[j + 1];
3313
1.11k
                        if (!pdf_objname_is_valid(objname->data,
3314
1.11k
                                                  objname->size)
3315
1.11k
                            )
3316
0
                            return_error(gs_error_rangecheck);
3317
                        /* Save the pairs without the name. */
3318
1.11k
                        size -= 2;
3319
1.11k
                        pairs = (gs_param_string *)
3320
1.11k
                            gs_alloc_byte_array(mem, size,
3321
1.11k
                                                sizeof(gs_param_string),
3322
1.11k
                                                "pdfmark_process(pairs)");
3323
1.11k
                        if (!pairs)
3324
0
                            return_error(gs_error_VMerror);
3325
3326
3.31k
                        for (index=0;index < size;index++)
3327
2.20k
                            pairs[index].data = NULL;
3328
1.11k
                        for (index=0;index < j;index++) {
3329
0
                            pairs[index].data = gs_alloc_bytes(mem, data[index].size, "pdfmark_process(pairs)");
3330
0
                            if (pairs[index].data == NULL)
3331
0
                                goto error;
3332
0
                            memcpy((byte *)pairs[index].data, data[index].data, data[index].size);
3333
0
                            pairs[index].size = data[index].size;
3334
0
                            pairs[index].persistent = 1;
3335
0
                        }
3336
3.31k
                        for (index=j+2;index < size + 2;index++) {
3337
2.20k
                            pairs[index - 2].data = gs_alloc_bytes(mem, data[index].size, "pdfmark_process(pairs)");
3338
2.20k
                            if (pairs[index - 2].data == NULL)
3339
0
                                goto error;
3340
2.20k
                            memcpy((byte *)pairs[index - 2].data, data[index].data, data[index].size);
3341
2.20k
                            pairs[index - 2].size = data[index].size;
3342
2.20k
                            pairs[index - 2].persistent = 1;
3343
2.20k
                        }
3344
1.11k
                        goto copied;
3345
1.11k
                    }
3346
6.26k
                }
3347
1.67k
            }
3348
            /* Save all the pairs. */
3349
27.9k
            pairs = (gs_param_string *)
3350
27.9k
                gs_alloc_byte_array(mem, size, sizeof(gs_param_string),
3351
27.9k
                                    "pdfmark_process(pairs)");
3352
27.9k
            if (!pairs)
3353
0
                return_error(gs_error_VMerror);
3354
70.9k
            for (j=0;j < size;j++)
3355
43.0k
                pairs[j].data = NULL;
3356
70.9k
            for (j=0;j < size;j++) {
3357
43.0k
                pairs[j].data = gs_alloc_bytes(mem, data[j].size, "pdfmark_process(pairs)");
3358
43.0k
                if (pairs[j].data == NULL)
3359
0
                    goto error;
3360
43.0k
                memcpy((byte *)pairs[j].data, data[j].data, data[j].size);
3361
43.0k
                pairs[j].size = data[j].size;
3362
43.0k
                pairs[j].persistent = 1;
3363
43.0k
            }
3364
29.0k
copied:   /* Substitute object references for names. */
3365
29.0k
            if (!(pmn->options & PDFMARK_NO_REFS)) {
3366
21.5k
                for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok);
3367
38.7k
                     j < size; j += 2 - odd_ok
3368
21.5k
                     ) {
3369
17.1k
                    code = pdf_replace_names(pdev, &pairs[j], &pairs[j]);
3370
17.1k
                    if (code < 0) {
3371
0
                        gs_free_object(mem, pairs, "pdfmark_process(pairs)");
3372
0
                        return code;
3373
0
                    }
3374
17.1k
                }
3375
21.5k
            }
3376
29.0k
            code = (*pmn->proc) (pdev, pairs, size, &ctm, objname);
3377
29.0k
error:
3378
74.2k
            for (j=0;j < size;j++)
3379
45.2k
                gs_free_object(mem, (byte *)pairs[j].data, "pdfmark_process(pairs)");
3380
29.0k
            gs_free_object(mem, pairs, "pdfmark_process(pairs)");
3381
29.0k
            break;
3382
29.0k
        }
3383
29.0k
    return code;
3384
29.0k
}