Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pdf/pdf_mark.c
Line
Count
Source
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
/* pdfmark handling for the PDF interpreter */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_file.h"
21
#include "pdf_dict.h"
22
#include "pdf_array.h"
23
#include "pdf_loop_detect.h"
24
#include "pdf_mark.h"
25
#include "pdf_obj.h"
26
#include "pdf_misc.h"
27
#include "pdf_page.h"
28
#include "pdf_deref.h"
29
30
#include "gscoord.h"         /* For gs_currentmatrix */
31
32
static int pdfi_get_named_dest(pdf_context *ctx, pdf_obj *Named, pdf_obj **Dest);
33
34
static int pdfi_pdfmark_setparam_obj(pdf_context *ctx, pdf_obj *obj, gs_param_string *entry)
35
156k
{
36
156k
    int code = 0;
37
156k
    byte *data = NULL;
38
156k
    int size = 0;
39
40
156k
    code = pdfi_obj_to_string(ctx, obj, &data, &size);
41
156k
    if (code < 0)
42
103
        return code;
43
156k
    entry->data = data;
44
156k
    entry->size = size;
45
156k
    entry->persistent = false;
46
156k
    return 0;
47
156k
}
48
49
static int pdfi_pdfmark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj *Value,
50
                                   gs_param_string *entry)
51
66.2k
{
52
66.2k
    int code = 0;
53
54
    /* Handle the Key */
55
66.2k
    if (pdfi_type_of(Key) != PDF_NAME) {
56
0
        code = gs_note_error(gs_error_typecheck);
57
0
        goto exit;
58
0
    }
59
60
66.2k
    code = pdfi_pdfmark_setparam_obj(ctx, (pdf_obj *)Key, entry);
61
66.2k
    if (code < 0)
62
0
        goto exit;
63
64
66.2k
    code = pdfi_pdfmark_setparam_obj(ctx, Value, entry+1);
65
66.2k
    if (code < 0)
66
73
        goto exit;
67
68
66.2k
 exit:
69
66.2k
    return code;
70
66.2k
}
71
72
73
/* Note: this isn't part of the obj_to_string() stuff */
74
static int pdfi_pdfmark_ctm_str(pdf_context *ctx, gs_matrix *ctm, byte **data, int *len)
75
40.4k
{
76
40.4k
    int size = 100;
77
40.4k
    char *buf;
78
79
40.4k
    buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_pdfmark_ctm_str(data)");
80
40.4k
    if (buf == NULL)
81
0
        return_error(gs_error_VMerror);
82
40.4k
    snprintf(buf, size, "[%.4f %.4f %.4f %.4f %.4f %.4f]",
83
40.4k
             ctm->xx, ctm->xy, ctm->yx, ctm->yy, ctm->tx, ctm->ty);
84
40.4k
    *data = (byte *)buf;
85
40.4k
    *len = strlen(buf);
86
40.4k
    return 0;
87
40.4k
}
88
89
/* Write an string array command to the device (e.g. pdfmark) */
90
static int pdfi_pdfmark_write_array(pdf_context *ctx, gs_param_string_array *array_list, const char *command)
91
40.4k
{
92
40.4k
    gs_c_param_list list;
93
40.4k
    int code;
94
95
    /* Set the list to writeable, and initialise it */
96
40.4k
    gs_c_param_list_write(&list, ctx->memory);
97
    /* We don't want keys to be persistent, as we are going to throw
98
     * away our array, force them to be copied
99
     */
100
40.4k
    gs_param_list_set_persistent_keys((gs_param_list *) &list, false);
101
102
    /* Make really sure the list is writable, but don't initialise it */
103
40.4k
    gs_c_param_list_write_more(&list);
104
105
    /* Add the param string array to the list */
106
40.4k
    code = param_write_string_array((gs_param_list *)&list, command, array_list);
107
40.4k
    if (code < 0)
108
0
        return code;
109
110
    /* Set the param list back to readable, so putceviceparams can readit (mad...) */
111
40.4k
    gs_c_param_list_read(&list);
112
113
    /* and set the actual device parameters */
114
40.4k
    code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list);
115
116
40.4k
    gs_c_param_list_release(&list);
117
118
40.4k
    return code;
119
40.4k
}
120
121
/* Write an string array command to the device (e.g. pdfmark) */
122
static int pdfi_pdfmark_write_string(pdf_context *ctx, gs_param_string *param_string, const char *command)
123
238
{
124
238
    gs_c_param_list list;
125
238
    int code;
126
127
    /* Set the list to writeable, and initialise it */
128
238
    gs_c_param_list_write(&list, ctx->memory);
129
    /* We don't want keys to be persistent, as we are going to throw
130
     * away our array, force them to be copied
131
     */
132
238
    gs_param_list_set_persistent_keys((gs_param_list *) &list, false);
133
134
    /* Make really sure the list is writable, but don't initialise it */
135
238
    gs_c_param_list_write_more(&list);
136
137
    /* Add the param string array to the list */
138
238
    code = param_write_string((gs_param_list *)&list, command, param_string);
139
238
    if (code < 0)
140
0
        return code;
141
142
    /* Set the param list back to readable, so putceviceparams can readit (mad...) */
143
238
    gs_c_param_list_read(&list);
144
145
    /* and set the actual device parameters */
146
238
    code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list);
147
148
238
    gs_c_param_list_release(&list);
149
150
238
    return code;
151
238
}
152
153
/* Apparently the strings to pass to the device are:
154
   key1 val1 .... keyN valN CTM "ANN"
155
   CTM is (for example) "[1.0 0 0 1.0 0 0]"
156
157
   This takes an optional 'label' argument which can be NULL.
158
159
   the /pdfmark command has this array of strings as a parameter, i.e.
160
   [ 'label' key1 val1 .... keyN valN CTM "ANN" ] /pdfmark
161
162
   Apparently the "type" doesn't have a leading "/" but the other names in the
163
   keys do need the "/"
164
165
   See plparams.c/process_pdfmark()
166
*/
167
static int pdfi_pdfmark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *label,
168
                                         pdf_dict *dict, gs_matrix *ctm, const char *type)
