Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/vector/gdevpdfu.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
17
/* Output utilities for PDF-writing driver */
18
#include "memory_.h"
19
#include "jpeglib_.h"   /* for sdct.h */
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gscdefs.h"
23
#include "gsdsrc.h"
24
#include "gsfunc.h"
25
#include "gsfunc3.h"
26
#include "gdevpdfx.h"
27
#include "gdevpdfo.h"
28
#include "gdevpdfg.h"
29
#include "gdevpdtd.h"
30
#include "strimpl.h"
31
#include "sa85x.h"
32
#include "scfx.h"
33
#include "sdct.h"
34
#include "slzwx.h"
35
#include "spngpx.h"
36
#include "srlx.h"
37
#include "sarc4.h"
38
#include "smd5.h"
39
#include "sbrotlix.h"
40
#include "sstring.h"
41
#include "strmio.h"
42
#include "szlibx.h"
43
#include "gsagl.h"
44
45
#include "opdfread.h"
46
#include "gs_mgl_e.h"
47
#include "gs_mro_e.h"
48
49
#include "gdevpdtx.h"
50
#include "gdevpdts.h"
51
52
extern single_glyph_list_t SingleGlyphList[];
53
54
    /* Define the size of internal stream buffers. */
55
/* (This is not a limitation, it only affects performance.) */
56
26.0k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
26.0k
#  define Flate_filter_name "FlateDecode"
61
26.0k
#  define Flate_filter_template s_zlibE_template
62
26.0k
#  define Flate_filter_state stream_zlib_state
63
0
#  define Brotli_filter_name "BrotliDecode"
64
0
#  define Brotli_filter_template s_brotliE_template
65
0
#  define Brotli_filter_state stream_brotlie_state
66
#else
67
#  define compression_filter_name "LZWDecode"
68
#  define compression_filter_template s_LZWE_template
69
#  define compression_filter_state stream_LZW_state
70
#endif
71
72
/* Import procedures for writing filter parameters. */
73
extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
74
extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
75
76
#define CHECK(expr)\
77
2.18M
  BEGIN if ((code = (expr)) < 0) return code; END
78
79
/* GC descriptors */
80
extern_st(st_pdf_color_space);
81
extern_st(st_pdf_font_resource);
82
extern_st(st_pdf_char_proc);
83
extern_st(st_pdf_font_descriptor);
84
public_st_pdf_resource();
85
private_st_pdf_x_object();
86
private_st_pdf_pattern();
87
88
/* ---------------- Utilities ---------------- */
89
90
#ifdef PS2WRITE_USES_ROMFS
91
/*
92
 * Strip whitespace and comments from a line of PostScript code as possible.
93
 * Return a pointer to any string that remains, or NULL if none.
94
 * Note that this may store into the string.
95
 */
96
/* This function copied from geninit.c . */
97
static char *
98
doit(char *line, bool intact)
99
{
100
    char *str = line;
101
    char *from;
102
    char *to;
103
    int in_string = 0;
104
105
    if (intact)
106
        return str;
107
    while (*str == ' ' || *str == '\t')   /* strip leading whitespace */
108
        ++str;
109
    if (*str == 0)    /* all whitespace */
110
        return NULL;
111
    if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
112
        return str;
113
    if (str[0] == '%')    /* comment line */
114
        return NULL;
115
    /*
116
     * Copy the string over itself removing:
117
     *  - All comments not within string literals;
118
     *  - Whitespace adjacent to '[' ']' '{' '}';
119
     *  - Whitespace before '/' '(' '<';
120
     *  - Whitespace after ')' '>'.
121
     */
122
    for (to = from = str; (*to = *from) != 0; ++from, ++to) {
123
        switch (*from) {
124
            case '%':
125
                if (!in_string)
126
                    break;
127
                continue;
128
            case ' ':
129
            case '\t':
130
                if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
131
                    --to;
132
                continue;
133
            case '(':
134
            case '<':
135
            case '/':
136
            case '[':
137
            case ']':
138
            case '{':
139
            case '}':
140
                if (to > str && !in_string && strchr(" \t", to[-1]))
141
                    *--to = *from;
142
                if (*from == '(')
143
                    ++in_string;
144
                continue;
145
            case ')':
146
                --in_string;
147
                continue;
148
            case '\\':
149
                if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
150
                    *++to = *++from;
151
                continue;
152
            default:
153
                continue;
154
        }
155
        break;
156
    }
157
    /* Strip trailing whitespace. */
158
    while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
159
        --to;
160
    *to = 0;
161
    return str;
162
}
163
164
static int
165
copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
166
{
167
    stream *f;
168
    char buf[1024], *p, *q  = buf;
169
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
170
    bool skipping = false;
171
172
    f = sfopen(fname, "r", s->memory);
173
    if (f == NULL)
174
        return_error(gs_error_undefinedfilename);
175
    n = sfread(buf, 1, m, f);
176
    buf[n] = 0;
177
    do {
178
        if (*q == '\r' || *q == '\n') {
179
            q++;
180
            continue;
181
        }
182
        p = strchr(q, '\r');
183
        if (p == NULL)
184
            p = strchr(q, '\n');
185
        if (p == NULL) {
186
            if (n < m)
187
                p = buf + n;
188
            else {
189
                strcpy(buf, q);
190
                l = strlen(buf);
191
                m = sizeof(buf) - 1 - l;
192
                if (!m) {
193
                    sfclose(f);
194
                    emprintf1(s->memory,
195
                              "The procset %s contains a too long line.",
196
                              fname);
197
                    return_error(gs_error_ioerror);
198
                }
199
                n = sfread(buf + l, 1, m, f);
200
                n += l;
201
                m += l;
202
                buf[n] = 0;
203
                q = buf;
204
                continue;
205
            }
206
        }
207
        *p = 0;
208
        if (q[0] == '%')
209
            l = 0;
210
        else {
211
            q = doit(q, false);
212
            if (q == NULL)
213
                l = 0;
214
            else
215
                l = strlen(q);
216
        }
217
        if (l) {
218
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
219
                skipping = true;
220
            if (!skipping) {
221
                outl += l + 1;
222
                if (outl > 100) {
223
                    q[l] = '\r';
224
                    outl = 0;
225
                } else
226
                    q[l] = ' ';
227
                stream_write(s, q, l + 1);
228
            }
229
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
230
                skipping = false;
231
        }
232
        q = p + 1;
233
    } while (n == m || q < buf + n);
234
    if (outl)
235
        stream_write(s, "\r", 1);
236
    sfclose(f);
237
    return 0;
238
}
239
240
static int
241
copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
242
{
243
    stream *f;
244
    char buf[1024], *p, *q  = buf;
245
    int n, l = 0, m = sizeof(buf) - 1, outl = 0;
246
    bool skipping = false;
247
248
    f = sfopen(fname, "r", s->memory);
249
    if (f == NULL)
250
        return_error(gs_error_undefinedfilename);
251
    n = sfread(buf, 1, m, f);
252
    buf[n] = 0;
253
    do {
254
        if (*q == '\r' || *q == '\n') {
255
            q++;
256
            continue;
257
        }
258
        p = strchr(q, '\r');
259
        if (p == NULL)
260
            p = strchr(q, '\n');
261
        if (p == NULL) {
262
            if (n < m)
263
                p = buf + n;
264
            else {
265
                strcpy(buf, q);
266
                l = strlen(buf);
267
                m = sizeof(buf) - 1 - l;
268
                if (!m) {
269
                    sfclose(f);
270
                    emprintf1(s->memory,
271
                              "The procset %s contains a too long line.",
272
                              fname);
273
                    return_error(gs_error_ioerror);
274
                }
275
                n = sfread(buf + l, 1, m, f);
276
                n += l;
277
                m += l;
278
                buf[n] = 0;
279
                q = buf;
280
                continue;
281
            }
282
        }
283
        *p = 0;
284
        if (q[0] == '%')
285
            l = 0;
286
        else {
287
            q = doit(q, false);
288
            if (q == NULL)
289
                l = 0;
290
            else
291
                l = strlen(q);
292
        }
293
        if (l) {
294
            if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
295
                skipping = true;
296
            if (!skipping) {
297
                outl += l + 1;
298
                if (outl > 100) {
299
                    q[l] = '\n';
300
                    outl = 0;
301
                } else
302
                    q[l] = '\n';
303
                stream_write(s, q, l + 1);
304
            }
305
            if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
306
                skipping = false;
307
        }
308
        q = p + 1;
309
    } while (n == m || q < buf + n);
310
    if (outl)
311
        stream_write(s, "\r", 1);
312
    sfclose(f);
313
    return 0;
314
}
315
#endif
316
317
static int write_opdfread(stream *s)
318
25.4k
{
319
25.4k
    int index = 0;
320
321
103M
    do {
322
103M
        if (opdfread_ps[index] == 0x00)
323
25.4k
            break;
324
103M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
103M
        index++;
326
103M
    } while (1);
327
25.4k
    return 0;
328
25.4k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
25.4k
{
332
25.4k
    int index = 0;
333
334
993k
    do {
335
993k
        if (gs_mro_e_ps[index] == 0x00)
336
25.4k
            break;
337
967k
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
967k
        index++;
339
967k
    } while (1);
340
341
25.4k
    if (HaveTrueTypes) {
342
25.4k
        char Buffer[256];
343
25.4k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
25.4k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
25.4k
        stream_write(s, Buffer, strlen(Buffer));
347
106M
        while (entry->Glyph) {
348
106M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
106M
            stream_write(s, Buffer, strlen(Buffer));
350
106M
            entry++;
351
106M
        };
352
25.4k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
25.4k
        stream_write(s, Buffer, strlen(Buffer));
354
355
25.4k
        index = 0;
356
916k
        do {
357
916k
            if (gs_mgl_e_ps[index] == 0x00)
358
25.4k
                break;
359
891k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
891k
            index++;
361
891k
        } while (1);
362
25.4k
    }
363
25.4k
    return 0;
364
25.4k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
25.4k
{
369
25.4k
    int code;
370
371
25.4k
    code = write_opdfread(s);
372
25.4k
    if (code < 0)
373
0
        return code;
374
375
25.4k
    code = write_tt_encodings(s, HaveTrueTypes);
376
25.4k
    return code;
377
378
25.4k
}
379
380
static int
381
encode(stream **s, const stream_template *t, gs_memory_t *mem)
382
0
{
383
0
    stream_state *st = s_alloc_state(mem, t->stype, "pdfwrite_pdf_open_document.encode");
384
385
0
    if (st == 0)
386
0
        return_error(gs_error_VMerror);
387
0
    if (t->set_defaults)
388
0
        t->set_defaults(st);
389
0
    if (s_add_filter(s, t, st, mem) == 0) {
390
0
        gs_free_object(mem, st, "pdfwrite_pdf_open_document.encode");
391
0
        return_error(gs_error_VMerror);
392
0
    }
393
0
    return 0;
394
0
}
395
396
/* ------ Document ------ */
397
398
/* Write out the arguments used to invoke GS as a comment in the PDF/PS
399
 * file. We don't write out paths, passwords, or any unrecognised string
400
 * parameters (all sanitised by the arg code) for privacy/security
401
 * reasons. This routine is only called by the PDF linearisation code.
402
 */
403
int
404
pdfwrite_fwrite_args_comment(gx_device_pdf *pdev, gp_file *f)
405
0
{
406
0
    const char * const *argv = NULL;
407
0
    const char *arg;
408
0
    int towrite, length, i, j, argc;
409
410
0
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
411
412
0
    gp_fwrite("%%Invocation:", 13, 1, f);
413
0
    length = 12;
414
0
    for (i=0;i < argc; i++) {
415
0
        arg = argv[i];
416
417
0
        if ((strlen(arg) + length) > 255) {
418
0
            gp_fwrite("\n%%+ ", 5, 1, f);
419
0
            length = 5;
420
0
        } else {
421
0
            gp_fwrite(" ", 1, 1, f);
422
0
            length++;
423
0
        }
424
425
0
        if (strlen(arg) > 250)
426
0
            towrite = 250;
427
0
        else
428
0
            towrite = strlen(arg);
429
430
0
        length += towrite;
431
432
0
        for (j=0;j < towrite;j++) {
433
0
            if (arg[j] == 0x0A) {
434
0
                gp_fwrite("<0A>", 4, 1, f);
435
0
            } else {
436
0
                if (arg[j] == 0x0D) {
437
0
                    gp_fwrite("<0D>", 4, 1, f);
438
0
                } else {
439
0
                    gp_fwrite(&arg[j], 1, 1, f);
440
0
                }
441
0
            }
442
0
        }
443
0
    }
444
0
    gp_fwrite("\n", 1, 1, f);
445
0
    return 0;
446
0
}
447
448
/* Exactly the same as pdfwrite_fwrite_args_comment() above, but uses a stream
449
 * instead of a gp_file *, because of course we aren't consistent.... This
450
 * routine is called by the regular PDF or PS file output code.
451
 */
452
int
453
pdfwrite_write_args_comment(gx_device_pdf *pdev, stream *s)
454
45.4k
{
455
45.4k
    const char * const *argv = NULL;
456
45.4k
    const char *arg;
457
45.4k
    int towrite, length, i, j, argc;
458
459
45.4k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
45.4k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
45.4k
    length = 12;
463
863k
    for (i=0;i < argc; i++) {
464
818k
        arg = argv[i];
465
466
818k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
818k
        } else {
470
818k
            stream_write(s, (byte *)" ", 1);
471
818k
            length++;
472
818k
        }
473
474
818k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
818k
        else
477
818k
            towrite = strlen(arg);
478
479
818k
        length += towrite;
480
481
9.88M
        for (j=0;j < towrite;j++) {
482
9.06M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
9.06M
            } else {
485
9.06M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
9.06M
                } else {
488
9.06M
                    stream_write(s, (byte *)&arg[j], 1);
489
9.06M
                }
490
9.06M
            }
491
9.06M
        }
492
818k
    }
