Coverage Report

Created: 2025-06-24 07:01

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