169
16.9k
{
170
16.9k
    int code = 0;
171
16.9k
    int size;
172
16.9k
    uint64_t dictsize;
173
16.9k
    uint64_t index;
174
16.9k
    uint64_t keynum = 0;
175
16.9k
    int i;
176
16.9k
    pdf_name *Key = NULL;
177
16.9k
    pdf_obj *Value = NULL;
178
16.9k
    pdf_obj *tempobj = NULL;
179
16.9k
    gs_param_string *parray = NULL;
180
16.9k
    gs_param_string_array array_list;
181
16.9k
    byte *ctm_data = NULL;
182
16.9k
    int ctm_len;
183
16.9k
    gs_matrix ctm_placeholder;
184
16.9k
    int offset = 0;
185
186
    /* If ctm not provided, make a placeholder */
187
16.9k
    if (!ctm) {
188
9.67k
        gs_currentmatrix(ctx->pgs, &ctm_placeholder);
189
9.67k
        ctm = &ctm_placeholder;
190
9.67k
    }
191
192
16.9k
    dictsize = pdfi_dict_entries(dict);
193
16.9k
    size = dictsize*2 + 2; /* pairs + CTM + type */
194
16.9k
    if (label)
195
1.07k
        size += 1;
196
197
16.9k
    parray = (gs_param_string *)gs_alloc_bytes(ctx->memory, (size_t)size*sizeof(gs_param_string),
198
16.9k
                                               "pdfi_pdfmark_from_dict(parray)");
199
16.9k
    if (parray == NULL) {
200
0
        code = gs_note_error(gs_error_VMerror);
201
0
        goto exit;
202
0
    }
203
16.9k
    memset(parray, 0, size*sizeof(gs_param_string));
204
205
16.9k
    if (label) {
206
1.07k
        code = pdfi_pdfmark_setparam_obj(ctx, (pdf_obj *)label, parray+0);
207
1.07k
        offset += 1;
208
1.07k
    }
209
210
    /* Get each (key,val) pair from dict and setup param for it */
211
16.9k
    if (dictsize > 0) {
212
11.6k
        code = pdfi_dict_key_first(ctx, dict, (pdf_obj **)&Key, &index);
213
66.2k
        while (code >= 0) {
214
66.2k
            code = pdfi_dict_get_no_deref(ctx, dict, Key, &Value);
215
66.2k
            if (code < 0) goto exit;
216
217
66.2k
            code = pdfi_pdfmark_setparam_pair(ctx, Key, Value, parray+offset+(keynum*2));
218
66.2k
            if (code < 0) goto exit;
219
220
66.2k
            pdfi_countdown(Key);
221
66.2k
            Key = NULL;
222
66.2k
            pdfi_countdown(Value);
223
66.2k
            Value = NULL;
224
66.2k
            pdfi_countdown(tempobj);
225
66.2k
            tempobj = NULL;
226
227
66.2k
            code = pdfi_dict_key_next(ctx, dict, (pdf_obj **)&Key, &index);
228
66.2k
            if (code == gs_error_undefined) {
229
11.5k
                code = 0;
230
11.5k
                break;
231
11.5k
            }
232
54.6k
            keynum ++;
233
54.6k
        }
234
11.6k
    }
235
16.8k
    if (code < 0) goto exit;
236
237
    /* CTM */
238
16.8k
    code = pdfi_pdfmark_ctm_str(ctx, ctm, &ctm_data, &ctm_len);
239
16.8k
    if (code < 0) goto exit;
240
16.8k
    parray[size-2].data = ctm_data;
241
16.8k
    parray[size-2].size = ctm_len;
242
243
    /* Type (e.g. ANN, DOCINFO) */
244
16.8k
    parray[size-1].data = (const byte *)type;
245
16.8k
    parray[size-1].size = strlen(type);
246
247
16.8k
    array_list.data = parray;
248
16.8k
    array_list.persistent = false;
249
16.8k
    array_list.size = size;
250
251
16.8k
    code = pdfi_pdfmark_write_array(ctx, &array_list, "pdfmark");
252
253
16.9k
 exit:
254
16.9k
    pdfi_countdown(Key);
255
16.9k
    pdfi_countdown(Value);
256
16.9k
    pdfi_countdown(tempobj);
257
16.9k
    if (parray != NULL) {
258
        /* Free the param data except the last two which are handled separately */
259
150k
        for (i=0; i<size-2; i++) {
260
133k
            if (parray[i].data)
261
133k
                gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_pdfmark_from_dict(parray)");
262
133k
        }
263
16.9k
    }
264
16.9k
    if (ctm_data)
265
16.8k
        gs_free_object(ctx->memory, ctm_data, "pdfi_pdfmark_from_dict(ctm_data)");
266
16.9k
    gs_free_object(ctx->memory, parray, "pdfi_pdfmark_from_dict(parray)");
267
16.9k
    return code;
268
16.8k
}
269
270
/* Do a pdfmark from a dictionary */
271
int pdfi_pdfmark_from_dict(pdf_context *ctx, pdf_dict *dict, gs_matrix *ctm, const char *type)
272
15.8k
{
273
15.8k
    return pdfi_pdfmark_from_dict_withlabel(ctx, NULL, dict, ctm, type);
274
15.8k
}
275
276
/* Does a pdfmark, from a c-array of pdf_obj's
277
 * This will put in a dummy ctm if none provided
278
 */
279
int pdfi_pdfmark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len,
280
                            gs_matrix *ctm, const char *type)
281
23.6k
{
282
23.6k
    int code = 0;
283
23.6k
    int size;
284
23.6k
    gs_param_string *parray = NULL;
285
23.6k
    gs_param_string_array array_list;
286
23.6k
    byte *ctm_data = NULL;
287
23.6k
    int ctm_len;
288
23.6k
    gs_matrix ctm_placeholder;
289
23.6k
    int i;
290
291
    /* If ctm not provided, make a placeholder */
292
23.6k
    if (!ctm) {
293
23.6k
        gs_currentmatrix(ctx->pgs, &ctm_placeholder);
294
23.6k
        ctm = &ctm_placeholder;
295
23.6k
    }
296
297
23.6k
    size = len + 2; /* data + CTM + type */
298
299
23.6k
    parray = (gs_param_string *)gs_alloc_bytes(ctx->memory, (size_t)size*sizeof(gs_param_string),
300
23.6k
                                               "pdfi_pdfmark_from_objarray(parray)");
301
23.6k
    if (parray == NULL) {
302
0
        code = gs_note_error(gs_error_VMerror);
303
0
        goto exit;
304
0
    }
305
23.6k
    memset(parray, 0, size *sizeof(gs_param_string));
306
307
46.4k
    for (i=0; i<len; i++) {
308
22.8k
        code = pdfi_pdfmark_setparam_obj(ctx, objarray[i], parray+i);
309
22.8k
        if (code < 0) goto exit;
310
22.8k
    }
311
312
    /* CTM */
313
23.5k
    code = pdfi_pdfmark_ctm_str(ctx, ctm, &ctm_data, &ctm_len);
314
23.5k
    if (code < 0) goto exit;
315
23.5k
    parray[len].data = ctm_data;
316
23.5k
    parray[len].size = ctm_len;
317
318
    /* Type (e.g. ANN, DOCINFO) */
319
23.5k
    parray[len+1].data = (const byte *)type;
320
23.5k
    parray[len+1].size = strlen(type);
321
322
23.5k
    array_list.data = parray;
323
23.5k
    array_list.persistent = false;
324
23.5k
    array_list.size = size;
325
326
23.5k
    code = pdfi_pdfmark_write_array(ctx, &array_list, "pdfmark");
327
328
23.6k
 exit:
329
23.6k
    if (parray != NULL) {
330
46.5k
        for (i=0; i<len; i++) {
331
22.8k
            gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_pdfmark_from_objarray(parray)");
332
22.8k
        }
333
23.6k
    }
334
23.6k
    if (ctm_data)
335
23.5k
        gs_free_object(ctx->memory, ctm_data, "pdfi_pdfmark_from_objarray(ctm_data)");
336
23.6k
    gs_free_object(ctx->memory, parray, "pdfi_pdfmark_from_objarray(parray)");
337
23.6k
    return code;
338
23.5k
}
339
340
/* Send an arbitrary object as a string, with command 'cmd'
341
 * This is not a pdfmark, has no ctm.
342
 */
343
int pdfi_pdfmark_object(pdf_context *ctx, pdf_obj *object, const char *cmd)
344
256
{
345
256
    gs_param_string param_string;
346
256
    int code = 0;
347
348
256
    param_string.data = NULL;
349
350
256
    code = pdfi_loop_detector_mark(ctx);
351
256
    if (code < 0)
352
0
        goto exit;
353
354
256
    code = pdfi_resolve_indirect_loop_detect(ctx, NULL, object, true);
355
256
    (void)pdfi_loop_detector_cleartomark(ctx);
356
256
    if (code < 0)
357
18
        goto exit;
358
359
238
    code = pdfi_pdfmark_setparam_obj(ctx, object, &param_string);
360
238
    if (code < 0)
361
0
        goto exit;
362
363
238
    code = pdfi_pdfmark_write_string(ctx, &param_string, cmd);
364
365
256
exit:
366
256
    if (param_string.data != NULL)
367
238
        gs_free_object(ctx->memory, (byte *)param_string.data, "free data transferred to param_string in pdfi_pdfmark_object\n");
368
256
    return code;
369
238
}
370
371
/* Convert a Dest array to the hacky Page and View keys that pdfwrite expects
372
 * dest_array: [<page_ref> <view_info>]
373
 * page_ref: indirect ref to a page dict
374
 * view_info: see table 8.2 in PDF 1.7, for example "/Fit"
375
 *
376
 * Removes /Dest and inserts two key pairs: /Page N and /View <view_info>
377
 * N is the page number, which starts at 1, not 0.
378
 */