493
45.4k
    stream_write(s, (byte *)"\n", 1);
494
45.4k
    return 0;
495
45.4k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
25.4k
{
499
25.4k
    stream *s = pdev->strm;
500
501
25.4k
    if (pdev->ForOPDFRead) {
502
25.4k
        char cre_date_time[41];
503
25.4k
        int code, status, cre_date_time_len;
504
25.4k
        char BBox[256];
505
506
25.4k
        if (pdev->Eps2Write)
507
12.5k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
12.9k
        else
509
12.9k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
25.4k
        pdfwrite_write_args_comment(pdev, s);
511
        /* We need to calculate the document BoundingBox which is a 'high water'
512
         * mark derived from the BoundingBox of all the individual pages.
513
         */
514
25.4k
        {
515
25.4k
            int pagecount = 1, j;
516
25.4k
            double urx=0, ury=0;
517
518
432k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
407k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
439k
                for (; pres != 0; pres = pres->next)
522
32.4k
                    if ((!pres->named || pdev->ForOPDFRead)
523
32.4k
                        && !pres->object->written) {
524
32.4k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
32.4k
                        if (ceil(page->MediaBox.x) > urx)
526
25.7k
                            urx = ceil(page->MediaBox.x);
527
32.4k
                        if (ceil(page->MediaBox.y) > ury)
528
25.5k
                            ury = ceil(page->MediaBox.y);
529
32.4k
                        pagecount++;
530
32.4k
                    }
531
407k
            }
532
25.4k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
19.0k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
6.37k
            else
535
6.37k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
536
25.4k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
25.4k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
19.0k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
6.37k
            else
540
6.37k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
541
25.4k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
25.4k
        }
543
25.4k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
25.4k
        cre_date_time[cre_date_time_len] = 0;
545
25.4k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
25.4k
                pdev->dname);
547
25.4k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
25.4k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
25.4k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
25.4k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
25.4k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
25.4k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
25.4k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
25.4k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
25.4k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
25.4k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
25.4k
        if (pdev->params.CompressPages) {
558
            /*  When CompressEntireFile is true and ASCII85EncodePages is false,
559
                the ASCII85Encode filter is applied, rather one may expect the opposite.
560
                Keeping it so due to no demand for this mode.
561
                A right implementation should compute the length of the compressed procset,
562
                write out an invocation of SubFileDecode filter, and write the length to
563
                there assuming the output file is positionable. */
564
0
            stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
565
0
            code = encode(&s, &s_A85E_template, pdev->pdf_memory);
566
0
            if (code < 0)
567
0
                return code;
568
0
            code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
569
0
            if (code < 0)
570
0
                return code;
571
0
        }
572
25.4k
        stream_puts(s, "10 dict dup begin\n");
573
25.4k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
25.4k
        if (pdev->Eps2Write) {
575
12.5k
            stream_puts(s, "/SetPageSize false def\n");
576
12.5k
            stream_puts(s, "/EPS2Write true def\n");
577
12.9k
        } else {
578
12.9k
            if (pdev->SetPageSize)
579
12.9k
                stream_puts(s, "/SetPageSize true def\n");
580
12.9k
            stream_puts(s, "/EPS2Write false def\n");
581
12.9k
        }
582
25.4k
        stream_puts(s, "end\n");
583
584
25.4k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
25.4k
        if (code < 0)
586
0
            return code;
587
25.4k
        status = s_close_filters(&s, pdev->strm);
588
25.4k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
25.4k
        stream_puts(s, "\n");
591
25.4k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
25.4k
    }
593
25.4k
    return 0;
594
25.4k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
562k
{
600
562k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
562k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
135k
        stream *s = pdev->strm;
605
135k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
135k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
135k
        if (pdev->ForOPDFRead) {
609
115k
            int code, status;
610
115k
            char BBox[256];
611
115k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
115k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
115k
            if (pdev->ProduceDSC)
615
115k
                pdev->CompressEntireFile = 0;
616
0
            else {
617
0
                stream_write(s, (byte *)"%!\r", 3);
618
0
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", width, height);
619
0
                stream_write(s, (byte *)BBox, strlen(BBox));
620
0
                if (pdev->params.CompressPages || pdev->CompressEntireFile) {
621
                    /*  When CompressEntireFile is true and ASCII85EncodePages is false,
622
                        the ASCII85Encode filter is applied, rather one may expect the opposite.
623
                        Keeping it so due to no demand for this mode.
624
                        A right implementation should compute the length of the compressed procset,
625
                        write out an invocation of SubFileDecode filter, and write the length to
626
                        there assuming the output file is positionable. */
627
0
                    stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
628
0
                    code = encode(&s, &s_A85E_template, pdev->pdf_memory);
629
0
                    if (code < 0)
630
0
                        return code;
631
0
                    code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
632
0
                    if (code < 0)
633
0
                        return code;
634
0
                }
635
0
                stream_puts(s, "10 dict dup begin\n");
636
0
                stream_puts(s, "/DSC_OPDFREAD false def\n");
637
0
                if (!pdev->Eps2Write)
638
0
                    stream_puts(s, "/EPS2Write false def\n");
639
0
                if(pdev->SetPageSize)
640
0
                    stream_puts(s, "/SetPageSize true def\n");
641
0
                if(pdev->RotatePages)
642
0
                    stream_puts(s, "/RotatePages true def\n");
643
0
                if(pdev->FitPages)
644
0
                    stream_puts(s, "/FitPages true def\n");
645
0
                if(pdev->CenterPages)
646
0
                    stream_puts(s, "/CenterPages true def\n");
647
0
                stream_puts(s, "end\n");
648
0
                code = copy_procsets(s, pdev->HaveTrueTypes, true);
649
0
                if (code < 0)
650
0
                    return code;
651
0
                if (!pdev->CompressEntireFile) {
652
0
                    status = s_close_filters(&s, pdev->strm);
653
0
                    if (status < 0)
654
0
                        return_error(gs_error_ioerror);
655
0
                } else
656
0
                    pdev->strm = s;
657
0
                pdev->OPDFRead_procset_length = stell(s);
658
0
            }
659
115k
        }
660
135k
        if (!(pdev->ForOPDFRead)) {
661
19.9k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
19.9k
            if (pdev->binary_ok)
663
19.9k
                stream_puts(s, "%\307\354\217\242\n");
664
19.9k
            pdfwrite_write_args_comment(pdev, s);
665
19.9k
        }
666
135k
    }
667
    /*
668
     * Determine the compression method.  Currently this does nothing.
669
     * It also isn't clear whether the compression method can now be
670
     * changed in the course of the document.
671
     *
672
     * Flate compression is available starting in PDF 1.2.  Since we no
673
     * longer support any older PDF versions, we ignore UseFlateCompression
674
     * and always use Flate compression.
675
     */
676
562k
    if (!pdev->params.CompressPages)
677
271k
        pdev->compression = pdf_compress_none;
678
290k
    else {
679
290k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
290k
        else
682
290k
            pdev->compression = pdf_compress_Flate;
683
290k
    }
684
562k
    return 0;
685
562k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
687k
{
693
687k
    return (pdev->next_id)++;
694
687k
}
695
696
/*
697
 * Return the current position in the output.  Note that this may be in the
698
 * main output file, the asides file, or the ObjStm file.  If the current
699
 * file is the ObjStm file, positions returned by pdf_stell must only be
700
 * used locally (for computing lengths or patching), since there is no way
701
 * to map them later to the eventual position in the output file.
702
 */
703
gs_offset_t
704
pdf_stell(gx_device_pdf * pdev)
705
1.53M
{
706
1.53M
    stream *s = pdev->strm;
707
1.53M
    gs_offset_t pos = stell(s);
708
709
1.53M
    if (s == pdev->asides.strm)
710
297k
        pos |= ASIDES_BASE_POSITION;
711
1.53M
    return pos;
712
1.53M
}
713
714
/* Allocate an ID for a future object.
715
 * pdf_obj_ref below allocates an object and assigns it a position assuming
716
 * it will be written at the current location in the PDF file. But we want
717
 * some way to notice when writing the PDF file if some kinds of objects have
718
 * never been written out (eg pages allocated for /Dest targets). Setting the
719
 * position to 0 is good, because we always write the header, which is 15
720
 * bytes. However, pdf_obj_ref is so wisely used its no longer possible to
721
 * tell whether writing the object out has been deferred (in which case the
722
 * pos is updated by pdf_open_obj) or not. Adding this function to allow us
723
 * to create an object whose writing is definitely deferred, in which case
724
 * we know it will be updated later. This allows setting the pos to 0,
725
 * and we can detect that when writing the xref, and set the object to
726
 * 'unused'.
727
 */
728
int64_t pdf_obj_forward_ref(gx_device_pdf * pdev)
729
78.5k
{
730
78.5k
    int64_t id = pdf_next_id(pdev);
731
78.5k
    gs_offset_t pos = 0;
732
733
78.5k
    if (pdev->doubleXref) {
734
78.5k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
78.5k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
78.5k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
78.5k
    return id;
740
78.5k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
609k
{
746
609k
    int64_t id = pdf_next_id(pdev);
747
609k
    gs_offset_t pos = 0;
748
749
609k
    if (pdev->doubleXref) {
750
609k
        if (pdev->strm == pdev->ObjStm.strm)
751
25.9k
            pos = pdev->ObjStm_id;
752
583k
        else
753
583k
            pos = 0;
754
609k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
609k
        if (pdev->strm == pdev->ObjStm.strm)
756
25.9k
            pos = pdev->NumObjStmObjects;
757
583k
        else
758
583k
            pos = pdf_stell(pdev);
759
609k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
609k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
609k
    return id;
766
609k
}
767
768
/* Set the offset in the xref table for an object to zero. This
769
 * means that whenwritingthe xref we will mark the object as 'unused'.
770
 * This is primarily of use when we encounter an error writing an object,
771
 * we want to elide the entry from the xref in order to not write a
772
 * broken PDF file. Of course, the missing object may still produce
773
 * a broken PDF file (more subtly broken), but because the PDF interpreter
774
 * generally doesn't stop if we signal an error, we try to avoid grossly
775
 * broken PDF files this way.
776
 */
777
int64_t
778
pdf_obj_mark_unused(gx_device_pdf *pdev, int64_t id)
779
41
{
780
41
    gp_file *tfile = pdev->xref.file;
781
41
    int64_t tpos = gp_ftell(tfile);
782
41
    gs_offset_t pos = 0;
783
784
41
    if (pdev->doubleXref) {
785
41
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
41
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
41
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
41
    }
790
0
    else {
791
0
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
792
0
              SEEK_SET) != 0)
793
0
            return_error(gs_error_ioerror);
794
0
    }
795
796
41
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
41
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
41
    return 0;
800
41
}
801
802
/* Begin an object, optionally allocating an ID. */
803
int64_t
804
pdf_open_obj(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
805
603k
{
806
603k
    stream *s = pdev->strm;
807
808
603k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
603k
    if (id <= 0) {
812
161k
        id = pdf_obj_ref(pdev);
813
442k
    } else {
814
442k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
442k
        gp_file *tfile = pdev->xref.file;
816
442k
        int64_t tpos = gp_ftell(tfile);
817
818
442k
        if (pdev->doubleXref) {
819
442k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
442k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
442k
            if (pdev->strm == pdev->ObjStm.strm)
823
163k
                fake_pos = pdev->ObjStm_id;
824
442k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
442k
            if (pdev->strm == pdev->ObjStm.strm)
826
163k
                pos = pdev->NumObjStmObjects;
827
442k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
442k
        } else {
829
0
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
830
0
                  SEEK_SET) != 0)
831
0
              return_error(gs_error_ioerror);
832
0
            gp_fwrite(&pos, sizeof(pos), 1, tfile);
833
0
        }
834
442k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
442k
    }
837
603k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
228k
        switch(type) {
839
914
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
914
                break;
844
32.1k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
32.1k
                break;
847
2.15k
            case resourceColorSpace:
848
2.15k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
2.15k
                break;
850
11.0k
            case resourceExtGState:
851
11.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
11.0k
                break;
853
888
            case resourcePattern:
854
888
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
888
                break;
856
0
            case resourceShading:
857
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Shading obj_%"PRId64")\n", id);
858
0
                break;
859
0
            case resourceCIDFont:
860
25.6k
            case resourceFont:
861
                /* Ought to write the font name here */
862
25.6k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
25.6k
                break;
864
87.7k
            case resourceCharProc:
865
87.7k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
87.7k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
10.7k
            case resourceFontDescriptor:
871
10.7k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
10.7k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.28k
            case resourceFunction:
877
2.28k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.28k
                break;
879
5.44k
            case resourceEncoding:
880
5.44k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
5.44k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
13.0k
            case resourceHalftone:
886
13.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
13.0k
                break;
888
0
            case resourceLength:
889
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Length obj_%"PRId64")\n", id);
890
0
                break;
