Coverage Report

Created: 2025-08-28 07:06

/src/ghostpdl/devices/vector/gdevpdfu.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* 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
30.6k
#define sbuf_size 512
57
58
/* Optionally substitute other filters for FlateEncode for debugging. */
59
#if 1
60
30.6k
#  define Flate_filter_name "FlateDecode"
61
30.6k
#  define Flate_filter_template s_zlibE_template
62
30.6k
#  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
3.03M
  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
27.2k
{
319
27.2k
    int index = 0;
320
321
110M
    do {
322
110M
        if (opdfread_ps[index] == 0x00)
323
27.2k
            break;
324
110M
        stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
325
110M
        index++;
326
110M
    } while (1);
327
0
    return 0;
328
27.2k
}
329
330
static int write_tt_encodings(stream *s, bool HaveTrueTypes)
331
27.2k
{
332
27.2k
    int index = 0;
333
334
1.06M
    do {
335
1.06M
        if (gs_mro_e_ps[index] == 0x00)
336
27.2k
            break;
337
1.03M
        stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
338
1.03M
        index++;
339
1.03M
    } while (1);
340
341
27.2k
    if (HaveTrueTypes) {
342
27.2k
        char Buffer[256];
343
27.2k
        single_glyph_list_t *entry = SingleGlyphList;
344
345
27.2k
        gs_snprintf(Buffer, sizeof(Buffer), "/AdobeGlyphList mark\n");
346
27.2k
        stream_write(s, Buffer, strlen(Buffer));
347
114M
        while (entry->Glyph) {
348
114M
            gs_snprintf(Buffer, sizeof(Buffer), "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
349
114M
            stream_write(s, Buffer, strlen(Buffer));
350
114M
            entry++;
351
114M
        };
352
27.2k
        gs_snprintf(Buffer, sizeof(Buffer), ".dicttomark readonly def\n");
353
27.2k
        stream_write(s, Buffer, strlen(Buffer));
354
355
27.2k
        index = 0;
356
979k
        do {
357
979k
            if (gs_mgl_e_ps[index] == 0x00)
358
27.2k
                break;
359
952k
            stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
360
952k
            index++;
361
952k
        } while (1);
362
27.2k
    }
363
0
    return 0;
364
27.2k
}
365
366
static int
367
copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
368
27.2k
{
369
27.2k
    int code;
370
371
27.2k
    code = write_opdfread(s);
372
27.2k
    if (code < 0)
373
0
        return code;
374
375
27.2k
    code = write_tt_encodings(s, HaveTrueTypes);
376
27.2k
    return code;
377
378
27.2k
}
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
50.0k
{
455
50.0k
    const char * const *argv = NULL;
456
50.0k
    const char *arg;
457
50.0k
    int towrite, length, i, j, argc;
458
459
50.0k
    argc = gs_lib_ctx_get_args(pdev->memory->gs_lib_ctx, &argv);
460
461
50.0k
    stream_write(s, (byte *)"%%Invocation:", 13);
462
50.0k
    length = 12;
463
951k
    for (i=0;i < argc; i++) {
464
901k
        arg = argv[i];
465
466
901k
        if ((strlen(arg) + length) > 255) {
467
0
            stream_write(s, (byte *)"\n%%+ ", 5);
468
0
            length = 5;
469
901k
        } else {
470
901k
            stream_write(s, (byte *)" ", 1);
471
901k
            length++;
472
901k
        }
473
474
901k
        if (strlen(arg) > 250)
475
0
            towrite = 250;
476
901k
        else
477
901k
            towrite = strlen(arg);
478
479
901k
        length += towrite;
480
481
10.8M
        for (j=0;j < towrite;j++) {
482
9.98M
            if (arg[j] == 0x0A) {
483
0
                stream_write(s, (byte *)"<0A>", 4);
484
9.98M
            } else {
485
9.98M
                if (arg[j] == 0x0D) {
486
0
                    stream_write(s, (byte *)"<0D>", 4);
487
9.98M
                } else {
488
9.98M
                    stream_write(s, (byte *)&arg[j], 1);
489
9.98M
                }
490
9.98M
            }
491
9.98M
        }
492
901k
    }
493
50.0k
    stream_write(s, (byte *)"\n", 1);
494
50.0k
    return 0;
495
50.0k
}
496
497
int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
498
27.2k
{
499
27.2k
    stream *s = pdev->strm;
500
501
27.2k
    if (pdev->ForOPDFRead) {
502
27.2k
        char cre_date_time[41];
503
27.2k
        int code, status, cre_date_time_len;
504
27.2k
        char BBox[256];
505
506
27.2k
        if (pdev->Eps2Write)
507
13.3k
            stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
508
13.8k
        else
509
13.8k
            stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
510
27.2k
        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
27.2k
        {
515
27.2k
            int pagecount = 1, j;
516
27.2k
            double urx=0, ury=0;
517
518
462k
            for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
519
435k
                pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
520
521
470k
                for (; pres != 0; pres = pres->next)
522
35.4k
                    if ((!pres->named || pdev->ForOPDFRead)
523
35.4k
                        && !pres->object->written) {
524
35.4k
                        pdf_page_t *page = &pdev->pages[pagecount - 1];
525
35.4k
                        if (ceil(page->MediaBox.x) > urx)
526
27.6k
                            urx = ceil(page->MediaBox.x);
527
35.4k
                        if (ceil(page->MediaBox.y) > ury)
528
27.3k
                            ury = ceil(page->MediaBox.y);
529
35.4k
                        pagecount++;
530
35.4k
                    }
531
435k
            }
532
27.2k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
533
19.9k
                gs_snprintf(BBox, sizeof(BBox), "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
534
7.29k
            else
535
7.29k
                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
27.2k
            stream_write(s, (byte *)BBox, strlen(BBox));
537
27.2k
            if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
538
19.9k
                gs_snprintf(BBox, sizeof(BBox), "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
539
7.29k
            else
540
7.29k
                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
27.2k
            stream_write(s, (byte *)BBox, strlen(BBox));
542
27.2k
        }
543
27.2k
        cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
544
27.2k
        cre_date_time[cre_date_time_len] = 0;
545
27.2k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
546
27.2k
                pdev->dname);
547
27.2k
        stream_write(s, (byte *)BBox, strlen(BBox));
548
27.2k
        stream_puts(s, "%%LanguageLevel: 2\n");
549
27.2k
        gs_snprintf(BBox, sizeof(BBox), "%%%%CreationDate: %s\n", cre_date_time);
550
27.2k
        stream_write(s, (byte *)BBox, strlen(BBox));
551
27.2k
        gs_snprintf(BBox, sizeof(BBox), "%%%%Pages: %d\n", pages);
552
27.2k
        stream_write(s, (byte *)BBox, strlen(BBox));
553
27.2k
        gs_snprintf(BBox, sizeof(BBox), "%%%%EndComments\n");
554
27.2k
        stream_write(s, (byte *)BBox, strlen(BBox));
555
27.2k
        gs_snprintf(BBox, sizeof(BBox), "%%%%BeginProlog\n");
556
27.2k
        stream_write(s, (byte *)BBox, strlen(BBox));
557
27.2k
        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
27.2k
        stream_puts(s, "10 dict dup begin\n");
573
27.2k
        stream_puts(s, "/DSC_OPDFREAD true def\n");
574
27.2k
        if (pdev->Eps2Write) {
575
13.3k
            stream_puts(s, "/SetPageSize false def\n");
576
13.3k
            stream_puts(s, "/EPS2Write true def\n");
577
13.8k
        } else {
578
13.8k
            if (pdev->SetPageSize)
579
13.8k
                stream_puts(s, "/SetPageSize true def\n");
580
13.8k
            stream_puts(s, "/EPS2Write false def\n");
581
13.8k
        }
582
27.2k
        stream_puts(s, "end\n");
583
584
27.2k
        code = copy_procsets(s, pdev->HaveTrueTypes, false);
585
27.2k
        if (code < 0)
586
0
            return code;
587
27.2k
        status = s_close_filters(&s, pdev->strm);
588
27.2k
        if (status < 0)
589
0
            return_error(gs_error_ioerror);
590
27.2k
        stream_puts(s, "\n");
591
27.2k
        pdev->OPDFRead_procset_length = (int)stell(s);
592
27.2k
    }
593
27.2k
    return 0;
594
27.2k
}
595
596
/* Open the document if necessary. */
597
int
598
pdfwrite_pdf_open_document(gx_device_pdf * pdev)
599
752k
{
600
752k
    if (!pdev->strm)
601
0
        return_error(gs_error_ioerror);
602
603
752k
    if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
604
158k
        stream *s = pdev->strm;
605
158k
        int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
606
607
158k
        pdev->binary_ok = !pdev->params.ASCII85EncodePages;
608
158k
        if (pdev->ForOPDFRead) {
609
135k
            int code, status;
610
135k
            char BBox[256];
611
135k
            int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
612
135k
            int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
613
614
135k
            if (pdev->ProduceDSC)
615
135k
                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
135k
        }
660
158k
        if (!(pdev->ForOPDFRead)) {
661
22.8k
            pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
662
22.8k
            if (pdev->binary_ok)
663
22.8k
                stream_puts(s, "%\307\354\217\242\n");
664
22.8k
            pdfwrite_write_args_comment(pdev, s);
665
22.8k
        }
666
158k
    }
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
752k
    if (!pdev->params.CompressPages)
677
343k
        pdev->compression = pdf_compress_none;
678
409k
    else {
679
409k
        if (pdev->UseBrotli)
680
0
            pdev->compression = pdf_compress_Brotli;
681
409k
        else
682
409k
            pdev->compression = pdf_compress_Flate;
683
409k
    }
684
752k
    return 0;
685
752k
}
686
687
/* ------ Objects ------ */
688
689
/* Allocate an object ID. */
690
static int64_t
691
pdf_next_id(gx_device_pdf * pdev)
692
860k
{
693
860k
    return (pdev->next_id)++;
694
860k
}
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.91M
{
706
1.91M
    stream *s = pdev->strm;
707
1.91M
    gs_offset_t pos = stell(s);
708
709
1.91M
    if (s == pdev->asides.strm)
710
392k
        pos |= ASIDES_BASE_POSITION;
711
1.91M
    return pos;
712
1.91M
}
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
89.1k
{
730
89.1k
    int64_t id = pdf_next_id(pdev);
731
89.1k
    gs_offset_t pos = 0;
732
733
89.1k
    if (pdev->doubleXref) {
734
89.1k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
735
89.1k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
736
89.1k
    }
737
0
    else
738
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
739
89.1k
    return id;
740
89.1k
}
741
742
/* Allocate an ID for a future object. */
743
int64_t
744
pdf_obj_ref(gx_device_pdf * pdev)
745
771k
{
746
771k
    int64_t id = pdf_next_id(pdev);
747
771k
    gs_offset_t pos = 0;
748
749
771k
    if (pdev->doubleXref) {
750
771k
        if (pdev->strm == pdev->ObjStm.strm)
751
32.5k
            pos = pdev->ObjStm_id;
752
739k
        else
753
739k
            pos = 0;
754
771k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
755
771k
        if (pdev->strm == pdev->ObjStm.strm)
756
32.5k
            pos = pdev->NumObjStmObjects;
757
739k
        else
758
739k
            pos = pdf_stell(pdev);
759
771k
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
760
771k
    }
761
0
    else {
762
0
        pos = pdf_stell(pdev);
763
0
        gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
764
0
    }
765
771k
    return id;
766
771k
}
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
192
{
780
192
    gp_file *tfile = pdev->xref.file;
781
192
    int64_t tpos = gp_ftell(tfile);
782
192
    gs_offset_t pos = 0;
783
784
192
    if (pdev->doubleXref) {
785
192
        if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
786
192
              SEEK_SET) != 0)