379
static int pdfi_pdfmark_add_Page_View(pdf_context *ctx, pdf_dict *link_dict, pdf_array *dest_array)
380
1.36k
{
381
1.36k
    int code = 0;
382
1.36k
    int i;
383
1.36k
    uint64_t page_num;
384
1.36k
    pdf_dict *page_dict = NULL;
385
1.36k
    pdf_array *view_array = NULL;
386
1.36k
    uint64_t array_size;
387
1.36k
    pdf_obj *temp_obj = NULL;
388
389
    /* Get the page_dict, without storing the deref in the array.
390
     * (This is needed because otherwise there could be a circular ref that will
391
     * lead to a memory leak)
392
     */
393
1.36k
    code = pdfi_array_get_no_store_R(ctx, dest_array, 0, (pdf_obj **)&page_dict);
394
1.36k
    if (code < 0) goto exit;
395
396
1.18k
    if(pdfi_type_of(page_dict) == PDF_INT) {
397
0
        page_num = ((pdf_num *)page_dict)->value.i;
398
1.18k
    } else {
399
1.18k
        if (pdfi_type_of(page_dict) != PDF_DICT) {
400
0
            if (pdfi_type_of(page_dict) != PDF_NULL) {
401
0
                code = gs_note_error(gs_error_typecheck);
402
0
                goto exit;
403
0
            }
404
0
            page_num = 0;
405
1.18k
        } else {
406
            /* Find out which page number this is */
407
1.18k
            code = pdfi_page_get_number(ctx, page_dict, &page_num);
408
1.18k
            if (code < 0) goto exit;
409
1.18k
        }
410
1.18k
    }
411
412
1.17k
    page_num += ctx->Pdfmark_InitialPage;
413
414
1.17k
    if (pdfi_type_of(page_dict) != PDF_NULL)
415
        /* Add /Page key to the link_dict
416
         * Of course pdfwrite is numbering its pages starting at 1, because... of course :(
417
         */
418
1.17k
        code = pdfi_dict_put_int(ctx, link_dict, "Page", page_num+1);
419
0
    else
420
0
        code = pdfi_dict_put_int(ctx, link_dict, "Page", 0);
421
422
1.17k
    if (code < 0)
423
0
        goto exit;
424
425
    /* Get and check the destination type, again without storing the deref in the array.
426
     * In this case a memory leak  can only happen if the destination
427
     * is an invalid type, but lets take no chances.
428
     */
429
1.17k
    code = pdfi_array_get_no_store_R(ctx, dest_array, 1, &temp_obj);
430
1.17k
    if (code < 0) goto exit;
431
432
1.17k
    if (pdfi_type_of(temp_obj) != PDF_NAME) {
433
0
        pdfi_countdown(temp_obj);
434
0
        temp_obj = NULL;
435
0
        code = gs_note_error(gs_error_typecheck);
436
0
        goto exit;
437
0
    }
438
1.17k
    pdfi_countdown(temp_obj);
439
1.17k
    temp_obj = NULL;
440
441
    /* Build an array for /View, out of the remainder of the Dest entry */
442
1.17k
    array_size = pdfi_array_size(dest_array) - 1;
443
1.17k
    code = pdfi_array_alloc(ctx, array_size, &view_array);
444
1.17k
    if (code < 0) goto exit;
445
1.17k
    pdfi_countup(view_array);
446
4.05k
    for (i=0; i<array_size; i++) {
447
2.88k
        code = pdfi_array_get(ctx, dest_array, i+1, &temp_obj);
448
2.88k
        if (code < 0) goto exit;
449
2.88k
        code = pdfi_array_put(ctx, view_array, i, temp_obj);
450
2.88k
        if (code < 0) goto exit;
451
452
2.88k
        pdfi_countdown(temp_obj);
453
2.88k
        temp_obj = NULL;
454
2.88k
    }
455
    /* Add /View key to the link_dict */
456
1.17k
    code = pdfi_dict_put(ctx, link_dict, "View", (pdf_obj *)view_array);
457
1.17k
    if (code < 0) goto exit;
458
459
1.36k
 exit:
460
1.36k
    pdfi_countdown(temp_obj);
461
1.36k
    pdfi_countdown(page_dict);
462
1.36k
    pdfi_countdown(view_array);
463
1.36k
    return code;
464
1.17k
}
465
466
/* Lookup a Dest string(or name) in the Names array and try to resolve it */
467
static int pdfi_pdfmark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict,
468
                                         pdf_obj *dest, pdf_array *Names)
469
0
{
470
0
    int code = 0;
471
0
    int i;
472
0
    uint64_t array_size;
473
0
    pdf_obj *name = NULL;
474
0
    pdf_dict *D_dict = NULL;
475
0
    bool found = false;
476
0
    pdf_array *dest_array = NULL;
477
478
0
    array_size = pdfi_array_size(Names);
479
480
    /* Needs to be an even number - pairs of [name, obj] */
481
0
    if (array_size % 2 != 0) {
482
        /* TODO: flag an error? */
483
        /* Let's just ignore the last unpaired item for now */
484
0
        array_size -= 1;
485
0
    }
486
487
0
    for (i=0; i<array_size; i+=2) {
488
0
        code = pdfi_array_get(ctx, Names, i, (pdf_obj **)&name);
489
0
        if (code < 0) goto exit;
490
        /* Note: in current implementation, PDF_STRING and PDF_NAME have all the same
491
         * fields, but just in case that changes I treat them separately here.
492
         */
493
0
        if (pdfi_type_of(name) == PDF_STRING && pdfi_type_of(dest) == PDF_STRING) {
494
0
            if (!pdfi_string_cmp((pdf_string *)name, (pdf_string *)dest)) {
495
0
                found = true;
496
0
                break;
497
0
            }
498
0
        } else if (pdfi_type_of(name) == PDF_NAME && pdfi_type_of(dest) == PDF_NAME) {
499
0
            if (!pdfi_name_cmp((pdf_name *)name, (pdf_name *)dest)) {
500
0
                found = true;
501
0
                break;
502
0
            }
503
0
        }
504
0
        pdfi_countdown(name);
505
0
        name = NULL;
506
0
    }
507
508
0
    if (!found) {
509
        /* TODO: flag a warning? */
510
0
        code = 0;
511
0
        goto exit;
512
0
    }
513
514
    /* Next entry is either a dictionary (with a /D key) or an array */
515
0
    code = pdfi_array_get(ctx, Names, i+1, (pdf_obj **)&D_dict);
516
0
    if (code < 0) goto exit;
517
518
0
    switch (pdfi_type_of(D_dict)) {
519
0
        case PDF_DICT:
520
            /* Dict is supposed to contain key "D" with Dest array */
521
0
            code = pdfi_dict_knownget_type(ctx, D_dict, "D", PDF_ARRAY, (pdf_obj **)&dest_array);
522
0
            if (code <= 0) goto exit;
523
0
            break;
524
0
        case PDF_ARRAY:
525
0
            dest_array = (pdf_array *)D_dict;
526
0
            D_dict = NULL;
527
0
            break;
528
0
        default:
529
0
            code = gs_note_error(gs_error_typecheck);
530
0
            goto exit;
531
0
    }
532
533
    /* Process the dest_array to replace with /Page /View */
534
0
    code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array);