891
0
            case resourceSoftMaskDict:
892
                /* This should not be possible, not valid in PostScript */
893
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF SoftMask obj_%"PRId64")\n", id);
894
0
                break;
895
15
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
15
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
15
                break;
899
0
            case resourceStream:
900
                /* Possibly we should not add comments to this type */
901
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF stream obj_%"PRId64")\n", id);
902
0
                break;
903
0
            case resourceOutline:
904
                /* This should not be possible, not valid in PostScript */
905
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Outline obj_%"PRId64")\n", id);
906
0
                break;
907
0
            case resourceArticle:
908
                /* This should not be possible, not valid in PostScript */
909
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Article obj_%"PRId64")\n", id);
910
0
                break;
911
0
            case resourceDests:
912
                /* This should not be possible, not valid in PostScript */
913
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Dests obj_%"PRId64")\n", id);
914
0
                break;
915
0
            case resourceEmbeddedFiles:
916
                /* This should not be possible, not valid in PostScript */
917
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF EmbeddedFiles obj_%"PRId64")\n", id);
918
0
                break;
919
0
            case resourceLabels:
920
                /* This should not be possible, not valid in PostScript */
921
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Page Labels obj_%"PRId64")\n", id);
922
0
                break;
923
0
            case resourceThread:
924
                /* This should not be possible, not valid in PostScript */
925
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Thread obj_%"PRId64")\n", id);
926
0
                break;
927
0
            case resourceCatalog:
928
                /* This should not be possible, not valid in PostScript */
929
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Catalog obj_%"PRId64")\n", id);
930
0
                break;
931
0
            case resourceEncrypt:
932
                /* This should not be possible, not valid in PostScript */
933
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Encryption obj_%"PRId64")\n", id);
934
0
                break;
935
0
            case resourcePagesTree:
936
                /* This should not be possible, not valid in PostScript */
937
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%"PRId64")\n", id);
938
0
                break;
939
0
            case resourceMetadata:
940
                /* This should not be possible, not valid in PostScript */
941
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Metadata obj_%"PRId64")\n", id);
942
0
                break;
943
0
            case resourceICC:
944
                /* This should not be possible, not valid in PostScript */
945
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%"PRId64")\n", id);
946
0
                break;
947
0
            case resourceAnnotation:
948
                /* This should not be possible, not valid in PostScript */
949
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Annotation obj_%"PRId64")\n", id);
950
0
                break;
951
10.5k
            case resourceFontFile:
952
10.5k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
10.5k
                break;
954
25.4k
            default:
955
25.4k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
25.4k
                break;
957
228k
        }
958
228k
    }
959
603k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
417k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
603k
    return id;
962
603k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
26.0k
{
966
26.0k
    return pdf_open_obj(pdev, 0L, type);
967
26.0k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
603k
{
973
603k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
417k
        stream_puts(pdev->strm, "endobj\n");
975
603k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
228k
        switch(type) {
977
32.1k
            case resourcePage:
978
32.1k
                break;
979
195k
            default:
980
195k
            stream_puts(pdev->strm, "%%EndResource\n");
981
195k
            break;
982
228k
        }
983
228k
    }
984
603k
    return 0;
985
603k
}
986
987
/* ------ Page contents ------ */
988
989
/* Handle transitions between contexts. */
990
static int
991
    none_to_stream(gx_device_pdf *), stream_to_text(gx_device_pdf *),
992
    string_to_text(gx_device_pdf *), text_to_stream(gx_device_pdf *),
993
    stream_to_none(gx_device_pdf *);
994
typedef int (*context_proc) (gx_device_pdf *);
995
static const context_proc context_procs[4][4] =
996
{
997
    {0, none_to_stream, none_to_stream, none_to_stream},
998
    {stream_to_none, 0, stream_to_text, stream_to_text},
999
    {text_to_stream, text_to_stream, 0, 0},
1000
    {string_to_text, string_to_text, string_to_text, 0}
1001
};
1002
1003
/* Compute an object encryption key. */
1004
static int
1005
pdf_object_key(const gx_device_pdf * pdev, gs_id object_id, byte key[16])
1006
0
{
1007
0
    gs_md5_state_t md5;
1008
0
    gs_md5_byte_t zero[2] = {0, 0}, t;
1009
0
    int KeySize = pdev->KeyLength / 8;
1010
1011
0
    gs_md5_init(&md5);
1012
0
    gs_md5_append(&md5, pdev->EncryptionKey, KeySize);
1013
0
    t = (byte)(object_id >>  0);  gs_md5_append(&md5, &t, 1);
1014
0
    t = (byte)(object_id >>  8);  gs_md5_append(&md5, &t, 1);
1015
0
    t = (byte)(object_id >> 16);  gs_md5_append(&md5, &t, 1);
1016
0
    gs_md5_append(&md5, zero, 2);
1017
0
    gs_md5_finish(&md5, key);
1018
0
    return min(KeySize + 5, 16);
1019
0
}
1020
1021
/* Initialize encryption. */
1022
int
1023
pdf_encrypt_init(const gx_device_pdf * pdev, gs_id object_id, stream_arcfour_state *psarc4)
1024
0
{
1025
0
    byte key[16];
1026
1027
0
    return s_arcfour_set_key(psarc4, key, pdf_object_key(pdev, object_id, key));
1028
0
}
1029
1030
/* Add the encryption filter. */
1031
int
1032
pdf_begin_encrypt(gx_device_pdf * pdev, stream **s, gs_id object_id)
1033
129k
{
1034
129k
    gs_memory_t *mem = pdev->v_memory;
1035
129k
    stream_arcfour_state *ss;
1036
129k
    gs_md5_byte_t key[16];
1037
129k
    int code, keylength;
1038
1039
129k
    if (!pdev->KeyLength)
1040
129k
        return 0;
1041
0
    keylength = pdf_object_key(pdev, object_id, key);
1042
0
    ss = gs_alloc_struct(mem, stream_arcfour_state,
1043
0
                    s_arcfour_template.stype, "psdf_encrypt");
1044
0
    if (ss == NULL)
1045
0
        return_error(gs_error_VMerror);
1046
0
    code = s_arcfour_set_key(ss, key, keylength);
1047
0
    if (code < 0)
1048
0
        return code;
1049
0
    if (s_add_filter(s, &s_arcfour_template, (stream_state *)ss, mem) == 0)
1050
0
        return_error(gs_error_VMerror);
1051
0
    return 0;
1052
    /* IMPORTANT NOTE :
1053
       We don't encrypt streams written into temporary files,
1054
       because they can be used for comparizon
1055
       (for example, for merging equal images).
1056
       Instead that the encryption is applied in pdf_copy_data,
1057
       when the stream is copied to the output file.
1058
     */
1059
0
}
1060
1061
/* Enter stream context. */
1062
static int
1063
none_to_stream(gx_device_pdf * pdev)
1064
58.4k
{
1065
58.4k
    stream *s;
1066
58.4k
    int code;
1067
1068
58.4k
    if (pdev->contents_id != 0)
1069
9
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
58.4k
    pdev->compression_at_page_start = pdev->compression;
1071
58.4k
    if (pdev->ResourcesBeforeUsage) {
1072
32.4k
        pdf_resource_t *pres;
1073
1074
32.4k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
32.4k
                    true, pdev->params.CompressPages);
1076
32.4k
        if (code < 0)
1077
0
            return code;
1078
32.4k
        pdev->contents_id = pres->object->id;
1079
32.4k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
32.4k
        pdev->contents_pos = -1; /* inapplicable */
1081
32.4k
        s = pdev->strm;
1082
32.4k
    } else {
1083
26.0k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
26.0k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
26.0k
        s = pdev->strm;
1086
26.0k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
26.0k
        if (pdev->compression == pdf_compress_Flate) {
1088
26.0k
            if (pdev->binary_ok)
1089
26.0k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
26.0k
        }
1093
26.0k
        if (pdev->compression == pdf_compress_Brotli) {
1094
0
            if (pdev->binary_ok)
1095
0
                pprints1(s, "/Filter /%s", Brotli_filter_name);
1096
0
            else
1097
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Brotli_filter_name);
1098
0
        }
1099
26.0k
        stream_puts(s, ">>\nstream\n");
1100
26.0k
        pdev->contents_pos = pdf_stell(pdev);
1101
26.0k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
26.0k
        if (code < 0)
1103
0
            return code;
1104
26.0k
        pdev->strm = s;
1105
26.0k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
26.0k
            const stream_template *templat;
1107
26.0k
            stream *es;
1108
26.0k
            byte *buf;
1109
26.0k
            Flate_filter_state *st;
1110
1111
26.0k
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1112
0
                const stream_template *templat2 = &s_A85E_template;
1113
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1114
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1115
0
                                           "PDF contents buffer");
1116
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1117
0
                                templat2->stype, "PDF contents state");
1118
0
                if (as == 0 || ast == 0 || buf == 0)
1119
0
                    return_error(gs_error_VMerror);
1120
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1121
0
                           s_mode_write);
1122
0
                ast->memory = pdev->pdf_memory;
1123
0
                ast->templat = templat2;
1124
0
                as->state = (stream_state *) ast;
1125
0
                as->procs.process = templat2->process;
1126
0
                as->strm = s;
1127
0
                (*templat2->init) ((stream_state *) ast);
1128
0
                pdev->strm = s = as;
1129
0
            }
1130
26.0k
            templat = &Flate_filter_template;
1131
26.0k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
26.0k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
26.0k
                                       "PDF compression buffer");
1134
26.0k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
26.0k
                                 templat->stype, "PDF compression state");
1136
26.0k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
26.0k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
26.0k
                       s_mode_write);
1140
26.0k
            st->memory = pdev->pdf_memory;
1141
26.0k
            st->templat = templat;
1142
26.0k
            es->state = (stream_state *) st;
1143
26.0k
            es->procs.process = templat->process;
1144
26.0k
            es->strm = s;
1145
26.0k
            (*templat->set_defaults) ((stream_state *) st);
1146
26.0k
            code = (*templat->init) ((stream_state *) st);
1147
26.0k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
26.0k
            pdev->strm = s = es;
1152
26.0k
        }
1153
26.0k
        if (pdev->compression == pdf_compress_Brotli) { /* Set up the Brotli filter. */
1154
0
            const stream_template *templat;
1155
0
            stream *es;
1156
0
            byte *buf;
1157
0
            Brotli_filter_state *st;
1158
1159
0
            if (!pdev->binary_ok) { /* Set up the A85 filter */
1160
0
                const stream_template *templat2 = &s_A85E_template;
1161
0
                stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
1162
0
                byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1163
0
                                           "PDF contents buffer");
1164
0
                stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
1165
0
                                templat2->stype, "PDF contents state");
1166
0
                if (as == 0 || ast == 0 || buf == 0)
1167
0
                    return_error(gs_error_VMerror);
1168
0
                s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
1169
0
                           s_mode_write);
1170
0
                ast->memory = pdev->pdf_memory;