787
0
            return_error(gs_error_ioerror);
788
192
        gp_fwrite(&pos, sizeof(pos), 1, tfile);
789
192
    }
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
192
    gp_fwrite(&pos, sizeof(pos), 1, tfile);
797
192
    if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
798
0
        return_error(gs_error_ioerror);
799
192
    return 0;
800
192
}
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
770k
{
806
770k
    stream *s = pdev->strm;
807
808
770k
    if (s == NULL)
809
0
        return_error(gs_error_ioerror);
810
811
770k
    if (id <= 0) {
812
220k
        id = pdf_obj_ref(pdev);
813
550k
    } else {
814
550k
        gs_offset_t pos = pdf_stell(pdev),fake_pos = 0;
815
550k
        gp_file *tfile = pdev->xref.file;
816
550k
        int64_t tpos = gp_ftell(tfile);
817
818
550k
        if (pdev->doubleXref) {
819
550k
            if (gp_fseek(tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos) * 2,
820
550k
                  SEEK_SET) != 0)
821
0
              return_error(gs_error_ioerror);
822
550k
            if (pdev->strm == pdev->ObjStm.strm)
823
212k
                fake_pos = pdev->ObjStm_id;
824
550k
            gp_fwrite(&fake_pos, sizeof(fake_pos), 1, pdev->xref.file);
825
550k
            if (pdev->strm == pdev->ObjStm.strm)
826
212k
                pos = pdev->NumObjStmObjects;
827
550k
            gp_fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
828
550k
        } 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
550k
        if (gp_fseek(tfile, tpos, SEEK_SET) != 0)
835
0
          return_error(gs_error_ioerror);
836
550k
    }
837
770k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
838
297k
        switch(type) {
839
1.24k
            case resourceNone:
840
                /* Used when outputting usage of a previously defined resource
841
                 * Does not want comments around its use
842
                 */
843
1.24k
                break;
844
35.4k
            case resourcePage:
845
                /* We *don't* want resource comments around pages */
846
35.4k
                break;
847
2.41k
            case resourceColorSpace:
848
2.41k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Color Space obj_%"PRId64")\n", id);
849
2.41k
                break;
850
13.0k
            case resourceExtGState:
851
13.0k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%"PRId64")\n", id);
852
13.0k
                break;
853
887
            case resourcePattern:
854
887
                pprinti64d1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%"PRId64")\n", id);
855
887
                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
31.8k
            case resourceFont:
861
                /* Ought to write the font name here */
862
31.8k
                pprinti64d1(s, "%%%%BeginResource: procset (PDF Font obj_%"PRId64")\n", id);
863
31.8k
                break;
864
130k
            case resourceCharProc:
865
130k
                pprinti64d1(s, "%%%%BeginResource: file (PDF CharProc obj_%"PRId64")\n", id);
866
130k
                break;
867
0
            case resourceCMap:
868
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CMap obj_%"PRId64")\n", id);
869
0
                break;
870
14.7k
            case resourceFontDescriptor:
871
14.7k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%"PRId64")\n", id);
872
14.7k
                break;
873
0
            case resourceGroup:
874
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF Group obj_%"PRId64")\n", id);
875
0
                break;
876
2.49k
            case resourceFunction:
877
2.49k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Function obj_%"PRId64")\n", id);
878
2.49k
                break;
879
6.92k
            case resourceEncoding:
880
6.92k
                pprinti64d1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%"PRId64")\n", id);
881
6.92k
                break;
882
0
            case resourceCIDSystemInfo:
883
0
                pprinti64d1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%"PRId64")\n", id);
884
0
                break;
885
15.8k
            case resourceHalftone:
886
15.8k
                pprinti64d1(s, "%%%%BeginResource: file (PDF Halftone obj_%"PRId64")\n", id);
887
15.8k
                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
11
            case resourceXObject:
896
                /* This should not be possible, we write these inline */
897
11
                pprinti64d1(s, "%%%%BeginResource: file (PDF XObject obj_%"PRId64")\n", id);
898
11
                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
14.7k
            case resourceFontFile:
952
14.7k
                pprinti64d1(s, "%%%%BeginResource: file (PDF FontFile obj_%"PRId64")\n", id);
953
14.7k
                break;
954
27.2k
            default:
955
27.2k
                pprinti64d1(s, "%%%%BeginResource: file (PDF object obj_%"PRId64")\n", id);
956
27.2k
                break;
957
297k
        }
958
297k
    }
959
770k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
960
530k
        pprinti64d1(s, "%"PRId64" 0 obj\n", id);
961
770k
    return id;
962
770k
}
963
int64_t
964
pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
965
30.6k
{
966
30.6k
    return pdf_open_obj(pdev, 0L, type);
967
30.6k
}
968
969
/* End an object. */
970
int
971
pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
972
770k
{
973
770k
    if (!pdev->WriteObjStms || pdev->strm != pdev->ObjStm.strm)
974
530k
        stream_puts(pdev->strm, "endobj\n");
975
770k
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
976
297k
        switch(type) {
977
35.4k
            case resourcePage:
978
35.4k
                break;
979
261k
            default:
980
261k
            stream_puts(pdev->strm, "%%EndResource\n");
981
261k
            break;
982
297k
        }
983
297k
    }
984
770k
    return 0;
985
770k
}
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
179k
{
1034
179k
    gs_memory_t *mem = pdev->v_memory;
1035
179k
    stream_arcfour_state *ss;
1036
179k
    gs_md5_byte_t key[16];
1037
179k
    int code, keylength;
1038
1039
179k
    if (!pdev->KeyLength)
1040
179k
        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
66.0k
{
1065
66.0k
    stream *s;
1066
66.0k
    int code;
1067
1068
66.0k
    if (pdev->contents_id != 0)
1069
9
        return_error(gs_error_Fatal);  /* only 1 contents per page */
1070
66.0k
    pdev->compression_at_page_start = pdev->compression;
1071
66.0k
    if (pdev->ResourcesBeforeUsage) {
1072
35.4k
        pdf_resource_t *pres;
1073
1074
35.4k
        code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
1075
35.4k
                    true, pdev->params.CompressPages);
1076
35.4k
        if (code < 0)
1077
0
            return code;
1078
35.4k
        pdev->contents_id = pres->object->id;
1079
35.4k
        pdev->contents_length_id = gs_no_id; /* inapplicable */
1080
35.4k
        pdev->contents_pos = -1; /* inapplicable */
1081
35.4k
        s = pdev->strm;
1082
35.4k
    } else {
1083
30.6k
        pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
1084
30.6k
        pdev->contents_length_id = pdf_obj_ref(pdev);
1085
30.6k
        s = pdev->strm;
1086
30.6k
        pprinti64d1(s, "<</Length %"PRId64" 0 R", pdev->contents_length_id);
1087
30.6k
        if (pdev->compression == pdf_compress_Flate) {
1088
30.6k
            if (pdev->binary_ok)
1089
30.6k
                pprints1(s, "/Filter /%s", Flate_filter_name);
1090
0
            else
1091
0
                pprints1(s, "/Filter [/ASCII85Decode /%s]", Flate_filter_name);
1092
30.6k
        }
1093
30.6k
        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
30.6k
        stream_puts(s, ">>\nstream\n");
1100
30.6k
        pdev->contents_pos = pdf_stell(pdev);
1101
30.6k
        code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
1102
30.6k
        if (code < 0)
1103
0
            return code;
1104
30.6k
        pdev->strm = s;
1105
30.6k
        if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
1106
30.6k
            const stream_template *templat;
1107
30.6k
            stream *es;
1108
30.6k
            byte *buf;
1109
30.6k
            Flate_filter_state *st;
1110
1111
30.6k
            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
30.6k
            templat = &Flate_filter_template;
1131
30.6k
            es = s_alloc(pdev->pdf_memory, "PDF compression stream");
1132
30.6k
            buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
1133
30.6k
                                       "PDF compression buffer");
1134
30.6k
            st = gs_alloc_struct(pdev->pdf_memory, Flate_filter_state,
1135
30.6k
                                 templat->stype, "PDF compression state");
1136
30.6k
            if (es == 0 || st == 0 || buf == 0)
1137
0
                return_error(gs_error_VMerror);
1138
30.6k
            s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
1139
30.6k
                       s_mode_write);