535
0
    if (code < 0) goto exit;
536
537
0
 exit:
538
0
    pdfi_countdown(name);
539
0
    pdfi_countdown(D_dict);
540
0
    pdfi_countdown(dest_array);
541
0
    return code;
542
0
}
543
544
/* Special handling for "Dest" in Links
545
 * Will replace /Dest with /Page /View in link_dict (for pdfwrite)
546
 */
547
int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict)
548
344
{
549
344
    int code = 0, code1 = 0;
550
344
    pdf_dict *Dests = NULL;
551
344
    pdf_obj *Dest = NULL;
552
344
    bool delete_Dest = true;
553
344
    pdf_array *dest_array = NULL;
554
344
    pdf_array *Names = NULL;
555
344
    pdf_dict *Names_dict = NULL;
556
557
344
    code = pdfi_dict_get(ctx, link_dict, "Dest", (pdf_obj **)&Dest);
558
344
    if (code < 0) goto exit;
559
560
344
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "Dests", PDF_DICT, (pdf_obj **)&Dests);
561
344
    if (code < 0) goto exit;
562
563
342
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names_dict);
564
342
    if (code < 0) goto exit;
565
566
321
    switch (pdfi_type_of(Dest)) {
567
317
    case PDF_ARRAY:
568
317
        code = pdfi_pdfmark_add_Page_View(ctx, link_dict, (pdf_array *)Dest);
569
317
        if (code < 0) goto exit;
570
275
        break;
571
275
    case PDF_NAME:
572
3
        if (Dests != NULL) {
573
            /* Case where it's a name to look up in Contents(Root) /Dests */
574
3
            code = pdfi_dict_get_by_key(ctx, Dests, (const pdf_name *)Dest, (pdf_obj **)&dest_array);
575
3
            if (code == gs_error_undefined) {
576
                /* TODO: Not found, should flag a warning */
577
0
                code = 0;
578
0
                goto exit;
579
0
            }
580
3
            if (code < 0) goto exit;
581
3
            if (pdfi_type_of(dest_array) != PDF_ARRAY) {
582
0
                code = gs_note_error(gs_error_typecheck);
583
0
                goto exit;
584
0
            }
585
3
            code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array);
586
3
            if (code < 0) goto exit;
587
3
            break;
588
3
        }
589
        /* fallthrough */
590
1
    case PDF_STRING:
591
1
        if (Names_dict != NULL) {
592
            /* Looking in Catalog(Root) for /Names<</Dests<</Names [name dict array]>>>> */
593
1
            code = pdfi_dict_knownget_type(ctx, Names_dict, "Dests", PDF_DICT, (pdf_obj **)&Dests);
594
1
            if (code < 0) goto exit;
595
1
            if (code == 0) {
596
                /* TODO: Not found -- not sure if there is another case here or not */
597
0
                code = gs_note_error(gs_error_undefined);
598
0
                goto exit;
599
0
            }
600
601
1
            code = pdfi_dict_knownget_type(ctx, Dests, "Names", PDF_ARRAY, (pdf_obj **)&Names);
602
1
            if (code < 0) goto exit;
603
1
            if (code == 0) {
604
1
                code = pdfi_get_named_dest(ctx, Dest, (pdf_obj **)&dest_array);
605
1
                if (code < 0)
606
0
                    goto exit;
607
1
                if (pdfi_type_of(dest_array) == PDF_DICT) {
608
0
                    pdf_obj *D = NULL;
609
                    /* Dict is supposed to contain key "D" with Dest array */
610
0
                    code = pdfi_dict_knownget_type(ctx, (pdf_dict *)dest_array, "D", PDF_ARRAY, &D);
611
0
                    if (code <= 0) goto exit;
612
613
0
                    pdfi_countdown(dest_array);
614
0
                    dest_array = (pdf_array *)D;
615
0
                }
616
1
                if (pdfi_type_of(dest_array) == PDF_ARRAY) {
617
1
                    code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array);
618
1
                    if (code < 0)
619
0
                        goto exit;
620
1
                } else {
621
0
                    if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_undefined), NULL, E_PDF_BAD_NAMED_DEST, "pdfi_pdfmark_modDest", NULL)) < 0)
622
0
                        goto exit;
623
0
                }
624
1
                break;
625
1
            } else
626
0
                code = pdfi_pdfmark_handle_dest_names(ctx, link_dict, Dest, Names);
627
0
            if (code < 0) goto exit;
628
0
        } else {
629
            /* TODO: Ignore it -- flag a warning? */
630
0
        }
631
0
        break;
632
0
    default:
633
        /* TODO: Ignore it -- flag a warning? */
634
0
        break;
635
321
    }
636
637
344
 exit:
638
344
    if (delete_Dest) {
639
        /* Delete the Dest key */
640
344
        code1 = pdfi_dict_delete(ctx, link_dict, "Dest");
641
344
        if (code1 < 0 && code >= 0)
642
0
            code = code1;
643
344
    }
644
344
    pdfi_countdown(Dest);
645
344
    pdfi_countdown(Dests);
646
344
    pdfi_countdown(Names);
647
344
    pdfi_countdown(Names_dict);
648
344
    pdfi_countdown(dest_array);
649
344
    return code;
650
321
}
651
652
static int pdfi_check_limits(pdf_context *ctx, pdf_dict *node, char *str, int len)
653
4
{
654
4
    int code = 0, min, i, len2 = 0;
655
4
    pdf_array *Limits = NULL;
656
4
    pdf_string *Str = NULL;
657
4
    char *str2 = NULL;
658
659
4
    code = pdfi_dict_get_type(ctx, node, "Limits", PDF_ARRAY, (pdf_obj **)&Limits);
660
4
    if (code < 0)
661
0
        goto error;
662
663
4
    if (pdfi_array_size(Limits) != 2) {
664
        /* Limits are not valid, just ignore them. The calling code will then check
665
         * the Names array.
666
         */
667
0
        pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_TREE_LIMITS, "pdfi_check_limits", 0);
668
0
        goto error;
669
0
    }
670
671
4
    code = pdfi_array_get_type(ctx, Limits, 0, PDF_STRING, (pdf_obj **)&Str);
672
4
    if (code < 0)
673
0
        goto error;
674
675
4
    if (pdfi_type_of(Str) == PDF_NAME) {
676
0
        code = pdfi_string_from_name(ctx, (pdf_name *)Str, &str2, &len2);
677
0
        if (code < 0)
678
0
            return code;
679
4
    } else {
680
4
        len2 = ((pdf_string *)Str)->length;
681
4
        str2 = (char *)gs_alloc_bytes(ctx->memory, (size_t)len2 + 1, "pdfi_check_limits");
682
4
        if (str2 == NULL) {
683
0
            code = gs_note_error(gs_error_VMerror);
684
0
            goto error;
685
0
        }
686
4
         memcpy(str2, ((pdf_string *)Str)->data, len2);
687
4
         str2[len2] = 0;
688
4
    }
689
690
4
    pdfi_countdown(Str);
691
4
    Str = NULL;
692
693
4
    min = len;
694
4
    if (len2 < min)
695
0
        min = len2;
696
697
21
    for (i=0;i< min;i++) {
698
19
        if (str[i] < str2[i]) {
699
0
            code = gs_note_error(gs_error_undefined);
700
0
            goto error;
701
0
        }
702
19
        if (str[i] != str2[i])
703
2
            break;
704
19
    }
705
4
    if (i > min && len2 < Str->length) {
706
0
        code = gs_note_error(gs_error_undefined);
707
0
        goto error;
708
0
    }
