Coverage Report

Created: 2022-10-31 07:00

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