1140
30.6k
            st->memory = pdev->pdf_memory;
1141
30.6k
            st->templat = templat;
1142
30.6k
            es->state = (stream_state *) st;
1143
30.6k
            es->procs.process = templat->process;
1144
30.6k
            es->strm = s;
1145
30.6k
            (*templat->set_defaults) ((stream_state *) st);
1146
30.6k
            code = (*templat->init) ((stream_state *) st);
1147
30.6k
            if (code < 0) {
1148
0
                gs_free_object(pdev->pdf_memory, st, "none_to_stream");
1149
0
                return code;
1150
0
            }
1151
30.6k
            pdev->strm = s = es;
1152
30.6k
        }
1153
30.6k
        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
30.6k
    }
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
66.0k
    pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
1208
66.0k
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1209
66.0k
    if (pdev->CompatibilityLevel >= 1.3) {
1210
        /* Set the default rendering intent. */
1211
30.5k
        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
30.5k
    }
1218
66.0k
    pdev->AR4_save_bug = false;
1219
66.0k
    return PDF_IN_STREAM;
1220
66.0k
}
1221
/* Enter text context from stream context. */
1222
static int
1223
stream_to_text(gx_device_pdf * pdev)
1224
297k
{
1225
297k
    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
297k
    if (!pdev->clipped_text_pending) {
1233
297k
        code = pdf_save_viewer_state(pdev, pdev->strm);
1234
297k
        if (code < 0)
1235
0
            return 0;
1236
297k
    }
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
297k
    pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
1246
297k
             pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
1247
297k
    pdev->procsets |= Text;
1248
297k
    code = pdf_from_stream_to_text(pdev);
1249
297k
    return (code < 0 ? code : PDF_IN_TEXT);
1250
297k
}
1251
/* Exit string context to text context. */
1252
static int
1253
string_to_text(gx_device_pdf * pdev)
1254
297k
{
1255
297k
    int code = pdf_from_string_to_text(pdev);
1256
1257
297k
    return (code < 0 ? code : PDF_IN_TEXT);
1258
297k
}
1259
/* Exit text context to stream context. */
1260
static int
1261
text_to_stream(gx_device_pdf * pdev)
1262
297k
{
1263
297k
    int code;
1264
1265
297k
    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
297k
    if (pdev->clipped_text_pending)
1273
192
        pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
1274
192
             72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
1275
297k
    else {
1276
297k
        code = pdf_restore_viewer_state(pdev, pdev->strm);
1277
297k
        if (code < 0)
1278
0
            return code;
1279
297k
        pdf_reset_text(pdev); /* because of Q */
1280
297k
    }
1281
297k
    return PDF_IN_STREAM;
1282
297k
}
1283
/* Exit stream context. */
1284
static int
1285
stream_to_none(gx_device_pdf * pdev)
1286
66.0k
{
1287
66.0k
    stream *s = pdev->strm;
1288
66.0k
    gs_offset_t length;
1289
66.0k
    int code;
1290
66.0k
    stream *target;
1291
66.0k
     char str[21];
1292
1293
66.0k
    if (pdev->ResourcesBeforeUsage) {
1294
35.4k
        int code = pdf_exit_substream(pdev);
1295
1296
35.4k
        if (code < 0)
1297
0
            return code;
1298
35.4k
    } else {
1299
30.6k
        if (pdev->vgstack_depth) {
1300
206
            code = pdf_restore_viewer_state(pdev, s);
1301
206
            if (code < 0)
1302
10
                return code;
1303
206
        }
1304
30.5k
        target = pdev->strm;
1305
1306
30.5k
        if (pdev->compression_at_page_start == pdf_compress_Flate || pdev->compression_at_page_start == pdf_compress_Brotli)
1307
30.5k
            target = target->strm;
1308
30.5k
        if (!pdev->binary_ok)
1309
0
            target = target->strm;
1310
30.5k
        if (pdf_end_encrypt(pdev))
1311
0
            target = target->strm;
1312
30.5k
        s_close_filters(&pdev->strm, target);
1313
1314
30.5k
        s = pdev->strm;
1315
30.5k
        length = pdf_stell(pdev) - pdev->contents_pos;
1316
30.5k
        if (pdev->PDFA != 0)
1317
0
            stream_puts(s, "\n");
1318
30.5k
        stream_puts(s, "endstream\n");
1319
30.5k
        pdf_end_obj(pdev, resourceStream);
1320
1321
30.5k
        if (pdev->WriteObjStms) {
1322
30.5k
            pdf_open_separate(pdev, pdev->contents_length_id, resourceLength);
1323
30.5k
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1324
30.5k
            stream_puts(pdev->strm, str);
1325
30.5k
            pdf_end_separate(pdev, resourceLength);
1326
30.5k
        } else {
1327
12
            pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
1328
12
            gs_snprintf(str, sizeof(str), "%"PRId64"\n", (int64_t)length);
1329
12
            stream_puts(s, str);
1330
12
            pdf_end_obj(pdev, resourceLength);
1331
12
        }
1332
30.5k
    }
1333
66.0k
    return PDF_IN_NONE;
1334
66.0k
}
1335
1336
/* Begin a page contents part. */
1337
int
1338
pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
1339
22.8M
{
1340
22.8M
    int (*proc) (gx_device_pdf *);
1341
1342
23.8M
    while ((proc = context_procs[pdev->context][context]) != 0) {
1343
1.02M
        int code = (*proc) (pdev);
1344
1345
1.02M
        if (code < 0)
1346
19
            return code;
1347
1.02M
        pdev->context = (pdf_context_t) code;
1348
1.02M
    }
1349
22.8M
    pdev->context = context;
1350
22.8M
    return 0;
1351
22.8M
}
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
66.0k
{
1357
66.0k
    if (pdev->context == PDF_IN_NONE)
1358
7
        return 0;
1359
66.0k
    if (last) {     /* Exit from the clipping path gsave. */
1360
66.0k
        int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1361
1362
66.0k
        if (code < 0)
1363
0
            return code;
1364
66.0k
        stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
1365
66.0k
        pdf_close_text_contents(pdev);
1366
66.0k
    }
1367
66.0k
    return pdf_open_contents(pdev, PDF_IN_NONE);
1368
66.0k
}
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
274k
{
1384
    /* fixme : Remove *pres from resource chain. */
1385
274k
    pres->where_used = 0;
1386
274k
    if (pres->object) {
1387
274k
        pres->object->written = true;
1388
274k
        if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
1389
274k
            || rtype >= NUM_RESOURCE_TYPES) {
1390
172k
            int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
1391
1392
172k
            if (code < 0)
1393
0
                return code;
1394
172k
        }
1395
274k
        cos_release(pres->object, "pdf_cancel_resource");
1396
274k
        gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
1397
274k
        pres->object = 0;
1398
274k
    }
1399
274k
    return 0;
1400
274k
}
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
272k
{   /* fixme : optimize. */
1406
272k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1407
272k
    pdf_resource_t *pres;
1408
272k
    pdf_resource_t **pprev = &pdev->last_resource;
1409
272k
    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
3.27M
    for (i = 0; i < pdev->sbstack_size; i++) {
1415
3.00M
        if (pres1 == pdev->sbstack[i].font3) {
1416
0
            pdev->sbstack[i].font3 = NULL;
1417
0
        }
1418
3.00M
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1419
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1420
0
        }
1421
3.00M
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1422
5
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1423
5
        }
1424
3.00M
    }
1425
1426
276k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1427
276k
        if (pres == pres1) {
1428
272k
            *pprev = pres->prev;
1429
272k
            break;
1430
272k
        }
1431
1432
272k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1433
272k
        pprev = pchain + i;
1434
276k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1435
276k
            if (pres == pres1) {
1436
272k
                *pprev = pres->next;
1437
272k
                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
272k
                gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
1443
272k
                return;
1444
272k
            }
1445
272k
    }
1446
272k
}
1447
1448
static int
1449
nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
1450
69.1k
{
1451
69.1k
    return 1;
1452
69.1k
}
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
163k
{
1467
163k
    pdf_resource_t *pres1 = *ppres;
1468
163k
    int code;
1469
1470
163k
    code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
1471
163k
    if (code < 0)
1472
0
        return code;
1473
163k
    if (code != 0) {
1474
110k
        code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
1475
110k
        if (code < 0)
1476
0
            return code;
1477
110k
        pdf_forget_resource(pdev, pres1, rtype);
1478
110k
        return 0;
1479
110k
    } else {
1480
53.2k
        if (pres1->object->id < 0)
1481
53.2k
            pdf_reserve_object_id(pdev, pres1, gs_no_id);
1482
53.2k
        if (write) {
1483
26.2k
            code = cos_write_object(pres1->object, pdev, rtype);
1484
26.2k
            if (code < 0)
1485
0
                return code;
1486
26.2k
            pres1->object->written = 1;
1487
26.2k
        }
1488
53.2k
        return 1;
1489
53.2k
    }
1490
163k
}
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
1.76M
{
1497
1.76M
    pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
1498
1.76M
    pdf_resource_t **pprev = pchain;
1499
1.76M
    pdf_resource_t *pres;
1500
1501
4.72M
    for (; (pres = *pprev) != 0; pprev = &pres->next)
1502
4.54M
        if (pres->rid == rid) {
1503
1.58M
            if (pprev != pchain) {
1504
578k
                *pprev = pres->next;
1505
578k
                pres->next = *pchain;
1506
578k
                *pchain = pres;
1507
578k
            }
1508
1.58M
            return pres;
1509
1.58M
        }
1510
176k
    return 0;
1511
1.76M
}
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
52.4k
{
1517
52.4k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1518
52.4k
    pdf_resource_t *pres;
1519
52.4k
    int i;
1520
1521
809k
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1522
6.34M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1523
5.59M
            if (pres->object && pres->object->id == id)
1524
9.93k
                return pres;
1525
5.59M
        }