709
4
    gs_free_object(ctx->memory, str2, "pdfi_check_limits");
710
4
    str2 = NULL;
711
712
4
    code = pdfi_array_get_type(ctx, Limits, 1, PDF_STRING, (pdf_obj **)&Str);
713
4
    if (code < 0)
714
0
        goto error;
715
716
4
    if (pdfi_type_of(Str) == PDF_NAME) {
717
0
        code = pdfi_string_from_name(ctx, (pdf_name *)Str, &str2, &len2);
718
0
        if (code < 0)
719
0
            return code;
720
4
    } else {
721
4
        len2 = ((pdf_string *)Str)->length;
722
4
        str2 = (char *)gs_alloc_bytes(ctx->memory, (size_t)len2 + 1, "pdfi_check_limits");
723
4
        if (str2 == NULL) {
724
0
            code = gs_note_error(gs_error_VMerror);
725
0
            goto error;
726
0
        }
727
4
         memcpy(str2, ((pdf_string *)Str)->data, len2);
728
4
         str2[len2] = 0;
729
4
    }
730
731
4
    pdfi_countdown(Str);
732
4
    Str = NULL;
733
734
4
    min = len;
735
4
    if (len2 < min)
736
0
        min = len2;
737
738
22
    for (i=0;i< min;i++) {
739
19
        if (str[i] > str2[i]) {
740
0
            code = gs_note_error(gs_error_undefined);
741
0
            goto error;
742
0
        }
743
19
        if (str[i] != str2[i])
744
1
            break;
745
19
    }
746
747
4
    if (i > min && len > len2)
748
0
        code = gs_note_error(gs_error_undefined);
749
750
4
error:
751
4
    gs_free_object(ctx->memory, str2, "pdfi_check_limits");
752
4
    pdfi_countdown(Str);
753
4
    pdfi_countdown(Limits);
754
4
    return code;
755
4
}
756
757
static int pdfi_get_name_from_node(pdf_context *ctx, pdf_dict *node, char *str, int len, pdf_obj **Name, bool is_root)
758
10
{
759
10
    int i = 0, code = 0;
760
10
    pdf_string *StrKey = NULL;
761
10
    pdf_array *NamesArray = NULL;
762
10
    pdf_dict *Kid = NULL;
763
10
    bool known;
764
765
10
    code = pdfi_loop_detector_mark(ctx);
766
10
    if (code < 0)
767
0
        return code;
768
769
10
    code = pdfi_dict_known(ctx, node, "Names", &known);
770
10
    if (code < 0)
771
0
        goto error;
772
773
10
    if (known) {
774
4
        code = pdfi_dict_known(ctx, node, "Limits", &known);
775
4
        if (code < 0)
776
0
            goto error;
777
778
4
        if (!known) {
779
0
            if (!is_root)
780
                /* No Limits array (a required entry, except in the Root), so just assume that the
781
                 * string is in this node and check all the Names anyway
782
                 */
783
0
                pdfi_set_warning(ctx, 0, NULL, W_PDF_NO_TREE_LIMITS, "pdfi_get_name_from_node", 0);
784
4
        } else {
785
4
            code = pdfi_check_limits(ctx, node, str, len);
786
4
            if (code < 0)
787
0
                goto error;
788
4
        }
789
790
4
        code = pdfi_dict_get_type(ctx, node, "Names", PDF_ARRAY, (pdf_obj **)&NamesArray);
791
4
        if (code < 0)
792
3
            goto error;
793
794
1
        if (pdfi_array_size(NamesArray) & 1)
795
0
            pdfi_set_warning(ctx, 0, NULL, W_PDF_NAMES_ARRAY_SIZE, "pdfi_get_name_from_node", 0);
796
797
1
        for (i = 0;i < pdfi_array_size(NamesArray) / 2; i++) {
798
1
            code = pdfi_array_get_type(ctx, NamesArray, i * 2, PDF_STRING, (pdf_obj **)&StrKey);
799
1
            if (code < 0)
800
0
                goto error;
801
802
1
            if (StrKey->length == len && memcmp((const char *)StrKey->data, str, len) == 0) {
803
1
                code = pdfi_array_get(ctx, NamesArray, (i * 2) + 1, (pdf_obj **)Name);
804
1
                goto error;
805
1
            }
806
0
            pdfi_countdown(StrKey);
807
0
            StrKey = NULL;
808
0
        }
809
0
        pdfi_countdown(NamesArray);
810
0
        NamesArray = NULL;
811
0
    }
812
813
    /* Either no Names array (initial node) or not in array */
814
6
    code = pdfi_dict_get_type(ctx, node, "Kids", PDF_ARRAY, (pdf_obj **)&NamesArray);
815
6
    if (code < 0)
816
1
        goto error;
817
818
5
    for (i = 0;i < pdfi_array_size(NamesArray); i++) {
819
5
        code = pdfi_array_get_type(ctx, NamesArray, i, PDF_DICT, (pdf_obj **)&Kid);
820
5
        if (code < 0)
821
1
            goto error;
822
823
4
        code = pdfi_get_name_from_node(ctx, Kid, str, len, Name, false);
824
4
        pdfi_countdown(Kid);
825
4
        Kid = NULL;
826
4
        if (code == 0)
827
1
            break;
828
829
3
        if (code < 0) {
830
3
            if (code == gs_error_undefined)
831
0
                continue;
832
3
            goto error;
833
3
        }
834
3
    }
835
836
10
error:
837
10
    pdfi_countdown(Kid);
838
10
    pdfi_countdown(StrKey);
839
10
    pdfi_countdown(NamesArray);
840
10
    (void)pdfi_loop_detector_cleartomark(ctx);
841
10
    return code;
842
5
}
843
844
static int pdfi_get_named_dest(pdf_context *ctx, pdf_obj *Named, pdf_obj **Dest)
845
8
{
846
8
    int code = 0, len = 0;
847
8
    pdf_dict *Names = NULL, *Dests = NULL;
848
8
    char *str = NULL;
849
850
8
    code = pdfi_loop_detector_mark(ctx);
851
8
    if (code < 0)
852
0
        return code;
853
854
8
    code = pdfi_dict_get_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names);
855
8
    if (code < 0)
856
2
        goto error;
857
858
6
    code = pdfi_dict_get_type(ctx, Names, "Dests", PDF_DICT, (pdf_obj **)&Dests);
859
6
    if (code < 0)
860
0
        goto error;
861
862
6
    if (pdfi_type_of(Named) == PDF_NAME) {
863
0
        code = pdfi_string_from_name(ctx, (pdf_name *)Named, &str, &len);
864
0
        if (code < 0)
865
0
            return code;
866
6
    } else {
867
6
        len = ((pdf_string *)Named)->length;
868
6
        str = (char *)gs_alloc_bytes(ctx->memory, (size_t)len + 1, "pdfi_get_named_dest");
869
6
        if (str == NULL) {
870
0
            code = gs_note_error(gs_error_VMerror);
871
0
            goto error;
872
0
        }
873
6
        memcpy(str, ((pdf_string *)Named)->data, len);
874
6
        str[len] = 0;
875
6
    }
876
877
6
    code = pdfi_get_name_from_node(ctx, Dests, str, len, Dest, true);
878
879
8
error:
880
8
    if (pdfi_type_of(Named) == PDF_NAME)
881
0
        (void)pdfi_free_string_from_name(ctx, str);
882
8
    else
883
8
        gs_free_object(ctx->memory, str, "pdfi_get_named_dest");
884
8
    pdfi_countdown(Names);
885
8
    pdfi_countdown(Dests);
886
8
    pdfi_loop_detector_cleartomark(ctx);