1171
0
                ast->templat = templat2;
1172
0
                as->state = (stream_state *) ast;
1173
0
                as->procs.process = templat2->process;
1174
0
                as->strm = s;
1175
0
                (*templat2->init) ((stream_state *) ast);
1176
0
                pdev->strm = s = as;
1177
0
            }
1178
0
            templat = &Brotli_filter_template;
1179
0
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1180
0
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1181
0
                                       "PDF compression buffer");
1182
0
            st = gs_alloc_struct(pdev->pdf_memory, Brotli_filter_state,
1183
0
                                 templat->stype, "PDF compression state");
1184
0
            if (es == 0 || st == 0 || buf == 0)
1185
0
                return_error(gs_error_VMerror);
1186
0
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1187
0
                       s_mode_write);
1188
0
            st->memory = pdev->pdf_memory;
1189
0
            st->templat = templat;
1190
0
            es->state = (stream_state *) st;
1191
0
            es->procs.process = templat->process;
1192
0
            es->strm = s;
1193
0
            (*templat->set_defaults) ((stream_state *) st);
1194
0
            code = (*templat->init) ((stream_state *) st);
1195
0
            if (code < 0) {
1196
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1197
0
                return code;
1198
0
            }
1199
0
            pdev->strm = s = es;
1200
0
        }
1201
26.0k
    }
1202
    /*
1203
     * Scale the coordinate system.  Use an extra level of q/Q for the
1204
     * sake of poorly designed PDF tools that assume that the contents
1205
     * stream restores the CTM.
1206
     */
1207
58.4k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
58.4k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
58.4k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
26.0k
        if (pdev->params.DefaultRenderingIntent != ri_Default) {
1212
0
            static const char *const ri_names[] = { psdf_ri_names };
1213
1214
0
            pprints1(s, "/%s ri\n",
1215
0
                     ri_names[(int)pdev->params.DefaultRenderingIntent]);
1216
0
        }
1217
26.0k
    }
1218
58.4k
    pdev->AR4_save_bug = false;
1219
58.4k
    return PDF_IN_STREAM;
1220
58.4k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
210k
{
1225
210k
    int code;
1226
1227
    /* This is to handle clipped text rendering modes. If we have started
1228
     * a text clipping operation (signalled via a spec_op) then we do
1229
     * *NOT* want to save the viewer state as that will write a q/Q round the text
1230
     * which will discard the clip.
1231
     */
1232
210k
    if (!pdev->clipped_text_pending) {
1233
210k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
210k
        if (code < 0)
1235
0
            return 0;
1236
210k
    }
1237
    /*
1238
     * Bizarrely enough, Acrobat Reader cares how the final font size is
1239
     * obtained -- the CTM (cm), text matrix (Tm), and font size (Tf)
1240
     * are *not* all equivalent.  In particular, it seems to use the
1241
     * product of the text matrix and font size to decide how to
1242
     * anti-alias characters.  Therefore, we have to temporarily patch
1243
     * the CTM so that the scale factors are unity.  What a nuisance!
1244
     */
1245
210k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
210k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
210k
    pdev->procsets |= Text;
1248
210k
    code = pdf_from_stream_to_text(pdev);
1249
210k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
210k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
210k
{
1255
210k
    int code = pdf_from_string_to_text(pdev);
1256
1257
210k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
210k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
210k
{
1263
210k
    int code;
1264
1265
210k
    stream_puts(pdev->strm, "ET\n");
1266
    /* This is to handle clipped text rendering modes. If we are in
1267
     * a text clipping operation (signalledd via a spec_op) then we do
1268
     * *NOT* want to restore the viewer state as that will write a q/Q round the text
1269
     * which will discard the clip. However, we *do* have to undo the 'cm'
1270
     * operation which is done for Acrobat (see stream_to_text above).
1271
     */
1272
210k
    if (pdev->clipped_text_pending)
1273
17
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
17
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
210k
    else {
1276
210k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
210k
        if (code < 0)
1278
0
            return code;
1279
210k
        pdf_reset_text(pdev); /* because of Q */
1280
210k
    }
1281
210k
    return PDF_IN_STREAM;
1282
210k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
58.4k
{
1287
58.4k
    stream *s = pdev->strm;
1288
58.4k
    gs_offset_t length;
1289
58.4k
    int code;
1290
58.4k
    stream *target;
1291
58.4k
     char str[21];
1292
1293
58.4k
    if (pdev->ResourcesBeforeUsage) {
1294
32.4k
        int code = pdf_exit_substream(pdev);
1295
1296
32.4k
        if (code < 0)
1297
0
            return code;
1298
32.4k
    } else {
1299
26.0k
        if (pdev->vgstack_depth) {
1300
157
            code = pdf_restore_viewer_state(pdev, s);
1301
157
            if (code < 0)
1302
11
                return code;
1303
157
        }
1304
26.0k
        target = pdev->strm;
1305
1306
26.0k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
26.0k
            target = target->strm;
1308
26.0k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
26.0k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
26.0k
        s_close_filters(&pdev->strm, target);
1313
1314
26.0k
        s = pdev->strm;
1315
26.0k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
26.0k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
26.0k
        stream_puts(s, "endstream\n");
1319
26.0k
        pdf_end_obj(pdev, resourceStream);
1320
1321
26.0k
        if (pdev->WriteObjStms) {
1322
25.9k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
25.9k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
25.9k
            stream_puts(pdev->strm, str);
1325
25.9k
            pdf_end_separate(pdev, resourceLength);
1326
25.9k
        } else {
1327
10
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
10
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
10
            stream_puts(s, str);
1330
10
            pdf_end_obj(pdev, resourceLength);
1331
10
        }
1332
26.0k
    }
1333
58.4k
    return PDF_IN_NONE;
1334
58.4k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
12.2M
{
1340
12.2M
    int (*proc) (gx_device_pdf *);
1341
1342
13.0M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
749k
        int code = (*proc) (pdev);
1344
1345
749k
        if (code < 0)
1346
20
            return code;
1347
749k
        pdev->context = (pdf_context_t) code;
1348
749k
    }
1349
12.2M
    pdev->context = context;
1350
12.2M
    return 0;
1351
12.2M
}
1352
1353
/* Close the current contents part if we are in one. */
1354
int
1355
pdf_close_contents(gx_device_pdf * pdev, bool last)
1356
58.4k
{
1357
58.4k
    if (pdev->context == PDF_IN_NONE)
1358
7
        return 0;
1359
58.4k
    if (last) {     /* Exit from the clipping path gsave. */
1360
58.4k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
58.4k
        if (code < 0)
1363
0
            return code;
1364
58.4k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
58.4k
        pdf_close_text_contents(pdev);
1366
58.4k
    }
1367
58.4k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
58.4k
}
1369
1370
/* ------ Resources et al ------ */
1371
1372
/* Define the allocator descriptors for the resource types. */
1373
const char *const pdf_resource_type_names[] = {
1374
    PDF_RESOURCE_TYPE_NAMES
1375
};
1376
const gs_memory_struct_type_t *const pdf_resource_type_structs[] = {
1377
    PDF_RESOURCE_TYPE_STRUCTS
1378
};
1379
1380
/* Cancel a resource (do not write it into PDF). */
1381
int
1382
pdf_cancel_resource(gx_device_pdf * pdev, pdf_resource_t *pres, pdf_resource_type_t rtype)
1383
181k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
181k
    pres->where_used = 0;
1386
181k
    if (pres->object) {
1387
181k
        pres->object->written = true;
1388
181k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
120k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
120k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
120k
            if (code < 0)
1393
0
                return code;
1394
120k
        }
1395
181k
        cos_release(pres->object, "pdf_cancel_resource");
1396
181k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
181k
        pres->object = 0;
1398
181k
    }
1399
181k
    return 0;
1400
181k
}
1401
1402
/* Remove a resource. */
1403
void
1404
pdf_forget_resource(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1405
180k
{   /* fixme : optimize. */
1406
180k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
180k
    pdf_resource_t *pres;
1408
180k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
180k
    int i;
1410
1411
    /* since we're about to free the resource, we can just set
1412
       any of these references to null
1413
    */
1414
2.16M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
1.98M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
1.98M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
1.98M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
4
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
4
        }
1424
1.98M
    }
1425
1426
182k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
182k
        if (pres == pres1) {
1428
180k
            *pprev = pres->prev;
1429
180k
            break;
1430
180k
        }
1431
1432
180k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
180k
        pprev = pchain + i;
1434
182k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
182k
            if (pres == pres1) {
1436
180k
                *pprev = pres->next;
1437
180k
                if (pres->object) {
1438
0
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1439
0
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1440
0
                    pres->object = 0;
1441
0
                }
1442
180k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
180k
                return;
1444
180k
            }
1445
180k
    }
1446
180k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
43.9k
{
1451
43.9k
    return 1;
1452
43.9k
}
1453
1454
/* Substitute a resource with a same one. */
1455
/* NB we cannot substitute resources which have already had an
1456
   id assigned to them, because they already have an entry in the
1457
   xref table. If we want to substiute a resource then it should
1458
   have been allocated with an initial id of -1.
1459
   (see pdf_alloc_resource)
1460
*/
1461
int
1462
pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
1463
        pdf_resource_type_t rtype,
1464
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
1465
        bool write)
1466
102k
{
1467
102k
    pdf_resource_t *pres1 = *ppres;
1468
102k
    int code;
1469
1470
102k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
102k
    if (code < 0)
1472
0
        return code;
1473
102k
    if (code != 0) {
1474
65.4k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
65.4k
        if (code < 0)
1476
0
            return code;
1477
65.4k
        pdf_forget_resource(pdev, pres1, rtype);
1478
65.4k
        return 0;
1479
65.4k
    } else {
1480
37.2k
        if (pres1->object->id < 0)
1481
37.2k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
37.2k
        if (write) {
1483
21.7k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
21.7k
            if (code < 0)
1485
0
                return code;
1486
21.7k
            pres1->object->written = 1;
1487
21.7k
        }
1488
37.2k
        return 1;
1489
37.2k
    }
1490
102k
}
1491
1492
/* Find a resource of a given type by gs_id. */
1493
pdf_resource_t *
1494
pdf_find_resource_by_gs_id(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1495
                           gs_id rid)
1496
958k
{
1497
958k
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
958k
    pdf_resource_t **pprev = pchain;
1499
958k
    pdf_resource_t *pres;
1500
1501
2.18M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
2.07M
        if (pres->rid == rid) {
1503
846k
            if (pprev != pchain) {
1504
302k
                *pprev = pres->next;
1505
302k
                pres->next = *pchain;
1506
302k
                *pchain = pres;
1507
302k
            }
1508
846k
            return pres;
1509
846k
        }
1510
111k
    return 0;
1511
958k
}
1512
1513
/* Find resource by resource id. */
1514
pdf_resource_t *
1515
pdf_find_resource_by_resource_id(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id id)
1516
25.8k
{
1517
25.8k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
25.8k
    pdf_resource_t *pres;
1519
25.8k
    int i;
1520
1521
426k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
1.92M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
1.52M
            if (pres->object && pres->object->id == id)
1524
1.66k
                return pres;
1525
1.52M
        }
1526
402k
    }
1527
24.2k
    return 0;
1528
25.8k
}
1529
1530
/* Find same resource. */
1531
int
1532
pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
1533
        int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
1534
131k
{
1535
131k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
131k
    pdf_resource_t *pres;
1537
131k
    cos_object_t *pco0 = (*ppres)->object;
1538
131k
    int i;
1539
1540
1.16M
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
2.13M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
1.09M
            if (*ppres != pres) {
1543
967k
                int code;
1544
967k
                cos_object_t *pco1 = pres->object;
1545
1546
967k
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
13.4k
                    continue;      /* don't compare different types */
1548
953k
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
953k
                if (code < 0)
1550
0
                    return code;
1551
953k
                if (code > 0) {
1552
67.1k
                    code = eq(pdev, *ppres, pres);
1553
67.1k
                    if (code < 0)
1554
0
                        return code;
1555
67.1k
                    if (code > 0) {
1556
67.1k
                        *ppres = pres;
1557
67.1k
                        return 1;
1558
67.1k
                    }
1559
67.1k
                }
1560
953k
            }
1561
1.09M
        }
1562
1.10M
    }
1563
64.2k
    return 0;
1564
131k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
13.5k
{
1569
13.5k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
13.5k
    pdf_resource_t *pres;
1571
13.5k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
13.5k
    int i;
1573
1574
    /* since we're about to free the resource, we can just set
1575
       any of these references to null
1576
    */
1577
163k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
149k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
149k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
149k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
149k
    }