1526
766k
    }
1527
42.4k
    return 0;
1528
52.4k
}
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
198k
{
1535
198k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1536
198k
    pdf_resource_t *pres;
1537
198k
    cos_object_t *pco0 = (*ppres)->object;
1538
198k
    int i;
1539
1540
1.57M
    for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
1541
5.98M
        for (pres = pchain[i]; pres != 0; pres = pres->next) {
1542
4.61M
            if (*ppres != pres) {
1543
4.41M
                int code;
1544
4.41M
                cos_object_t *pco1 = pres->object;
1545
1546
4.41M
                if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
1547
42.8k
                    continue;      /* don't compare different types */
1548
4.37M
                code = pco0->cos_procs->equal(pco0, pco1, pdev);
1549
4.37M
                if (code < 0)
1550
0
                    return code;
1551
4.37M
                if (code > 0) {
1552
112k
                    code = eq(pdev, *ppres, pres);
1553
112k
                    if (code < 0)
1554
0
                        return code;
1555
112k
                    if (code > 0) {
1556
112k
                        *ppres = pres;
1557
112k
                        return 1;
1558
112k
                    }
1559
112k
                }
1560
4.37M
            }
1561
4.61M
        }
1562
1.48M
    }
1563
85.6k
    return 0;
1564
198k
}
1565
1566
void
1567
pdf_drop_resource_from_chain(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
1568
16.8k
{
1569
16.8k
    pdf_resource_t **pchain = pdev->resources[rtype].chains;
1570
16.8k
    pdf_resource_t *pres;
1571
16.8k
    pdf_resource_t **pprev = &pdev->last_resource;
1572
16.8k
    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
202k
    for (i = 0; i < pdev->sbstack_size; i++) {
1578
185k
        if (pres1 == pdev->sbstack[i].font3) {
1579
0
            pdev->sbstack[i].font3 = NULL;
1580
0
        }
1581
185k
        else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
1582
0
            pdev->sbstack[i].accumulating_substream_resource = NULL;
1583
0
        }
1584
185k
        else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
1585
0
            pdev->sbstack[i].pres_soft_mask_dict = NULL;
1586
0
        }
1587
185k
    }
1588
1589
20.3k
    for (; (pres = *pprev) != 0; pprev = &pres->prev)
1590
20.3k
        if (pres == pres1) {
1591
16.8k
            *pprev = pres->prev;
1592
16.8k
            break;
1593
16.8k
        }
1594
1595
16.8k
    for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
1596
16.8k
        pprev = pchain + i;
1597
17.2k
        for (; (pres = *pprev) != 0; pprev = &pres->next)
1598
17.2k
            if (pres == pres1) {
1599
16.8k
                *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
16.8k
                return;
1609
16.8k
            }
1610
16.8k
    }
1611
16.8k
}
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
23.0k
{
1670
23.0k
    int code = 0, i, len = 0, id = 0, end;
1671
23.0k
    char offset[21], offsets [(20*MAX_OBJSTM_OBJECTS) + 1];
1672
23.0k
    pdf_resource_t *pres;
1673
23.0k
    int options = DATA_STREAM_BINARY;
1674
1675
23.0k
    if (pdev->ObjStm_id == 0)
1676
0
        return 0;
1677
1678
23.0k
    pdev->WriteObjStms = false;
1679
1680
23.0k
    sflush(pdev->strm);
1681
23.0k
    sflush(pdev->ObjStm.strm);
1682
23.0k
    end = stell(pdev->ObjStm.strm);
1683
1684
23.0k
    if (pdev->CompressStreams)
1685
23.0k
        options |= DATA_STREAM_COMPRESS;
1686
1687
23.0k
    code = pdf_open_aside(pdev, resourceStream, pdev->ObjStm_id, &pres, false, options);
1688
23.0k
    if (code < 0) {
1689
0
        pdev->WriteObjStms = true;
1690
0
        return code;
1691
0
    }
1692
23.0k
    pdf_reserve_object_id(pdev, pres, pdev->ObjStm_id);
1693
1694
23.0k
    code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/ObjStm", 7);
1695
23.0k
    if (code < 0) {
1696
0
        pdf_close_aside(pdev);
1697
0
        pdev->WriteObjStms = true;
1698
0
        return code;
1699
0
    }
1700
23.0k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/N", pdev->NumObjStmObjects);
1701
23.0k
    if (code < 0) {
1702
0
        pdf_close_aside(pdev);
1703
0
        pdev->WriteObjStms = true;
1704
0
        return code;
1705
0
    }
1706
1707
23.0k
    memset(offsets, 0x00, (20*MAX_OBJSTM_OBJECTS) + 1);
1708
263k
    for (i=0;i < pdev->NumObjStmObjects;i++) {
1709
239k
        len = pdev->ObjStmOffsets[(i * 2) + 1];
1710
239k
        id = pdev->ObjStmOffsets[(i * 2)];
1711
239k
        gs_snprintf(offset, 21, "%ld %ld ", id, len);
1712
239k
        strcat(offsets, offset);
1713
239k
    }
1714
1715
23.0k
    code = cos_dict_put_c_key_int((cos_dict_t *)pres->object, "/First", strlen(offsets));
1716
23.0k
    if (code < 0) {
1717
0
        pdf_close_aside(pdev);
1718
0
        pdev->WriteObjStms = true;
1719
0
        return code;
1720
0
    }
1721
1722
23.0k
    stream_puts(pdev->strm, offsets);
1723
1724
23.0k
    gp_fseek(pdev->ObjStm.file, 0L, SEEK_SET);
1725
23.0k
    code = pdf_copy_data(pdev->strm, pdev->ObjStm.file, end, NULL);
1726
23.0k
    if (code < 0) {
1727
0
        pdf_close_aside(pdev);
1728
0
        pdev->WriteObjStms = true;
1729
0
        return code;
1730
0
    }
1731
23.0k
    code = pdf_close_aside(pdev);
1732
23.0k
    if (code < 0)
1733
0
        return code;
1734
23.0k
    code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
1735
23.0k
    if (code < 0) {
1736
0
        pdev->WriteObjStms = true;
1737
0
        return code;
1738
0
    }
1739
23.0k
    pdev->WriteObjStms = true;
1740
23.0k
    code = pdf_close_temp_file(pdev, &pdev->ObjStm, code);
1741
23.0k
    if (pdev->ObjStmOffsets != NULL) {
1742
23.0k
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1743
23.0k
        pdev->ObjStmOffsets = NULL;
1744
23.0k
    }
1745
23.0k
    pdev->NumObjStmObjects = 0;
1746
23.0k
    pdev->ObjStm_id = 0;
1747
1748
23.0k
    pdev->WriteObjStms = true;
1749
23.0k
    return code;
1750
23.0k
}
1751
1752
int NewObjStm(gx_device_pdf *pdev)
1753
23.0k
{
1754
23.0k
    int code;
1755
1756
23.0k
    pdev->ObjStm_id = pdf_obj_forward_ref(pdev);
1757
1758
23.0k
    code = pdf_open_temp_stream(pdev, &pdev->ObjStm);
1759
23.0k
    if (code < 0)
1760
0
        return code;
1761
1762
23.0k
    pdev->NumObjStmObjects = 0;
1763
23.0k
    if (pdev->ObjStmOffsets != NULL)
1764
0
        gs_free_object(pdev->pdf_memory->non_gc_memory, pdev->ObjStmOffsets, "NewObjStm");
1765
1766
23.0k
    pdev->ObjStmOffsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory->non_gc_memory, MAX_OBJSTM_OBJECTS * sizeof(gs_offset_t) * 2, "NewObjStm");
1767
23.0k
    if (pdev->ObjStmOffsets == NULL) {
1768
0
        code = gs_note_error(gs_error_VMerror);
1769
0
    } else
1770
23.0k
        memset(pdev->ObjStmOffsets, 0x00, MAX_OBJSTM_OBJECTS * sizeof(int) * 2);
1771
23.0k
    return code;