887
8
    return code;
888
6
}
889
890
/* Special handling for "A" in Link annotations and Outlines
891
 * Will delete A if handled and if A_key is provided.
892
 */
893
int pdfi_pdfmark_modA(pdf_context *ctx, pdf_dict *dict)
894
1.72k
{
895
1.72k
    int code = 0;
896
1.72k
    pdf_dict *A_dict = NULL;
897
1.72k
    bool known;
898
1.72k
    pdf_name *S_name = NULL;
899
1.72k
    pdf_array *D_array = NULL;
900
1.72k
    bool delete_A = false;
901
1.72k
    bool deref_A = true;
902
903
1.72k
    code = pdfi_dict_get_no_store_R(ctx, dict, "A", (pdf_obj **)&A_dict);
904
1.72k
    if (code < 0) goto exit;
905
906
1.71k
    if (pdfi_type_of(A_dict) != PDF_DICT) {
907
        /* Invalid AP, just delete it because I dunno what to do...
908
         * TODO: Should flag a warning here
909
         */
910
43
        delete_A = true;
911
43
        goto exit;
912
43
    }
913
914
    /* Handle URI */
915
1.67k
    code = pdfi_dict_known(ctx, A_dict, "URI", &known);
916
1.67k
    if (code < 0) goto exit;
917
1.67k
    if (known) {
918
575
        code = pdfi_resolve_indirect_loop_detect(ctx, (pdf_obj *)NULL, (pdf_obj *)A_dict, true);
919
575
        goto exit;
920
575
    }
921
922
    /* Handle S = GoTo */
923
    /* TODO: this handles <</S /GoTo /D [dest array]>>
924
     * Not sure if there are other cases to handle?
925
     */
926
1.10k
    code = pdfi_dict_knownget_type(ctx, A_dict, "S", PDF_NAME, (pdf_obj **)&S_name);
927
1.10k
    if (code <= 0) goto exit;
928
    /* We only handle GoTo for now */
929
1.09k
    if (pdfi_name_is(S_name, "GoTo")) {
930
1.05k
        code = pdfi_dict_knownget(ctx, A_dict, "D", (pdf_obj **)&D_array);
931
1.05k
        if (code <= 0)
932
1
            goto exit;
933
1.04k
        if (pdfi_type_of(D_array) == PDF_STRING || pdfi_type_of(D_array) == PDF_NAME)
934
7
        {
935
7
            pdf_obj *Dest = NULL;
936
937
7
            code = pdfi_get_named_dest(ctx, (pdf_obj *)D_array, &Dest);
938
7
            if (code < 0)
939
7
                goto exit;
940
0
            pdfi_countdown(D_array);
941
0
            D_array = NULL;
942
0
            if (pdfi_type_of(Dest) != PDF_ARRAY) {
943
0
                if (pdfi_type_of(Dest) != PDF_DICT) {
944
0
                    pdfi_countdown(Dest);
945
0
                    code = gs_note_error(gs_error_typecheck);
946
0
                    goto exit;
947
0
                }
948
0
                code = pdfi_dict_knownget(ctx, (pdf_dict *)Dest, "D", (pdf_obj **)&D_array);
949
0
                pdfi_countdown(Dest);
950
0
                if (code <= 0)
951
0
                    goto exit;
952
0
            } else
953
0
                D_array = (pdf_array *)Dest;
954
0
        }
955
1.04k
        if (pdfi_type_of(D_array) != PDF_ARRAY) {
956
0
            code = gs_note_error(gs_error_typecheck);
957
0
            goto exit;
958
0
        }
959
960
        /* Process the D array to replace with /Page /View */
961
1.04k
        code = pdfi_pdfmark_add_Page_View(ctx, dict, D_array);
962
1.04k
        if (code < 0) goto exit;
963
894
        delete_A = true;
964
894
    } else if (pdfi_name_is(S_name, "GoToR") || pdfi_name_is(S_name, "Launch")) {
965
        /* These point out of the document.
966
         * Flatten out the reference, but otherwise leave it alone.
967
         * gs does some wack stuff here.
968
         *
969
         * Currently this is same behavior as gs, but it is not correct behavior.
970
         * In at least some cases we could do better, for example if the doc
971
         * pointed to happens to be the same file.
972
         * Sample: fts_28_2808.pdf
973
         * Sample: ~/work/samples/tests_private/pdf/sumatra/1874_-_clicking_ToC_link_crashes.pdf
974
         */
975
0
        code = pdfi_resolve_indirect_loop_detect(ctx, (pdf_obj *)dict, (pdf_obj *)A_dict, true);
976
0
        delete_A = false;
977
0
        code = 0;
978
45
    } else if (pdfi_name_is(S_name, "Named")) {
979
        /* We can just pass this through and it will work fine
980
         * This should be a name like "FirstPage" or "LastPage".
981
         * Note: gs implementation translates into page numbers and also has some bugs...
982
         * Sample: fts_33_3310.pdf
983
         */
984
0
        delete_A = false;
985
0
        code = 0;
986
45
    } else if (pdfi_name_is(S_name, "GoToE")) {
987
        /* TODO: ??
988
         * File: fts_33_3303.pdf
989
         */
990
26
    } else if (pdfi_name_is(S_name, "Thread")) {
991
        /* TODO: For basically all of these below, I think just need to preserve
992
         * any references and streams and pass it all through.
993
         * File: fts_33_3305.pdf fts_33_3317.pdf
994
         */
995
0
        deref_A = false;
996
19
    } else if (pdfi_name_is(S_name, "Sound")) {
997
        /* TODO: ??
998
         * File: fts_33_3307.pdf
999
         */
1000
0
        deref_A = false;
1001
19
    } else if (pdfi_name_is(S_name, "Movie")) {
1002
        /* TODO: ??
1003
         * File: fts_33_3308.pdf
1004
         */
1005
0
        deref_A = false;
1006
19
    } else if (pdfi_name_is(S_name, "GoTo3DView")) {
1007
        /* TODO: ??
1008
         * File: fts_33_3318.pdf
1009
         */
1010
19
    } else if (pdfi_name_is(S_name, "RichMediaExecute")) {
1011
        /* TODO: ??
1012
         * File: fts_33_3319.pdf
1013
         */
1014
19
    } else if (pdfi_name_is(S_name, "Rendition")) {
1015
        /* TODO: make sure to pass through accurately?
1016
         * File: fts_07_0709.pdf fts_33_3316.pdf
1017
         */
1018
19
    } else {
1019
        /* TODO: flag warning? */
1020
19
    }
1021
1022
1.72k
 exit:
1023
1.72k
    if (delete_A) {
1024
937
        code = pdfi_dict_delete(ctx, dict, "A");
1025
937
    } else if (deref_A) {
1026
790
        pdfi_countdown(A_dict);
1027
790
        A_dict = NULL;
1028
790
    }
1029
1.72k
    pdfi_countdown(A_dict);
1030
1.72k
    pdfi_countdown(S_name);
1031
1.72k
    pdfi_countdown(D_array);
1032
1.72k
    return code;
1033
1.09k
}
1034
1035
/* Begin defining an object
1036
 * Send an OBJ (_objdef) command
1037
 * (_objdef) (<label>) (/type) (/<type>) OBJ
1038
 */