1588
1589
16.4k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
16.4k
        if (pres == pres1) {
1591
13.5k
            *pprev = pres->prev;
1592
13.5k
            break;
1593
13.5k
        }
1594
1595
13.5k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
13.5k
        pprev = pchain + i;
1597
13.9k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
13.9k
            if (pres == pres1) {
1599
13.5k
                *pprev = pres->next;
1600
#if 0
1601
                if (pres->object) {
1602
                    COS_RELEASE(pres->object, "pdf_forget_resource");
1603
                    gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
1604
                    pres->object = 0;
1605
                }
1606
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1607
#endif
1608
13.5k
                return;
1609
13.5k
            }
1610
13.5k
    }
1611
13.5k
}
1612
1613
/* Drop resources by a condition. */
1614
void
1615
pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1616
        int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
1617
0
{
1618
0
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1619
0
    pdf_resource_t **pprev;
1620
0
    pdf_resource_t *pres;
1621
0
    int i;
1622
1623
0
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1624
0
        pprev = pchain + i;
1625
0
        for (; (pres = *pprev) != 0; ) {
1626
0
            if (cond(pdev, pres)) {
1627
0
                *pprev = pres->next;
1628
0
                pres->next = pres; /* A temporary mark - see below */
1629
0
            } else
1630
0
                pprev = &pres->next;
1631
0
        }
1632
0
    }
1633
0
    pprev = &pdev->last_resource;
1634
0
    for (; (pres = *pprev) != 0; )
1635
0
        if (pres->next == pres) {
1636
0
            *pprev = pres->prev;
1637
0
            if (pres->object) {
1638
0
                COS_RELEASE(pres->object, "pdf_drop_resources");
1639
0
                gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
1640
0
                pres->object = 0;
1641
0
            }
1642
0
            gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
1643
0
        } else
1644
0
            pprev = &pres->prev;
1645
0
}
1646
1647
/* Print resource statistics. */
1648
void
1649
pdf_print_resource_statistics(gx_device_pdf * pdev)
1650
0
{
1651
1652
0
    int rtype;
1653
1654
0
    for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
1655
0
        pdf_resource_t **pchain = pdev->resources[rtype].chains;
1656
0
        pdf_resource_t *pres;
1657
0
        const char *name = pdf_resource_type_names[rtype];
1658
0
        int i, n = 0;
1659
1660
0
        for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1661
0
            for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
1662
0
        }
1663
0
        dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
1664
0
                (name ? name : ""), n);
1665
0
    }
1666
0
}
1667
1668
int FlushObjStm(gx_device_pdf *pdev)
1669
20.1k
{
1670
20.1k
    int code = 0, i, len = 0, id = 0, end;
1671
20.1k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
20.1k
    pdf_resource_t *pres;
1673
20.1k
    int options = DATA_STREAM_BINARY;
1674
1675
20.1k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
20.1k
    pdev->WriteObjStms = false;
1679
1680
20.1k
    sflush(pdev->strm);
1681
20.1k
    sflush(pdev->ObjStm.strm);
1682
20.1k
    end = stell(pdev->ObjStm.strm);
1683
1684
20.1k
    if (pdev->CompressStreams)
1685
20.1k
        options |= DATA_STREAM_COMPRESS;
1686
1687
20.1k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
20.1k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
20.1k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
20.1k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
20.1k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
20.1k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
20.1k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
20.1k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
206k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
185k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
185k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
185k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
185k
        strcat(offsets, offset);
1713
185k
    }
1714
1715
20.1k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
20.1k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
20.1k
    stream_puts(pdev->strm, offsets);
1723
1724
20.1k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
20.1k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
20.1k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
20.1k
    code = pdf_close_aside(pdev);
1732
20.1k
    if (code < 0)
1733
0
        return code;
1734
20.1k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
20.1k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
20.1k
    pdev->WriteObjStms = true;
1740
20.1k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
20.1k
    if (pdev->ObjStmOffsets != NULL) {
1742
20.1k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
20.1k
        pdev->ObjStmOffsets = NULL;
1744
20.1k
    }
1745
20.1k
    pdev->NumObjStmObjects = 0;
1746
20.1k
    pdev->ObjStm_id = 0;
1747
1748
20.1k
    pdev->WriteObjStms = true;
1749
20.1k
    return code;
1750
20.1k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
20.1k
{
1754
20.1k
    int code;
1755
1756
20.1k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
20.1k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
20.1k
    if (code < 0)
1760
0
        return code;
1761
1762
20.1k
    pdev->NumObjStmObjects = 0;
1763
20.1k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
20.1k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
20.1k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
20.1k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
20.1k
    return code;
1772
20.1k
}
1773
1774
/* Begin an object logically separate from the contents. */
1775
int64_t
1776
pdf_open_separate_noObjStm(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1777
110k
{
1778
110k
    int code;
1779
1780
110k
    code = pdfwrite_pdf_open_document(pdev);
1781
110k
    if (code < 0)
1782
0
        return code;
1783
110k
    pdev->asides.save_strm = pdev->strm;
1784
110k
    pdev->strm = pdev->asides.strm;
1785
110k
    code = pdf_open_obj(pdev, id, type);
1786
110k
    return code;
1787
110k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
382k
{
1791
382k
    if (type == resourceStream)
1792
0
        return true;
1793
382k
    if (type == resourceCharProc)
1794
392
        return true;
1795
382k
    if (type == resourcePattern)
1796
6.69k
        return true;
1797
375k
    if (type == resourceXObject)
1798
3.75k
        return true;
1799
371k
    return false;
1800
375k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
368k
{
1805
368k
    int code;
1806
1807
368k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
182k
        code = pdfwrite_pdf_open_document(pdev);
1809
182k
        if (code < 0)
1810
0
            return code;
1811
182k
        pdev->asides.save_strm = pdev->strm;
1812
182k
        pdev->strm = pdev->asides.strm;
1813
182k
        code = pdf_open_obj(pdev, id, type);
1814
185k
    } else {
1815
185k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
129
            code = FlushObjStm(pdev);
1817
129
            if (code < 0)
1818
0
                return code;
1819
129
        }
1820
185k
        if (!pdev->ObjStm.strm) {
1821
20.1k
            code = NewObjStm(pdev);
1822
20.1k
            if (code < 0)
1823
0
                return code;
1824
20.1k
        }
1825
185k
        pdev->ObjStm.save_strm = pdev->strm;
1826
185k
        pdev->strm = pdev->ObjStm.strm;
1827
185k
        code = pdf_open_obj(pdev, id, type);
1828
185k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
185k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
185k
    }
1831
368k
    return code;
1832
368k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
133k
{
1836
133k
    return pdf_open_separate(pdev, 0L, type);
1837
133k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
331k
{
1842
331k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
331k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
331k
}
1845
1846
/* Begin an aside (resource, annotation, ...). */
1847
int
1848
pdf_alloc_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1849
                const gs_memory_struct_type_t * pst, pdf_resource_t **ppres,
1850
                int64_t id)
1851
525k
{
1852
525k
    pdf_resource_t *pres;
1853
525k
    cos_object_t *object;
1854
1855
525k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
525k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
525k
                           "pdf_alloc_aside(resource)");
1859
525k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
525k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
525k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
525k
    memset(pres, 0, pst->ssize);
1865
525k
    pres->object = object;
1866
525k
    if (id < 0) {
1867
343k
        object->id = -1L;
1868
343k
        pres->rname[0] = 0;
1869
343k
    } else
1870
181k
        pdf_reserve_object_id(pdev, pres, id);
1871
525k
    pres->next = *plist;
1872
525k
    pres->rid = 0;
1873
525k
    *plist = pres;
1874
525k
    pres->prev = pdev->last_resource;
1875
525k
    pdev->last_resource = pres;
1876
525k
    pres->named = false;
1877
525k
    pres->global = false;
1878
525k
    pres->where_used = pdev->used_mask;
1879
525k
    *ppres = pres;
1880
525k
    return 0;
1881
525k
}
1882
int64_t
1883
pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
1884
                const gs_memory_struct_type_t * pst, pdf_resource_t ** ppres,
1885
                pdf_resource_type_t type)
1886
84.1k
{
1887
84.1k
    int64_t id = pdf_begin_separate(pdev, type);
1888
84.1k
    int code = 0;
1889
1890
84.1k
    if (id < 0)
1891
0
        return (int)id;
1892
84.1k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
84.1k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
84.1k
    return code;
1897
84.1k
}
1898
1899
/* Begin a resource of a given type. */
1900
int
1901
pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
1902
                        gs_id rid, pdf_resource_t ** ppres)
1903
84.1k
{
1904
84.1k
    int code;
1905
1906
84.1k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
84.1k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
84.1k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
84.1k
    if (code >= 0)
1913
84.1k
        (*ppres)->rid = rid;
1914
84.1k
    return code;
1915
84.1k
}
1916
int
1917
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1918
                   pdf_resource_t ** ppres)
1919
83.5k
{
1920
83.5k
    int code;
1921
1922
83.5k
    if (rtype >= NUM_RESOURCE_TYPES)
1923
0
        rtype = resourceOther;
1924
1925
83.5k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1926
1927
83.5k
    if (code >= 0 && pdf_resource_type_names[rtype] != 0) {
1928
0
        stream *s = pdev->strm;
1929
1930
0
        pprints1(s, "<</Type%s", pdf_resource_type_names[rtype]);
1931
0
        pprinti64d1(s, "/Name/R%"PRId64, (*ppres)->object->id);
1932
0
    }
1933
83.5k
    return code;
1934
83.5k
}
1935
1936
/* Allocate a resource, but don't open the stream. */
1937
/* If the passed in id 'id' is -1 then in pdf_alloc_aside
1938
   We *don't* reserve an object id (if its 0 or more we do).
1939
   This has important consequences; once an id is created we
1940
   can't 'cancel' it, it will always be written to the xref.
1941
   So if we want to not write duplicates we should create
1942
   the object with an 'id' of -1, and when we finish writing it
1943
   we should call 'pdf_substitute_resource'. If that finds a
1944
   duplicate then it will throw away the new one ands use the old.
1945
   If it doesn't find a duplicate then it will create an object
1946
   id for the new resource.
1947
*/
1948
int
1949
pdf_alloc_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1950
                   pdf_resource_t ** ppres, int64_t id)