1772
23.0k
}
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
137k
{
1778
137k
    int code;
1779
1780
137k
    code = pdfwrite_pdf_open_document(pdev);
1781
137k
    if (code < 0)
1782
0
        return code;
1783
137k
    pdev->asides.save_strm = pdev->strm;
1784
137k
    pdev->strm = pdev->asides.strm;
1785
137k
    code = pdf_open_obj(pdev, id, type);
1786
137k
    return code;
1787
137k
}
1788
1789
static int is_stream_resource(pdf_resource_type_t type)
1790
505k
{
1791
505k
    if (type == resourceStream)
1792
0
        return true;
1793
505k
    if (type == resourceCharProc)
1794
864
        return true;
1795
504k
    if (type == resourcePattern)
1796
19.7k
        return true;
1797
484k
    if (type == resourceXObject)
1798
4.86k
        return true;
1799
479k
    return false;
1800
484k
}
1801
1802
int64_t
1803
pdf_open_separate(gx_device_pdf * pdev, int64_t id, pdf_resource_type_t type)
1804
490k
{
1805
490k
    int code;
1806
1807
490k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1808
250k
        code = pdfwrite_pdf_open_document(pdev);
1809
250k
        if (code < 0)
1810
0
            return code;
1811
250k
        pdev->asides.save_strm = pdev->strm;
1812
250k
        pdev->strm = pdev->asides.strm;
1813
250k
        code = pdf_open_obj(pdev, id, type);
1814
250k
    } else {
1815
239k
        if (pdev->ObjStm.strm != NULL && pdev->NumObjStmObjects >= MAX_OBJSTM_OBJECTS) {
1816
228
            code = FlushObjStm(pdev);
1817
228
            if (code < 0)
1818
0
                return code;
1819
228
        }
1820
239k
        if (!pdev->ObjStm.strm) {
1821
23.0k
            code = NewObjStm(pdev);
1822
23.0k
            if (code < 0)
1823
0
                return code;
1824
23.0k
        }
1825
239k
        pdev->ObjStm.save_strm = pdev->strm;
1826
239k
        pdev->strm = pdev->ObjStm.strm;
1827
239k
        code = pdf_open_obj(pdev, id, type);
1828
239k
        pdev->ObjStmOffsets[pdev->NumObjStmObjects * 2] = code;
1829
239k
        pdev->ObjStmOffsets[(pdev->NumObjStmObjects * 2) + 1] = pdf_stell(pdev);
1830
239k
    }
1831
490k
    return code;
1832
490k
}
1833
int64_t
1834
pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1835
187k
{
1836
187k
    return pdf_open_separate(pdev, 0L, type);
1837
187k
}
1838
1839
void
1840
pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, int64_t id)
1841
448k
{
1842
448k
    pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
1843
448k
    gs_snprintf(pres->rname, sizeof(pres->rname), "R%"PRId64, pres->object->id);
1844
448k
}
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
741k
{
1852
741k
    pdf_resource_t *pres;
1853
741k
    cos_object_t *object;
1854
1855
741k
    if (pst == NULL)
1856
0
        pst = &st_pdf_resource;
1857
741k
    pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
1858
741k
                           "pdf_alloc_aside(resource)");
1859
741k
    if (pres == 0)
1860
0
        return_error(gs_error_VMerror);
1861
741k
    object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
1862
741k
    if (object == 0)
1863
0
        return_error(gs_error_VMerror);
1864
741k
    memset(pres, 0, pst->ssize);
1865
741k
    pres->object = object;
1866
741k
    if (id < 0) {
1867
485k
        object->id = -1L;
1868
485k
        pres->rname[0] = 0;
1869
485k
    } else
1870
256k
        pdf_reserve_object_id(pdev, pres, id);
1871
741k
    pres->next = *plist;
1872
741k
    pres->rid = 0;
1873
741k
    *plist = pres;
1874
741k
    pres->prev = pdev->last_resource;
1875
741k
    pdev->last_resource = pres;
1876
741k
    pres->named = false;
1877
741k
    pres->global = false;
1878
741k
    pres->where_used = pdev->used_mask;
1879
741k
    *ppres = pres;
1880
741k
    return 0;
1881
741k
}
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
126k
{
1887
126k
    int64_t id = pdf_begin_separate(pdev, type);
1888
126k
    int code = 0;
1889
1890
126k
    if (id < 0)
1891
0
        return (int)id;
1892
126k
    code = pdf_alloc_aside(pdev, plist, pst, ppres, id);
1893
126k
    if (code < 0)
1894
0
        (void)pdf_end_separate(pdev, type);
1895
1896
126k
    return code;
1897
126k
}
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
126k
{
1904
126k
    int code;
1905
1906
126k
    if (rtype >= NUM_RESOURCE_TYPES)
1907
0
        rtype = resourceOther;
1908
1909
126k
    code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1910
126k
                               pdf_resource_type_structs[rtype], ppres, rtype);
1911
1912
126k
    if (code >= 0)
1913
126k
        (*ppres)->rid = rid;
1914
126k
    return code;
1915
126k
}
1916
int
1917
pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
1918
                   pdf_resource_t ** ppres)
1919
126k
{
1920
126k
    int code;
1921
1922
126k
    if (rtype >= NUM_RESOURCE_TYPES)
1923
0
        rtype = resourceOther;
1924
1925
126k
    code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
1926
1927
126k
    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
126k
    return code;
1934
126k
}
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
294k
{
1952
294k
    int code;
1953
1954
294k
    if (rtype >= NUM_RESOURCE_TYPES)
1955
0
        rtype = resourceOther;
1956
1957
294k
    code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
1958
294k
                               pdf_resource_type_structs[rtype], ppres, id);
1959
1960
294k
    if (code >= 0)
1961
294k
        (*ppres)->rid = rid;
1962
294k
    return code;
1963
294k
}
1964
1965
/* Get the object id of a resource. */
1966
int64_t
1967
pdf_resource_id(const pdf_resource_t *pres)
1968
3.03M
{
1969
3.03M
    return pres->object->id;
1970
3.03M
}
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
137k
{
1976
137k
    int code = pdf_end_obj(pdev, type);
1977
1978
137k
    pdev->strm = pdev->asides.save_strm;
1979
137k
    pdev->asides.save_strm = 0;
1980
137k
    return code;
1981
137k
}
1982
int
1983
pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
1984
490k
{
1985
490k
    int code = pdf_end_obj(pdev, type);
1986
1987
490k
    if (!pdev->WriteObjStms || is_stream_resource(type)) {
1988
250k
        pdev->strm = pdev->asides.save_strm;
1989
250k
        pdev->asides.save_strm = 0;
1990
250k
    } else {
1991
239k
        pdev->strm = pdev->ObjStm.save_strm;
1992
239k
        pdev->ObjStm.save_strm = 0;
1993
239k
        pdev->NumObjStmObjects++;
1994
239k
    }
1995
490k
    return code;
1996
490k
}
1997
int
1998
pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
1999
644
{
2000
644
    return pdf_end_separate(pdev, type);
2001
644
}
2002
2003
/* End a resource. */
2004
int
2005
pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
2006
644
{
2007
644
    return pdf_end_aside(pdev, type);
2008
644
}
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
669k
{
2018
669k
    int j, code = 0;
2019
2020
11.3M
    for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
2021
10.7M
        pdf_resource_t *pres = pdev->resources[rtype].chains[j];
2022
2023
10.9M
        for (; pres != 0; pres = pres->next)
2024
271k
            if ((!pres->named || pdev->ForOPDFRead)
2025
271k
                && pres->object && !pres->object->written) {
2026
44.9k
                    code = cos_write_object(pres->object, pdev, rtype);
2027
44.9k
            }
2028
10.7M
    }
2029
669k
    return code;
2030
669k
}
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
27.2k
{
2040
27.2k
    pdf_resource_t *pres = pdev->resources[rtype].chains[0];
2041
27.2k
    pdf_resource_t *pres1, *pres0 = pres, *pres2;
2042
2043
27.2k
    if (pres == NULL)
2044
0
        return;
2045
27.2k
    pres1 = pres->next;
2046
35.4k
    for (;;) {
2047
35.4k
        if (pres1 == NULL)
2048
27.2k
            break;
2049
8.22k
        pres2 = pres1->next;
2050
8.22k
        pres1->next = pres;
2051
8.22k
        pres = pres1;
2052
8.22k
        pres1 = pres2;
2053
8.22k
    }
2054
27.2k
    pres0->next = NULL;
2055
27.2k
    pdev->resources[rtype].chains[0] = pres;
2056
27.2k
}
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
350k
{
2065
350k
    int j;
2066
2067
5.95M
    for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2068
5.60M
        pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
2069
5.60M
        pdf_resource_t *pres;
2070
2071
5.77M
        while ((pres = *prev) != 0) {
2072
168k
            if (pres->named) { /* named, don't free */
2073
6
                prev = &pres->next;
2074
168k
            } else {
2075
168k
                if (pres->object) {
2076
168k
                    cos_free(pres->object, "pdf_free_resource_objects");
2077
168k
                    pres->object = 0;
2078
168k
                }
2079
168k
                *prev = pres->next;
2080
168k
            }
2081
168k
        }
2082
5.60M
    }
2083
350k
    return 0;
2084
350k
}
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
66.0k
{
2093
66.0k
    int i;
2094
2095
    /* Write any resource dictionaries. */
2096
2097
594k
    for (i = 0; i <= resourceFont; ++i) {
2098
528k
        stream *s = 0;
2099
528k
        int j;
2100
2101
528k
        if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
2102
66.0k
            continue;
2103
462k
        page->resource_ids[i] = 0;
2104
7.85M
        for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
2105
7.39M
            pdf_resource_t *pres = pdev->resources[i].chains[j];
2106
2107
7.66M
            for (; pres != 0; pres = pres->next) {
2108
267k
                if (pres->where_used & pdev->used_mask) {
2109
166k
                    int64_t id = pdf_resource_id(pres);
2110
2111
166k
                    if (id == -1L)
2112
14.7k
                        continue;
2113
152k
                    if (s == 0) {
2114
43.0k
                        page->resource_ids[i] = pdf_begin_separate(pdev, i);
2115
43.0k
                        pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
2116
43.0k
                        s = pdev->strm;
2117
43.0k
                        stream_puts(s, "<<");
2118
43.0k
                    }
2119
152k
                    pprints1(s, "/%s\n", pres->rname);
2120
152k
                    pprinti64d1(s, "%"PRId64" 0 R", id);
2121
152k
                    pdf_record_usage(pdev, id, pdev->next_page);
2122
152k
                    if (clear_usage)
2123
152k
                        pres->where_used -= pdev->used_mask;
2124
152k
                }
2125
267k
            }
2126
7.39M
        }
2127
462k
        if (s) {
2128
43.0k
            stream_puts(s, ">>\n");
2129
43.0k
            pdf_end_separate(pdev, i);
2130
43.0k
        }
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
462k
        if (i != resourceFont && i != resourceProperties)
2137
330k
            pdf_write_resource_objects(pdev, i);
2138
462k
    }