1039
static int pdfi_pdfmark_objdef_begin(pdf_context *ctx, pdf_indirect_ref *label, const char *type)
1040
1.07k
{
1041
1.07k
    int code;
1042
1.07k
    pdf_obj *objarray[4];
1043
1.07k
    int num_objects = 4;
1044
1.07k
    int i;
1045
1046
1.07k
    memset(objarray, 0, sizeof(objarray));
1047
1048
1.07k
    code = pdfi_obj_charstr_to_name(ctx, "_objdef", (pdf_name **)&objarray[0]);
1049
1.07k
    if (code < 0) goto exit;
1050
1051
1.07k
    objarray[1] = (pdf_obj *)label;
1052
1.07k
    pdfi_countup(label);
1053
1054
1.07k
    code = pdfi_obj_charstr_to_name(ctx, "type", (pdf_name **)&objarray[2]);
1055
1.07k
    if (code < 0) goto exit;
1056
1057
1.07k
    code = pdfi_obj_charstr_to_name(ctx, type, (pdf_name **)&objarray[3]);
1058
1.07k
    if (code < 0) goto exit;
1059
1060
1.07k
    code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, "OBJ");
1061
1.07k
    if (code < 0) goto exit;
1062
1063
1.07k
 exit:
1064
5.38k
    for (i=0; i<num_objects; i++)
1065
4.30k
        pdfi_countdown(objarray[i]);
1066
1.07k
    return code;
1067
1.07k
}
1068
1069
/* Close an object
1070
 * Send a CLOSE command
1071
 * (<label>) CLOSE
1072
 */
1073
static int pdfi_pdfmark_objdef_close(pdf_context *ctx, pdf_indirect_ref *label)
1074
247
{
1075
247
    int code;
1076
247
    pdf_obj *objarray[1];
1077
247
    int num_objects = 1;
1078
247
    int i;
1079
1080
247
    memset(objarray, 0, sizeof(objarray));
1081
1082
247
    objarray[0] = (pdf_obj *)label;
1083
247
    pdfi_countup(label);
1084
1085
247
    code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, "CLOSE");
1086
247
    if (code < 0) goto exit;
1087
1088
247
 exit:
1089
494
    for (i=0; i<num_objects; i++)
1090
247
        pdfi_countdown(objarray[i]);
1091
247
    return code;
1092
247
}
1093
1094
static int pdfi_pdfmark_stream_contents(pdf_context *ctx, pdf_indirect_ref *label, pdf_stream *stream)
1095
247
{
1096
247
    int code;
1097
247
    pdf_obj *objarray[2];
1098
247
    int num_objects = 2;
1099
247
    int i;
1100
1101
247
    objarray[0] = (pdf_obj *)label;
1102
247
    pdfi_countup(label);
1103
1104
247
    objarray[1] = (pdf_obj *)stream;
1105
247
    pdfi_countup(stream);
1106
247
    stream->is_marking = true;
1107
1108
247
    code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, ".PUTSTREAM");
1109
247
    if (code < 0) goto exit;
1110
1111
247
 exit:
1112
247
    stream->is_marking = false;
1113
741
    for (i=0; i<num_objects; i++)
1114
494
        pdfi_countdown(objarray[i]);
1115
247
    return code;
1116
247
}
1117
1118
/* Mark a stream object */
1119
int pdfi_pdfmark_stream(pdf_context *ctx, pdf_stream *stream)
1120
259
{
1121
259
    int code;
1122
259
    pdf_dict *streamdict = NULL;
1123
259
    pdf_indirect_ref *streamref = NULL;
1124
259
    pdf_dict *tempdict = NULL;
1125
259
    uint64_t dictsize;
1126
259
    uint64_t index;
1127
259
    pdf_name *Key = NULL;
1128
1129
259
    if (stream->stream_written)
1130
11
        return 0;
1131
1132
248
    stream->stream_written = true;
1133
1134
248
    if (!ctx->device_state.writepdfmarks)
1135
0
        return 0;
1136
1137
    /* Create an indirect ref for the stream */
1138
248
    code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&streamref);
1139
248
    if (code < 0) goto exit;
1140
248
    pdfi_countup(streamref);
1141
248
    streamref->ref_object_num = stream->object_num;
1142
248
    streamref->ref_generation_num = stream->generation_num;
1143
248
    streamref->is_marking = true;
1144
1145
248
    code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream, &streamdict);
1146
248
    if (code < 0) goto exit;
1147
1148
    /* Make a copy of the dict and remove Filter keyword */
1149
248
    dictsize = pdfi_dict_entries(streamdict);
1150
248
    code = pdfi_dict_alloc(ctx, dictsize, &tempdict);
1151
248
    if (code < 0) goto exit;
1152
248
    pdfi_countup(tempdict);
1153
248
    code = pdfi_dict_copy(ctx, tempdict, streamdict);
1154
248
    if (code < 0) goto exit;
1155
248
    code = pdfi_dict_key_first(ctx, streamdict, (pdf_obj **)&Key, &index);
1156
835
    while (code >= 0) {
1157
835
        if (pdfi_name_is(Key, "Filter") || pdfi_name_is(Key, "Length")) {
1158
321
            code = pdfi_dict_delete_pair(ctx, tempdict, Key);
1159
321
            if (code < 0) goto exit;
1160
321
        }
1161
835
        pdfi_countdown(Key);
1162
835
        Key = NULL;
1163
1164
835
        code = pdfi_dict_key_next(ctx, streamdict, (pdf_obj **)&Key, &index);
1165
835
        if (code == gs_error_undefined) {
1166
248
            code = 0;
1167
248
            break;
1168
248
        }
1169
835
    }
1170
248
    if (code < 0) goto exit;
1171
1172
248
    code = pdfi_pdfmark_objdef_begin(ctx, streamref, "stream");
1173
248
    if (code < 0) goto exit;
1174
1175
248
    code = pdfi_pdfmark_from_dict_withlabel(ctx, streamref, tempdict, NULL, ".PUTDICT");
1176
248
    if (code < 0) goto exit;
1177
1178
247
    code = pdfi_pdfmark_stream_contents(ctx, streamref, stream);
1179
247
    if (code < 0) goto exit;
1180
1181
247
    code = pdfi_pdfmark_objdef_close(ctx, streamref);
1182
247
    if (code < 0) goto exit;
1183
1184
248
 exit:
1185
248
    pdfi_countdown(tempdict);
1186
248
    pdfi_countdown(streamref);
1187
248
    return code;
1188
247
}
1189
1190
/* Mark a dict object */
1191
int pdfi_pdfmark_dict(pdf_context *ctx, pdf_dict *dict)
1192
1.53k
{
1193
1.53k
    int code;
1194
1.53k
    pdf_indirect_ref *dictref = NULL;
1195
1196
1.53k
    if (dict->dict_written)
1197
707
        return 0;
1198
1199
828
    dict->dict_written = true;
1200
1201
828
    if (!ctx->device_state.writepdfmarks)
1202
0
        return 0;
1203
1204
    /* Create an indirect ref for the dict */
1205
828
    code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&dictref);
1206
828
    if (code < 0) goto exit;
1207
828
    pdfi_countup(dictref);
1208
828
    dictref->ref_object_num = dict->object_num;
1209
828
    dictref->ref_generation_num = dict->generation_num;
1210
828
    dictref->is_marking = true;
1211
1212
828
    code = pdfi_pdfmark_objdef_begin(ctx, dictref, "dict");
1213
828
    if (code < 0) goto exit;
1214
1215
827
    code = pdfi_pdfmark_from_dict_withlabel(ctx, dictref, dict, NULL, ".PUTDICT");
1216
827
    if (code < 0) goto exit;
1217
1218
828
 exit:
1219
828
    pdfi_countdown(dictref);
1220
828
    return code;