1951
191k
{
1952
191k
    int code;
1953
1954
191k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
191k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
191k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
191k
    if (code >= 0)
1961
191k
        (*ppres)->rid = rid;
1962
191k
    return code;
1963
191k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
1.84M
{
1969
1.84M
    return pres->object->id;
1970
1.84M
}
1971
1972
/* End an aside or other separate object. */
1973
int
1974
pdf_end_separate_noObjStm(gx_device_pdf * pdev, pdf_resource_type_t type)
1975
110k
{
1976
110k
    int code = pdf_end_obj(pdev, type);
1977
1978
110k
    pdev->strm = pdev->asides.save_strm;
1979
110k
    pdev->asides.save_strm = 0;
1980
110k
    return code;
1981
110k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
368k
{
1985
368k
    int code = pdf_end_obj(pdev, type);
1986
1987
368k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
182k
        pdev->strm = pdev->asides.save_strm;
1989
182k
        pdev->asides.save_strm = 0;
1990
185k
    } else {
1991
185k
        pdev->strm = pdev->ObjStm.save_strm;
1992
185k
        pdev->ObjStm.save_strm = 0;
1993
185k
        pdev->NumObjStmObjects++;
1994
185k
    }
1995
368k
    return code;
1996
368k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
545
{
2000
545
    return pdf_end_separate(pdev, type);
2001
545
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
545
{
2007
545
    return pdf_end_aside(pdev, type);
2008
545
}
2009
2010
/*
2011
 * Write the Cos objects for resources local to a content stream.  Formerly,
2012
 * this procedure also freed such objects, but this doesn't work, because
2013
 * resources of one type might refer to resources of another type.
2014
 */
2015
int
2016
pdf_write_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2017
598k
{
2018
598k
    int j, code = 0;
2019
2020
10.1M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
9.56M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
9.75M
        for (; pres != 0; pres = pres->next)
2024
184k
            if ((!pres->named || pdev->ForOPDFRead)
2025
184k
                && pres->object && !pres->object->written) {
2026
30.3k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
30.3k
            }
2028
9.56M
    }
2029
598k
    return code;
2030
598k
}
2031
2032
/*
2033
 * Reverse resource chains.
2034
 * ps2write uses it with page resources.
2035
 * Assuming only the 0th chain contauns something.
2036
 */
2037
void
2038
pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2039
25.4k
{
2040
25.4k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
25.4k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
25.4k
    if (pres == NULL)
2044
0
        return;
2045
25.4k
    pres1 = pres->next;
2046
32.4k
    for (;;) {
2047
32.4k
        if (pres1 == NULL)
2048
25.4k
            break;
2049
6.97k
        pres2 = pres1->next;
2050
6.97k
        pres1->next = pres;
2051
6.97k
        pres = pres1;
2052
6.97k
        pres1 = pres2;
2053
6.97k
    }
2054
25.4k
    pres0->next = NULL;
2055
25.4k
    pdev->resources[rtype].chains[0] = pres;
2056
25.4k
}
2057
2058
/*
2059
 * Free unnamed Cos objects for resources local to a content stream,
2060
 * since they can't be used again.
2061
 */
2062
int
2063
pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
2064
318k
{
2065
318k
    int j;
2066
2067
5.40M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
5.09M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
5.09M
        pdf_resource_t *pres;
2070
2071
5.21M
        while ((pres = *prev) != 0) {
2072
128k
            if (pres->named) { /* named, don't free */
2073
7
                prev = &pres->next;
2074
128k
            } else {
2075
128k
                if (pres->object) {
2076
128k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
128k
                    pres->object = 0;
2078
128k
                }
2079
128k
                *prev = pres->next;
2080
128k
            }
2081
128k
        }
2082
5.09M
    }
2083
318k
    return 0;
2084
318k
}
2085
2086
/*
2087
 * Store the resource sets for a content stream (page or XObject).
2088
 * Sets page->{procsets, resource_ids[]}.
2089
 */
2090
int
2091
pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage)
2092
58.4k
{
2093
58.4k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
526k
    for (i = 0; i <= resourceFont; ++i) {
2098
467k
        stream *s = 0;
2099
467k
        int j;
2100
2101
467k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
58.4k
            continue;
2103
409k
        page->resource_ids[i] = 0;
2104
6.95M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
6.54M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
6.70M
            for (; pres != 0; pres = pres->next) {
2108
162k
                if (pres->where_used & pdev->used_mask) {
2109
109k
                    int64_t id = pdf_resource_id(pres);
2110
2111
109k
                    if (id == -1L)
2112
8.03k
                        continue;
2113
101k
                    if (s == 0) {
2114
35.4k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
35.4k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
35.4k
                        s = pdev->strm;
2117
35.4k
                        stream_puts(s, "<<");
2118
35.4k
                    }
2119
101k
                    pprints1(s, "/%s\n", pres->rname);
2120
101k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
101k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
101k
                    if (clear_usage)
2123
101k
                        pres->where_used -= pdev->used_mask;
2124
101k
                }
2125
162k
            }
2126
6.54M
        }
2127
409k
        if (s) {
2128
35.4k
            stream_puts(s, ">>\n");
2129
35.4k
            pdf_end_separate(pdev, i);
2130
35.4k
        }
2131
        /* If an object isn't used, we still need to emit it :-( This is because
2132
         * we reserved an object number for it, and the xref will have an entry
2133
         * for it. If we don't actually emit it then the xref will be invalid.
2134
         * An alternative would be to modify the xref to mark the object as unused.
2135
         */
2136
409k
        if (i != resourceFont && i != resourceProperties)
2137
292k
            pdf_write_resource_objects(pdev, i);
2138
409k
    }
2139
58.4k
    page->procsets = pdev->procsets;
2140
58.4k
    return 0;
2141
58.4k
}
2142
2143
/* Copy data from a temporary file to a stream. */
2144
int
2145
pdf_copy_data(stream *s, gp_file *file, gs_offset_t count, stream_arcfour_state *ss)
2146
291k
{
2147
291k
    gs_offset_t r, left = count;
2148
291k
    byte buf[sbuf_size];
2149
2150
2.74M
    while (left > 0) {
2151
2.45M
        uint copy = min(left, sbuf_size);
2152
2153
2.45M
        r = gp_fread(buf, 1, copy, file);
2154
2.45M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
2.45M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
2.45M
        stream_write(s, buf, copy);
2160
2.45M
        left -= copy;
2161
2.45M
    }
2162
291k
    return 0;
2163
291k
}
2164
2165
/* Copy data from a temporary file to a stream,
2166
   which may be targetted to the same file. */
2167
int
2168
pdf_copy_data_safe(stream *s, gp_file *file, gs_offset_t position, int64_t count)
2169
137k
{
2170
137k
    int64_t r, left = count;
2171
2172
722k
    while (left > 0) {
2173
585k
        byte buf[sbuf_size];
2174
585k
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
585k
        int64_t end_pos = gp_ftell(file);
2176
2177
585k
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
585k
        r = gp_fread(buf, 1, copy, file);
2181
585k
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
585k
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
585k
        stream_write(s, buf, copy);
2188
585k
        sflush(s);
2189
585k
        left -= copy;
2190
585k
    }
2191
137k
    return 0;
2192
137k
}
2193
2194
/* ------ Pages ------ */
2195
2196
/* Get or assign the ID for a page. */
2197
/* Returns 0 if the page number is out of range. */
2198
int64_t
2199
pdf_page_id(gx_device_pdf * pdev, int page_num)
2200
234k
{
2201
234k
    cos_dict_t *Page;
2202
2203
234k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
234k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
56
        uint new_num_pages;
2207
56
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
56
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
56
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
56
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
56
                             "pdf_page_id(resize pages)");
2216
2217
56
        if (new_pages == 0)
2218
0
            return 0;
2219
56
        memset(&new_pages[pdev->num_pages], 0,
2220
56
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
56
        pdev->pages = new_pages;
2222
56
        pdev->num_pages = new_num_pages;
2223
56
    }
2224
234k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
58.4k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
58.4k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
58.4k
        Page->id = pdf_obj_forward_ref(pdev);
2230
58.4k
    }
2231
234k
    return Page->id;
2232
234k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
3.57M
{
2238
3.57M
    return &pdev->pages[pdev->next_page];
2239
3.57M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
11.6k
{
2245
11.6k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
11.6k
    return pdev->pages[pdev->next_page].Page;
2248
11.6k
}
2249
2250
/* Write saved page- or document-level information. */
2251
int
2252
pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr)
2253
0
{
2254
0
    if (pstr->data != 0) {
2255
0
        stream_write(pdev->strm, pstr->data, pstr->size);
2256
0
        gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
2257
0
                       "pdf_write_saved_string");
2258
0
        pstr->data = 0;
2259
0
    }
2260
0
    return 0;
2261
0
}
2262
2263
/* Open a page for writing. */
2264
int
2265
pdf_open_page(gx_device_pdf * pdev, pdf_context_t context)
2266
11.8M
{
2267
11.8M
    if (!is_in_page(pdev)) {
2268
57.5k
        int code;
2269
2270
57.5k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
57.5k
        code = pdfwrite_pdf_open_document(pdev);
2273
57.5k
        if (code < 0)
2274
0
            return code;
2275
57.5k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
11.8M
    return pdf_open_contents(pdev, context);
2278
11.8M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
128k
{
2284
128k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
128k
    if (pdev->sbstack_depth <= bottom) {
2289
112k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
112k
        if (code < 0)
2292
0
            return code;
2293
112k
    }
2294
128k
    if (pdev->context > PDF_IN_STREAM) {
2295
2
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2296
2297
2
        if (code < 0)
2298
0
            return code;
2299
2
    }
2300
128k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
84.7k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
84.7k
        if (code < 0)
2304
0
            return code;
2305
84.7k
        code = pdf_remember_clip_path(pdev, NULL);
2306
84.7k
        if (code < 0)
2307
0
            return code;
2308
84.7k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
84.7k
    }
2310
128k
    return 0;
2311
128k
}
2312
2313
/* ------ Miscellaneous output ------ */
2314
2315
/* Generate the default Producer string. */
2316
/* This calculation is also performed for Ghostscript generally
2317
 * The code is in ghostpdl/base/gsmisc.c printf_program_ident().
2318
 * Should we change this calculation both sets of code need to be updated.
2319
 */
2320
void
2321
pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER])
2322
45.4k
{
2323
45.4k
    int major = (int)(gs_revision / 1000);
2324
45.4k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
45.4k
    int patch = gs_revision % 10;
2326
2327
45.4k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
45.4k
}
2329
2330
/* Write matrix values. */
2331
void
2332
pdf_put_matrix(gx_device_pdf * pdev, const char *before,
2333
               const gs_matrix * pmat, const char *after)
2334
153k
{
2335
153k
    stream *s = pdev->strm;
2336
2337
153k
    if (before)
2338
151k
        stream_puts(s, before);
2339
153k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
153k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
153k
    if (after)
2342
153k
        stream_puts(s, after);
2343
153k
}
2344
2345
/*
2346
 * Write a name, with escapes for unusual characters.  Since we only support
2347
 * PDF 1.2 and above, we can use an escape sequence for anything except a
2348
 * null <00>, and the machinery for selecting the put_name_chars procedure
2349
 * depending on CompatibilityLevel is no longer needed.
2350
 */
2351
static int
2352
pdf_put_name_chars_1_2(stream *s, const byte *nstr, uint size)
2353
3.58M
{
2354
3.58M
    uint i;
2355
2356
18.9M
    for (i = 0; i < size; ++i) {
2357
15.3M
        uint c = nstr[i];
2358
15.3M
        char hex[4];
2359
2360
15.3M
        switch (c) {
2361
15.3M
            default:
2362
15.3M
                if (c >= 0x21 && c <= 0x7e) {
2363
15.3M
                    stream_putc(s, (byte)c);
2364
15.3M
                    break;
2365
15.3M
                }
2366
                /* falls through */
2367
4.96k
            case '#':
2368
4.98k
            case '%':
2369
5.00k
            case '(': case ')':
2370
5.00k
            case '<': case '>':
2371
5.02k
            case '[': case ']':
2372
5.04k
            case '{': case '}':
2373
5.06k
            case '/':
2374
5.06k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
5.06k
                stream_puts(s, hex);
2376
5.06k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
15.3M
        }
2380
15.3M
    }
2381
3.58M
    return 0;
2382
3.58M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
3.58M
{
2386
3.58M
    return &pdf_put_name_chars_1_2;
2387
3.58M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
3.58M
{
2391
3.58M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
3.58M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
3.58M
{
2396
3.58M
    stream_putc(pdev->strm, '/');
2397
3.58M
    return pdf_put_name_chars(pdev, nstr, size);
2398
3.58M
}
2399
2400
/* Write an encoded string with encryption. */
2401
static int
2402
pdf_encrypt_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2403
0
{
2404
0
    stream sinp, sstr, sout;
2405
0
    stream_PSSD_state st;
2406
0
    stream_state so;
2407
0
    byte buf[100], bufo[100];
2408
0
    stream_arcfour_state sarc4;
2409
2410
0
    if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
2411
        /* The interface can't pass an error. */
2412
0
        stream_write(pdev->strm, str, size);
2413
0
        return size;
2414
0
    }
2415
0
    s_init(&sinp, NULL);
2416
0
    sread_string(&sinp, str + 1, size);
2417
0
    s_init(&sstr, NULL);
2418
0
    sstr.close_at_eod = false;
2419
0
    s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
2420
0
    s_init_filter(&sstr, (stream_state *)&st, buf, sizeof(buf), &sinp);
2421
0
    s_init(&sout, NULL);
2422
0
    s_init_state(&so, &s_PSSE_template, NULL);
2423
0
    s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
2424
0
    stream_putc(pdev->strm, '(');
2425
0
    for (;;) {
2426
0
        uint n;
2427
0
        int code = sgets(&sstr, buf, sizeof(buf), &n);
2428
2429
0
        if (n > 0) {
2430
0
            s_arcfour_process_buffer(&sarc4, buf, n);
2431
0
            stream_write(&sout, buf, n);
2432
0
        }
2433
0
        if (code == EOFC)
2434
0
            break;
2435
0
        if (code < 0 || n < sizeof(buf)) {
2436
            /* The interface can't pass an error. */
2437
0
            break;
2438
0
        }
2439
0
    }
2440
    /* Another case where we use sclose() and not sclose_filters(), because the
2441
     * buffer we supplied to s_init_filter is a heap based C object, so we
2442
     * must not free it.
2443
     */
2444
0
    sclose(&sout); /* Writes ')'. */
2445
0
    return (int)stell(&sinp) + 1;
2446
0
}
2447
2448
/* Write an encoded string with possible encryption. */
2449
static int
2450
pdf_put_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2451
191k
{
2452
191k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2453
191k
        stream_write(pdev->strm, str, size);
2454
191k
        return 0;
2455
191k
    } else
2456
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2457
191k
}
2458
/* Write an encoded string with possible encryption. */
2459
static int
2460
pdf_put_encoded_string_as_hex(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2461
76.7k
{
2462
76.7k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
76.7k
        int i, oct, width = 0;
2464
76.7k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
76.7k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
76.7k
            stream_write(pdev->strm, "\n", 1);
2468
76.7k
        stream_write(pdev->strm, "<", 1);
2469
76.7k
        width++;
2470
2.39M
        for (i = 1; i < size - 1; i++) {
2471
2.31M
            if (str[i] == '\\') {
2472
145k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2473
144k
                    oct = (str[i+1] - 0x30) * 64;
2474
144k
                    oct += (str[i+2] - 0x30) *8;
2475
144k
                    oct += str[i+3] - 0x30;
2476
144k
                    i+=3;
2477
144k
                } else {
2478
823
                    switch (str[++i]) {
2479
8
                        case 'b' :
2480
8
                            oct = 8;
2481
8
                            break;
2482
669
                        case 't' :
2483
669
                            oct = 9;
2484
669
                            break;
2485
40
                        case 'n' :
2486
40
                            oct = 10;
2487
40
                            break;
2488
35
                        case 'f' :
2489
35
                            oct = 12;
2490
35
                            break;
2491
10
                        case 'r' :
2492
10
                            oct = 13;
2493
10
                            break;
2494
61
                        default:
2495
61
                            oct = str[i];
2496
61
                            break;
2497
823
                    }
2498
823
                }
2499
145k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2500
1.10k
                    stream_write(pdev->strm, "\n", 1);
2501
1.10k
                    width = 0;
2502
1.10k
                }
2503
145k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2504
145k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2505
145k
                width += 2;
2506
2.16M
            } else {
2507
2.16M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
238
                    stream_write(pdev->strm, "\n", 1);
2509
238
                    width = 0;
2510
238
                }
2511
2.16M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
2.16M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
2.16M
                width += 2;
2514
2.16M
            }
2515
2.31M
        }