2139
66.0k
    page->procsets = pdev->procsets;
2140
66.0k
    return 0;
2141
66.0k
}
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
350k
{
2147
350k
    gs_offset_t r, left = count;
2148
350k
    byte buf[sbuf_size];
2149
2150
5.75M
    while (left > 0) {
2151
5.40M
        uint copy = min(left, sbuf_size);
2152
2153
5.40M
        r = gp_fread(buf, 1, copy, file);
2154
5.40M
        if (r < 1) {
2155
0
            return gs_note_error(gs_error_ioerror);
2156
0
        }
2157
5.40M
        if (ss)
2158
0
            s_arcfour_process_buffer(ss, buf, copy);
2159
5.40M
        stream_write(s, buf, copy);
2160
5.40M
        left -= copy;
2161
5.40M
    }
2162
350k
    return 0;
2163
350k
}
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
191k
{
2170
191k
    int64_t r, left = count;
2171
2172
2.96M
    while (left > 0) {
2173
2.76M
        byte buf[sbuf_size];
2174
2.76M
        int64_t copy = min(left, (int64_t)sbuf_size);
2175
2.76M
        int64_t end_pos = gp_ftell(file);
2176
2177
2.76M
        if (gp_fseek(file, position + count - left, SEEK_SET) != 0) {
2178
0
            return_error(gs_error_ioerror);
2179
0
        }
2180
2.76M
        r = gp_fread(buf, 1, copy, file);
2181
2.76M
        if (r < 1) {
2182
0
            return_error(gs_error_ioerror);
2183
0
        }
2184
2.76M
        if (gp_fseek(file, end_pos, SEEK_SET) != 0) {
2185
0
            return_error(gs_error_ioerror);
2186
0
        }
2187
2.76M
        stream_write(s, buf, copy);
2188
2.76M
        sflush(s);
2189
2.76M
        left -= copy;
2190
2.76M
    }
2191
191k
    return 0;
2192
191k
}
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
279k
{
2201
279k
    cos_dict_t *Page;
2202
2203
279k
    if (page_num < 1 || pdev->pages == NULL)
2204
0
        return 0;
2205
279k
    if (page_num >= pdev->num_pages) { /* Grow the pages array. */
2206
60
        uint new_num_pages;
2207
60
        pdf_page_t *new_pages;
2208
2209
        /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
2210
60
        if (page_num > (1LU << 31) - 11)
2211
0
            page_num = (1LU << 31) - 11;
2212
60
        new_num_pages = max(page_num + 10, pdev->num_pages << 1);
2213
2214
60
        new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
2215
60
                             "pdf_page_id(resize pages)");
2216
2217
60
        if (new_pages == 0)
2218
0
            return 0;
2219
60
        memset(&new_pages[pdev->num_pages], 0,
2220
60
               (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
2221
60
        pdev->pages = new_pages;
2222
60
        pdev->num_pages = new_num_pages;
2223
60
    }
2224
279k
    if ((Page = pdev->pages[page_num - 1].Page) == 0) {
2225
66.0k
        pdev->pages[page_num - 1].Page = Page = cos_dict_alloc(pdev, "pdf_page_id");
2226
66.0k
        if (Page == NULL) {
2227
0
            return 0;
2228
0
        }
2229
66.0k
        Page->id = pdf_obj_forward_ref(pdev);
2230
66.0k
    }
2231
279k
    return Page->id;
2232
279k
}
2233
2234
/* Get the page structure for the current page. */
2235
pdf_page_t *
2236
pdf_current_page(gx_device_pdf *pdev)
2237
6.53M
{
2238
6.53M
    return &pdev->pages[pdev->next_page];
2239
6.53M
}
2240
2241
/* Get the dictionary object for the current page. */
2242
cos_dict_t *
2243
pdf_current_page_dict(gx_device_pdf *pdev)
2244
14.8k
{
2245
14.8k
    if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
2246
0
        return 0;
2247
14.8k
    return pdev->pages[pdev->next_page].Page;
2248
14.8k
}
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
22.2M
{
2267
22.2M
    if (!is_in_page(pdev)) {
2268
73.5k
        int code;
2269
2270
73.5k
        if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
2271
0
            return_error(gs_error_VMerror);
2272
73.5k
        code = pdfwrite_pdf_open_document(pdev);
2273
73.5k
        if (code < 0)
2274
0
            return code;
2275
73.5k
    }
2276
    /* Note that context may be PDF_IN_NONE here. */
2277
22.2M
    return pdf_open_contents(pdev, context);
2278
22.2M
}
2279
2280
/*  Go to the unclipped stream context. */
2281
int
2282
pdf_unclip(gx_device_pdf * pdev)
2283
196k
{
2284
196k
    const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
2285
    /* When ResourcesBeforeUsage != 0, one sbstack element
2286
       appears from the page contents stream. */
2287
2288
196k
    if (pdev->sbstack_depth <= bottom) {
2289
171k
        int code = pdf_open_page(pdev, PDF_IN_STREAM);
2290
2291
171k
        if (code < 0)
2292
0
            return code;
2293
171k
    }
2294
196k
    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
196k
    if (pdev->vgstack_depth > pdev->vgstack_bottom) {
2301
106k
        int code = pdf_restore_viewer_state(pdev, pdev->strm);
2302
2303
106k
        if (code < 0)
2304
0
            return code;
2305
106k
        code = pdf_remember_clip_path(pdev, NULL);
2306
106k
        if (code < 0)
2307
0
            return code;
2308
106k
        pdev->clip_path_id = pdev->no_clip_path_id;
2309
106k
    }
2310
196k
    return 0;
2311
196k
}
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
50.0k
{
2323
50.0k
    int major = (int)(gs_revision / 1000);
2324
50.0k
    int minor = (int)(gs_revision - (major * 1000)) / 10;
2325
50.0k
    int patch = gs_revision % 10;
2326
2327
50.0k
    gs_snprintf(buf, PDF_MAX_PRODUCER, "(%s %d.%02d.%d)", gs_product, major, minor, patch);
2328
50.0k
}
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
247k
{
2335
247k
    stream *s = pdev->strm;
2336
2337
247k
    if (before)
2338
245k
        stream_puts(s, before);
2339
247k
    pprintg6(s, "%g %g %g %g %g %g ",
2340
247k
             pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
2341
247k
    if (after)
2342
247k
        stream_puts(s, after);
2343
247k
}
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
4.71M
{
2354
4.71M
    uint i;
2355
2356
24.8M
    for (i = 0; i < size; ++i) {
2357
20.0M
        uint c = nstr[i];
2358
20.0M
        char hex[4];
2359
2360
20.0M
        switch (c) {
2361
20.0M
            default:
2362
20.0M
                if (c >= 0x21 && c <= 0x7e) {
2363
20.0M
                    stream_putc(s, (byte)c);
2364
20.0M
                    break;
2365
20.0M
                }
2366
                /* falls through */
2367
6.16k
            case '#':
2368
6.18k
            case '%':
2369
6.20k
            case '(': case ')':
2370
6.20k
            case '<': case '>':
2371
6.22k
            case '[': case ']':
2372
6.24k
            case '{': case '}':
2373
6.27k
            case '/':
2374
6.27k
                gs_snprintf(hex, sizeof(hex), "#%02x", c);
2375
6.27k
                stream_puts(s, hex);
2376
6.27k
                break;
2377
0
            case 0:
2378
0
                stream_puts(s, "BnZr"); /* arbitrary */
2379
20.0M
        }
2380
20.0M
    }
2381
4.71M
    return 0;
2382
4.71M
}
2383
pdf_put_name_chars_proc_t
2384
pdf_put_name_chars_proc(const gx_device_pdf *pdev)
2385
4.71M
{
2386
4.71M
    return &pdf_put_name_chars_1_2;
2387
4.71M
}
2388
int
2389
pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
2390
4.70M
{
2391
4.70M
    return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
2392
4.70M
}
2393
int
2394
pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
2395
4.70M
{
2396
4.70M
    stream_putc(pdev->strm, '/');
2397
4.70M
    return pdf_put_name_chars(pdev, nstr, size);
2398
4.70M
}
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
225k
{
2452
225k
    if ((!pdev->KeyLength || pdev->WriteObjStms) || object_id == (gs_id)-1) {
2453
225k
        stream_write(pdev->strm, str, size);
2454
225k
        return 0;
2455
225k
    } else
2456
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2457
225k
}
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
82.0k
{
2462
82.0k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2463
82.0k
        int i, oct, width = 0;
2464
82.0k
        char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2465
2466
82.0k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2467
82.0k
            stream_write(pdev->strm, "\n", 1);
2468
82.0k
        stream_write(pdev->strm, "<", 1);
2469
82.0k
        width++;
2470
2.56M
        for (i = 1; i < size - 1; i++) {
2471
2.47M
            if (str[i] == '\\') {
2472
161k
                if (str[i + 1] >= '0' && str[i + 1] <= '9') {
2473
160k
                    oct = (str[i+1] - 0x30) * 64;
2474
160k
                    oct += (str[i+2] - 0x30) *8;
2475
160k
                    oct += str[i+3] - 0x30;
2476
160k
                    i+=3;
2477
160k
                } else {
2478
910
                    switch (str[++i]) {
2479
8
                        case 'b' :
2480
8
                            oct = 8;
2481
8
                            break;
2482
738
                        case 't' :
2483
738
                            oct = 9;
2484
738
                            break;
2485
42
                        case 'n' :
2486
42
                            oct = 10;
2487
42
                            break;
2488
35
                        case 'f' :
2489
35
                            oct = 12;
2490
35
                            break;
2491
12
                        case 'r' :
2492
12
                            oct = 13;
2493
12
                            break;
2494
75
                        default:
2495
75
                            oct = str[i];
2496
75
                            break;
2497
910
                    }
2498
910
                }
2499
161k
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2500
1.22k
                    stream_write(pdev->strm, "\n", 1);
2501
1.22k
                    width = 0;
2502
1.22k
                }
2503
161k
                stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
2504
161k
                stream_write(pdev->strm, &hex[oct & 0x0f], 1);
2505
161k
                width += 2;
2506
2.31M
            } else {
2507
2.31M
                if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
2508
261
                    stream_write(pdev->strm, "\n", 1);
2509
261
                    width = 0;
2510
261
                }
2511
2.31M
                stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
2512
2.31M
                stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
2513
2.31M
                width += 2;
2514
2.31M
            }
2515
2.47M
        }
2516
82.0k
        stream_write(pdev->strm, ">", 1);
2517
82.0k
        if (pdev->ForOPDFRead && pdev->ProduceDSC)
2518
82.0k
            stream_write(pdev->strm, "\n", 1);
2519
82.0k
        return 0;
2520
82.0k
    } else