1221
827
}
1222
1223
static int pdfi_pdfmark_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec)
1224
20
{
1225
20
    int code;
1226
20
    pdf_dict *tempdict = NULL;
1227
1228
20
    code = pdfi_dict_alloc(ctx, 40, &tempdict);
1229
20
    if (code < 0) goto exit;
1230
20
    pdfi_countup(tempdict);
1231
1232
20
    code = pdfi_dict_put(ctx, tempdict, "Name", (pdf_obj *)name);
1233
20
    if (code < 0) goto exit;
1234
1235
    /* Flatten the filespec */
1236
20
    code = pdfi_resolve_indirect(ctx, (pdf_obj *)filespec, true);
1237
20
    if (code < 0) goto exit;
1238
1239
17
    code = pdfi_dict_put(ctx, tempdict, "FS", (pdf_obj *)filespec);
1240
17
    if (code < 0) goto exit;
1241
1242
17
    code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "EMBED");
1243
17
    if (code < 0) goto exit;
1244
1245
20
 exit:
1246
20
    pdfi_countdown(tempdict);
1247
20
    return code;
1248
17
}
1249
1250
/* embed a file */
1251
int pdfi_pdfmark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec)
1252
20
{
1253
20
    int code;
1254
1255
20
    code = pdfi_pdfmark_filespec(ctx, name, filespec);
1256
20
    if (code < 0) goto exit;
1257
1258
20
 exit:
1259
20
    return code;
1260
20
}
1261
1262
/*
1263
 * Create and emit a /DOCINFO pdfmark for any and all of Title,
1264
 * Author, Subject, Keywords and Creator
1265
 */
1266
void pdfi_pdfmark_write_docinfo(pdf_context *ctx, pdf_dict *info_dict)
1267
19.1k
{
1268
19.1k
    int i, code = 0;
1269
19.1k
    pdf_dict *Info = NULL;
1270
19.1k
    pdf_obj *o = NULL;
1271
    /* We don't preserve the Producer, we are the Producer */
1272
19.1k
    const char *KeyNames[] = {
1273
19.1k
        "Title", "Author", "Subject", "Keywords", "Creator"
1274
19.1k
    };
1275
1276
19.1k
    if (!ctx->device_state.writepdfmarks)
1277
18.5k
        return;
1278
1279
627
    code = pdfi_dict_alloc(ctx, 5, &Info);
1280
627
    if (code < 0)
1281
0
        goto exit;
1282
627
    pdfi_countup(Info);
1283
1284
3.76k
    for (i=0;i<5;i++)
1285
3.13k
    {
1286
3.13k
        if (pdfi_dict_knownget(ctx, info_dict, KeyNames[i], &o) > 0)
1287
1.15k
        {
1288
1.15k
            (void)pdfi_dict_put(ctx, Info, KeyNames[i], (pdf_obj *)o);
1289
1.15k
            pdfi_countdown(o);
1290
1.15k
        }
1291
3.13k
    }
1292
1293
627
    code = pdfi_pdfmark_from_dict(ctx, Info, NULL, "DOCINFO");
1294
627
exit:
1295
627
    pdfi_countdown(Info);
1296
627
    return;
1297
627
}
1298
1299
/*
1300
 * Create and emit a /PAGE pdfmark for any and all of
1301
 * CropBox, TrimBox, Artbox and BleedBox. If the interpreter
1302
 * has used something other than the MediaBox as the media size, then
1303
 * we don't send these, they are almost certainly incorrect.
1304
 *
1305
 * Because we will have used the MediaBox to create the media size, and
1306
 * will have accounted for any rotation or scaling, we can use the CTM
1307
 * to adjust the various Box entries (note this routine must be called
1308
 * early!).
1309
 */
1310
void pdfi_pdfmark_write_boxes(pdf_context *ctx, pdf_dict *page_dict)
1311
92.8k
{
1312
92.8k
    int i, code = 0;
1313
92.8k
    pdf_dict *BoxDict = NULL;
1314
92.8k
    pdf_obj *o = NULL;
1315
92.8k
    gx_device *device = gs_currentdevice(ctx->pgs);
1316
92.8k
    gs_matrix scale, m, ctm;
1317
92.8k
    const char *BoxNames[] = {
1318
92.8k
        "CropBox", "BleedBox", "TrimBox", "ArtBox"
1319
92.8k
    };
1320
1321
    /* If the device doesn't support pdfmark, or the device modifies the Page Size (making the other boxes unreliable) exit now */
1322
92.8k
    if (!ctx->device_state.writepdfmarks || ctx->device_state.ModifiesPageSize)
1323
86.3k
        return;
1324
1325
    /* If we are using somethign other than the MediaBox, don't send these */
1326
6.40k
    if (ctx->args.usecropbox || ctx->args.usebleedbox ||
1327
6.40k
        ctx->args.usetrimbox || ctx->args.useartbox)
1328
0
        return;
1329
1330
6.40k
    code = pdfi_dict_alloc(ctx, 4, &BoxDict);
1331
6.40k
    if (code < 0)
1332
0
        goto exit;
1333
1334
6.40k
    pdfi_countup(BoxDict);
1335
1336
    /* Undo the resolution scaling from the CTM, we don't want to apply that */
1337
6.40k
    scale.xx = 72.0 / device->HWResolution[0];
1338
6.40k
    scale.xy = 0;
1339
6.40k
    scale.yx = 0;
1340
6.40k
    scale.yy = 72.0 / device->HWResolution[1];
1341
6.40k
    scale.tx = 0;
1342
6.40k
    scale.ty = 0;
1343
1344
    /* And multiply that by the CTM to get a matrix which represents the
1345
     * scaling/rotation used to set the conetnt to the media.
1346
     */
1347
6.40k
    gs_currentmatrix(ctx->pgs, &ctm);
1348
6.40k
    code = gs_matrix_multiply(&ctm, &scale, &m);
1349
6.40k
    if (code < 0) goto exit;
1350
1351
32.0k
    for (i=0;i<4;i++)
1352
25.6k
    {
1353
        /* Check each Bos name in turn */
1354
25.6k
        if (pdfi_dict_knownget(ctx, page_dict, BoxNames[i], &o) > 0){
1355
2.68k
            gs_rect box;
1356
2.68k
            pdf_array *new_array = NULL;
1357
1358
            /* Box is present in page dicitonayr, check it's an array */
1359
2.68k
            if (pdfi_type_of(o) != PDF_ARRAY) {
1360
0
                pdfi_countdown(o);
1361
0
                continue;
1362
0
            }
1363
1364
            /* Turn the contents into a gs_rect */
1365
2.68k
            code = pdfi_array_to_gs_rect(ctx, (pdf_array *)o, &box);
1366
2.68k
            pdfi_countdown(o);
1367
2.68k
            if (code < 0)
1368
18
                continue;
1369
1370
            /* Rectangles in PDF need not be llx,lly,urx,ury, they can be any
1371
             * two opposite corners. Turn that into the usual format.
1372
             */
1373
2.67k
            pdfi_normalize_rect(ctx, &box);
1374
1375
            /* Transform the resulting box by the calculated matrix */
1376
2.67k
            pdfi_bbox_transform(ctx, &box, &m);
1377
1378
            /* Get a new array created from the box values */
1379
2.67k
            code = pdfi_gs_rect_to_array(ctx, &box, &new_array);
1380
2.67k
            if (code < 0)
1381
0
                continue;
1382
1383
            /* And store it in the working dictionary */
1384
2.67k
            (void)pdfi_dict_put(ctx, BoxDict, BoxNames[i], (pdf_obj *)new_array);
1385
2.67k
            pdfi_countdown(new_array);
1386
2.67k
        }
1387
25.6k
    }
1388
1389
    /* Send all the Box entries to the device */
1390
6.40k
    (void)pdfi_pdfmark_from_dict(ctx, BoxDict, NULL, "PAGE");
1391
1392
6.40k
exit:
1393
6.40k
    pdfi_countdown(BoxDict);
1394
6.40k
    return;
1395
6.40k
}