2516
76.7k
        stream_write(pdev->strm, ">", 1);
2517
76.7k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
76.7k
            stream_write(pdev->strm, "\n", 1);
2519
76.7k
        return 0;
2520
76.7k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
76.7k
}
2523
2524
/* Write an encoded hexadecimal string with possible encryption. */
2525
static int
2526
pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
2527
0
{
2528
0
    emprintf(pdev->memory,
2529
0
             "Unimplemented function : pdf_put_encoded_hex_string\n");
2530
0
    stream_write(pdev->strm, str, size);
2531
0
    return_error(gs_error_unregistered);
2532
0
}
2533
/*  Scan an item in a serialized array or dictionary.
2534
    This is a very simplified Postscript lexical scanner.
2535
    It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
2536
    We only need to select strings and encrypt them.
2537
    Other items are passed identically.
2538
    Note we don't reconstruct the nesting of arrays|dictionaries.
2539
*/
2540
static int
2541
pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
2542
0
{
2543
0
    const byte *q = p;
2544
0
    int n = l;
2545
2546
0
    if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2547
0
        return (l > 0 ? 1 : 0);
2548
0
    for (q++, n--; n; q++, n--) {
2549
0
        if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
2550
0
            return q - p;
2551
0
        if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
2552
0
            return q - p;
2553
        /* Note : immediate names are not allowed in PDF. */
2554
0
    }
2555
0
    return l;
2556
0
}
2557
2558
/* Write a serialized array or dictionary with possible encryption. */
2559
static int
2560
pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2561
130k
{
2562
130k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
130k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
692
            stream_putc(pdev->strm, (byte)'\n');
2565
692
            if (size > 255) {
2566
0
                const byte *start, *p, *end, *save;
2567
0
                int width = 0;
2568
2569
0
                end = vstr + size;
2570
0
                start = p = vstr;
2571
0
                while (p < end) {
2572
0
                    if(*p == '\r' || *p == '\n') {
2573
0
                        width = 0;
2574
0
                        p++;
2575
0
                        continue;
2576
0
                    }
2577
0
                    if (width > 254) {
2578
0
                        save = p;
2579
                        /* search backwards for a point to split */
2580
0
                        while (p > start) {
2581
0
                            if (*p == '/' || *p == '[' || *p == '{' || *p == '(' || *p == ' ') {
2582
0
                                stream_write(pdev->strm, start, p - start);
2583
0
                                stream_putc(pdev->strm, (byte)'\n');
2584
0
                                width = 0;
2585
0
                                start = p;
2586
0
                                break;
2587
0
                            }
2588
0
                            else p--;
2589
0
                        }
2590
0
                        if (p == start && width != 0) {
2591
0
                            stream_write(pdev->strm, start, save - start);
2592
0
                            stream_putc(pdev->strm, (byte)'\n');
2593
0
                            p = start = save;
2594
0
                            width = 0;
2595
0
                        }
2596
0
                    } else {
2597
0
                        width++;
2598
0
                        p++;
2599
0
                    }
2600
0
                }
2601
0
                if (width) {
2602
0
                    stream_write(pdev->strm, start, p - start);
2603
0
                    stream_putc(pdev->strm, (byte)'\n');
2604
0
                }
2605
692
            } else {
2606
692
                stream_write(pdev->strm, vstr, size);
2607
692
            }
2608
129k
        } else {
2609
129k
            stream_write(pdev->strm, vstr, size);
2610
129k
        }
2611
130k
    } else {
2612
0
        const byte *p = vstr;
2613
0
        int l = size, n;
2614
2615
0
        for (;l > 0 ;) {
2616
0
            if (*p == '(')
2617
0
                n = pdf_encrypt_encoded_string(pdev, p, l, object_id);
2618
0
            else {
2619
0
                n = pdf_scan_item(pdev, p, l, object_id);
2620
0
                stream_write(pdev->strm, p, n);
2621
0
            }
2622
0
            l -= n;
2623
0
            p += n;
2624
0
        }
2625
0
    }
2626
130k
    return 0;
2627
130k
}
2628
2629
/*
2630
 * Write a string in its shortest form ( () or <> ).  Note that
2631
 * this form is different depending on whether binary data are allowed.
2632
 * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
2633
 */
2634
int
2635
pdf_put_string(const gx_device_pdf * pdev, const byte * str, uint size)
2636
6.01M
{
2637
6.01M
    psdf_write_string(pdev->strm, str, size,
2638
6.01M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
6.01M
    return 0;
2640
6.01M
}
2641
2642
/* Write a value, treating names specially. */
2643
int
2644
pdf_write_value(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
2645
5.27M
{
2646
5.27M
    if (size > 0 && vstr[0] == '/')
2647
3.03M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
2.23M
    else if (size > 5 && vstr[0] == 0 && vstr[1] == 0 && vstr[2] == 0 && vstr[size - 1] == 0 && vstr[size - 2] == 0)
2649
0
        return pdf_put_name(pdev, vstr + 4, size - 5);
2650
2.23M
    else if (size > 3 && vstr[0] == 0 && vstr[1] == 0 && vstr[size - 1] == 0)
2651
0
        return pdf_put_name(pdev, vstr + 3, size - 4);
2652
2.23M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
107k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
2.12M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
22.7k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
2.10M
    else if (size > 1 && vstr[0] == '(') {
2657
267k
        if (pdev->ForOPDFRead)
2658
76.7k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2659
191k
        else
2660
191k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2661
267k
    }
2662
1.83M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
1.83M
    stream_write(pdev->strm, vstr, size);
2665
1.83M
    return 0;
2666
5.27M
}
2667
2668
/* Store filters for a stream. */
2669
/* Currently this only saves parameters for CCITTFaxDecode. */
2670
int
2671
pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
2672
                const pdf_filter_names_t *pfn)
2673
444k
{
2674
444k
    const char *filter_name = 0;
2675
444k
    bool binary_ok = true;
2676
444k
    stream *fs = s;
2677
444k
    cos_dict_t *decode_parms = 0;
2678
444k
    int code;
2679
2680
1.62M
    for (; fs != 0; fs = fs->strm) {
2681
1.18M
        const stream_state *st = fs->state;
2682
1.18M
        const stream_template *templat = st->templat;
2683
2684
1.18M
#define TEMPLATE_IS(atemp)\
2685
6.64M
  (templat->process == (atemp).process)
2686
1.18M
        if (TEMPLATE_IS(s_A85E_template))
2687
213k
            binary_ok = false;
2688
967k
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
95.9k
            cos_param_list_writer_t writer;
2690
95.9k
            stream_CF_state cfs;
2691
2692
95.9k
            decode_parms =
2693
95.9k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
95.9k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
95.9k
            CHECK(cos_param_list_writer_init(pdev, &writer, decode_parms, 0));
2697
            /*
2698
             * If EndOfBlock is true, we mustn't write a Rows value.
2699
             * This is a hack....
2700
             */
2701
95.9k
            cfs = *(const stream_CF_state *)st;
2702
95.9k
            if (cfs.EndOfBlock)
2703
12.5k
                cfs.Rows = 0;
2704
95.9k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
95.9k
            filter_name = pfn->CCITTFaxDecode;
2706
872k
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
5.77k
            filter_name = pfn->DCTDecode;
2708
866k
        else if (TEMPLATE_IS(s_zlibE_template))
2709
123k
            filter_name = pfn->FlateDecode;
2710
743k
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
743k
        else if (TEMPLATE_IS(s_LZWE_template))
2713
105k
            filter_name = pfn->LZWDecode;
2714
637k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
7.76k
            const stream_PNGP_state *const ss =
2717
7.76k
                (const stream_PNGP_state *)st;
2718
2719
7.76k
            decode_parms =
2720
7.76k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
7.76k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
7.76k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
7.76k
                                         ss->Predictor));
2725
7.76k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
7.76k
                                         ss->Columns));
2727
7.76k
            if (ss->Colors != 1)
2728
7.76k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
7.76k
                                             ss->Colors));
2730
7.76k
            if (ss->BitsPerComponent != 8)
2731
7.76k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
7.76k
                                             "/BitsPerComponent",
2733
7.76k
                                             ss->BitsPerComponent));
2734
630k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
1.18M
#undef TEMPLATE_IS
2737
1.18M
    }
2738
444k
    if (filter_name) {
2739
330k
        if (binary_ok) {
2740
152k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
152k
            if (decode_parms)
2742
152k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
152k
                                                COS_OBJECT(decode_parms)));
2744
178k
        } else {
2745
178k
            cos_array_t *pca =
2746
178k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
178k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
178k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
178k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
178k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
178k
                                            COS_OBJECT(pca)));
2754
178k
            if (decode_parms) {
2755
90.0k
                pca = cos_array_alloc(pdev,
2756
90.0k
                                      "pdf_put_image_filters(DecodeParms)");
2757
90.0k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
90.0k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
90.0k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
90.0k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
90.0k
                                                COS_OBJECT(pca)));
2763
90.0k
            }
2764
178k
        }
2765
330k
    } else if (!binary_ok)
2766
114k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
444k
    return 0;
2768
444k
}
2769
2770
/* Add a Flate compression filter to a binary writer. */
2771
static int
2772
pdf_flate_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
2773
210k
{
2774
210k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
114k
                    &s_LZWE_template : &s_zlibE_template);
2776
210k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
210k
                                     "pdf_write_function");
2778
2779
210k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
210k
    if (templat->set_defaults)
2782
210k
        templat->set_defaults(st);
2783
210k
    return psdf_encode_binary(pbw, templat, st);
2784
210k
}
2785
2786
/* Add a Brotli compression filter to a binary writer. */
2787
static int
2788
pdf_brotli_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
2789
0
{
2790
0
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2791
0
                    &s_LZWE_template : &s_brotliE_template);
2792
0
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2793
0
                                     "pdf_write_function");
2794
2795
0
    if (st == 0)
2796
0
        return_error(gs_error_VMerror);
2797
0
    if (templat->set_defaults)
2798
0
        templat->set_defaults(st);
2799
0
    return psdf_encode_binary(pbw, templat, st);
2800
0
}
2801
2802
/*
2803
 * Begin a data stream.  The client has opened the object and written
2804
 * the << and any desired dictionary keys.
2805
 */