2521
0
        return pdf_encrypt_encoded_string(pdev, str, size, object_id);
2522
82.0k
}
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
160k
{
2562
160k
    if (!pdev->KeyLength || object_id == (gs_id)-1) {
2563
160k
        if (pdev->ForOPDFRead && pdev->ProduceDSC) {
2564
693
            stream_putc(pdev->strm, (byte)'\n');
2565
693
            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
693
            } else {
2606
693
                stream_write(pdev->strm, vstr, size);
2607
693
            }
2608
160k
        } else {
2609
160k
            stream_write(pdev->strm, vstr, size);
2610
160k
        }
2611
160k
    } 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
160k
    return 0;
2627
160k
}
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
11.4M
{
2637
11.4M
    psdf_write_string(pdev->strm, str, size,
2638
11.4M
                      (pdev->binary_ok ? PRINT_BINARY_OK : 0));
2639
11.4M
    return 0;
2640
11.4M
}
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
6.99M
{
2646
6.99M
    if (size > 0 && vstr[0] == '/')
2647
4.00M
        return pdf_put_name(pdev, vstr + 1, size - 1);
2648
2.99M
    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.99M
    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.99M
    else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
2653
132k
        return pdf_put_composite(pdev, vstr, size, object_id);
2654
2.86M
    else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
2655
27.8k
        return pdf_put_composite(pdev, vstr, size, object_id);
2656
2.83M
    else if (size > 1 && vstr[0] == '(') {
2657
307k
        if (pdev->ForOPDFRead)
2658
82.0k
            return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
2659
225k
        else
2660
225k
            return pdf_put_encoded_string(pdev, vstr, size, object_id);
2661
307k
    }
2662
2.52M
    else if (size > 1 && vstr[0] == '<')
2663
0
        return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
2664
2.52M
    stream_write(pdev->strm, vstr, size);
2665
2.52M
    return 0;
2666
6.99M
}
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
600k
{
2674
600k
    const char *filter_name = 0;
2675
600k
    bool binary_ok = true;
2676
600k
    stream *fs = s;
2677
600k
    cos_dict_t *decode_parms = 0;
2678
600k
    int code;
2679
2680
2.23M
    for (; fs != 0; fs = fs->strm) {
2681
1.63M
        const stream_state *st = fs->state;
2682
1.63M
        const stream_template *templat = st->templat;
2683
2684
1.63M
#define TEMPLATE_IS(atemp)\
2685
9.16M
  (templat->process == (atemp).process)
2686
1.63M
        if (TEMPLATE_IS(s_A85E_template))
2687
284k
            binary_ok = false;
2688
1.34M
        else if (TEMPLATE_IS(s_CFE_template)) {
2689
126k
            cos_param_list_writer_t writer;
2690
126k
            stream_CF_state cfs;
2691
2692
126k
            decode_parms =
2693
126k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2694
126k
            if (decode_parms == 0)
2695
0
                return_error(gs_error_VMerror);
2696
126k
            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
126k
            cfs = *(const stream_CF_state *)st;
2702
126k
            if (cfs.EndOfBlock)
2703
782
                cfs.Rows = 0;
2704
126k
            CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
2705
126k
            filter_name = pfn->CCITTFaxDecode;
2706
1.22M
        } else if (TEMPLATE_IS(s_DCTE_template))
2707
10.3k
            filter_name = pfn->DCTDecode;
2708
1.20M
        else if (TEMPLATE_IS(s_zlibE_template))
2709
197k
            filter_name = pfn->FlateDecode;
2710
1.01M
        else if (TEMPLATE_IS(s_brotliE_template))
2711
0
            filter_name = pfn->BrotliDecode;
2712
1.01M
        else if (TEMPLATE_IS(s_LZWE_template))
2713
136k
            filter_name = pfn->LZWDecode;
2714
876k
        else if (TEMPLATE_IS(s_PNGPE_template)) {
2715
            /* This is a predictor for FlateDecode or LZWEncode. */
2716
19.1k
            const stream_PNGP_state *const ss =
2717
19.1k
                (const stream_PNGP_state *)st;
2718
2719
19.1k
            decode_parms =
2720
19.1k
                cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
2721
19.1k
            if (decode_parms == 0)
2722
0
                return_error(gs_error_VMerror);
2723
19.1k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
2724
19.1k
                                         ss->Predictor));
2725
19.1k
            CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
2726
19.1k
                                         ss->Columns));
2727
19.1k
            if (ss->Colors != 1)
2728
19.1k
                CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
2729
19.1k
                                             ss->Colors));
2730
19.1k
            if (ss->BitsPerComponent != 8)
2731
19.1k
                CHECK(cos_dict_put_c_key_int(decode_parms,
2732
19.1k
                                             "/BitsPerComponent",
2733
19.1k
                                             ss->BitsPerComponent));
2734
856k
        } else if (TEMPLATE_IS(s_RLE_template))
2735
0
            filter_name = pfn->RunLengthDecode;
2736
1.63M
#undef TEMPLATE_IS
2737
1.63M
    }
2738
600k
    if (filter_name) {
2739
470k
        if (binary_ok) {
2740
225k
            CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
2741
225k
            if (decode_parms)
2742
225k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2743
225k
                                                COS_OBJECT(decode_parms)));
2744
244k
        } else {
2745
244k
            cos_array_t *pca =
2746
244k
                cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
2747
2748
244k
            if (pca == 0)
2749
0
                return_error(gs_error_VMerror);
2750
244k
            CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
2751
244k
            CHECK(cos_array_add_c_string(pca, filter_name));
2752
244k
            CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
2753
244k
                                            COS_OBJECT(pca)));
2754
244k
            if (decode_parms) {
2755
125k
                pca = cos_array_alloc(pdev,
2756
125k
                                      "pdf_put_image_filters(DecodeParms)");
2757
125k
                if (pca == 0)
2758
0
                    return_error(gs_error_VMerror);
2759
125k
                CHECK(cos_array_add_c_string(pca, "null"));
2760
125k
                CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
2761
125k
                CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
2762
125k
                                                COS_OBJECT(pca)));
2763
125k
            }
2764
244k
        }
2765
470k
    } else if (!binary_ok)
2766
129k
        CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
2767
600k
    return 0;
2768
600k
}
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
290k
{
2774
290k
    const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
2775
176k
                    &s_LZWE_template : &s_zlibE_template);
2776
290k
    stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
2777
290k
                                     "pdf_write_function");
2778
2779
290k
    if (st == 0)
2780
0
        return_error(gs_error_VMerror);
2781
290k
    if (templat->set_defaults)
2782
290k
        templat->set_defaults(st);
2783
290k
    return psdf_encode_binary(pbw, templat, st);
