Coverage Report

Created: 2025-06-10 07:27

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