2806
int
2807
pdf_begin_data(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
2808
0
{
2809
0
    return pdf_begin_data_stream(pdev, pdw,
2810
0
                                 DATA_STREAM_BINARY | DATA_STREAM_COMPRESS, 0);
2811
0
}
2812
2813
int
2814
pdf_append_data_stream_filters(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2815
                      int orig_options, gs_id object_id)
2816
249k
{
2817
249k
    stream *s = pdev->strm;
2818
249k
    int options = orig_options;
2819
249k
#define USE_ASCII85 1
2820
445k
#define USE_FLATE 2
2821
249k
#define USE_BROTLI 4
2822
249k
    static const char *const fnames[6] = {
2823
249k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
249k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
249k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
249k
    };
2827
249k
    static const char *const fnames1_2[6] = {
2828
249k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
249k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
249k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
249k
    };
2832
249k
    int filters = 0;
2833
249k
    int code;
2834
2835
249k
    if (options & DATA_STREAM_COMPRESS) {
2836
196k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
196k
        } else {
2840
196k
            filters |= USE_FLATE;
2841
196k
            options |= DATA_STREAM_BINARY;
2842
196k
        }
2843
196k
    }
2844
249k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
71.7k
        filters |= USE_ASCII85;
2846
249k
    if (!(options & DATA_STREAM_NOLENGTH)) {
2847
0
        stream_puts(s, (pdev->CompatibilityLevel < 1.3 ?
2848
0
            fnames1_2[filters] : fnames[filters]));
2849
0
        if (pdev->ResourcesBeforeUsage) {
2850
0
            pdw->length_pos = stell(s) + 8;
2851
0
            stream_puts(s, "/Length             >>stream\n");
2852
0
            pdw->length_id = -1;
2853
0
        } else {
2854
0
            pdw->length_pos = -1;
2855
0
            pdw->length_id = pdf_obj_ref(pdev);
2856
0
            pprinti64d1(s, "/Length %"PRId64" 0 R>>stream\n", pdw->length_id);
2857
0
        }
2858
0
    }
2859
249k
    if (options & DATA_STREAM_ENCRYPT) {
2860
19.9k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
19.9k
        if (code < 0)
2862
0
            return code;
2863
19.9k
        pdev->strm = s;
2864
19.9k
        pdw->encrypted = true;
2865
19.9k
    } else
2866
229k
        pdw->encrypted = false;
2867
249k
    if (options & DATA_STREAM_BINARY) {
2868
196k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
196k
        if (code < 0)
2870
0
            return code;
2871
196k
    } else {
2872
52.4k
        code = 0;
2873
52.4k
        pdw->binary.target = pdev->strm;
2874
52.4k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
52.4k
        pdw->binary.strm = pdev->strm;
2876
52.4k
    }
2877
249k
    pdw->start = stell(s);
2878
249k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
249k
    else
2881
249k
        if (filters & USE_FLATE)
2882
196k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
249k
    return code;
2884
249k
#undef USE_ASCII85
2885
249k
#undef USE_FLATE
2886
249k
#undef USE_BROTLI
2887
249k
}
2888
2889
int
2890
pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
2891
                      int options, gs_id object_id)
2892
30.0k
{   int code;
2893
    /* object_id is an unused rudiment from the old code,
2894
       when the encription was applied when creating the stream.
2895
       The new code encrypts than copying stream from the temporary file. */
2896
30.0k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
30.0k
    pdw->binary.target = pdev->strm;
2898
30.0k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
30.0k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
30.0k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
30.0k
                options);
2902
30.0k
    if (object_id != 0)
2903
155
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
30.0k
    pdw->binary.strm = pdev->strm;
2905
30.0k
    return code;
2906
30.0k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
5.33k
{   int code;
2912
2913
5.33k
    code = pdf_close_aside(pdw->pdev);
2914
5.33k
    if (code < 0)
2915
0
        return code;
2916
5.33k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
5.33k
    if (code < 0)
2918
0
        return code;
2919
5.33k
    return 0;
2920
5.33k
}
2921
2922
/* Create a Function object. */
2923
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
2924
                               const gs_function_info_t *pinfo);
2925
int
2926
pdf_function_scaled(gx_device_pdf *pdev, const gs_function_t *pfn,
2927
                    const gs_range_t *pranges, cos_value_t *pvalue)
2928
5.53k
{
2929
5.53k
    if (pranges == NULL)
2930
5.53k
        return pdf_function(pdev, pfn, pvalue);
2931
0
    {
2932
        /*
2933
         * Create a temporary scaled function.  Note that the ranges
2934
         * represent the inverse scaling from what gs_function_make_scaled
2935
         * expects.
2936
         */
2937
0
        gs_memory_t *mem = pdev->pdf_memory;
2938
0
        gs_function_t *psfn;
2939
0
        gs_range_t *ranges = (gs_range_t *)
2940
0
            gs_alloc_byte_array(mem, pfn->params.n, sizeof(gs_range_t),
2941
0
                                "pdf_function_scaled");
2942
0
        int i, code;
2943
2944
0
        if (ranges == 0)
2945
0
            return_error(gs_error_VMerror);
2946
0
        for (i = 0; i < pfn->params.n; ++i) {
2947
0
            double rbase = pranges[i].rmin;
2948
0
            double rdiff = pranges[i].rmax - rbase;
2949
0
            double invbase = -rbase / rdiff;
2950
2951
0
            ranges[i].rmin = invbase;
2952
0
            ranges[i].rmax = invbase + 1.0 / rdiff;
2953
0
        }
2954
0
        code = gs_function_make_scaled(pfn, &psfn, ranges, mem);
2955
0
        if (code >= 0) {
2956
0
            code = pdf_function(pdev, psfn, pvalue);
2957
0
            gs_function_free(psfn, true, mem);
2958
0
        }
2959
0
        gs_free_object(mem, ranges, "pdf_function_scaled");
2960
0
        return code;
2961
0
    }
2962
0
}
2963
static int
2964
pdf_function_aux(gx_device_pdf *pdev, const gs_function_t *pfn,
2965
             pdf_resource_t **ppres)
2966
22.3k
{
2967
22.3k
    gs_function_info_t info;
2968
22.3k
    cos_param_list_writer_t rlist;
2969
22.3k
    pdf_resource_t *pres;
2970
22.3k
    cos_object_t *pcfn;
2971
22.3k
    cos_dict_t *pcd;
2972
22.3k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
22.3k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
22.3k
    *ppres = pres;
2979
22.3k
    pcfn = pres->object;
2980
22.3k
    gs_function_get_info(pfn, &info);
2981
22.3k
    if (FunctionType(pfn) == function_type_ArrayedOutput) {
2982
        /*
2983
         * Arrayed Output Functions are used internally to represent
2984
         * Shading Function entries that are arrays of Functions.
2985
         * They require special handling.
2986
         */
2987
0
        cos_array_t *pca;
2988
2989
0
        cos_become(pcfn, cos_type_array);
2990
0
        pca = (cos_array_t *)pcfn;
2991
0
        return pdf_function_array(pdev, pca, &info);
2992
0
    }
2993
22.3k
    if (info.DataSource != 0) {
2994
14.8k
        psdf_binary_writer writer;
2995
14.8k
        stream *save = pdev->strm;
2996
14.8k
        cos_stream_t *pcos;
2997
14.8k
        stream *s;
2998
2999
14.8k
        cos_become(pcfn, cos_type_stream);
3000
14.8k
        pcos = (cos_stream_t *)pcfn;
3001
14.8k
        pcd = cos_stream_dict(pcos);
3002
14.8k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
14.8k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
14.8k
        pdev->strm = s;
3006
14.8k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
14.8k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
14.8k
            )
3009
14.0k
            code = pdf_flate_binary(pdev, &writer);
3010
14.8k
        if (code >= 0) {
3011
14.8k
            static const pdf_filter_names_t fnames = {
3012
14.8k
                PDF_FILTER_NAMES
3013
14.8k
            };
3014
3015
14.8k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
14.8k
        }
3017
14.8k
        if (code >= 0) {
3018
14.8k
            byte buf[100];    /* arbitrary */
3019
14.8k
            ulong pos;
3020
14.8k
            uint count;
3021
14.8k
            const byte *ptr;
3022
3023
122k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
108k
                count = min(sizeof(buf), info.data_size - pos);
3025
108k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
108k
                                        &ptr);
3027
108k
                stream_write(writer.strm, ptr, count);
3028
108k
            }
3029
14.8k
            code = psdf_end_binary(&writer);
3030
14.8k
            s_close_filters(&s, s->strm);
3031
14.8k
        }
3032
14.8k
        pdev->strm = save;
3033
14.8k
        if (code < 0)
3034
0
            return code;
3035
14.8k
    } else {
3036
7.54k
        cos_become(pcfn, cos_type_dict);
3037
7.54k
        pcd = (cos_dict_t *)pcfn;
3038
7.54k
    }
3039
22.3k
    if (info.Functions != 0) {
3040
1.39k
        cos_array_t *functions =
3041
1.39k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.39k
        cos_value_t v;
3043
3044
1.39k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.39k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.39k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.39k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.39k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.39k
    }
3054
22.3k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
22.3k
    if (code < 0)
3056
0
        return code;
3057
22.3k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
22.3k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
16.5k
{
3062
16.5k
    return true;
3063
16.5k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
22.3k
{
3067
22.3k
    pdf_resource_t *pres;
3068
22.3k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
22.3k
    if (code < 0)
3071
0
        return code;
3072
22.3k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
22.3k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
22.3k
    if (code < 0)
3077
0
        return code;
3078
22.3k
    pres->where_used |= pdev->used_mask;
3079
22.3k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
22.3k
    return 0;
3081
22.3k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.39k
{
3085
1.39k
    int i, code = 0;
3086
1.39k
    cos_value_t v;
3087
3088
6.27k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
4.87k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
4.87k
            (code = cos_array_add(pca, &v)) < 0
3091
4.87k
            ) {
3092
0
            break;
3093
0
        }
3094
4.87k
    }
3095
1.39k
    return code;
3096
1.39k
}
3097
3098
/* Write a Function object. */
3099
int
3100
pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, int64_t *pid)
3101
11.9k
{
3102
11.9k
    cos_value_t value;
3103
11.9k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
11.9k
    if (code < 0)
3106
0
        return code;
3107
11.9k
    *pid = value.contents.object->id;
3108
11.9k
    return 0;
3109
11.9k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
5.79k
{
3114
5.79k
    char key[] = "/Functions";
3115
5.79k
    cos_value_t *v, v2;
3116
3117
5.79k
    if (cos_type(pco) == cos_type_dict) {
3118
1.91k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
1.91k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
445
            if (cos_type(v->contents.object) == cos_type_array){
3121
445
                int code=0;
3122
2.37k
                while (code == 0) {
3123
1.93k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
1.93k
                }
3125
445
            }
3126
445
        }
3127
1.91k
    }
3128
5.79k
    if (cos_type(pco) == cos_type_array) {
3129
0
        int64_t index;
3130
0
        cos_array_t *pca = (cos_array_t *)pco;
3131
0
        const cos_array_element_t *element = cos_array_element_first(pca);
3132
0
        cos_value_t *v;
3133
3134
0
        while (element) {
3135
0
            element = cos_array_element_next(element, &index, (const cos_value_t **)&v);
3136
0
            if (v->value_type == COS_VALUE_OBJECT) {
3137
0
                if (pdf_find_resource_by_resource_id(pdev, resourceFunction, v->contents.object->id)){
3138
0
                    v->value_type = COS_VALUE_CONST;
3139
                    /* Need to remove the element from the array here */
3140
0
                }
3141
0
            }
3142
0
        }
3143
0
    }
3144
5.79k
    return 0;
3145
5.79k
}
3146
3147
/* Write a FontBBox dictionary element. */
3148
int
3149
pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
3150
25.4k
{
3151
25.4k
    stream *s = pdev->strm;
3152
    /*
3153
     * AR 4 doesn't like fonts with empty FontBBox, which
3154
     * happens when the font contains only space characters.
3155
     * Small bbox causes AR 4 to display a hairline. So we use
3156
     * the full BBox.
3157
     */
3158
25.4k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
25.4k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
25.4k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
25.4k
             pbox->p.x, pbox->p.y, x, y);
3163
25.4k
    return 0;
3164
25.4k
}
3165
3166
/* Write a FontBBox dictionary element using floats for the values. */
3167
int
3168
pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
3169
3.18k
{
3170
3.18k
    stream *s = pdev->strm;
3171
    /*
3172
     * AR 4 doesn't like fonts with empty FontBBox, which
3173
     * happens when the font contains only space characters.
3174
     * Small bbox causes AR 4 to display a hairline. So we use
3175
     * the full BBox.
3176
     */
3177
3.18k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
3.18k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
3.18k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
3.18k
             pbox->p.x, pbox->p.y, x, y);
3182
3.18k
    return 0;
3183
3.18k
}