2784
290k
}
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
320k
{
2817
320k
    stream *s = pdev->strm;
2818
320k
    int options = orig_options;
2819
320k
#define USE_ASCII85 1
2820
583k
#define USE_FLATE 2
2821
320k
#define USE_BROTLI 4
2822
320k
    static const char *const fnames[6] = {
2823
320k
        "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
2824
320k
        "/Filter[/ASCII85Decode/FlateDecode]", "/Filter/BrotliDecode",
2825
320k
        "/Filter[/ASCII85Decode/BrotliDecode]"
2826
320k
    };
2827
320k
    static const char *const fnames1_2[6] = {
2828
320k
        "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
2829
320k
        "/Filter[/ASCII85Decode/LZWDecode]", "/Filter/LZWDecode",
2830
320k
        "/Filter[/ASCII85Decode/LZWDecode]"
2831
320k
    };
2832
320k
    int filters = 0;
2833
320k
    int code;
2834
2835
320k
    if (options & DATA_STREAM_COMPRESS) {
2836
262k
        if (pdev->UseBrotli) {
2837
0
            filters |= USE_BROTLI;
2838
0
            options |= DATA_STREAM_BINARY;
2839
262k
        } else {
2840
262k
            filters |= USE_FLATE;
2841
262k
            options |= DATA_STREAM_BINARY;
2842
262k
        }
2843
262k
    }
2844
320k
    if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
2845
88.1k
        filters |= USE_ASCII85;
2846
320k
    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
320k
    if (options & DATA_STREAM_ENCRYPT) {
2860
22.8k
        code = pdf_begin_encrypt(pdev, &s, object_id);
2861
22.8k
        if (code < 0)
2862
0
            return code;
2863
22.8k
        pdev->strm = s;
2864
22.8k
        pdw->encrypted = true;
2865
22.8k
    } else
2866
298k
        pdw->encrypted = false;
2867
320k
    if (options & DATA_STREAM_BINARY) {
2868
262k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
2869
262k
        if (code < 0)
2870
0
            return code;
2871
262k
    } else {
2872
58.2k
        code = 0;
2873
58.2k
        pdw->binary.target = pdev->strm;
2874
58.2k
        pdw->binary.dev = (gx_device_psdf *)pdev;
2875
58.2k
        pdw->binary.strm = pdev->strm;
2876
58.2k
    }
2877
320k
    pdw->start = stell(s);
2878
320k
    if (filters & USE_BROTLI)
2879
0
        code = pdf_brotli_binary(pdev, &pdw->binary);
2880
320k
    else
2881
320k
        if (filters & USE_FLATE)
2882
262k
            code = pdf_flate_binary(pdev, &pdw->binary);
2883
320k
    return code;
2884
320k
#undef USE_ASCII85
2885
320k
#undef USE_FLATE
2886
320k
#undef USE_BROTLI
2887
320k
}
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
39.9k
{   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
39.9k
    pdw->pdev = pdev;  /* temporary for backward compatibility of pdf_end_data prototype. */
2897
39.9k
    pdw->binary.target = pdev->strm;
2898
39.9k
    pdw->binary.dev = (gx_device_psdf *)pdev;
2899
39.9k
    pdw->binary.strm = 0;   /* for GC in case of failure */
2900
39.9k
    code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
2901
39.9k
                options);
2902
39.9k
    if (object_id != 0)
2903
237
        pdf_reserve_object_id(pdev, pdw->pres, object_id);
2904
39.9k
    pdw->binary.strm = pdev->strm;
2905
39.9k
    return code;
2906
39.9k
}
2907
2908
/* End a data stream. */
2909
int
2910
pdf_end_data(pdf_data_writer_t *pdw)
2911
6.87k
{   int code;
2912
2913
6.87k
    code = pdf_close_aside(pdw->pdev);
2914
6.87k
    if (code < 0)
2915
0
        return code;
2916
6.87k
    code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
2917
6.87k
    if (code < 0)
2918
0
        return code;
2919
6.87k
    return 0;
2920
6.87k
}
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
12.9k
{
2929
12.9k
    if (pranges == NULL)
2930
12.9k
        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
38.4k
{
2967
38.4k
    gs_function_info_t info;
2968
38.4k
    cos_param_list_writer_t rlist;
2969
38.4k
    pdf_resource_t *pres;
2970
38.4k
    cos_object_t *pcfn;
2971
38.4k
    cos_dict_t *pcd;
2972
38.4k
    int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
2973
2974
38.4k
    if (code < 0) {
2975
0
        *ppres = 0;
2976
0
        return code;
2977
0
    }
2978
38.4k
    *ppres = pres;
2979
38.4k
    pcfn = pres->object;
2980
38.4k
    gs_function_get_info(pfn, &info);
2981
38.4k
    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
38.4k
    if (info.DataSource != 0) {
2994
29.1k
        psdf_binary_writer writer;
2995
29.1k
        stream *save = pdev->strm;
2996
29.1k
        cos_stream_t *pcos;
2997
29.1k
        stream *s;
2998
2999
29.1k
        cos_become(pcfn, cos_type_stream);
3000
29.1k
        pcos = (cos_stream_t *)pcfn;
3001
29.1k
        pcd = cos_stream_dict(pcos);
3002
29.1k
        s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
3003
29.1k
        if (s == 0)
3004
0
            return_error(gs_error_VMerror);
3005
29.1k
        pdev->strm = s;
3006
29.1k
        code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
3007
29.1k
        if (code >= 0 && info.data_size > 30  /* 30 is arbitrary */
3008
29.1k
            )
3009
28.3k
            code = pdf_flate_binary(pdev, &writer);
3010
29.1k
        if (code >= 0) {
3011
29.1k
            static const pdf_filter_names_t fnames = {
3012
29.1k
                PDF_FILTER_NAMES
3013
29.1k
            };
3014
3015
29.1k
            code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
3016
29.1k
        }
3017
29.1k
        if (code >= 0) {
3018
29.1k
            byte buf[100];    /* arbitrary */
3019
29.1k
            ulong pos;
3020
29.1k
            uint count;
3021
29.1k
            const byte *ptr;
3022
3023
437k
            for (pos = 0; pos < info.data_size; pos += count) {
3024
408k
                count = min(sizeof(buf), info.data_size - pos);
3025
408k
                data_source_access_only(info.DataSource, pos, count, buf,
3026
408k
                                        &ptr);
3027
408k
                stream_write(writer.strm, ptr, count);
3028
408k
            }
3029
29.1k
            code = psdf_end_binary(&writer);
3030
29.1k
            s_close_filters(&s, s->strm);
3031
29.1k
        }
3032
29.1k
        pdev->strm = save;
3033
29.1k
        if (code < 0)
3034
0
            return code;
3035
29.1k
    } else {
3036
9.28k
        cos_become(pcfn, cos_type_dict);
3037
9.28k
        pcd = (cos_dict_t *)pcfn;
3038
9.28k
    }
3039
38.4k
    if (info.Functions != 0) {
3040
1.72k
        cos_array_t *functions =
3041
1.72k
            cos_array_alloc(pdev, "pdf_function(Functions)");
3042
1.72k
        cos_value_t v;
3043
3044
1.72k
        if (functions == 0)
3045
0
            return_error(gs_error_VMerror);
3046
1.72k
        if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
3047
1.72k
            (code = cos_dict_put_c_key(pcd, "/Functions",
3048
1.72k
                                       COS_OBJECT_VALUE(&v, functions))) < 0
3049
1.72k
            ) {
3050
0
            COS_FREE(functions, "pdf_function(Functions)");
3051
0
            return code;
3052
0
        }
3053
1.72k
    }
3054
38.4k
    code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
3055
38.4k
    if (code < 0)
3056
0
        return code;
3057
38.4k
    return gs_function_get_params(pfn, (gs_param_list *)&rlist);
3058
38.4k
}
3059
static int
3060
functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
3061
31.6k
{
3062
31.6k
    return true;
3063
31.6k
}
3064
int
3065
pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
3066
38.4k
{
3067
38.4k
    pdf_resource_t *pres;
3068
38.4k
    int code = pdf_function_aux(pdev, pfn, &pres);
3069
3070
38.4k
    if (code < 0)
3071
0
        return code;
3072
38.4k
    if (pres->object->md5_valid)
3073
0
        pres->object->md5_valid = 0;
3074
3075
38.4k
    code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
3076
38.4k
    if (code < 0)
3077
0
        return code;
3078
38.4k
    pres->where_used |= pdev->used_mask;
3079
38.4k
    COS_OBJECT_VALUE(pvalue, pres->object);
3080
38.4k
    return 0;
3081
38.4k
}
3082
static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
3083
                               const gs_function_info_t *pinfo)
3084
1.72k
{
3085
1.72k
    int i, code = 0;
3086
1.72k
    cos_value_t v;
3087
3088
7.80k
    for (i = 0; i < pinfo->num_Functions; ++i) {
3089
6.08k
        if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
3090
6.08k
            (code = cos_array_add(pca, &v)) < 0
3091
6.08k
            ) {
3092
0
            break;
3093
0
        }
3094
6.08k
    }
3095
1.72k
    return code;
3096
1.72k
}
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
19.3k
{
3102
19.3k
    cos_value_t value;
3103
19.3k
    int code = pdf_function(pdev, pfn, &value);
3104
3105
19.3k
    if (code < 0)
3106
0
        return code;
3107
19.3k
    *pid = value.contents.object->id;
3108
19.3k
    return 0;
3109
19.3k
}
3110
3111
int
3112
free_function_refs(gx_device_pdf *pdev, cos_object_t *pco)
3113
6.79k
{
3114
6.79k
    char key[] = "/Functions";
3115
6.79k
    cos_value_t *v, v2;
3116
3117
6.79k
    if (cos_type(pco) == cos_type_dict) {
3118
2.31k
        v = (cos_value_t *)cos_dict_find((const cos_dict_t *)pco, (const byte *)key, strlen(key));
3119
2.31k
        if (v && v->value_type == COS_VALUE_OBJECT) {
3120
543
            if (cos_type(v->contents.object) == cos_type_array){
3121
543
                int code=0;
3122
2.90k
                while (code == 0) {
3123
2.36k
                    code = cos_array_unadd((cos_array_t *)v->contents.object, &v2);
3124
2.36k
                }
3125
543
            }
3126
543
        }
3127
2.31k
    }
3128
6.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
6.79k
    return 0;
3145
6.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
33.1k
{
3151
33.1k
    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
33.1k
    int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3159
33.1k
    int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3160
3161
33.1k
    pprintd4(s, "/FontBBox[%d %d %d %d]",
3162
33.1k
             pbox->p.x, pbox->p.y, x, y);
3163
33.1k
    return 0;
3164
33.1k
}
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.63k
{
3170
3.63k
    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.63k
    float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
3178
3.63k
    float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
3179
3180
3.63k
    pprintg4(s, "/FontBBox[%g %g %g %g]",
3181
3.63k
             pbox->p.x, pbox->p.y, x, y);
3182
3.63k
    return 0;
3183
3.63k
}